feat(core/bootloader): make firmware flashing more safe

pull/3363/head
cepetr 8 months ago committed by matejcik
parent 523e50db49
commit da7125f427

@ -349,13 +349,17 @@ void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
MSG_SEND(Success);
}
static uint32_t firmware_remaining, firmware_block, chunk_requested;
static uint32_t firmware_remaining;
static uint32_t firmware_block;
static uint32_t chunk_requested;
static uint32_t erase_offset;
void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size,
uint8_t *buf) {
firmware_remaining = 0;
firmware_block = 0;
chunk_requested = 0;
erase_offset = 0;
MSG_RECV_INIT(FirmwareErase);
MSG_RECV(FirmwareErase);
@ -411,8 +415,8 @@ static bool _read_payload(pb_istream_t *stream, const pb_field_t *field,
// update loader but skip first block
if (firmware_block > 0) {
ui_screen_install_progress_upload(
250 + 750 * (firmware_block * IMAGE_CHUNK_SIZE + chunk_written) /
(firmware_block * IMAGE_CHUNK_SIZE + firmware_remaining));
1000 * (firmware_block * IMAGE_CHUNK_SIZE + chunk_written) /
(firmware_block * IMAGE_CHUNK_SIZE + firmware_remaining));
}
// read data
if (!pb_read(
@ -660,8 +664,6 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
ensure(flash_area_erase_bulk(STORAGE_AREAS, STORAGE_AREAS_COUNT, NULL),
NULL);
}
ensure(flash_area_erase(&FIRMWARE_AREA, ui_screen_install_progress_erase),
NULL);
headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen;
read_offset = IMAGE_INIT_CHUNK_SIZE;
@ -713,22 +715,60 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
return UPLOAD_ERR_INVALID_CHUNK_HASH;
}
ensure(flash_unlock_write(), NULL);
const uint32_t *const src = (const uint32_t *const)CHUNK_BUFFER_PTR;
// buffer with the received data
const uint32_t *quadword_ptr = (const uint32_t *)CHUNK_BUFFER_PTR;
// number of received bytes
uint32_t bytes_remaining = chunk_size;
// offset into the FIRMWARE_AREA part of the flash
uint32_t write_offset = firmware_block * IMAGE_CHUNK_SIZE;
while (bytes_remaining > 0) {
// erase flash before writing
uint32_t bytes_erased = 0;
if (write_offset >= erase_offset) {
// erase the next flash section
ensure(
flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased),
NULL);
erase_offset += bytes_erased;
} else {
// some erased space left from the previous round => use it
bytes_erased = erase_offset - write_offset;
}
// write the received data
uint32_t bytes_to_write = MIN(bytes_erased, bytes_remaining);
uint32_t write_end = write_offset + bytes_to_write;
ensure(flash_unlock_write(), NULL);
while (write_offset < write_end) {
// write a quad word (16 bytes) to the flash
ensure(
flash_area_write_quadword(&FIRMWARE_AREA, write_offset, quadword_ptr),
NULL);
write_offset += 4 * sizeof(uint32_t);
quadword_ptr += 4;
}
ensure(flash_lock_write(), NULL);
for (int i = 0; i < chunk_size / (sizeof(uint32_t) * 4); i++) {
ensure(flash_area_write_quadword(
&FIRMWARE_AREA,
firmware_block * IMAGE_CHUNK_SIZE + i * 4 * sizeof(uint32_t),
&src[4 * i]),
NULL);
bytes_remaining -= bytes_to_write;
}
ensure(flash_lock_write(), NULL);
firmware_remaining -= chunk_requested;
if (firmware_remaining == 0) {
// erase the rest (unused part) of the FIRMWARE_AREA
uint32_t bytes_erased = 0;
do {
ensure(
flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased),
NULL);
erase_offset += bytes_erased;
} while (bytes_erased > 0);
}
headers_offset = 0;
firmware_remaining -= chunk_requested;
firmware_block++;
firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT;

@ -57,6 +57,14 @@ secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data);
uint32_t flash_wait_and_clear_status_flags(void);
// Erases the single sector in the designated flash area
// The 'offset' parameter must indicate the relative sector offset within the
// flash area If 'offset' is outside the bounds of the flash area,
// 'bytes_erased' is set to 0 otherwise, 'bytes_erased' is set to the size of
// the erased sector
secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset,
uint32_t *bytes_erased);
secbool __wur flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data,
uint8_t datalen);
secbool __wur flash_otp_write(uint8_t block, uint8_t offset,

@ -167,6 +167,50 @@ secbool flash_area_erase_bulk(const flash_area_t *area, int count,
return sectrue;
}
secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset,
uint32_t *bytes_erased) {
uint32_t sector_offset = 0;
*bytes_erased = 0;
for (int s = 0; s < area->num_subareas; s++) {
for (int i = 0; i < area->subarea[s].num_sectors; i++) {
uint32_t sector_index = area->subarea[s].first_sector + i;
uint32_t sector_size = FLASH_SECTOR_TABLE[sector_index + 1] -
FLASH_SECTOR_TABLE[sector_index];
if (offset == sector_offset) {
ensure(flash_unlock_write(), NULL);
FLASH_EraseInitTypeDef erase_init = {
.TypeErase = FLASH_TYPEERASE_SECTORS,
.VoltageRange = FLASH_VOLTAGE_RANGE_3,
.Sector = sector_index,
.NbSectors = 1};
uint32_t sector_error;
if (HAL_FLASHEx_Erase(&erase_init, &sector_error) != HAL_OK) {
ensure(flash_lock_write(), NULL);
return secfalse;
}
ensure(flash_lock_write(), NULL);
*bytes_erased = sector_size;
return sectrue;
}
sector_offset += sector_size;
}
}
if (offset == sector_offset) {
return sectrue;
}
return secfalse;
}
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
uint32_t address = (uint32_t)flash_get_address(sector, offset, 1);
if (address == 0) {

@ -190,6 +190,36 @@ secbool flash_area_erase_bulk(const flash_area_t *area, int count,
return sectrue;
}
secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset,
uint32_t *bytes_erased) {
uint32_t sector_offset = 0;
*bytes_erased = 0;
for (int s = 0; s < area->num_subareas; s++) {
for (int i = 0; i < area->subarea[s].num_sectors; i++) {
uint32_t sector_index = area->subarea[s].first_sector + i;
uint32_t sector_size = FLASH_SECTOR_TABLE[sector_index + 1] -
FLASH_SECTOR_TABLE[sector_index];
if (offset == sector_offset) {
uint8_t *flash =
(uint8_t *)flash_get_address(sector_index, 0, sector_size);
memset(flash, 0xFF, sector_size);
*bytes_erased = sector_size;
return sectrue;
}
sector_offset += sector_size;
}
}
if (offset == sector_offset) {
return sectrue;
}
return secfalse;
}
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, 1);
if (!flash) {

Loading…
Cancel
Save