mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-08-29 05:28:22 +00:00
Abstract the flash.c to avoid hard coding that makes it impossible to cross platforms.
This commit is contained in:
+317
-105
@@ -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();
|
||||
}
|
||||
|
||||
+14
-6
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user