F-9740: update_disk: FSP low-mem rejection falls back to other slot

The WOLFBOOT_FSP low-memory size check was the only per-slot rejection
in the boot retry loop that used a bare break, so an image whose
header-declared fw_size exceeded the tolum window aborted the boot
instead of trying the other slot. Every other rejection in the same
loop switches partitions and retries; match that.
pull/882/head
Daniele Lacamera 2026-09-04 10:32:13 +02:00
parent 77fe733e7f
commit a2d5543ebb
3 changed files with 290 additions and 2 deletions

View File

@ -702,7 +702,8 @@ void RAMFUNCTION wolfBoot_start(void)
(uint32_t)(uintptr_t)load_address)) {
wolfBoot_printf("Image size %u doesn't fit in low memory\r\n",
os_image.fw_size);
break;
selected ^= 1;
continue;
}
/* Log memory load */
x86_log_memory_load((uint32_t)(uintptr_t)load_address,

View File

@ -63,7 +63,7 @@ TESTS:=unit-parser unit-parser-large-header unit-fdt unit-extflash unit-string \
unit-update-flash-hook \
unit-update-flash-self-update \
unit-update-flash-enc unit-update-flash-enc-full unit-update-ram unit-update-ram-uboot unit-update-ram-enc unit-update-ram-enc-nopart unit-update-ram-nofixed unit-update-ram-noramboot unit-update-flash-hwswap unit-pkcs11_store unit-psa_store unit-wolfhsm_flash_hal unit-disk \
unit-update-disk unit-update-disk-oob unit-update-disk-fit unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \
unit-update-disk unit-update-disk-fsp unit-update-disk-oob unit-update-disk-fit unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \
unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-image-dts \
unit-image-dts-sha384 unit-image-dts-sha3-384 unit-store-sbrk \
unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \
@ -899,6 +899,16 @@ unit-update-flash-hwswap: ../../include/target.h unit-update-flash-hwswap.c
unit-update-disk: ../../include/target.h unit-update-disk.c
gcc -o $@ unit-update-disk.c $(CFLAGS) $(LDFLAGS)
# WOLFBOOT_FSP (x86) boot path of update_disk.c: the low-memory (tolum)
# size check must reject a slot and fall back to the other one, like every
# other per-slot rejection in the retry loop.
unit-update-disk-fsp:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED -DWOLFBOOT_FSP \
-DUCODE0_ADDRESS=0 -DWOLFBOOT_LOAD_BASE=0x100000 \
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT \
-DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
unit-update-disk-fsp: ../../include/target.h unit-update-disk-fsp.c
gcc -o $@ unit-update-disk-fsp.c $(CFLAGS) $(LDFLAGS)
unit-update-disk-oob: ../../include/target.h unit-update-disk-oob.c
gcc -o $@ unit-update-disk-oob.c $(CFLAGS) $(LDFLAGS)

View File

@ -0,0 +1,277 @@
/* unit-update-disk-fsp.c
*
* Unit tests for the WOLFBOOT_FSP (x86 FSP) boot path of update_disk.c:
* the low-memory (tolum) size check must reject a slot and try the other
* one, like every other per-slot rejection in the retry loop.
*/
#define WOLFBOOT_UPDATE_DISK
#define WOLFBOOT_SELF_UPDATE_MONOLITHIC
#define RAM_CODE
#define WOLFBOOT_SELF_HEADER
#define IMAGE_HEADER_SIZE 256
#define BOOT_PART_A 0
#define BOOT_PART_B 1
#define MOCK_ADDRESS_BOOT 0xCD000000
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <check.h>
#include "hal.h"
#include "target.h"
#include "wolfboot/wolfboot.h"
#include "image.h"
#include "loader.h"
#include "stage2_params.h"
#include "x86/common.h"
#define TEST_PAYLOAD_SIZE 64
static uint8_t load_buffer[TEST_PAYLOAD_SIZE];
#define WOLFBOOT_LOAD_ADDRESS ((uintptr_t)load_buffer)
static uint8_t part_a_image[IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE];
static uint8_t part_b_image[IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE];
static int mock_do_boot_called;
static const uint32_t *mock_boot_address;
static struct stage2_parameter mock_stage2_params;
static void set_u16_le(uint8_t *dst, uint16_t value)
{
dst[0] = (uint8_t)(value & 0xFF);
dst[1] = (uint8_t)(value >> 8);
}
static void set_u32_le(uint8_t *dst, uint32_t value)
{
dst[0] = (uint8_t)(value & 0xFF);
dst[1] = (uint8_t)((value >> 8) & 0xFF);
dst[2] = (uint8_t)((value >> 16) & 0xFF);
dst[3] = (uint8_t)((value >> 24) & 0xFF);
}
/* fw_size may legitimately claim more than the bytes actually present in
* the partition buffer: the low-memory check must reject such a slot
* before the payload is read. */
static void build_image(uint8_t *image, uint32_t version, uint32_t fw_size,
uint8_t fill)
{
memset(image, 0, IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE);
set_u32_le(image, WOLFBOOT_MAGIC);
set_u32_le(image + sizeof(uint32_t), fw_size);
set_u16_le(image + IMAGE_HEADER_OFFSET, HDR_VERSION);
set_u16_le(image + IMAGE_HEADER_OFFSET + sizeof(uint16_t), 4);
set_u32_le(image + IMAGE_HEADER_OFFSET + 2 * sizeof(uint16_t), version);
memset(image + IMAGE_HEADER_SIZE, fill, TEST_PAYLOAD_SIZE);
}
static void reset_mocks(void)
{
memset(load_buffer, 0, sizeof(load_buffer));
build_image(part_a_image, 7, TEST_PAYLOAD_SIZE, 0xA1);
build_image(part_b_image, 7, TEST_PAYLOAD_SIZE, 0xB2);
mock_do_boot_called = 0;
mock_boot_address = NULL;
/* The image must fit between load_address and tolum: exactly one
* TEST_PAYLOAD_SIZE payload. */
mock_stage2_params.tolum =
(uint32_t)((uintptr_t)load_buffer + TEST_PAYLOAD_SIZE);
wolfBoot_panicked = 0;
}
/* --- mocks ---------------------------------------------------------- */
int disk_init(int drv)
{
(void)drv;
return 0;
}
int disk_open(int drv)
{
(void)drv;
return 0;
}
void disk_close(int drv)
{
(void)drv;
}
int disk_part_read(int drv, int part, uint64_t off, uint64_t sz, uint8_t *buf)
{
uint8_t *image;
uint64_t max = IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE;
(void)drv;
image = (part == BOOT_PART_B) ? part_b_image : part_a_image;
if ((off > max) || (sz > (max - off)))
return -1;
memcpy(buf, image + off, (size_t)sz);
return (int)sz;
}
uint32_t wolfBoot_get_blob_version(uint8_t *blob)
{
uint8_t *p = blob + IMAGE_HEADER_OFFSET;
uint8_t *end = blob + IMAGE_HEADER_SIZE;
uint16_t type;
uint16_t len;
uint32_t version = 0;
while (((uintptr_t)p + 4) <= (uintptr_t)end) {
type = (uint16_t)(p[0] | (p[1] << 8));
if (type == 0)
break;
len = (uint16_t)(p[2] | (p[3] << 8));
if (type == HDR_VERSION) {
memcpy(&version, p + 4, sizeof(version));
break;
}
p += 4 + len;
}
return version;
}
int wolfBoot_open_image_address(struct wolfBoot_image* img, uint8_t* image)
{
uint32_t magic;
uint32_t fw_size;
memcpy(&magic, image, sizeof(magic));
if (magic != WOLFBOOT_MAGIC)
return -1;
memset(img, 0, sizeof(*img));
img->hdr = image;
memcpy(&fw_size, image + sizeof(uint32_t), sizeof(fw_size));
img->fw_size = fw_size;
img->fw_base = image + IMAGE_HEADER_SIZE;
img->hdr_ok = 1;
return 0;
}
int wolfBoot_verify_integrity(struct wolfBoot_image* img)
{
img->sha_ok = 1;
return 0;
}
int wolfBoot_verify_authenticity(struct wolfBoot_image* img)
{
img->signature_ok = 1;
return 0;
}
int wolfBoot_get_dts_size(void *dts_addr, uint32_t capacity)
{
(void)capacity;
(void)dts_addr;
return -1;
}
struct stage2_parameter *stage2_get_parameters(void)
{
return &mock_stage2_params;
}
void x86_log_memory_load(uint32_t start, uint32_t end, const char *name)
{
(void)start;
(void)end;
(void)name;
}
void hal_prepare_boot(void)
{
}
int hal_flash_protect(haladdr_t address, int len)
{
(void)address;
(void)len;
return 0;
}
void do_boot(const uint32_t *address)
{
mock_do_boot_called++;
mock_boot_address = address;
}
#include "update_disk.c"
/* --- tests ---------------------------------------------------------- */
START_TEST(test_fsp_oversized_slot_falls_back_to_other_slot)
{
/* Slot A declares a payload that does not fit in low memory; slot B
* holds a good image. Boot must continue with B instead of aborting. */
reset_mocks();
build_image(part_a_image, 7, TEST_PAYLOAD_SIZE * 2, 0xA1);
build_image(part_b_image, 7, TEST_PAYLOAD_SIZE, 0xB2);
wolfBoot_start();
ck_assert_int_eq(wolfBoot_panicked, 0);
ck_assert_int_eq(mock_do_boot_called, 1);
ck_assert_ptr_eq(mock_boot_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS);
ck_assert_int_eq(memcmp(load_buffer, part_b_image + IMAGE_HEADER_SIZE,
TEST_PAYLOAD_SIZE), 0);
}
END_TEST
START_TEST(test_fsp_both_slots_oversized_panics)
{
/* Neither slot fits: the retry loop must exhaust and panic. */
reset_mocks();
build_image(part_a_image, 7, TEST_PAYLOAD_SIZE * 2, 0xA1);
build_image(part_b_image, 7, TEST_PAYLOAD_SIZE * 2, 0xB2);
wolfBoot_start();
ck_assert_int_gt(wolfBoot_panicked, 0);
ck_assert_int_eq(mock_do_boot_called, 0);
}
END_TEST
START_TEST(test_fsp_fitting_slot_boots)
{
/* Both slots fit and versions are equal: primary (A) boots. */
reset_mocks();
wolfBoot_start();
ck_assert_int_eq(wolfBoot_panicked, 0);
ck_assert_int_eq(mock_do_boot_called, 1);
ck_assert_ptr_eq(mock_boot_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS);
ck_assert_int_eq(memcmp(load_buffer, part_a_image + IMAGE_HEADER_SIZE,
TEST_PAYLOAD_SIZE), 0);
}
END_TEST
Suite *wolfboot_suite(void)
{
Suite *s = suite_create("wolfBoot");
TCase *tc = tcase_create("update-disk-fsp");
tcase_add_test(tc, test_fsp_oversized_slot_falls_back_to_other_slot);
tcase_add_test(tc, test_fsp_both_slots_oversized_panics);
tcase_add_test(tc, test_fsp_fitting_slot_boots);
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
int fails;
Suite *s = wolfboot_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
fails = srunner_ntests_failed(sr);
srunner_free(sr);
return fails;
}