diff --git a/client/src/flash.c b/client/src/flash.c index b1c557897..962192cda 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -27,20 +27,28 @@ #include "ui.h" #include "elf.h" #include "proxendian.h" -#include "at91sam7s512.h" #include "util_posix.h" #include "comms.h" #include "commonutil.h" #include "fileutils.h" #include "frame_progress.h" -#define FLASH_START 0x100000 +#include "at91sam7s512.h" +// #include "at32f435_437_flash.h" TODO DXL makefile include dirs need add 'armlib/at32_sys/drivers/inc' -#define BOOTLOADER_SIZE 0x2000 -#define BOOTLOADER_END (FLASH_START + BOOTLOADER_SIZE) +// #define BLOCK_SIZE_AT32 0x800 // For at32, if flash size is 4m, the sector size is 4096byte, otherwise 2048byte. +#define FLASH_START_AT32 0x08000000 +#define BOOTLOADER_SIZE_AT32 0x4000 // defined in 'ldscript.defs.at32' +#define BOOTLOADER_END_AT32 (FLASH_START_AT32 + BOOTLOADER_SIZE_AT32) -#define BLOCK_SIZE 0x200 +// AT91 series universal definition. +#define BLOCK_SIZE_AT91 0x200 // For at91, 512byte = 2page +#define FLASH_START_AT91 0x100000 +#define BOOTLOADER_SIZE_AT91 0x2000 // defined in 'ldscript.defs.at91' +#define BOOTLOADER_END_AT91 (FLASH_START_AT91 + BOOTLOADER_SIZE_AT91) +// It is best for the version number of the flasher to be consistent with the version number of the bootrom, +// otherwise some capabilities may be missing. #define FLASHER_VERSION BL_VERSION_1_0_0 static const uint8_t elf_ident[] = { @@ -50,6 +58,7 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; +// TODO DXL It's best to encapsulate and reuse the code here, and put it in commonutil static int chipid_to_mem_avail(uint32_t iChipID) { int mem_avail = 0; switch ((iChipID & 0xF00) >> 8) { @@ -86,10 +95,48 @@ static int chipid_to_mem_avail(uint32_t iChipID) { return mem_avail; } +// TODO DXL It's best to encapsulate and reuse the code here, and put it in commonutil +static int chipid_to_mem_avail_at32(uint32_t idcode) { + struct { + uint32_t id; // idcode + uint32_t flash_size; // KB + } at32_idcode_mem_map[] = { + {0x70084540, 4032}, // AT32F435ZMT7 + {0x70083341, 1024}, // AT32F435ZGT7 + {0x70083242, 256}, // AT32F435ZCT7 + {0x70084543, 4032}, // AT32F435VMT7 + {0x70083344, 1024}, // AT32F435VGT7 + {0x70083245, 256}, // AT32F435VCT7 + {0x70084546, 4032}, // AT32F435RMT7 + {0x70083347, 1024}, // AT32F435RGT7 + {0x70083248, 256}, // AT32F435RCT7 + {0x70084549, 4032}, // AT32F435CMT7 + {0x7008334A, 1024}, // AT32F435CGT7 + {0x7008324B, 256}, // AT32F435CCT7 + {0x7008454C, 4032}, // AT32F435CMU7 + {0x7008334D, 1024}, // AT32F435CGU7 + {0x7008324E, 256}, // AT32F435CCU7 + {0x7008454F, 4032}, // AT32F437ZMT7 + {0x70083350, 1024}, // AT32F437ZGT7 + {0x70083251, 256}, // AT32F437ZCT7 + {0x70084552, 4032}, // AT32F437VMT7 + {0x70083353, 1024}, // AT32F437VGT7 + {0x70083254, 256}, // AT32F437VCT7 + {0x70084555, 4032}, // AT32F437RMT7 + {0x70083356, 1024}, // AT32F437RGT7 + {0x70083257, 256}, // AT32F437RCT7 + }; + for (size_t i = 0; i < ARRAYLEN(at32_idcode_mem_map); i++) { + if (at32_idcode_mem_map[i].id == idcode) { + return at32_idcode_mem_map[i].flash_size; + } + } + return 256; // No idcode found? return a min size. +} + // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed -static int build_segs_from_phdrs(flash_file_t *ctx, uint32_t flash_size) { - uint32_t flash_end = FLASH_START + flash_size; +static int build_segs_from_phdrs(flash_file_t *ctx, flash_dev_t *flash_dev) { Elf32_Phdr_t *phdr = ctx->phdrs; flash_seg_t *seg; uint32_t last_end = 0; @@ -133,35 +180,35 @@ static int build_segs_from_phdrs(flash_file_t *ctx, uint32_t flash_size) { PrintAndLogEx(ERR, "Error: PHDRs not sorted or overlap"); return PM3_EFILE; } - if (paddr < FLASH_START || (paddr + filesz) > flash_end) { + if (paddr < flash_dev->flash_start || (paddr + filesz) > flash_dev->flash_end) { PrintAndLogEx(ERR, "Error: PHDR is not contained in Flash"); - if ((paddr + filesz) > flash_end) { + if ((paddr + filesz) > flash_dev->flash_end) { PrintAndLogEx(ERR, "Firmware is probably too big for your device"); PrintAndLogEx(ERR, "See README.md for information on compiling for platforms with 256KB of flash memory"); } return PM3_EFILE; } - if (vaddr >= FLASH_START && vaddr < flash_end && (flags & PF_W)) { + if (vaddr >= flash_dev->flash_start && vaddr < flash_dev->flash_end && (flags & PF_W)) { PrintAndLogEx(ERR, "Error: Flash VMA segment is writable"); return PM3_EFILE; } uint8_t *data; // make extra space if we need to move the data forward - data = calloc(filesz + BLOCK_SIZE, sizeof(uint8_t)); + data = calloc(filesz + flash_dev->block_size, sizeof(uint8_t)); if (!data) { PrintAndLogEx(ERR, "Error: Out of memory"); return PM3_EMALLOC; } memcpy(data, ctx->elf + offset, filesz); - uint32_t block_offset = paddr & (BLOCK_SIZE - 1); + uint32_t block_offset = paddr & (flash_dev->block_size - 1); if (block_offset) { if (ctx->num_segs) { flash_seg_t *prev_seg = seg - 1; uint32_t this_end = paddr + filesz; - uint32_t this_firstblock = paddr & ~(BLOCK_SIZE - 1); - uint32_t prev_lastblock = (last_end - 1) & ~(BLOCK_SIZE - 1); + uint32_t this_firstblock = paddr & ~(flash_dev->block_size - 1); + uint32_t prev_lastblock = (last_end - 1) & ~(flash_dev->block_size - 1); if (this_firstblock == prev_lastblock) { uint32_t new_length = this_end - prev_seg->start; @@ -209,28 +256,27 @@ static int build_segs_from_phdrs(flash_file_t *ctx, uint32_t flash_size) { } // Sanity check segments and check for bootloader writes -static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_size) { - uint32_t flash_end = FLASH_START + flash_size; +static int check_segs(flash_file_t *ctx, int can_write_bl, flash_dev_t *flash_dev) { for (int i = 0; i < ctx->num_segs; i++) { flash_seg_t *seg = &ctx->segments[i]; - if (seg->start & (BLOCK_SIZE - 1)) { + if (seg->start & (flash_dev->block_size - 1)) { PrintAndLogEx(ERR, "Error: Segment is not aligned"); return PM3_EFILE; } - if (seg->start < FLASH_START) { + if (seg->start < flash_dev->flash_start) { PrintAndLogEx(ERR, "Error: Segment is outside of flash bounds"); return PM3_EFILE; } - if (seg->start + seg->length > flash_end) { + if (seg->start + seg->length > flash_dev->flash_end) { PrintAndLogEx(ERR, "Error: Segment is outside of flash bounds"); return PM3_EFILE; } - if (!can_write_bl && seg->start < BOOTLOADER_END) { + if (!can_write_bl && seg->start < flash_dev->boot_end) { PrintAndLogEx(ERR, "Attempted to write bootloader but bootloader writes are not enabled"); return PM3_EINVARG; } - if (can_write_bl && seg->start < BOOTLOADER_END && (seg->start + seg->length > BOOTLOADER_END)) { + if (can_write_bl && seg->start < flash_dev->boot_end && (seg->start + seg->length > flash_dev->boot_end)) { PrintAndLogEx(ERR, "Error: Segment is outside of bootloader bounds"); return PM3_EFILE; } @@ -238,18 +284,21 @@ static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_size) return PM3_SUCCESS; } -static int print_and_validate_version(struct version_information_t *vi) { - if (vi->magic != VERSION_INFORMATION_MAGIC) { +// Check version information section for sanity and compatibility with the client, and print it if valid +static int print_and_validate_version(flash_file_t *ctx) { + if (!CheckValidInformationMagic(ctx->ver_info)) { + PrintAndLogEx(ERR, _RED_("ELF file does not contain valid version information" + "(magic = 0x%08x)"), ctx->ver_info->magic); return PM3_EFILE; } // same limit as for ARM image char temp[PM3_CMD_DATA_SIZE - 12] = {0}; - FormatVersionInformation(temp, sizeof(temp), "", vi); + FormatVersionInformation(temp, sizeof(temp), "", ctx->ver_info); PrintAndLogEx(SUCCESS, _CYAN_("ELF file version") _YELLOW_(" %s"), temp); if (strlen(g_version_information.armsrc) == 9) { - if (strncmp(vi->armsrc, g_version_information.armsrc, 9) != 0) { + if (strncmp(ctx->ver_info->armsrc, g_version_information.armsrc, 9) != 0) { PrintAndLogEx(WARNING, _RED_("ARM firmware does not match the source at the time the client was compiled")); return PM3_EINVARG; } else { @@ -265,7 +314,6 @@ int flash_load(flash_file_t *ctx, bool force) { Elf32_Ehdr_t *ehdr; Elf32_Shdr_t *shdrs = NULL; uint8_t *shstr = NULL; - struct version_information_t *vi = NULL; int res = PM3_EUNDEF; fd = fopen(ctx->filename, "rb"); @@ -347,8 +395,8 @@ int flash_load(flash_file_t *ctx, bool force) { for (uint16_t i = 0; i < le16(ehdr->e_shnum); i++) { if (strcmp(((char *)shstr) + shdrs[i].sh_name, ".version_information") == 0) { - vi = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset)); - res = print_and_validate_version(vi); + ctx->ver_info = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset)); + res = print_and_validate_version(ctx); break; } @@ -358,8 +406,8 @@ int flash_load(flash_file_t *ctx, bool force) { if (offset >= le32(shdrs[i].sh_addr)) { offset -= le32(shdrs[i].sh_addr); if (offset < le32(shdrs[i].sh_size)) { - vi = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset) + offset); - res = print_and_validate_version(vi); + ctx->ver_info = (struct version_information_t *)(ctx->elf + le32(shdrs[i].sh_offset) + offset); + res = print_and_validate_version(ctx); } } break; @@ -387,15 +435,22 @@ fail: } // Prepare an ELF file for flashing -int flash_prepare(flash_file_t *ctx, int can_write_bl, int flash_size) { +int flash_prepare(flash_file_t *ctx, int can_write_bl, flash_dev_t *flash_dev) { int res = PM3_EUNDEF; - res = build_segs_from_phdrs(ctx, flash_size); + // Check elf file is build for currently connected device? + if (!CheckInformationMagicAndChipType(ctx->ver_info, flash_dev->chiptype)) { + PrintAndLogEx(ERR, "The elf file is not applicable to the currently connected device.", flash_dev->chiptype); + res = PM3_EFILE; + goto fail; + } + + res = build_segs_from_phdrs(ctx, flash_dev); if (res != PM3_SUCCESS) { goto fail; } - res = check_segs(ctx, can_write_bl, flash_size); + res = check_segs(ctx, can_write_bl, flash_dev); if (res != PM3_SUCCESS) { goto fail; } @@ -489,11 +544,11 @@ static int enter_bootloader(char *serial_port_name, bool wait_appear) { return PM3_EFATAL; } +// Wait for the device to respond with either ACK or NACK. static int wait_for_ack(PacketResponseNG *ack) { WaitForResponse(CMD_UNKNOWN, ack); - if (ack->cmd != CMD_ACK) { - PrintAndLogEx(ERR, "Error: Unexpected reply 0x%04x %s (expected ACK)", + PrintAndLogEx(ERR, "\nError: Unexpected reply 0x%04x %s (expected ACK)", ack->cmd, (ack->cmd == CMD_NACK) ? "NACK" : "" ); @@ -502,8 +557,10 @@ static int wait_for_ack(PacketResponseNG *ack) { return PM3_SUCCESS; } -static bool gs_printed_msg = false; +// If the BOOTLOADER is too old or damaged, we can suggest that the user update the BOOT. static void flash_suggest_update_bootloader(void) { + // Since it's only used internally, we can define it internally. + static bool gs_printed_msg = false; if (gs_printed_msg) { return; } @@ -522,12 +579,66 @@ static void flash_suggest_update_bootloader(void) { gs_printed_msg = true; } +// If the device's boot is newer than the current flasher, we can suggest the user update the flasher. static void flash_suggest_update_flasher(void) { PrintAndLogEx(ERR, _RED_("It is recommended that you first " _YELLOW_("update your flasher"))); } +// AT32 series has a wide range of flash sizes, so we check the chipinfo to set the flash end address and block size. +static void flash_dev_at32_init(uint32_t chipinfo, flash_dev_t *flash_dev) { + flash_dev->flash_start = FLASH_START_AT32; + uint32_t flash_size = chipid_to_mem_avail_at32(chipinfo); + if (flash_size > 1024) { + flash_dev->block_size = 0x1000; // 4K block size for >1M flash + } else { + flash_dev->block_size = 0x800; // 2K block size for <=1M flash + } + flash_dev->flash_end = FLASH_START_AT32 + flash_size * 1024; + flash_dev->boot_size = BOOTLOADER_SIZE_AT32; + flash_dev->boot_end = BOOTLOADER_END_AT32; +} + +// AT91 series has some variations in flash size, so we check the chipinfo to set the flash end address +// and warn the user if they have a large flash but an old bootloader that doesn't support it. +static void flash_dev_at91_init(uint32_t chipinfo, flash_dev_t *flash_dev, int version) { + flash_dev->block_size = BLOCK_SIZE_AT91; + flash_dev->flash_start = FLASH_START_AT91; + flash_dev->flash_end = FLASH_START_AT91 + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES / 2; // Default 256K MAX + flash_dev->boot_size = BOOTLOADER_SIZE_AT91; + flash_dev->boot_end = BOOTLOADER_END_AT91; + // Check the flash capacity based on the idcode returned by the device, that is, enable support for 512K FLASH. + int mem_avail = chipid_to_mem_avail(chipinfo); + if (mem_avail != 0) { + PrintAndLogEx(INFO, "Available memory on this board: "_YELLOW_("%uK") " bytes\n", mem_avail); + if (mem_avail > 256) { + if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(BL_VERSION_1_0_0)) { + PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================")); + PrintAndLogEx(ERR, _RED_("Your bootloader does not support writing above 256k")); + flash_suggest_update_bootloader(); + } else { + // The capacity of the main chip of the device is greater than 256K, + // and BL also supports OTA for chips with such a large capacity. + flash_dev->flash_end = FLASH_START_AT91 + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; + } + } + } else { + PrintAndLogEx(INFO, "Available memory on this board: "_RED_("UNKNOWN")"\n"); + PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================================")); + PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new" _YELLOW_(" CHIP_INFO") _RED_(" command"))); + flash_suggest_update_bootloader(); + } +} + +// Sending simple cmd without any parameters or data payload, just for arg0. +static void send_cmd_for_arg0(const uint64_t cmd, uint32_t *arg0) { + SendCommandBL(cmd, 0, 0, 0, NULL, 0); + PacketResponseNG resp; + WaitForResponse(cmd, &resp); + *arg0 = resp.oldarg[0]; +} + // Go into flashing mode -int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed) { +int flash_start_flashing(int enable_bl_writes, char *serial_port_name, flash_dev_t *flash_dev) { int ret = enter_bootloader(serial_port_name, true); if (ret != PM3_SUCCESS) { @@ -540,31 +651,26 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t return ret; } - uint32_t chipinfo = 0; + flash_dev->chiptype = MAIN_CHIP_TYPE_NONE; + if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_TYPE) == DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_TYPE) { + send_cmd_for_arg0(CMD_CHIP_TYPE, &flash_dev->chiptype); + } + uint32_t chipinfo = 0; if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO) == DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO) { - SendCommandBL(CMD_CHIP_INFO, 0, 0, 0, NULL, 0); - PacketResponseNG resp; - WaitForResponse(CMD_CHIP_INFO, &resp); - chipinfo = resp.oldarg[0]; + send_cmd_for_arg0(CMD_CHIP_INFO, &chipinfo); } int version = BL_VERSION_INVALID; - if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_VERSION) == DEVICE_INFO_FLAG_UNDERSTANDS_VERSION) { - - SendCommandBL(CMD_BL_VERSION, 0, 0, 0, NULL, 0); - PacketResponseNG resp; - WaitForResponse(CMD_BL_VERSION, &resp); - version = resp.oldarg[0]; - + // Get bootrom version for features and sanity checks + send_cmd_for_arg0(CMD_BL_VERSION, (uint32_t *)&version); + // Is version invalid or outside of expected range? maybe bootrom is very old or corrupted? if ((BL_VERSION_MAJOR(version) < BL_VERSION_FIRST_MAJOR) || (BL_VERSION_MAJOR(version) > BL_VERSION_LAST_MAJOR)) { - // version info seems fishy - version = BL_VERSION_INVALID; + version = BL_VERSION_INVALID; // version info seems fishy PrintAndLogEx(ERR, _RED_("====================== OBS ! ===========================")); PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported an invalid version number")); flash_suggest_update_bootloader(); - // } else if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(FLASHER_VERSION)) { PrintAndLogEx(ERR, _RED_("====================== OBS ! ===================================")); PrintAndLogEx(ERR, _RED_("Note: Your bootloader reported a version older than this flasher")); @@ -580,48 +686,39 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t flash_suggest_update_bootloader(); } - uint32_t flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES / 2; - *max_allowed = 256; + // 1. The old bootloader does not support pm5, nor does it support the 'CMD_CHIP_TYPE' command. + // 2. In the absence of CMD_CHIP_TYPE cmd support, pm3 (at91 platform) is selected as a backup solution. + // 3. Only by combining the parameters of chiptype and chipinfo can the detailed information of the chip currently used by the device be correctly obtained + // 4. This function does not check if the elf file is compatible with the device, so it needs to be checked within the flash_prepare function + switch (flash_dev->chiptype) { + case MAIN_CHIP_TYPE_NONE: + PrintAndLogEx(ERR, _RED_("Bootloader does not support CMD_CHIP_TYPE, assuming AT91 platform")); + flash_dev->chiptype = MAIN_CHIP_TYPE_AT91; + flash_suggest_update_bootloader(); + // break; -> Don't break !!! We want to execute the code for MAIN_CHIP_TYPE_AT91 as well to initialize flash_dev with correct values. - int mem_avail = chipid_to_mem_avail(chipinfo); - if (mem_avail != 0) { + case MAIN_CHIP_TYPE_AT91: + default: + flash_dev_at91_init(chipinfo, flash_dev, version); + break; - PrintAndLogEx(INFO, "Available memory on this board: "_YELLOW_("%uK") " bytes\n", mem_avail); - - if (mem_avail > 256) { - if (BL_VERSION_MAJOR(version) < BL_VERSION_MAJOR(BL_VERSION_1_0_0)) { - PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================")); - PrintAndLogEx(ERR, _RED_("Your bootloader does not support writing above 256k")); - flash_suggest_update_bootloader(); - } else { - flash_end = FLASH_START + AT91C_IFLASH_PAGE_SIZE * AT91C_IFLASH_NB_OF_PAGES; - *max_allowed = mem_avail; - } - } - - } else { - PrintAndLogEx(INFO, "Available memory on this board: "_RED_("UNKNOWN")"\n"); - PrintAndLogEx(ERR, _RED_("====================== OBS ! ======================================")); - PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new" _YELLOW_(" CHIP_INFO") _RED_(" command"))); - flash_suggest_update_bootloader(); + case MAIN_CHIP_TYPE_AT32: + flash_dev_at32_init(chipinfo, flash_dev); + break; } - if (enable_bl_writes) { - PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", FLASH_START, flash_end); - } else { - PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", BOOTLOADER_END, flash_end); - } + // If you need to flash bootrom, the start addr must be 'flash_start', otherwise, it can be 'boot_end' to skip the bootrom area and save some time. + uint32_t start_flash_addr = enable_bl_writes ? flash_dev->flash_start : flash_dev->boot_end; + PrintAndLogEx(INFO, "Permitted flash range: 0x%08x-0x%08x", start_flash_addr, flash_dev->flash_end); if ((state & DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) == DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH) { - if (enable_bl_writes) { - SendCommandBL(CMD_START_FLASH, FLASH_START, flash_end, START_FLASH_MAGIC, NULL, 0); + SendCommandBL(CMD_START_FLASH, start_flash_addr, flash_dev->flash_end, START_FLASH_MAGIC, NULL, 0); } else { - SendCommandBL(CMD_START_FLASH, BOOTLOADER_END, flash_end, 0, NULL, 0); + SendCommandBL(CMD_START_FLASH, start_flash_addr, flash_dev->flash_end, 0, NULL, 0); } PacketResponseNG resp; return wait_for_ack(&resp); - } else { PrintAndLogEx(ERR, _RED_("====================== OBS ! ========================================")); PrintAndLogEx(ERR, _RED_("Note: Your bootloader does not understand the new" _YELLOW_(" START_FLASH") _RED_(" command"))); @@ -635,32 +732,148 @@ int flash_reboot_bootloader(char *serial_port_name, bool wait_appear) { return enter_bootloader(serial_port_name, wait_appear); } -static int write_block(uint32_t address, uint8_t *data, uint32_t length) { - uint8_t block_buf[BLOCK_SIZE]; - memset(block_buf, 0xFF, BLOCK_SIZE); - memcpy(block_buf, data, length); - PacketResponseNG resp; -#if defined ICOPYX - SendCommandBL(CMD_FINISH_WRITE, address, 0xFF, 0x1FD, block_buf, length); -#else - SendCommandBL(CMD_FINISH_WRITE, address, 0, 0, block_buf, length); -#endif - int ret = wait_for_ack(&resp); - if (ret && resp.oldarg[0]) { - uint32_t lock_bits = resp.oldarg[0] >> 16; - bool lock_error = resp.oldarg[0] & AT91C_MC_LOCKE; - bool prog_error = resp.oldarg[0] & AT91C_MC_PROGE; - bool security_bit = resp.oldarg[0] & AT91C_MC_SECURITY; +// Show error information after write failed on AT91 platform. +static void flash_write_err_on_at91(uint32_t err) { + if (err) { + uint32_t lock_bits = err >> 16; + bool lock_error = err & AT91C_MC_LOCKE; + bool prog_error = err & AT91C_MC_PROGE; + bool security_bit = err & AT91C_MC_SECURITY; PrintAndLogEx(NORMAL, "%s", lock_error ? " Lock Error" : ""); PrintAndLogEx(NORMAL, "%s", prog_error ? " Invalid Command or bad Keyword" : ""); PrintAndLogEx(NORMAL, "%s", security_bit ? " Security Bit is set!" : ""); PrintAndLogEx(NORMAL, " Lock Bits: 0x%04x", lock_bits); } +} + +// Show error information after write failed on AT32 platform. +static void flash_write_err_on_at32(uint32_t err) { + // TODO DXL Need to add the header file path of at32 in the makefile/cake of the client(for flash_status_type). + // In order to quickly compile and verify, we will temporarily define constant value. + // However, later on, the types in the header file should be used. + switch (err) { + case 0: // FLASH_OPERATE_BUSY + PrintAndLogEx(ERR, "Flash is busy"); + break; + case 1: // FLASH_PROGRAM_ERROR + PrintAndLogEx(ERR, "Flash program error"); + break; + case 2: // FLASH_EPP_ERROR + PrintAndLogEx(ERR, "Erase/Program protection error"); + break; + case 3: // FLASH_OPERATE_DONE + // Nothing to do... + break; + case 4: // FLASH_OPERATE_TIMEOUT + PrintAndLogEx(ERR, "Flash operation timeout"); + break; + default: + PrintAndLogEx(ERR, "Unknown flash error"); + break; + } +} + +// The error did not occur while writing to the flash memory, but rather during data copying and write boundary checks. +// This is a software error and is unrelated to the hardware. +static void flash_write_err_software(int pm3_err) { + if (pm3_err == PM3_EINVARG) { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware, invalid argument"); + PrintAndLogEx(ERR, "This may be because the firmware is not compatible with the device or the bootloader is too old"); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } else if (pm3_err == PM3_EOVFLOW) { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware, overflow"); + PrintAndLogEx(ERR, "This may be because the firmware is too large for the device"); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } else if (pm3_err == PM3_EOUTOFBOUND) { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware, out of bound"); + PrintAndLogEx(ERR, "This may be because the firmware is trying to write outside of the flash bounds"); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } else { + PrintAndLogEx(ERR, _RED_("Error:") " Device rejected the firmware with error code 0x%02x", pm3_err); + PrintAndLogEx(ERR, "Make sure to use a compatible ELF file and try updating the bootloader if it's old"); + } +} + +// Send finish write cmd and waiting for response. +// The send_buf length is always 512byte(PM3_CMD_DATA_SIZE) +static int send_finish_write_cmd(uint32_t address, int magic, uint8_t *send_buf, PacketResponseNG *resp) { + // The sending length is always PM3_CMD_DATA_SIZE, which is 512 bytes, because of the limitation of the old frame. + const int send_len = PM3_CMD_DATA_SIZE; +#if defined ICOPYX + // To prevent users from flashing unsupported firmware, icopyx checks arg1 and arg2 in this command. + // Therefore, when sending magic to the device, we should not choose a value that happens to be the same as icopyx. + // In fact, neither PM3V nor PM5V will be 0xff or 0x1fd, so this should have strong robustness. + SendCommandBL(CMD_FINISH_WRITE, address, 0xff, 0x1fd, send_buf, send_len); +#else + // If it's an older version of the flashher or a flashher specific to icopyx, then arg1 should be 0x00 or 0xff, + // not a valid magic value. The client is specifically designed for icopyx. + // --- + // For devices with older firmware, it doesn't care about arg1, + // so OTA can be performed regardless of whether it's a new version of flasher (sending arg1) + // or an old version of flasher (arg1 is not a valid magic). + // --- + // For devices with new firmware, if the sent magic is a valid magic value, + // but the firmware cannot work on the device, the device will refuse to write the firmware. + // --- + // The older client version could always OTA update older devices, + // but it couldn't OTA update newer PM5 versions. + // This met our needs because the older client version didn't support PM5's ELF files. + // --- + // The new client version can always continue to OTA update the device version, + // and can also OTA update the latest version of PM5. + SendCommandBL(CMD_FINISH_WRITE, address, magic, 0, send_buf, send_len); +#endif + return wait_for_ack(resp); +} + +// Write a block of data to flash, padding to the block size if needed. The bootloader will read the entire block, +// so we need to make sure to pad it with 0xFF if the data is smaller than the block size. +static int write_block(uint32_t address, int magic, uint8_t *data, uint32_t length, flash_dev_t *flash_dev) { + // Align length to PM3_CMD_DATA_SIZE or block_size + // It is necessary to align with the minimum write unit of the target chip, + // otherwise it may cause the device to lose the data or offset errors. + uint32_t padded_len = length % MAX(PM3_CMD_DATA_SIZE, flash_dev->block_size); + if (padded_len) { + padded_len = MAX(PM3_CMD_DATA_SIZE, flash_dev->block_size) - padded_len; + } + // After aligning PM3_CMD_DATA_SIZE, allocate a new buffer, copy the data, and pad the end with 0xFF. + uint32_t aligned_len = length + padded_len; + uint8_t *block_buf = malloc(aligned_len); + if (block_buf == NULL) { + return PM3_EMALLOC; + } + memset(block_buf, 0xFF, aligned_len); // fill 0xFF by aligned length + memcpy(block_buf, data, length); // copy data by valid length + // Send in packets + int ret = PM3_SUCCESS; + uint32_t sent = 0; + while (sent < aligned_len) { + PacketResponseNG resp; + ret = send_finish_write_cmd(address, magic, block_buf + sent, &resp); + if (ret) { + // On new version of flasher, the arg1 is error code of PM3_E*, old version is 0x00, so we can always check it. + if (resp.oldarg[1]) { // 0x00 == PM3_SUCCESS + flash_write_err_software(resp.oldarg[1]); + } else { + // If not PM3_E*, maybe some errors of flash write occurred. Or is old version boot. + if (flash_dev->chiptype == MAIN_CHIP_TYPE_AT91) { + flash_write_err_on_at91(resp.oldarg[0]); + } else if (flash_dev->chiptype == MAIN_CHIP_TYPE_AT32) { + flash_write_err_on_at32(resp.oldarg[0]); + } else { + PrintAndLogEx(ERR, "Unknown chip type, cannot decode error information"); + } + } + break; + } + sent += PM3_CMD_DATA_SIZE; + } + free(block_buf); // remember to free buffer return ret; } // Write a file's segments to Flash -int flash_write(flash_file_t *ctx) { +int flash_write(flash_file_t *ctx, flash_dev_t *flash_dev) { PrintAndLogEx(SUCCESS, "Writing segments for file: %s", ctx->filename); @@ -668,7 +881,7 @@ int flash_write(flash_file_t *ctx) { flash_seg_t *seg = &ctx->segments[i]; uint32_t length = seg->length; - uint32_t blocks = (length + BLOCK_SIZE - 1) / BLOCK_SIZE; + uint32_t blocks = (length + flash_dev->block_size - 1) / flash_dev->block_size; uint32_t end = seg->start + length; PrintAndLogEx(SUCCESS, " 0x%08x..0x%08x [0x%x / %u blocks]", seg->start, end - 1, length, blocks); @@ -692,12 +905,11 @@ int flash_write(flash_file_t *ctx) { while (length) { uint32_t block_size = length; - if (block_size > BLOCK_SIZE) { - block_size = BLOCK_SIZE; + if (block_size > flash_dev->block_size) { + block_size = flash_dev->block_size; } - if (write_block(baddr, data, block_size) < 0) { - + if (write_block(baddr, ctx->ver_info->magic, data, block_size, flash_dev) < 0) { if (blocks > 50) { hadouken_stop(); } diff --git a/client/src/flash.h b/client/src/flash.h index 8f66a2025..ef9415fb7 100644 --- a/client/src/flash.h +++ b/client/src/flash.h @@ -23,7 +23,6 @@ #include "elf.h" #define FLASH_MAX_FILES 4 -#define ONE_KB 1024 typedef struct { void *data; @@ -36,17 +35,26 @@ typedef struct { uint8_t *elf; Elf32_Phdr_t *phdrs; uint16_t num_phdrs; + struct version_information_t *ver_info; // point to an address in *elf, no need to free. int can_write_bl; int num_segs; flash_seg_t *segments; } flash_file_t; -int flash_load(flash_file_t *ctx, bool force); -int flash_prepare(flash_file_t *ctx, int can_write_bl, int flash_size); -int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed); +typedef struct { + uint32_t chiptype; // see: main_chip_type_t + uint32_t block_size; + uint32_t flash_start; + uint32_t flash_end; + uint32_t boot_size; // Boot must be at the top of the flash, so flash_start is boot_start. + uint32_t boot_end; +} flash_dev_t; + int flash_reboot_bootloader(char *serial_port_name, bool wait_appear); -int flash_write(flash_file_t *ctx); +int flash_load(flash_file_t *ctx, bool force); +int flash_prepare(flash_file_t *ctx, int can_write_bl, flash_dev_t *flash_dev); +int flash_start_flashing(int enable_bl_writes, char *serial_port_name, flash_dev_t *flash_dev); +int flash_write(flash_file_t *ctx, flash_dev_t *flash_dev); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); #endif - diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index fa1d6c75d..85a2bbd03 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -931,8 +931,8 @@ finish: static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *filenames[FLASH_MAX_FILES], bool can_write_bl, bool force) { int ret = PM3_EUNDEF; - flash_file_t files[FLASH_MAX_FILES]; - memset(files, 0, sizeof(files)); + flash_file_t files[FLASH_MAX_FILES] = {0}; + flash_dev_t flash_dev = {0}; if (serial_port_name == NULL) { PrintAndLogEx(ERR, "You must specify a port.\n"); @@ -977,8 +977,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file goto finish2; } - uint32_t max_allowed = 0; - ret = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed); + ret = flash_start_flashing(can_write_bl, serial_port_name, &flash_dev); if (ret != PM3_SUCCESS) { goto finish; } @@ -988,7 +987,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file } for (int i = 0 ; i < num_files; ++i) { - ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB); + ret = flash_prepare(&files[i], can_write_bl, &flash_dev); if (ret != PM3_SUCCESS) { goto finish; } @@ -998,7 +997,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file PrintAndLogEx(SUCCESS, _CYAN_("Flashing...")); for (int i = 0; i < num_files; i++) { - ret = flash_write(&files[i]); + ret = flash_write(&files[i], &flash_dev); if (ret != PM3_SUCCESS) { goto finish; }