|
7 | 7 |
|
8 | 8 | #define PARTITION_NUM_TO_STRING(P) (P == RPI_EEPROM_AB_PARTITION_A ? "A" : (P == RPI_EEPROM_AB_PARTITION_B ? "B" : "Unknown")) |
9 | 9 |
|
| 10 | +#define DT_MIN_BOOT_VER_PATH "/proc/device-tree/chosen/rpi-min-boot-ver" |
| 11 | + |
10 | 12 | /* Print the usage message */ |
11 | 13 | static void usage(const char *progname, int exit_status) { |
12 | 14 | fprintf(stderr, |
@@ -69,6 +71,86 @@ static int hex2bin(const char *hexstr, uint8_t *bin, size_t bin_len) { |
69 | 71 | return 0; |
70 | 72 | } |
71 | 73 |
|
| 74 | +/* Read the device's minimum required bootloader version from the device tree */ |
| 75 | +static int read_device_min_boot_ver(uint32_t *min_boot_ver) { |
| 76 | + FILE *f; |
| 77 | + uint8_t buf[sizeof(uint32_t)]; |
| 78 | + size_t n; |
| 79 | + |
| 80 | + *min_boot_ver = 0; |
| 81 | + |
| 82 | + f = fopen(DT_MIN_BOOT_VER_PATH, "rb"); |
| 83 | + if (!f) { |
| 84 | + return -1; |
| 85 | + } |
| 86 | + |
| 87 | + n = fread(buf, 1, sizeof(buf), f); |
| 88 | + fclose(f); |
| 89 | + |
| 90 | + if (n != sizeof(uint32_t)) { |
| 91 | + return -1; |
| 92 | + } |
| 93 | + |
| 94 | + *min_boot_ver = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; |
| 95 | + return 0; |
| 96 | +} |
| 97 | + |
| 98 | +/* Scan the update image for the MFG_VER value */ |
| 99 | +static int read_image_boot_ver(const uint8_t *data, size_t data_len, uint32_t *image_boot_ver) { |
| 100 | + static const char marker[] = "MFG_VER: "; |
| 101 | + const size_t marker_len = sizeof(marker) - 1; |
| 102 | + const uint8_t *match; |
| 103 | + |
| 104 | + *image_boot_ver = 0; |
| 105 | + |
| 106 | + match = memmem(data, data_len, marker, marker_len); |
| 107 | + if (!match) { |
| 108 | + return -1; |
| 109 | + } |
| 110 | + |
| 111 | + uint32_t value = 0; |
| 112 | + size_t digits = 0; |
| 113 | + for (const uint8_t *p = match + marker_len; p < data + data_len; p++) { |
| 114 | + char c = (char) *p; |
| 115 | + if (c < '0' || c > '9') { |
| 116 | + break; |
| 117 | + } |
| 118 | + value = (value * 10) + (uint32_t) (c - '0'); |
| 119 | + digits++; |
| 120 | + } |
| 121 | + if (digits == 0) { |
| 122 | + return -1; |
| 123 | + } |
| 124 | + *image_boot_ver = value; |
| 125 | + return 0; |
| 126 | +} |
| 127 | + |
| 128 | +/* Verify that the update image meets the board's minimum |
| 129 | + * required bootloader version */ |
| 130 | +static int check_min_boot_ver(const uint8_t *update_data, size_t update_len) { |
| 131 | + uint32_t device_min_boot_ver = 0; |
| 132 | + uint32_t image_boot_ver = 0; |
| 133 | + |
| 134 | + if (read_device_min_boot_ver(&device_min_boot_ver) != 0) { |
| 135 | + // Failed to read device minimum bootloader version from device tree |
| 136 | + // Default to 0 |
| 137 | + device_min_boot_ver = 0; |
| 138 | + } |
| 139 | + |
| 140 | + if (read_image_boot_ver(update_data, update_len, &image_boot_ver) != 0) { |
| 141 | + // Failed to find MFG_VER in update image |
| 142 | + // Default to 0 |
| 143 | + image_boot_ver = 0; |
| 144 | + } |
| 145 | + |
| 146 | + if (device_min_boot_ver > image_boot_ver) { |
| 147 | + printf("EEPROM image version %u does not meet the minimum bootloader version %u required by this board.\n", |
| 148 | + image_boot_ver, device_min_boot_ver); |
| 149 | + return -1; |
| 150 | + } |
| 151 | + return 0; |
| 152 | +} |
| 153 | + |
72 | 154 | static int cmd_write_eeprom_update(int argc, char *argv[]) { |
73 | 155 | RPI_EEPROM_AB_ERROR err; |
74 | 156 | const char *update_filename = NULL; |
@@ -121,6 +203,10 @@ static int cmd_write_eeprom_update(int argc, char *argv[]) { |
121 | 203 | return -1; |
122 | 204 | } |
123 | 205 |
|
| 206 | + if (check_min_boot_ver(update_data, (size_t) file_size) != 0) { |
| 207 | + return -1; |
| 208 | + } |
| 209 | + |
124 | 210 | // Cancel any existing update |
125 | 211 | err = rpi_eeprom_ab_update_cancel(); |
126 | 212 | if (err != RPI_EEPROM_AB_ERROR_NO_ERROR) { |
|
0 commit comments