Flasher: read the firmware file in one go

This commit is contained in:
Philippe Teuwen 2022-02-16 01:03:32 +01:00
parent 2a705d3a44
commit 72f7bc762d
3 changed files with 120 additions and 191 deletions

View file

@ -85,7 +85,8 @@ static int chipid_to_mem_avail(uint32_t iChipID) {
// 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, FILE *fd, uint32_t flash_end) {
static int build_segs_from_phdrs(flash_file_t *ctx, uint32_t flash_size) {
uint32_t flash_end = FLASH_START + flash_size;
Elf32_Phdr_t *phdr = ctx->phdrs;
flash_seg_t *seg;
uint32_t last_end = 0;
@ -149,11 +150,7 @@ static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, uint32_t flash_end
PrintAndLogEx(ERR, "Error: Out of memory");
return PM3_EMALLOC;
}
if (fseek(fd, offset, SEEK_SET) < 0 || fread(data, 1, filesz, fd) != filesz) {
PrintAndLogEx(ERR, "Error while reading PHDR payload");
free(data);
return PM3_EFILE;
}
memcpy(data, ctx->elf + offset, filesz);
uint32_t block_offset = paddr & (BLOCK_SIZE - 1);
if (block_offset) {
@ -209,7 +206,8 @@ static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, uint32_t flash_end
}
// Sanity check segments and check for bootloader writes
static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_end) {
static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_size) {
uint32_t flash_end = FLASH_START + flash_size;
for (int i = 0; i < ctx->num_segs; i++) {
flash_seg_t *seg = &ctx->segments[i];
@ -237,170 +235,13 @@ static int check_segs(flash_file_t *ctx, int can_write_bl, uint32_t flash_end) {
return PM3_SUCCESS;
}
// Read an ELF file and do some checks
int flash_check(flash_file_t *ctx, const char *name) {
// Load an ELF file for flashing
int flash_load(flash_file_t *ctx) {
FILE *fd;
Elf32_Ehdr_t ehdr;
Elf32_Phdr_t *phdrs = NULL;
Elf32_Ehdr_t *ehdr;
Elf32_Shdr_t *shdrs = NULL;
uint16_t num_phdrs;
uint16_t num_shdrs;
uint8_t *shstr = NULL;
struct version_information_t *vi = NULL;
int res = PM3_EUNDEF;
fd = fopen(name, "rb");
if (!fd) {
PrintAndLogEx(ERR, _RED_("Could not open file") " %s >>> ", name);
res = PM3_EFILE;
goto fail;
}
PrintAndLogEx(SUCCESS, _CYAN_("Loading ELF file") _YELLOW_(" %s"), name);
if (fread(&ehdr, sizeof(ehdr), 1, fd) != 1) {
PrintAndLogEx(ERR, "Error while reading ELF file header");
res = PM3_EFILE;
goto fail;
}
if (memcmp(ehdr.e_ident, elf_ident, sizeof(elf_ident))
|| le32(ehdr.e_version) != 1) {
PrintAndLogEx(ERR, "Not an ELF file or wrong ELF type");
res = PM3_EFILE;
goto fail;
}
if (le16(ehdr.e_type) != ET_EXEC) {
PrintAndLogEx(ERR, "ELF is not executable");
res = PM3_EFILE;
goto fail;
}
if (le16(ehdr.e_machine) != EM_ARM) {
PrintAndLogEx(ERR, "Wrong ELF architecture");
res = PM3_EFILE;
goto fail;
}
if (!ehdr.e_phnum || !ehdr.e_phoff) {
PrintAndLogEx(ERR, "ELF has no PHDRs");
res = PM3_EFILE;
goto fail;
}
if (le16(ehdr.e_phentsize) != sizeof(Elf32_Phdr_t)) {
// could be a structure padding issue...
PrintAndLogEx(ERR, "Either the ELF file or this code is made of fail");
res = PM3_EFILE;
goto fail;
}
num_phdrs = le16(ehdr.e_phnum);
phdrs = calloc(le16(ehdr.e_phnum) * sizeof(Elf32_Phdr_t), sizeof(uint8_t));
if (!phdrs) {
PrintAndLogEx(ERR, "Out of memory");
res = PM3_EMALLOC;
goto fail;
}
if (fseek(fd, le32(ehdr.e_phoff), SEEK_SET) < 0) {
PrintAndLogEx(ERR, "Error while reading ELF PHDRs");
res = PM3_EFILE;
goto fail;
}
if (fread(phdrs, sizeof(Elf32_Phdr_t), num_phdrs, fd) != num_phdrs) {
res = PM3_EFILE;
PrintAndLogEx(ERR, "Error while reading ELF PHDRs");
goto fail;
}
num_shdrs = le16(ehdr.e_shnum);
shdrs = calloc(le16(ehdr.e_shnum) * sizeof(Elf32_Shdr_t), sizeof(uint8_t));
if (!shdrs) {
PrintAndLogEx(ERR, "Out of memory");
res = PM3_EMALLOC;
goto fail;
}
if (fseek(fd, le32(ehdr.e_shoff), SEEK_SET) < 0) {
PrintAndLogEx(ERR, "Error while reading ELF SHDRs");
res = PM3_EFILE;
goto fail;
}
if (fread(shdrs, sizeof(Elf32_Shdr_t), num_shdrs, fd) != num_shdrs) {
res = PM3_EFILE;
PrintAndLogEx(ERR, "Error while reading ELF SHDRs");
goto fail;
}
shstr = calloc(shdrs[ehdr.e_shstrndx].sh_size, sizeof(uint8_t));
if (!shstr) {
PrintAndLogEx(ERR, "Out of memory");
res = PM3_EMALLOC;
goto fail;
}
if (fseek(fd, le32(shdrs[ehdr.e_shstrndx].sh_offset), SEEK_SET) < 0) {
PrintAndLogEx(ERR, "Error while reading ELF section string table");
res = PM3_EFILE;
goto fail;
}
if (fread(shstr, shdrs[ehdr.e_shstrndx].sh_size, 1, fd) != 1) {
res = PM3_EFILE;
PrintAndLogEx(ERR, "Error while reading ELF section string table");
goto fail;
}
for (uint16_t i = 0; i < ehdr.e_shnum; i++) {
if (strcmp(((char *)shstr) + shdrs[i].sh_name, ".version_information") == 0) {
vi = calloc(shdrs[i].sh_size, sizeof(uint8_t));
if (!vi) {
PrintAndLogEx(ERR, "Out of memory");
res = PM3_EMALLOC;
goto fail;
}
if (fseek(fd, le32(shdrs[i].sh_offset), SEEK_SET) < 0) {
PrintAndLogEx(ERR, "Error while reading ELF version_information section");
res = PM3_EFILE;
goto fail;
}
if (fread(vi, shdrs[i].sh_size, 1, fd) != 1) {
res = PM3_EFILE;
PrintAndLogEx(ERR, "Error while reading ELF version_information section");
goto fail;
}
if (strlen(g_version_information.armsrc) == 9) {
if (strncmp(vi->armsrc, g_version_information.armsrc, 9) != 0) {
PrintAndLogEx(WARNING, _RED_("ARM firmware does not match the source at the time the client was compiled"));
PrintAndLogEx(WARNING, "Make sure to flash a correct and up-to-date version");
// TODO: prompt user to continue or abort
}
}
free(vi);
}
}
free(shdrs);
free(shstr);
fclose(fd);
ctx->filename = name;
ctx->phdrs = phdrs;
ctx->num_phdrs = num_phdrs;
return PM3_SUCCESS;
fail:
if (phdrs)
free(phdrs);
if (shdrs)
free(shdrs);
if (shstr)
free(shstr);
if (vi)
free(vi);
if (fd)
fclose(fd);
flash_free(ctx);
return res;
}
// Load an ELF file and prepare it for flashing
int flash_load(flash_file_t *ctx, int can_write_bl, int flash_size) {
FILE *fd;
uint32_t flash_end = FLASH_START + flash_size;
struct version_information_t *vi = NULL;
int res = PM3_EUNDEF;
fd = fopen(ctx->filename, "rb");
@ -410,19 +251,104 @@ int flash_load(flash_file_t *ctx, int can_write_bl, int flash_size) {
goto fail;
}
res = build_segs_from_phdrs(ctx, fd, flash_end);
if (res != PM3_SUCCESS)
goto fail;
res = check_segs(ctx, can_write_bl, flash_end);
if (res != PM3_SUCCESS)
goto fail;
PrintAndLogEx(SUCCESS, _CYAN_("Loading ELF file") _YELLOW_(" %s"), ctx->filename);
// get filesize in order to malloc memory
fseek(fd, 0, SEEK_END);
long fsize = ftell(fd);
fseek(fd, 0, SEEK_SET);
if (fsize <= 0) {
PrintAndLogEx(ERR, "Error, when getting filesize");
res = PM3_EFILE;
fclose(fd);
goto fail;
}
ctx->elf = calloc(fsize, sizeof(uint8_t));
if (!ctx->elf) {
PrintAndLogEx(ERR, "Error, cannot allocate memory");
res = PM3_EMALLOC;
fclose(fd);
goto fail;
}
size_t bytes_read = fread(ctx->elf, 1, fsize, fd);
fclose(fd);
if (bytes_read != fsize) {
PrintAndLogEx(ERR, "Error, bytes read mismatch file size");
res = PM3_EFILE;
goto fail;
}
ehdr = (Elf32_Ehdr_t *)ctx->elf;
if (memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))
|| le32(ehdr->e_version) != 1) {
PrintAndLogEx(ERR, "Not an ELF file or wrong ELF type");
res = PM3_EFILE;
goto fail;
}
if (le16(ehdr->e_type) != ET_EXEC) {
PrintAndLogEx(ERR, "ELF is not executable");
res = PM3_EFILE;
goto fail;
}
if (le16(ehdr->e_machine) != EM_ARM) {
PrintAndLogEx(ERR, "Wrong ELF architecture");
res = PM3_EFILE;
goto fail;
}
if (!ehdr->e_phnum || !ehdr->e_phoff) {
PrintAndLogEx(ERR, "ELF has no PHDRs");
res = PM3_EFILE;
goto fail;
}
if (le16(ehdr->e_phentsize) != sizeof(Elf32_Phdr_t)) {
// could be a structure padding issue...
PrintAndLogEx(ERR, "Either the ELF file or this code is made of fail");
res = PM3_EFILE;
goto fail;
}
ctx->num_phdrs = le16(ehdr->e_phnum);
ctx->phdrs = (Elf32_Phdr_t *)(ctx->elf + le32(ehdr->e_phoff));
shdrs = (Elf32_Shdr_t *)(ctx->elf + le32(ehdr->e_shoff));
shdrs = (Elf32_Shdr_t *)(ctx->elf + le32(ehdr->e_shoff));
shstr = ctx->elf + le32(shdrs[ehdr->e_shstrndx].sh_offset);
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));
if (strlen(g_version_information.armsrc) == 9) {
if (strncmp(vi->armsrc, g_version_information.armsrc, 9) != 0) {
PrintAndLogEx(WARNING, _RED_("ARM firmware does not match the source at the time the client was compiled"));
PrintAndLogEx(WARNING, "Make sure to flash a correct and up-to-date version");
// TODO: prompt user to continue or abort
}
}
}
}
return PM3_SUCCESS;
fail:
flash_free(ctx);
return res;
}
// Prepare an ELF file for flashing
int flash_prepare(flash_file_t *ctx, int can_write_bl, int flash_size) {
int res = PM3_EUNDEF;
res = build_segs_from_phdrs(ctx, flash_size);
if (res != PM3_SUCCESS)
goto fail;
res = check_segs(ctx, can_write_bl, flash_size);
if (res != PM3_SUCCESS)
goto fail;
return PM3_SUCCESS;
fail:
if (fd)
fclose(fd);
flash_free(ctx);
return res;
}
@ -719,6 +645,16 @@ int flash_write(flash_file_t *ctx) {
void flash_free(flash_file_t *ctx) {
if (!ctx)
return;
if (ctx->filename != NULL) {
free(ctx->filename);
ctx->filename = NULL;
}
if (ctx->elf) {
free(ctx->elf);
ctx->elf = NULL;
ctx->phdrs = NULL;
ctx->num_phdrs = 0;
}
if (ctx->segments) {
for (int i = 0; i < ctx->num_segs; i++)
free(ctx->segments[i].data);
@ -726,11 +662,6 @@ void flash_free(flash_file_t *ctx) {
ctx->segments = NULL;
ctx->num_segs = 0;
}
if (ctx->phdrs) {
free(ctx->phdrs);
ctx->phdrs = NULL;
ctx->num_phdrs = 0;
}
}
// just reset the unit

View file

@ -32,7 +32,8 @@ typedef struct {
} flash_seg_t;
typedef struct {
const char *filename;
char *filename;
uint8_t *elf;
Elf32_Phdr_t *phdrs;
uint16_t num_phdrs;
int can_write_bl;
@ -40,8 +41,8 @@ typedef struct {
flash_seg_t *segments;
} flash_file_t;
int flash_check(flash_file_t *ctx, const char *name);
int flash_load(flash_file_t *ctx, int can_write_bl, int flash_size);
int flash_load(flash_file_t *ctx);
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);
int flash_write(flash_file_t *ctx);
void flash_free(flash_file_t *ctx);

View file

@ -607,7 +607,6 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[
int ret = PM3_EUNDEF;
flash_file_t files[FLASH_MAX_FILES];
memset(files, 0, sizeof(files));
char *filepaths[FLASH_MAX_FILES] = {0};
if (serial_port_name == NULL) {
PrintAndLogEx(ERR, "You must specify a port.\n");
@ -627,16 +626,16 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[
if (ret != PM3_SUCCESS) {
goto finish2;
}
filepaths[i] = path;
files[i].filename = path;
}
PrintAndLogEx(SUCCESS, "About to use the following file%s:", num_files > 1 ? "s" : "");
for (int i = 0 ; i < num_files; ++i) {
PrintAndLogEx(SUCCESS, " "_YELLOW_("%s"), filepaths[i]);
PrintAndLogEx(SUCCESS, " "_YELLOW_("%s"), files[i].filename);
}
for (int i = 0 ; i < num_files; ++i) {
ret = flash_check(&files[i], filepaths[i]);
ret = flash_load(&files[i]);
if (ret != PM3_SUCCESS) {
goto finish;
}
@ -661,7 +660,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[
goto finish;
for (int i = 0 ; i < num_files; ++i) {
ret = flash_load(&files[i], can_write_bl, max_allowed * ONE_KB);
ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB);
if (ret != PM3_SUCCESS) {
goto finish;
}
@ -686,8 +685,6 @@ finish:
finish2:
for (int i = 0 ; i < num_files; ++i) {
flash_free(&files[i]);
if (filepaths[i] != NULL)
free(filepaths[i]);
}
if (ret == PM3_SUCCESS)
PrintAndLogEx(SUCCESS, _CYAN_("All done"));