mirror of https://github.com/wolfSSL/wolfBoot.git
F-5674: cap PCI IO BAR allocator at the 16-bit IO ceiling
A device advertising a 64KB IO BAR pushed the IO allocator cursor (info->io) past 0x10000 because pci_program_bar used 0xffffffff as the IO BAR limit. The PCI-to-PCI bridge IO base/limit registers are 8-bit and only carry address bits [15:8], so io_start >> 8 silently narrows a 0x20000 cursor to 0x00, programming a bogus bridge IO window 0x0000-0x0FFF that forwards legacy IO (8259A PIC, 8254 PIT, MC146818 RTC) to the secondary bus. x86 IO space is 16-bit, so IO BARs must never be allocated above 0xFFFF. Cap the IO BAR allocator limit at PCI_IO32_LIMIT (0x10000): oversized IO BARs are now skipped at allocation time and the bridge IO window is programmed from the real cursor, never narrowing onto legacy IO. Add test_program_bridge_io_64k_no_narrow proving the bridge IO window is no longer mis-programmed. The post-enum IO OOM case is removed as the cap makes that wrap unreachable.pull/791/head
parent
9a68ef15dd
commit
3bfd7de34d
10
src/pci.c
10
src/pci.c
|
|
@ -70,6 +70,14 @@
|
|||
#define PCI_IO32_BASE 0x2000
|
||||
#endif /* PCI_IO32_BASE */
|
||||
|
||||
#ifndef PCI_IO32_LIMIT
|
||||
/* x86 IO space is 16-bit: valid IO addresses are 0x0000-0xFFFF. IO BARs must
|
||||
* not be allocated past this ceiling, otherwise the 8-bit bridge IO base/limit
|
||||
* registers (which only carry address bits [15:8]) silently truncate the high
|
||||
* bits, mis-programming the window onto the legacy IO range (PIC/PIT/RTC). */
|
||||
#define PCI_IO32_LIMIT 0x10000
|
||||
#endif /* PCI_IO32_LIMIT */
|
||||
|
||||
#define PCI_ENUM_MAX_DEV 32
|
||||
#define PCI_ENUM_MAX_FUN 8
|
||||
#define PCI_ENUM_MAX_BARS 6
|
||||
|
|
@ -474,7 +482,7 @@ static int pci_program_bar(uint8_t bus, uint8_t dev, uint8_t fun,
|
|||
if ((bar_align & PCI_DATA_HI16_MASK) == 0)
|
||||
bar_align |= PCI_DATA_HI16_MASK;
|
||||
base = &info->io;
|
||||
limit = 0xffffffff;
|
||||
limit = PCI_IO32_LIMIT;
|
||||
}
|
||||
|
||||
PCI_DEBUG_PRINTF("PCI enum: %s %x:%x.%x bar: %d val: %x (%s %s)\r\n",
|
||||
|
|
|
|||
|
|
@ -1243,6 +1243,55 @@ START_TEST(test_program_bridge)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* test_program_bridge_io_64k_no_narrow: a device advertising a 64KB IO BAR
|
||||
* must not push the IO allocator past the 16-bit IO space (0xFFFF). The PCI
|
||||
* bridge IO base/limit registers carry only address bits [15:8] in a uint8_t,
|
||||
* so an io cursor >= 0x10000 truncates: io_start 0x20000 -> base reg 0x00,
|
||||
* programming a bogus window 0x0000-0x0FFF that forwards legacy IO (8259A PIC,
|
||||
* 8254 PIT, MC146818 RTC) to the secondary bus. With the allocator capped at
|
||||
* the 16-bit IO ceiling the oversized BAR is skipped and the bridge window is
|
||||
* programmed from the real cursor. */
|
||||
START_TEST(test_program_bridge_io_64k_no_narrow)
|
||||
{
|
||||
struct test_pci_topology t;
|
||||
struct pci_enum_info info;
|
||||
int dev0, br, ep;
|
||||
uint8_t iobase, iolimit;
|
||||
|
||||
test_pci_init(&t);
|
||||
/* dev 0 on bus 0: hostile/oversized 64KB IO BAR (decodes only 16 bits) */
|
||||
dev0 = test_pci_add_dev(&t, 0, 0, 0x1111, 0x2222, TEST_PCI_ROOT_BUS);
|
||||
test_pci_dev_set_bar(&t, dev0, 0, 0x10000, TEST_PCI_BAR_IO);
|
||||
t.nodes[dev0].bars[0].io_hi16_zero = 1;
|
||||
/* dev 1 on bus 0: bridge with a small 256B IO device behind it */
|
||||
br = test_pci_add_bridge(&t, 1, 0, 0xAAAA, 0xBBBB, TEST_PCI_ROOT_BUS);
|
||||
ep = test_pci_add_dev(&t, 0, 0, 0xCCCC, 0xDDDD, br);
|
||||
test_pci_dev_set_bar(&t, ep, 0, 256, TEST_PCI_BAR_IO);
|
||||
test_pci_commit(&t);
|
||||
|
||||
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 = 0;
|
||||
|
||||
pci_enum_bus(0, &info);
|
||||
|
||||
/* The oversized IO BAR must be skipped, leaving the cursor in 16-bit IO
|
||||
* space; the bridge IO window must reflect the real device (0x2000-0x2FFF)
|
||||
* and never decode down to 0x0000 over the legacy IO range. */
|
||||
iobase = pci_config_read8(0, 1, 0, PCI_IO_BASE_OFF);
|
||||
iolimit = pci_config_read8(0, 1, 0, PCI_IO_LIMIT_OFF);
|
||||
ck_assert_uint_eq(iobase, 0x20);
|
||||
ck_assert_uint_eq(iolimit, 0x2F);
|
||||
ck_assert_uint_le(info.io, 0x10000);
|
||||
|
||||
test_pci_cleanup(&t);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* test_program_bridge_oom_initial: initial alignment failures */
|
||||
START_TEST(test_program_bridge_oom_initial)
|
||||
{
|
||||
|
|
@ -1335,13 +1384,11 @@ START_TEST(test_program_bridge_oom_post_enum)
|
|||
.mem_pf = 0x90000000, .mem_pf_limit = 0xFFFFFFFF,
|
||||
.io = 0x2000 }
|
||||
},
|
||||
{
|
||||
"io: post-enum 4KB align wraps 32-bit space",
|
||||
256, TEST_PCI_BAR_IO,
|
||||
{ .mem = 0x80000000, .mem_limit = 0x88000000,
|
||||
.mem_pf = 0x90000000, .mem_pf_limit = 0xFFFFFFFF,
|
||||
.io = 0xFFFFF000 }
|
||||
},
|
||||
/* No "io: post-enum align wraps" case: the IO allocator is capped at
|
||||
* the 16-bit IO ceiling (PCI_IO32_LIMIT), so info.io can never reach
|
||||
* the top of the 32-bit range and the post-enum IO alignment cannot
|
||||
* wrap. Oversized IO BARs are now skipped at allocation time, covered
|
||||
* by test_program_bridge_io_64k_no_narrow. */
|
||||
};
|
||||
int i;
|
||||
|
||||
|
|
@ -1751,6 +1798,10 @@ Suite *wolfboot_suite(void)
|
|||
tcase_add_test(tc_bridge, test_program_bridge);
|
||||
suite_add_tcase(s, tc_bridge);
|
||||
|
||||
TCase *tc_io_64k = tcase_create("bridge-io-64k-no-narrow");
|
||||
tcase_add_test(tc_io_64k, test_program_bridge_io_64k_no_narrow);
|
||||
suite_add_tcase(s, tc_io_64k);
|
||||
|
||||
TCase *tc_oom_init = tcase_create("bridge-oom-initial");
|
||||
tcase_add_test(tc_oom_init, test_program_bridge_oom_initial);
|
||||
suite_add_tcase(s, tc_oom_init);
|
||||
|
|
|
|||
Loading…
Reference in New Issue