altoslib/altosuilib: Validate rom image is for target device
authorKeith Packard <keithp@keithp.com>
Sat, 13 Jan 2018 06:27:41 +0000 (22:27 -0800)
committerKeith Packard <keithp@keithp.com>
Sat, 13 Jan 2018 06:27:41 +0000 (22:27 -0800)
This should avoid mis-programming devices with incorrect firmware.

Signed-off-by: Keith Packard <keithp@keithp.com>
altoslib/AltosFlash.java
altoslib/AltosHexfile.java
altoslib/AltosProgrammer.java
altoslib/AltosRomconfig.java
altoslib/AltosSelfFlash.java
altoslib/AltosUsbId.java [new file with mode: 0644]
altoslib/Makefile.am
altosuilib/AltosFlashUI.java

index c8db1f778ab32ff2421f68d7fbac0de9cf208705..9bf0da2526e8e9339f1ca496836d7e829f83c454 100644 (file)
@@ -254,7 +254,7 @@ public class AltosFlash extends AltosProgrammer {
                        clock_init();
 
                        int remain = image.data.length;
-                       int flash_addr = image.address;
+                       int flash_addr = (int) image.address;
                        int image_start = 0;
 
                        action("start", 0);
@@ -295,7 +295,7 @@ public class AltosFlash extends AltosProgrammer {
                        if (!aborted) {
                                action("done", 100);
                                if (debug != null) {
-                                       debug.set_pc(image.address);
+                                       debug.set_pc((int) image.address);
                                        debug.resume();
                                }
                        }
@@ -331,12 +331,16 @@ public class AltosFlash extends AltosProgrammer {
                rom_config = romconfig;
        }
 
-       public AltosRomconfig romconfig() throws InterruptedException {
+       public AltosRomconfig target_romconfig() throws InterruptedException {
                if (!check_rom_config())
                        return null;
                return rom_config;
        }
 
+       public AltosRomconfig image_romconfig() {
+               return new AltosRomconfig(image);
+       }
+
        public AltosFlash(File file, AltosLink link, AltosFlashListener listener)
                throws IOException, FileNotFoundException, InterruptedException {
                this.file = file;
index 7ab121ad7a7e2f9b9a287a29ccc654930c70a51a..6aa98383e9fc38d92d93b928a97d4990c6fc8258 100644 (file)
@@ -46,7 +46,7 @@ class HexFileInputStream extends PushbackInputStream {
 }
 
 class HexRecord implements Comparable<Object> {
-       public int      address;
+       public long     address;
        public int      type;
        public byte     checksum;
        public byte[]   data;
@@ -110,7 +110,14 @@ class HexRecord implements Comparable<Object> {
 
        public int compareTo(Object other) {
                HexRecord       o = (HexRecord) other;
-               return address - o.address;
+
+               long diff = address - o.address;
+
+               if (diff > 0)
+                       return 1;
+               if (diff < 0)
+                       return -1;
+               return 0;
        }
 
        public String toString() {
@@ -119,8 +126,8 @@ class HexRecord implements Comparable<Object> {
 
        public HexRecord(HexFileInputStream input) throws IOException, EOFException {
                read_state      state = read_state.marker;
-               int             nhexbytes = 0;
-               int             hex = 0;
+               long            nhexbytes = 0;
+               long            hex = 0;
                int             ndata = 0;
                byte            got_checksum;
 
@@ -154,7 +161,7 @@ class HexRecord implements Comparable<Object> {
 
                                switch (state) {
                                case length:
-                                       data = new byte[hex];
+                                       data = new byte[(int) hex];
                                        state = read_state.address;
                                        nhexbytes = 4;
                                        break;
@@ -164,7 +171,7 @@ class HexRecord implements Comparable<Object> {
                                        nhexbytes = 2;
                                        break;
                                case type:
-                                       type = hex;
+                                       type = (int) hex;
                                        if (data.length > 0)
                                                state = read_state.data;
                                        else
@@ -211,12 +218,21 @@ class HexRecord implements Comparable<Object> {
 }
 
 public class AltosHexfile {
-       public int              address;
+       public long             address;
+       public long             max_address;
        public byte[]           data;
        LinkedList<AltosHexsym> symlist = new LinkedList<AltosHexsym>();
 
-       public byte get_byte(int a) {
-               return data[a - address];
+       public byte get_byte(long a) {
+               return data[(int) (a - address)];
+       }
+
+       public int get_u8(long a) {
+               return ((int) get_byte(a)) & 0xff;
+       }
+
+       public int get_u16(long a) {
+               return get_u8(a) | (get_u8(a+1) << 8);
        }
 
        /* CC1111-based products have the romconfig stuff located
@@ -237,6 +253,15 @@ public class AltosHexfile {
                new AltosHexsym("ao_usb_descriptors", ao_usb_descriptors_addr)
        };
 
+       static final int AO_USB_DESC_DEVICE             = 1;
+       static final int AO_USB_DESC_STRING             = 3;
+
+       static final int AO_ROMCONFIG_VERSION_INDEX     = 0;
+       static final int AO_ROMCONFIG_CHECK_INDEX       = 1;
+       static final int AO_SERIAL_NUMBER_INDEX         = 2;
+       static final int AO_RADIO_CAL_INDEX             = 3;
+       static final int AO_USB_DESCRIPTORS_INDEX       = 4;
+
        private void add_cc_symbols() {
                for (int i = 0; i < cc_symbols.length; i++)
                        symlist.add(cc_symbols[i]);
@@ -262,6 +287,92 @@ public class AltosHexfile {
                return null;
        }
 
+       private long find_usb_descriptors() {
+               AltosHexsym     usb_descriptors = lookup_symbol("ao_usb_descriptors");
+               long            a;
+
+               if (usb_descriptors == null)
+                       return -1;
+
+               /* Walk the descriptors looking for the device */
+               a = usb_descriptors.address;
+               while (get_u8(a+1) != AO_USB_DESC_DEVICE) {
+                       int delta = get_u8(a);
+                       a += delta;
+                       if (delta == 0 || a >= max_address)
+                               return -1;
+               }
+               return a;
+       }
+
+       public AltosUsbId find_usb_id() {
+               long a = find_usb_descriptors();
+
+               if (a == -1)
+                       return null;
+
+               /* Walk the descriptors looking for the device */
+               while (get_u8(a+1) != AO_USB_DESC_DEVICE) {
+                       int delta = get_u8(a);
+                       a += delta;
+                       if (delta == 0 || a >= max_address)
+                               return null;
+               }
+
+               return new AltosUsbId(get_u16(a + 8),
+                                     get_u16(a + 10));
+       }
+
+       public String find_usb_product() {
+               long            a = find_usb_descriptors();
+               int             num_strings;
+               int             product_string;
+
+               if (a == -1)
+                       return null;
+
+               product_string = get_u8(a+15);
+
+               /* Walk the descriptors looking for the device */
+               num_strings = 0;
+               for (;;) {
+                       if (get_u8(a+1) == AO_USB_DESC_STRING) {
+                               ++num_strings;
+                               if (num_strings == product_string + 1)
+                                       break;
+                       }
+
+                       int delta = get_u8(a);
+                       a += delta;
+                       if (delta == 0 || a >= max_address)
+                               return null;
+               }
+
+               int product_len = get_u8(a);
+
+               System.out.printf("Product is at %x length %d\n", a, product_len);
+
+               for (int i = 0; i < product_len; i++)
+                       System.out.printf(" %2d: %02x\n", i, get_u8(a+i));
+
+               if (product_len <= 0)
+                       return null;
+
+               String product = "";
+
+               for (int i = 0; i < product_len - 2; i += 2) {
+                       int     c = get_u16(a + 2 + i);
+
+                       System.out.printf("character %x\n", c);
+
+                       product += Character.toString((char) c);
+               }
+
+               System.out.printf("product %s\n", product);
+
+               return product;
+       }
+
        private String make_string(byte[] data, int start, int length) {
                String s = "";
                for (int i = 0; i < length; i++)
@@ -269,9 +380,10 @@ public class AltosHexfile {
                return s;
        }
 
-       public AltosHexfile(byte[] bytes, int offset) {
+       public AltosHexfile(byte[] bytes, long offset) {
                data = bytes;
                address = offset;
+               max_address = address + bytes.length;
        }
 
        public AltosHexfile(FileInputStream file) throws IOException {
@@ -335,7 +447,8 @@ public class AltosHexfile {
                        throw new IOException("hex file too large");
 
                data = new byte[(int) (bound - base)];
-               address = (int) base;
+               address = base;
+               max_address = bound;
                Arrays.fill(data, (byte) 0xff);
 
                /* Paint the records into the new array */
@@ -366,4 +479,4 @@ public class AltosHexfile {
                        }
                }
        }
-}
\ No newline at end of file
+}
index 0a828a32917535c8a913037483a9af99a08d7765..e4f575789261e7d4108e3338bb9e347733cdb2b9 100644 (file)
@@ -28,7 +28,9 @@ public abstract class AltosProgrammer {
 
        abstract public void abort();
 
-       abstract public AltosRomconfig romconfig() throws InterruptedException;
+       abstract public AltosRomconfig target_romconfig() throws InterruptedException;
+
+       abstract public AltosRomconfig image_romconfig();
 
        abstract public void set_romconfig(AltosRomconfig config);
-}
\ No newline at end of file
+}
index 46ee2b6e66bc225ce8deddd488d5a3eb34a4852b..1fbb411540c534601631735949c7cddc55b3489b 100644 (file)
@@ -26,20 +26,31 @@ public class AltosRomconfig {
        public int      check;
        public int      serial_number;
        public int      radio_calibration;
+       public AltosUsbId       usb_id;
+       public String           usb_product;
 
-       static private int find_offset(AltosHexfile hexfile, String name, int len) throws AltosNoSymbol {
+       static private long find_address(AltosHexfile hexfile, String name, int len) throws AltosNoSymbol {
                AltosHexsym symbol = hexfile.lookup_symbol(name);
-               if (symbol == null)
-                       throw new AltosNoSymbol(name);
-               int offset = (int) symbol.address - hexfile.address;
-               if (offset < 0 || hexfile.data.length < offset + len)
+               if (symbol == null) {
+                       System.out.printf("no symbol %s\n", name);
                        throw new AltosNoSymbol(name);
-               return offset;
+               }
+               if (hexfile.address <= symbol.address && symbol.address + len < hexfile.max_address) {
+                       System.out.printf("%s: %x\n", name, symbol.address);
+                       return symbol.address;
+               }
+               System.out.printf("invalid symbol addr %x range is %x - %x\n",
+                                 symbol.address, hexfile.address, hexfile.max_address);
+               throw new AltosNoSymbol(name);
+       }
+
+       static private int find_offset(AltosHexfile hexfile, String name, int len) throws AltosNoSymbol {
+               return (int) (find_address(hexfile, name, len) - hexfile.address);
        }
 
        static int get_int(AltosHexfile hexfile, String name, int len) throws AltosNoSymbol {
                byte[] bytes = hexfile.data;
-               int start = find_offset(hexfile, name, len);
+               int start = (int) find_offset(hexfile, name, len);
 
                int     v = 0;
                int     o = 0;
@@ -112,13 +123,17 @@ public class AltosRomconfig {
 
        public AltosRomconfig(AltosHexfile hexfile) {
                try {
+                       System.out.printf("Attempting symbols\n");
                        version = get_int(hexfile, ao_romconfig_version, 2);
+                       System.out.printf("version %d\n", version);
                        check = get_int(hexfile, ao_romconfig_check, 2);
+                       System.out.printf("check %d\n", check);
                        if (check == (~version & 0xffff)) {
                                switch (version) {
                                case 2:
                                case 1:
                                        serial_number = get_int(hexfile, ao_serial_number, 2);
+                                       System.out.printf("serial %d\n", serial_number);
                                        try {
                                                radio_calibration = get_int(hexfile, ao_radio_cal, 4);
                                        } catch (AltosNoSymbol missing) {
@@ -128,6 +143,19 @@ public class AltosRomconfig {
                                        break;
                                }
                        }
+                       System.out.printf("attempting usbid\n");
+                       usb_id = hexfile.find_usb_id();
+                       if (usb_id == null)
+                               System.out.printf("No usb id\n");
+                       else
+                               System.out.printf("usb id: %04x:%04x\n",
+                                                 usb_id.vid, usb_id.pid);
+                       usb_product = hexfile.find_usb_product();
+                       if (usb_product == null)
+                               System.out.printf("No usb product\n");
+                       else
+                               System.out.printf("usb product: %s\n", usb_product);
+
                } catch (AltosNoSymbol missing) {
                        valid = false;
                }
@@ -137,9 +165,16 @@ public class AltosRomconfig {
                ao_romconfig_version,
                ao_romconfig_check,
                ao_serial_number,
-               ao_radio_cal
+               ao_radio_cal,
+               ao_usb_descriptors,
        };
 
+       private static int fetch_len(String name) {
+               if (name.equals(ao_usb_descriptors))
+                       return 256;
+               return 2;
+       }
+
        private final static String[] required_names = {
                ao_romconfig_version,
                ao_romconfig_check,
@@ -153,13 +188,16 @@ public class AltosRomconfig {
                return false;
        }
 
-       public static int fetch_base(AltosHexfile hexfile) throws AltosNoSymbol {
-               int     base = 0x7fffffff;
+       public static long fetch_base(AltosHexfile hexfile) throws AltosNoSymbol {
+               long    base = 0xffffffffL;
                for (String name : fetch_names) {
                        try {
-                               int     addr = find_offset(hexfile, name, 2) + hexfile.address;
+                               int     len = fetch_len(name);
+                               long    addr = find_address(hexfile, name, len);
+
                                if (addr < base)
                                        base = addr;
+                               System.out.printf("symbol %s at %x base %x\n", name, addr, base);
                        } catch (AltosNoSymbol ns) {
                                if (name_required(name))
                                        throw (ns);
@@ -168,19 +206,22 @@ public class AltosRomconfig {
                return base;
        }
 
-       public static int fetch_bounds(AltosHexfile hexfile) throws AltosNoSymbol {
-               int     bounds = 0;
+       public static long fetch_bounds(AltosHexfile hexfile) throws AltosNoSymbol {
+               long    bounds = 0;
                for (String name : fetch_names) {
                        try {
-                               int     addr = find_offset(hexfile, name, 2) + hexfile.address;
+                               int     len = fetch_len(name);
+                               long    addr = find_address(hexfile, name, len) + len;
                                if (addr > bounds)
                                        bounds = addr;
+                               System.out.printf("symbol %s at %x bounds %x\n", name, addr, bounds);
                        } catch (AltosNoSymbol ns) {
                                if (name_required(name))
                                        throw (ns);
                        }
                }
-               return bounds + 2;
+
+               return bounds;
        }
 
        public void write (AltosHexfile hexfile) throws IOException {
index 537821729fc9a03adb4885d4d38337f2cede870b..c7ea147f97d70bd667b165e093ba6afef0652944 100644 (file)
@@ -45,18 +45,33 @@ public class AltosSelfFlash extends AltosProgrammer {
                int b;
                byte[]  data = new byte[len];
 
+               System.out.printf("read_memory %x %d\n", addr, len);
                for (int offset = 0; offset < len; offset += 0x100) {
                        link.printf("R %x\n", addr + offset);
                        byte[]  reply = link.get_binary_reply(5000, 0x100);
 
                        if (reply == null)
                                throw new IOException("Read device memory timeout");
-                       for (b = 0; b < len; b++)
+                       for (b = 0; b < 0x100 && b + offset < len; b++)
                                data[b+offset] = reply[b];
                }
                return data;
        }
 
+       AltosHexfile read_hexfile(long addr, int len) throws InterruptedException {
+               try {
+                       byte[] mem = read_memory(addr, len);
+
+                       AltosHexfile    hexfile = new AltosHexfile(mem, addr);
+
+                       if (image != null)
+                               hexfile.add_symbols(image);
+                       return hexfile;
+               } catch (IOException ie) {
+                       return null;
+               }
+       }
+
        void write_memory(long addr, byte[] data, int start, int len) {
                int b;
                link.printf("W %x\n", addr);
@@ -143,18 +158,14 @@ public class AltosSelfFlash extends AltosProgrammer {
 
        private AltosHexfile get_rom() throws InterruptedException {
                try {
-                       int base = AltosRomconfig.fetch_base(image);
-                       int bounds = AltosRomconfig.fetch_bounds(image);
-                       byte[] data = read_memory(base, bounds - base);
-                       AltosHexfile hexfile = new AltosHexfile(data, base);
-                       hexfile.add_symbols(image);
-                       return hexfile;
-               } catch (AltosNoSymbol none) {
-                       return null;
-               } catch (IOException ie) {
+                       long base = AltosRomconfig.fetch_base(image);
+                       long bounds = AltosRomconfig.fetch_bounds(image);
+
+                       System.out.printf("rom base %x bounds %x\n", base, bounds);
+                       return read_hexfile(base, (int) (bounds - base));
+               } catch (AltosNoSymbol ns) {
                        return null;
                }
-
        }
 
        public boolean check_rom_config() throws InterruptedException {
@@ -173,12 +184,16 @@ public class AltosSelfFlash extends AltosProgrammer {
                rom_config = romconfig;
        }
 
-       public AltosRomconfig romconfig() throws InterruptedException {
+       public AltosRomconfig target_romconfig() throws InterruptedException {
                if (!check_rom_config())
                        return null;
                return rom_config;
        }
 
+       public AltosRomconfig image_romconfig() {
+               return new AltosRomconfig(image);
+       }
+
        public AltosSelfFlash(File file, AltosLink link, AltosFlashListener listener)
                throws IOException, FileNotFoundException, InterruptedException {
                this.file = file;
@@ -187,4 +202,4 @@ public class AltosSelfFlash extends AltosProgrammer {
                input = new FileInputStream(file);
                image = new AltosHexfile(input);
        }
-}
\ No newline at end of file
+}
diff --git a/altoslib/AltosUsbId.java b/altoslib/AltosUsbId.java
new file mode 100644 (file)
index 0000000..e379430
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+package org.altusmetrum.altoslib_12;
+
+public class AltosUsbId {
+       public int      vid;
+       public int      pid;
+
+
+       public AltosUsbId(int vid, int pid) {
+               this.vid = vid;
+               this.pid = pid;
+       }
+}
index 2a1cb8e4763810847382f799f32f5a17486a2144..7c5d767d7916d34ac1fad9f32e7c1560c67c9a0f 100644 (file)
@@ -99,6 +99,7 @@ altoslib_JAVA = \
        AltosRomconfig.java \
        AltosSavedState.java \
        AltosSelfFlash.java \
+       AltosUsbId.java \
        AltosSensorMM.java \
        AltosSensorEMini.java \
        AltosSensorTM.java \
index ca089ca845a6a4dd1bd068df1cee8bf67cc18a99..c717e47c1840a30cc07c3d8e50830e9896a2f6fa 100644 (file)
@@ -276,8 +276,37 @@ public class AltosFlashUI
                return true;
        }
 
-       boolean update_rom_config_info(AltosRomconfig existing_config) {
+       boolean rom_config_matches (AltosRomconfig a, AltosRomconfig b) {
+               if (a.usb_id != null && b.usb_id != null &&
+                   (a.usb_id.vid != b.usb_id.vid ||
+                    a.usb_id.pid != b.usb_id.pid))
+                       return false;
+
+               if (a.usb_product != null && b.usb_product != null &&
+                   !a.usb_product.equals(b.usb_product))
+                       return false;
+
+               return true;
+       }
+
+       boolean update_rom_config_info(AltosRomconfig existing_config, AltosRomconfig image_config) {
                AltosRomconfig  new_config;
+
+               if (!rom_config_matches(existing_config, image_config)) {
+                       int ret = JOptionPane.showConfirmDialog(this,
+                                                               String.format("Device is %04x:%04x %s\nImage is %04x:%04x %s\nFlash anyways?",
+                                                                             existing_config.usb_id.vid,
+                                                                             existing_config.usb_id.pid,
+                                                                             existing_config.usb_product,
+                                                                             image_config.usb_id.vid,
+                                                                             image_config.usb_id.pid,
+                                                                             image_config.usb_product),
+                                                               "Image doesn't match Device",
+                                                               JOptionPane.YES_NO_OPTION);
+                       if (ret != JOptionPane.YES_OPTION)
+                               return false;
+               }
+
                new_config = AltosRomconfigUI.show(frame, existing_config);
                if (new_config == null)
                        return false;
@@ -335,13 +364,15 @@ public class AltosFlashUI
                                else
                                        programmer = new AltosSelfFlash(ui.file, link, this);
 
-                               final AltosRomconfig    current_config = programmer.romconfig();
+                               final AltosRomconfig    current_config = programmer.target_romconfig();
+
+                               final AltosRomconfig    image_config = programmer.image_romconfig();
 
                                final Semaphore await_rom_config = new Semaphore(0);
                                SwingUtilities.invokeLater(new Runnable() {
                                                public void run() {
                                                        ui.programmer = programmer;
-                                                       ui.update_rom_config_info(current_config);
+                                                       ui.update_rom_config_info(current_config, image_config);
                                                        await_rom_config.release();
                                                }
                                        });