Skip to content

Commit 38e8159

Browse files
nordic-mik7rlubos
authored andcommitted
[nrf noup] decompression: Fix flash writes alignment
nrf-squash! [nrf noup] zephyr: Add support for compressed image updates This commit aligns flash writes to any boundaries returned by flash_area_align() instead of using a fixed 4-byte alignment. This change ensures that the decompression code works correctly with flash areas that may require different alignment, such as those used by the nRF54H20 SoC. This commit also fixes following issues: - hash calculation of header padding with bytes other than flash 'erased value' (may be 0x00) - buffer overflow from previous approach of caching unaligned ARM thumb filter output. For 'excess_data_buffer_full == true', decomp_buf could be of DECOMP_BUF_ALLOC_SIZE size already before moving and restoring cached data to the beginning of the buffer. - missing ARM thumb cached bytes restoration after leaving main decompression loop. ref: NCSDK-33841 Signed-off-by: Michal Kozikowski <[email protected]>
1 parent 6821cbf commit 38e8159

File tree

1 file changed

+144
-87
lines changed

1 file changed

+144
-87
lines changed

boot/zephyr/decompression.c

Lines changed: 144 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131

3232
#define DECOMP_BUF_SIZE CONFIG_BOOT_DECOMPRESSION_BUFFER_SIZE
3333
#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB)
34+
/* Extra buffer space for being able to writeback ARM thumb decompression output,
35+
* which may be of +2 bytes more size than its input.
36+
*/
3437
#define DECOMP_BUF_EXTRA_SIZE 2
3538
#else
3639
#define DECOMP_BUF_EXTRA_SIZE 0
@@ -184,7 +187,6 @@ int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_h
184187
struct nrf_compress_implementation *compression_arm_thumb = NULL;
185188
TARGET_STATIC struct image_header modified_hdr;
186189
bootutil_sha_context sha_ctx;
187-
uint8_t flash_erased_value;
188190

189191
#ifdef MCUBOOT_ENC_IMAGES
190192
struct enc_key_data *enc_state;
@@ -301,8 +303,6 @@ int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_h
301303
modified_hdr.ih_protect_tlv_size = protected_tlv_size;
302304
bootutil_sha_update(&sha_ctx, &modified_hdr, sizeof(modified_hdr));
303305
read_pos = sizeof(modified_hdr);
304-
flash_erased_value = flash_area_erased_val(fap);
305-
memset(tmp_buf, flash_erased_value, tmp_buf_sz);
306306

307307
while (read_pos < modified_hdr.ih_hdr_size) {
308308
uint32_t copy_size = tmp_buf_sz;
@@ -311,6 +311,15 @@ int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_h
311311
copy_size = modified_hdr.ih_hdr_size - read_pos;
312312
}
313313

314+
rc = flash_area_read(fap, read_pos, tmp_buf, copy_size);
315+
316+
if (rc != 0) {
317+
BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d",
318+
(hdr->ih_hdr_size + read_pos), copy_size, fap->fa_id, rc);
319+
rc = BOOT_EFLASH;
320+
goto finish;
321+
}
322+
314323
bootutil_sha_update(&sha_ctx, tmp_buf, copy_size);
315324
read_pos += copy_size;
316325
}
@@ -993,6 +1002,75 @@ static int boot_copy_unprotected_tlvs(const struct image_header *hdr,
9931002
return rc;
9941003
}
9951004

1005+
#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB)
1006+
/**
1007+
* @brief Helper function for in-place ARM Thumb filtering.
1008+
* This function places the decompressed data back into the same buffer
1009+
* at the beginning, overwriting the compressed data. WARNING: because
1010+
* ARM Thumb filtering can return +-2 more/less bytes than the input,
1011+
* the buffer provided needs to have free DECOMP_BUF_EXTRA_SIZE bytes at
1012+
* the beginning and provide valid data for filtering after these.
1013+
*
1014+
* @param[in] arm_thumb_impl Pointer to the ARM Thumb decompression implementation.
1015+
* @param[in,out] buf Pointer to the buffer containing the compressed data / filtered data.
1016+
* @param[in] buf_size Size of the buffer (including DECOMP_BUF_EXTRA_SIZE bytes at the beginning).
1017+
* @param[out] out_size Pointer to a variable where the size of the filtered data will be stored.
1018+
* @param[in] last_part Indicates if this is the last part of the data to be filtered.
1019+
*
1020+
* @return 0 on success, BOOT_EBADSTATUS on error.
1021+
*/
1022+
static int boot_arm_thumb_filter(struct nrf_compress_implementation * const arm_thumb_impl,
1023+
uint8_t *buf, size_t buf_size, size_t *out_size, bool last_part) {
1024+
1025+
uint32_t filter_writeback_pos = 0;
1026+
uint32_t processed_size = 0;
1027+
int rc;
1028+
1029+
while (processed_size < (buf_size - DECOMP_BUF_EXTRA_SIZE)) {
1030+
uint32_t offset_arm_thumb = 0;
1031+
uint32_t output_size_arm_thumb = 0;
1032+
uint8_t *output_arm_thumb = NULL;
1033+
uint32_t current_size = (buf_size - DECOMP_BUF_EXTRA_SIZE - processed_size);
1034+
bool arm_thumb_last_packet = false;
1035+
1036+
if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) {
1037+
current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE;
1038+
}
1039+
1040+
if (last_part && (processed_size + current_size) == (buf_size - DECOMP_BUF_EXTRA_SIZE)) {
1041+
arm_thumb_last_packet = true;
1042+
}
1043+
1044+
rc = arm_thumb_impl->decompress(NULL,
1045+
&buf[processed_size +
1046+
DECOMP_BUF_EXTRA_SIZE],
1047+
current_size,
1048+
arm_thumb_last_packet,
1049+
&offset_arm_thumb,
1050+
&output_arm_thumb,
1051+
&output_size_arm_thumb);
1052+
1053+
if (rc) {
1054+
BOOT_LOG_ERR("Decompression error: %d", rc);
1055+
return BOOT_EBADSTATUS;
1056+
}
1057+
1058+
if (output_size_arm_thumb > (buf_size - filter_writeback_pos)) {
1059+
BOOT_LOG_ERR("Filter writeback position exceeds buffer size");
1060+
return BOOT_EBADSTATUS;
1061+
}
1062+
1063+
memcpy(&buf[filter_writeback_pos], output_arm_thumb,
1064+
output_size_arm_thumb);
1065+
filter_writeback_pos += output_size_arm_thumb;
1066+
processed_size += offset_arm_thumb;
1067+
}
1068+
*out_size = filter_writeback_pos;
1069+
1070+
return 0;
1071+
}
1072+
#endif /* CONFIG_NRF_COMPRESS_ARM_THUMB */
1073+
9961074
int boot_copy_region_decompress(struct boot_loader_state *state, const struct flash_area *fap_src,
9971075
const struct flash_area *fap_dst, uint32_t off_src,
9981076
uint32_t off_dst, uint32_t sz, uint8_t *buf, size_t buf_size)
@@ -1011,10 +1089,10 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl
10111089
struct image_header *hdr;
10121090
TARGET_STATIC uint8_t decomp_buf[DECOMP_BUF_ALLOC_SIZE] __attribute__((aligned(4)));
10131091
TARGET_STATIC struct image_header modified_hdr;
1092+
uint16_t decomp_buf_max_size;
10141093

10151094
#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB)
1016-
uint8_t excess_data_buffer[DECOMP_BUF_EXTRA_SIZE];
1017-
bool excess_data_buffer_full = false;
1095+
uint8_t unaligned_data_length = 0;
10181096
#endif
10191097

10201098
#ifdef MCUBOOT_ENC_IMAGES
@@ -1069,6 +1147,8 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl
10691147

10701148
write_alignment = flash_area_align(fap_dst);
10711149

1150+
decomp_buf_max_size = DECOMP_BUF_SIZE - (DECOMP_BUF_SIZE % write_alignment);
1151+
10721152
memcpy(&modified_hdr, hdr, sizeof(modified_hdr));
10731153

10741154
rc = bootutil_get_img_decomp_size(hdr, fap_src, &decompressed_image_size);
@@ -1199,7 +1279,7 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl
11991279

12001280
/* Copy data to secondary buffer for writing out */
12011281
while (output_size > 0) {
1202-
uint32_t data_size = (DECOMP_BUF_SIZE - decomp_buf_size);
1282+
uint32_t data_size = (decomp_buf_max_size - decomp_buf_size);
12031283

12041284
if (data_size > output_size) {
12051285
data_size = output_size;
@@ -1222,106 +1302,62 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl
12221302
output_size -= data_size;
12231303

12241304
/* Write data out from secondary buffer when it is full */
1225-
if (decomp_buf_size == DECOMP_BUF_SIZE) {
1305+
if (decomp_buf_size == decomp_buf_max_size) {
12261306
#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB)
12271307
if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) {
1228-
uint32_t filter_writeback_pos = 0;
1229-
uint32_t processed_size = 0;
1308+
1309+
uint32_t filter_output_size;
12301310

12311311
/* Run this through the ARM thumb filter */
1232-
while (processed_size < DECOMP_BUF_SIZE) {
1233-
uint32_t offset_arm_thumb = 0;
1234-
uint32_t output_size_arm_thumb = 0;
1235-
uint8_t *output_arm_thumb = NULL;
1236-
uint32_t current_size = DECOMP_BUF_SIZE;
1237-
bool arm_thumb_last_packet = false;
1238-
1239-
if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) {
1240-
current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE;
1241-
}
1242-
1243-
if (last_packet && (processed_size + current_size) == DECOMP_BUF_SIZE
1244-
&& output_size == 0) {
1245-
arm_thumb_last_packet = true;
1246-
}
1247-
1248-
rc = compression_arm_thumb->decompress(NULL,
1249-
&decomp_buf[processed_size +
1250-
DECOMP_BUF_EXTRA_SIZE],
1251-
current_size,
1252-
arm_thumb_last_packet,
1253-
&offset_arm_thumb,
1254-
&output_arm_thumb,
1255-
&output_size_arm_thumb);
1256-
1257-
if (rc) {
1258-
BOOT_LOG_ERR("Decompression error: %d", rc);
1259-
rc = BOOT_EBADSTATUS;
1260-
goto finish;
1261-
}
1262-
1263-
memcpy(&decomp_buf[filter_writeback_pos], output_arm_thumb,
1264-
output_size_arm_thumb);
1265-
filter_writeback_pos += output_size_arm_thumb;
1266-
processed_size += current_size;
1267-
}
1312+
rc = boot_arm_thumb_filter(compression_arm_thumb,
1313+
&decomp_buf[unaligned_data_length],
1314+
decomp_buf_size - unaligned_data_length + DECOMP_BUF_EXTRA_SIZE,
1315+
&filter_output_size,
1316+
last_packet && output_size == 0);
12681317

1269-
if (excess_data_buffer_full == true)
1270-
{
1271-
/* Restore extra data removed from previous iteration to the write
1272-
* buffer
1273-
*/
1274-
memmove(&decomp_buf[DECOMP_BUF_EXTRA_SIZE], decomp_buf,
1275-
filter_writeback_pos);
1276-
memcpy(decomp_buf, excess_data_buffer, DECOMP_BUF_EXTRA_SIZE);
1277-
excess_data_buffer_full = false;
1278-
filter_writeback_pos += DECOMP_BUF_EXTRA_SIZE;
1318+
if (rc) {
1319+
goto finish;
12791320
}
12801321

1281-
if ((filter_writeback_pos % sizeof(uint32_t)) != 0)
1282-
{
1283-
/* Since there are an extra 2 bytes here, remove them and stash for
1284-
* later usage to prevent flash write issues with non-word boundary
1285-
* writes
1286-
*/
1287-
memcpy(excess_data_buffer, &decomp_buf[filter_writeback_pos -
1288-
DECOMP_BUF_EXTRA_SIZE],
1289-
DECOMP_BUF_EXTRA_SIZE);
1290-
excess_data_buffer_full = true;
1291-
filter_writeback_pos -= DECOMP_BUF_EXTRA_SIZE;
1292-
}
1322+
decomp_buf_size = filter_output_size + unaligned_data_length;
1323+
unaligned_data_length = decomp_buf_size % write_alignment;
12931324

1294-
rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos),
1295-
decomp_buf, filter_writeback_pos);
1325+
rc = flash_area_write(fap_dst,
1326+
(off_dst + hdr->ih_hdr_size + write_pos),
1327+
decomp_buf,
1328+
(decomp_buf_size - unaligned_data_length));
12961329

12971330
if (rc != 0) {
12981331
BOOT_LOG_ERR(
12991332
"Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d",
1300-
(off_dst + hdr->ih_hdr_size + write_pos), DECOMP_BUF_SIZE,
1333+
(off_dst + hdr->ih_hdr_size + write_pos),
1334+
(decomp_buf_size - unaligned_data_length),
13011335
fap_dst->fa_id, rc);
13021336
rc = BOOT_EFLASH;
13031337
goto finish;
13041338
}
13051339

1306-
write_pos += filter_writeback_pos;
1307-
decomp_buf_size = 0;
1308-
filter_writeback_pos = 0;
1340+
memmove(decomp_buf,
1341+
&decomp_buf[decomp_buf_size - unaligned_data_length],
1342+
unaligned_data_length);
1343+
write_pos += decomp_buf_size - unaligned_data_length;
1344+
decomp_buf_size = unaligned_data_length;
13091345
} else
13101346
#endif
13111347
{
13121348
rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos),
1313-
decomp_buf, DECOMP_BUF_SIZE);
1349+
decomp_buf, decomp_buf_max_size);
13141350

13151351
if (rc != 0) {
13161352
BOOT_LOG_ERR(
13171353
"Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d",
1318-
(off_dst + hdr->ih_hdr_size + write_pos), DECOMP_BUF_SIZE,
1354+
(off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_max_size,
13191355
fap_dst->fa_id, rc);
13201356
rc = BOOT_EFLASH;
13211357
goto finish;
13221358
}
13231359

1324-
write_pos += DECOMP_BUF_SIZE;
1360+
write_pos += decomp_buf_max_size;
13251361
decomp_buf_size = 0;
13261362
}
13271363
}
@@ -1336,21 +1372,42 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl
13361372
#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB)
13371373
if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT && decomp_buf_size > 0) {
13381374
/* Extra data that has not been written out that needs ARM thumb filter applied */
1339-
uint32_t offset_arm_thumb = 0;
1340-
uint32_t output_size_arm_thumb = 0;
1341-
uint8_t *output_arm_thumb = NULL;
13421375

1343-
rc = compression_arm_thumb->decompress(NULL, &decomp_buf[DECOMP_BUF_EXTRA_SIZE],
1344-
decomp_buf_size, true, &offset_arm_thumb,
1345-
&output_arm_thumb, &output_size_arm_thumb);
1376+
uint32_t filter_output_size;
1377+
1378+
rc = boot_arm_thumb_filter(compression_arm_thumb,
1379+
&decomp_buf[unaligned_data_length],
1380+
decomp_buf_size - unaligned_data_length + DECOMP_BUF_EXTRA_SIZE,
1381+
&filter_output_size,
1382+
true);
13461383

13471384
if (rc) {
1348-
BOOT_LOG_ERR("Decompression error: %d", rc);
1349-
rc = BOOT_EBADSTATUS;
13501385
goto finish;
13511386
}
13521387

1353-
memcpy(decomp_buf, output_arm_thumb, output_size_arm_thumb);
1388+
decomp_buf_size = filter_output_size + unaligned_data_length;
1389+
1390+
if (decomp_buf_size > decomp_buf_max_size) {
1391+
/* It can happen if ARM thumb decompression returned +2 bytes and we had near full
1392+
* decomp_buf. We still can hold these additional 2 bytes because of
1393+
* DECOMP_BUF_EXTRA_SIZE allocated. */
1394+
1395+
rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos),
1396+
decomp_buf, decomp_buf_max_size);
1397+
1398+
if (rc != 0) {
1399+
BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d",
1400+
(off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_max_size,
1401+
fap_dst->fa_id, rc);
1402+
rc = BOOT_EFLASH;
1403+
goto finish;
1404+
}
1405+
memmove(decomp_buf, &decomp_buf[decomp_buf_max_size],
1406+
(decomp_buf_size - decomp_buf_max_size));
1407+
1408+
decomp_buf_size = decomp_buf_size - decomp_buf_max_size;
1409+
write_pos += decomp_buf_max_size;
1410+
}
13541411
}
13551412
#endif
13561413

@@ -1361,7 +1418,7 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl
13611418
if (protected_tlv_size > 0) {
13621419
rc = boot_copy_protected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size +
13631420
write_pos), protected_tlv_size,
1364-
decomp_buf, DECOMP_BUF_SIZE, &decomp_buf_size,
1421+
decomp_buf, decomp_buf_max_size, &decomp_buf_size,
13651422
&tlv_write_size);
13661423

13671424
if (rc) {
@@ -1375,7 +1432,7 @@ int boot_copy_region_decompress(struct boot_loader_state *state, const struct fl
13751432
tlv_write_size = 0;
13761433
rc = boot_copy_unprotected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size +
13771434
write_pos), unprotected_tlv_size,
1378-
decomp_buf, DECOMP_BUF_SIZE, &decomp_buf_size,
1435+
decomp_buf, decomp_buf_max_size, &decomp_buf_size,
13791436
&tlv_write_size);
13801437

13811438
if (rc) {

0 commit comments

Comments
 (0)