Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion hal/samr21.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
while (len > 0) {
NVMCTRL_ADDR = (address >> 1); /* This register holds the address of a 16-bit row */
NVMCTRLA_REG = NVMCMD_ERASE | NVMCMD_KEY;
while(!(NVMCTRL_INTFLAG & NVMCTRL_INTFLAG_NVMREADY))
while (!(NVMCTRL_INTFLAG & NVMCTRL_INTFLAG_NVMREADY)) { }
address += FLASH_PAGESIZE;
len -= FLASH_PAGESIZE;
}
return 0;
Expand Down
5 changes: 5 additions & 0 deletions src/libwolfboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,11 @@ void RAMFUNCTION wolfBoot_update_trigger(void)
/* erase the previously selected sector */
hal_flash_erase(lastSector - WOLFBOOT_SECTOR_SIZE * selSec,
WOLFBOOT_SECTOR_SIZE);
/* The staged sector may hold the firmware key/nonce (see
* ENCRYPT_CACHE under NVM_FLASH_WRITEONCE): scrub it, as the
* partition-trailer helpers do, before releasing the flash
* lock. */
nvm_cache_scrub();
#endif
}

Expand Down
26 changes: 24 additions & 2 deletions src/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,13 @@ static int pci_program_bridge(uint8_t bus, uint8_t dev, uint8_t fun,
orig_cmd = pci_config_read16(bus, dev, fun, PCI_COMMAND_OFFSET);
pci_config_write16(bus, dev, fun, PCI_COMMAND_OFFSET, 0);

/* curr_bus_number is one bus per bridge level; at 0xFF the next
* increment wraps to 0, which would write SECONDARY_BUS 0 and
* re-enumerate bus 0 over the already configured tree. Disable
* this bridge instead. */
if (info->curr_bus_number == 0xFF)
goto err;

info->curr_bus_number++;
PCI_DEBUG_PRINTF("Bridge: %x.%x.%x (using bus number: %d)\r\n",
(int)bus, (int)dev, (int)fun, info->curr_bus_number);
Expand Down Expand Up @@ -926,11 +933,26 @@ int pci_enum_do(void)
struct pci_enum_info enum_info;
int ret;

/* Pool limits are exclusive ends: the allocator accepts a region
* when its end is <= limit (pci_enum_next_aligned32, the BAR end
* check, pci_align_check_up) and the IO limit is the 16-bit IO
* ceiling, not the last usable address. A region ending exactly
* at base + length must fit, so initialize base + length; reject
* a pool whose end would wrap the 32-bit address space. */
if ((uint64_t)PCI_MMIO32_BASE + PCI_MMIO32_LENGTH > 0xFFFFFFFFULL ||
(uint64_t)PCI_MMIO32_PREFETCH_BASE +
PCI_MMIO32_PREFETCH_LENGTH > 0xFFFFFFFFULL)
{
PCI_DEBUG_PRINTF("PCI MMIO pool overflows the 32-bit address "
"space\r\n");
return -1;
}

enum_info.mem = PCI_MMIO32_BASE;
enum_info.mem_limit = enum_info.mem + (PCI_MMIO32_LENGTH - 1);
enum_info.mem_limit = enum_info.mem + PCI_MMIO32_LENGTH;
enum_info.mem_pf = PCI_MMIO32_PREFETCH_BASE;
enum_info.mem_pf_limit = enum_info.mem_pf +
(PCI_MMIO32_PREFETCH_LENGTH - 1);
PCI_MMIO32_PREFETCH_LENGTH;
enum_info.io = PCI_IO32_BASE;
enum_info.curr_bus_number = 0;

Expand Down
21 changes: 19 additions & 2 deletions src/sdhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,13 @@ static int sdcard_card_init(uint32_t acmd41_arg, uint32_t *ocr_reg)
static int sdcard_set_bus_width(uint32_t bus_width);
static int sdcard_set_function(uint32_t function_number, uint32_t group_number);

/* A card that answers ACMD41 forever without setting OCR ready must
* not hold the boot. The budget matches the sdhci_wait_busy() wait;
* a healthy card reports ready in milliseconds. */
#ifndef SDCARD_ACMD41_TIMEOUT_MS
#define SDCARD_ACMD41_TIMEOUT_MS 30000
#endif

/* Full SD card initialization sequence
* Returns 0 on success */
static int sdcard_card_full_init(void)
Expand Down Expand Up @@ -898,6 +905,9 @@ static int sdcard_card_full_init(void)
}

if (status == 0) {
uint64_t start = hal_get_timer_us();
const uint64_t timeout_us =
(uint64_t)SDCARD_ACMD41_TIMEOUT_MS * 1000U;
/* configure operating conditions */
uint32_t cmd_arg = SDCARD_ACMD41_HCS;
cmd_arg |= card_volts;
Expand All @@ -911,10 +921,17 @@ static int sdcard_card_full_init(void)
wolfBoot_printf("sdcard_init: sending OCR arg: 0x%08X\n", cmd_arg);
#endif

/* retry until OCR ready */
/* retry until OCR ready; a card that never sets it must not
* hold the boot, so bound the poll like sdhci_wait_busy() and
* service the watchdog inside it */
do {
status = sdcard_card_init(cmd_arg, &reg);
} while (status == 0 && (reg & SDCARD_REG_OCR_READY) == 0);
if (status != 0 || (reg & SDCARD_REG_OCR_READY) != 0)
break;
sdhci_platform_wdt_pet();
if (hal_get_timer_us() - start > timeout_us)
status = -1;
} while (status == 0);
}

if (status == 0) {
Expand Down
46 changes: 46 additions & 0 deletions tools/unit-tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ TESTS+=unit-versal-ext-write
TESTS+=unit-t10xx-qe-firmware
TESTS+=unit-t10xx-flash-status
TESTS+=unit-p1021-erase-advance
TESTS+=unit-samr21-erase-advance
TESTS+=unit-hifive1-flash-write
TESTS+=unit-fwtpm-rsp-overrun
TESTS+=unit-fwtpm-cmd-toctou
Expand All @@ -127,8 +128,10 @@ TESTS+=unit-stm32g4-write
TESTS+=unit-stm32l5-write
TESTS+=unit-stm32u5-write
TESTS+=unit-nvm-cache-scrub
TESTS+=unit-update-trigger-scrub
TESTS+=unit-sdhci-uhs-recover
TESTS+=unit-sdhci-wait-busy
TESTS+=unit-sdhci-acmd41-timeout
TESTS+=unit-ti-hercules-write
TESTS+=unit-p1021-qe-firmware
TESTS+=unit-t10xx-dts-memac
Expand Down Expand Up @@ -1078,6 +1081,26 @@ unit-p1021-erase-advance: unit-p1021-erase-advance.c p1021_erase_extract.h \
p1021_erase_fn_extract.h
gcc -o $@ unit-p1021-erase-advance.c $(CFLAGS) $(LDFLAGS)

# unit-samr21-erase-advance runs the real hal_flash_erase() from
# hal/samr21.c against a host NVMCTRL register window (F-11036: the
# length decrement was the body of the NVMREADY wait and the address
# never advanced, so the loop re-erased the first page forever).
samr21_erase_extract.h: ../../hal/samr21.c
sed -n '/#define FLASH_PAGESIZE /p' $< > $@
sed -n '/#define NVMCTRLA_REG /p' $< >> $@
sed -n '/#define NVMCTRL_INTFLAG /p' $< >> $@
sed -n '/#define NVMCTRL_ADDR /p' $< >> $@
sed -n '/#define NVMCMD_KEY /p' $< >> $@
sed -n '/#define NVMCMD_ERASE /p' $< >> $@
sed -n '/#define NVMCTRL_INTFLAG_NVMREADY /p' $< >> $@

samr21_erase_fn_extract.h: ../../hal/samr21.c
sed -n '/^int RAMFUNCTION hal_flash_erase/,/^}/p' $< > $@

unit-samr21-erase-advance: unit-samr21-erase-advance.c samr21_erase_extract.h \
samr21_erase_fn_extract.h
gcc -o $@ unit-samr21-erase-advance.c $(CFLAGS) $(LDFLAGS)

# unit-hifive1-flash-write runs the real hal_flash_write() from
# hal/hifive1.c against a mock fespi model (F-11035: the final partial
# page of a multi-page write took the full-page branch, over-reading
Expand Down Expand Up @@ -1164,6 +1187,19 @@ nvm_cache_scrub_extract.h: ../../src/libwolfboot.c
unit-nvm-cache-scrub: unit-nvm-cache-scrub.c nvm_cache_scrub_extract.h
gcc -o $@ unit-nvm-cache-scrub.c $(CFLAGS) $(LDFLAGS)

# unit-update-trigger-scrub runs the real wolfBoot_update_trigger()
# from src/libwolfboot.c over the NVM_FLASH_WRITEONCE branch
# (F-11037: the staged trailer sector, which in EXT_ENCRYPTED builds
# carries the firmware key/nonce, was never scrubbed from NVM_CACHE).
# The function is extracted together with nvm_cache_scrub(); the build
# defines NVM_FLASH_WRITEONCE so the write-once branch compiles.
update_trigger_scrub_extract.h: ../../src/libwolfboot.c
sed -n '/^static void RAMFUNCTION nvm_cache_scrub(/,/^}/p' $< > $@
sed -n '/^void RAMFUNCTION wolfBoot_update_trigger(/,/^}/p' $< >> $@

unit-update-trigger-scrub: unit-update-trigger-scrub.c update_trigger_scrub_extract.h
gcc -o $@ unit-update-trigger-scrub.c -DNVM_FLASH_WRITEONCE $(CFLAGS) $(LDFLAGS)

# unit-sdhci-uhs-recover drives disk_read()'s UHS recovery path from the
# real src/sdhci.c (any read error permanently switched the host
# to 1.8V signaling with no rollback). sdhci_host.c (generated below) is
Expand Down Expand Up @@ -1191,6 +1227,16 @@ unit-sdhci-uhs-recover: unit-sdhci-uhs-recover.c sdhci_host.c
unit-sdhci-wait-busy: unit-sdhci-wait-busy.c sdhci_host.c
gcc -o $@ unit-sdhci-wait-busy.c -DDISK_SDCARD -DWOLFBOOT_NO_PRINTF $(CFLAGS) $(LDFLAGS)

# unit-sdhci-acmd41-timeout drives sdcard_card_full_init()'s ACMD41 OCR
# readiness poll from the real src/sdhci.c (F-11032: the do/while had no
# bound, so a card answering ACMD41 without ever setting OCR ready held
# the boot forever). Same sdhci_host.c generation as the wait-busy test;
# the card model sets OCR ready after N scripted ACMD41 polls, the timer
# stub steps 10 ms per read, and a command-write cap turns the pre-fix
# infinite loop into an abort.
unit-sdhci-acmd41-timeout: unit-sdhci-acmd41-timeout.c sdhci_host.c
gcc -o $@ unit-sdhci-acmd41-timeout.c -DDISK_SDCARD -DWOLFBOOT_NO_PRINTF $(CFLAGS) $(LDFLAGS)

# unit-ti-hercules-write runs the real hal_flash_write() staging logic
# from hal/ti_hercules.c (a short write crossing a block
# boundary overran the staging buffer and lost the next block's bytes).
Expand Down
108 changes: 108 additions & 0 deletions tools/unit-tests/unit-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,74 @@ START_TEST(test_program_bridge_oom_post_enum)
}
END_TEST

/* test_program_bridge_bus_exhaustion: curr_bus_number is one bus per
* bridge level; at 0xFF the next increment wraps to 0, writing
* SECONDARY_BUS 0 and re-enumerating bus 0 over the already configured
* tree (unbounded recursion). The bridge at the exhaustion boundary
* must take the error path (bridge disabled, info restored); one below
* it the last usable number 0xFF is assigned. */
START_TEST(test_program_bridge_bus_exhaustion)
{
struct {
const char *label;
uint8_t curr;
int exp_ret;
uint8_t exp_curr;
} cases[] = {
{ "last usable bus number 0xFE", 0xFE, 0, 0xFF },
{ "exhausted at 0xFF", 0xFF, -1, 0xFF },
};
int i;

for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) {
struct test_pci_topology t;
struct pci_enum_info info;
int br, ret;
uint16_t cmd_before = 0x0007;
uint8_t sec, sub;

test_pci_init(&t);
br = test_pci_add_bridge(&t, 1, 0, 0xAAAA, 0xBBBB, TEST_PCI_ROOT_BUS);
test_pci_commit(&t);
memcpy(&t.nodes[br].cfg[PCI_COMMAND_OFFSET], &cmd_before, 2);

memset(&info, 0, sizeof(info));
info.mem = 0x80000000;
info.mem_limit = 0x88000000;
info.mem_pf = 0x90000000;
info.mem_pf_limit = 0xFFFFFFFF;
info.io = 0x2000;
info.curr_bus_number = cases[i].curr;

ret = pci_program_bridge(0, 1, 0, &info);
ck_assert_msg(ret == cases[i].exp_ret,
"%s: ret", cases[i].label);
ck_assert_msg(info.curr_bus_number == cases[i].exp_curr,
"%s: curr_bus_number", cases[i].label);

/* command register restored on both paths */
ck_assert_msg(pci_config_read16(0, 1, 0, PCI_COMMAND_OFFSET)
== cmd_before, "%s: cmd", cases[i].label);

if (cases[i].exp_ret != 0) {
/* the exhausted bridge must be left disabled */
sec = pci_config_read8(0, 1, 0, PCI_SECONDARY_BUS);
sub = pci_config_read8(0, 1, 0, PCI_SUB_SEC_BUS);
ck_assert_msg(sec == 0, "%s: secondary not cleared",
cases[i].label);
ck_assert_msg(sub == 0, "%s: subordinate not cleared",
cases[i].label);
}
else {
ck_assert_msg(pci_config_read8(0, 1, 0, PCI_SECONDARY_BUS)
== 0xFF, "%s: secondary", cases[i].label);
}

test_pci_cleanup(&t);
}
}
END_TEST

/* test_enum_bus_topology: device dispatch + multifunction handling */
START_TEST(test_enum_bus_topology)
{
Expand Down Expand Up @@ -1631,6 +1699,38 @@ START_TEST(test_enum_do_full)
}
END_TEST

/* test_enum_do_pool_fill: a BAR that exactly fills the configured MMIO
* pool [PCI_MMIO32_BASE, PCI_MMIO32_BASE + PCI_MMIO32_LENGTH) must be
* mapped. The pool limits are exclusive ends: the allocator accepts a
* region when its end is <= limit (pci_enum_next_aligned32, the BAR
* end check, pci_align_check_up) and the IO pool limit is the 16-bit
* ceiling, not the last usable address. Initializing the MMIO limits
* as base + length - 1 rejects this BAR and strands the last byte of
* the pool. */
START_TEST(test_enum_do_pool_fill)
{
struct test_pci_topology t;
int dev_node;
uint32_t bar_val;
int ret;

test_pci_init(&t);
dev_node = test_pci_add_dev(&t, 0, 0, 0x1234, 0x5678, TEST_PCI_ROOT_BUS);
/* 128 MB MMIO BAR: exactly the default pool size */
test_pci_dev_set_bar(&t, dev_node, 0, 0x08000000, TEST_PCI_BAR_MMIO);
test_pci_commit(&t);

ret = pci_enum_do();
ck_assert_int_eq(ret, 0);

/* The BAR must be programmed at the pool base */
bar_val = pci_config_read32(0, 0, 0, PCI_BAR0_OFFSET);
ck_assert_uint_eq(bar_val, 0x80000000);

test_pci_cleanup(&t);
}
END_TEST

/* test_enum_do_nested_bridges: end-to-end nested bridge enumeration */

START_TEST(test_enum_do_nested_bridges)
Expand Down Expand Up @@ -1924,6 +2024,10 @@ Suite *wolfboot_suite(void)
tcase_add_test(tc_oom_post, test_program_bridge_oom_post_enum);
suite_add_tcase(s, tc_oom_post);

TCase *tc_bus_exhaust = tcase_create("bridge-bus-exhaustion");
tcase_add_test(tc_bus_exhaust, test_program_bridge_bus_exhaustion);
suite_add_tcase(s, tc_bus_exhaust);

TCase *tc_enum_topo = tcase_create("enum-bus-topology");
tcase_add_test(tc_enum_topo, test_enum_bus_topology);
suite_add_tcase(s, tc_enum_topo);
Expand All @@ -1936,6 +2040,10 @@ Suite *wolfboot_suite(void)
tcase_add_test(tc_enum_nested, test_enum_do_nested_bridges);
suite_add_tcase(s, tc_enum_nested);

TCase *tc_enum_pool = tcase_create("enum-do-pool-fill");
tcase_add_test(tc_enum_pool, test_enum_do_pool_fill);
suite_add_tcase(s, tc_enum_pool);

TCase *tc_rw8 = tcase_create("config-rw-8bit-positions");
tcase_add_test(tc_rw8, test_config_rw_8bit_all_positions);
suite_add_tcase(s, tc_rw8);
Expand Down
Loading
Loading