mirror of https://github.com/wolfSSL/wolfBoot.git
pci: keep the allocation cursors 64-bit at the 4 GiB pool end
The cursors mem, mem_pf and io advanced as 32-bit values: after a BAR allocation whose end is exactly the pool end 0x100000000 (now reachable), *base = bar_value + length wrapped to 0. Every later allocation then passed the start and end checks and programmed its BAR at address 0, over the legacy IO range and DRAM. Widen the three cursors to 64-bit and advance with a 64-bit sum, so an exhausted pool leaves the cursor at the end. The address parameters of pci_enum_next_aligned32 and pci_align_check_up widen with them; pci_enum_next_aligned32 computes in uint64_t rather than uintptr_t, which on 32-bit targets would truncate the exhausted cursor back to 0 and defeat the addr > 0xffffffff rejection. The programmed BAR value stays 32-bit. The 4 GiB pool unit test now also adds a second device with a preset (previously programmed) BAR: exactly filling the pool must leave that BAR untouched instead of re-allocating it from a wrapped cursor. The mock learns to seed a BAR preset from the bar info, and the loop variable shadowing in test_pci_commit that it exposed is fixed (the inner preset loop clobbered the outer node loop counter, so only the first node was ever committed). Verified: unit-pci 30/30, unit-pci-4gib green, full nxp_t1024 powerpc build green.pull/874/head
parent
ceae23e34f
commit
2fdc99f323
|
|
@ -89,12 +89,14 @@ typedef struct {
|
|||
} pci_ctrlr_info_t;
|
||||
|
||||
struct pci_enum_info {
|
||||
uint32_t mem;
|
||||
/* Exclusive pool ends. 64-bit: a pool may end exactly at 4 GiB
|
||||
* (0x100000000), which a 32-bit field cannot represent. */
|
||||
/* Allocation cursors and exclusive pool ends. All 64-bit: a pool
|
||||
* may end exactly at 4 GiB (0x100000000), which a 32-bit value
|
||||
* cannot represent, and an exhausted cursor must stay at the pool
|
||||
* end instead of wrapping to 0 and re-allocating over address 0. */
|
||||
uint64_t mem;
|
||||
uint64_t mem_limit;
|
||||
uint32_t io;
|
||||
uint32_t mem_pf;
|
||||
uint64_t io;
|
||||
uint64_t mem_pf;
|
||||
uint64_t mem_pf_limit;
|
||||
uint8_t curr_bus_number;
|
||||
};
|
||||
|
|
|
|||
52
src/pci.c
52
src/pci.c
|
|
@ -100,18 +100,18 @@
|
|||
static int pci_enum_is_64bit(uint32_t value);
|
||||
static int pci_enum_is_mmio(uint32_t value);
|
||||
|
||||
static inline uint32_t align_up(uint32_t address, uint32_t alignment) {
|
||||
return (address + alignment - 1) & ~(alignment - 1);
|
||||
static inline uint64_t align_up(uint64_t address, uint32_t alignment) {
|
||||
return (address + alignment - 1) & ~(uint64_t)(alignment - 1);
|
||||
}
|
||||
|
||||
static inline uint32_t align_down(uint32_t address, uint32_t alignment) {
|
||||
return address & ~(alignment - 1);
|
||||
}
|
||||
|
||||
static int pci_align_check_up(uint32_t address, uint32_t alignment,
|
||||
uint64_t limit, uint32_t *aligned)
|
||||
static int pci_align_check_up(uint64_t address, uint32_t alignment,
|
||||
uint64_t limit, uint64_t *aligned)
|
||||
{
|
||||
uint32_t a;
|
||||
uint64_t a;
|
||||
a = align_up(address, alignment);
|
||||
if (a < address || a >= limit)
|
||||
return -1;
|
||||
|
|
@ -363,17 +363,20 @@ static int pci_enum_is_mmio(uint32_t value)
|
|||
return (value & PCI_ENUM_MMIND_MASK) == 0;
|
||||
}
|
||||
|
||||
static int pci_enum_next_aligned32(uint32_t address, uint32_t *next,
|
||||
static int pci_enum_next_aligned32(uint64_t address, uint32_t *next,
|
||||
uint32_t align, uint64_t limit)
|
||||
{
|
||||
uintptr_t addr;
|
||||
uint64_t addr;
|
||||
|
||||
addr = (uintptr_t)address;
|
||||
/* 64-bit on purpose: an exhausted pool leaves the cursor at
|
||||
* 0x100000000, which a 32-bit type (uintptr_t included on 32-bit
|
||||
* targets) would truncate back to 0. */
|
||||
addr = address;
|
||||
align = align-1;
|
||||
addr = (addr + align) & (~align);
|
||||
addr = (addr + align) & (~(uint64_t)align);
|
||||
if (addr > 0xffffffff)
|
||||
return -1;
|
||||
if (addr < (uintptr_t)address)
|
||||
if (addr < address)
|
||||
return -1;
|
||||
if (addr >= limit)
|
||||
return -1;
|
||||
|
|
@ -421,7 +424,7 @@ static int pci_program_bar(uint8_t bus, uint8_t dev, uint8_t fun,
|
|||
uint32_t length, align;
|
||||
uint8_t bar_off;
|
||||
int is_prefetch;
|
||||
uint32_t *base;
|
||||
uint64_t *base;
|
||||
uint64_t limit;
|
||||
uint32_t reg;
|
||||
int is_mmio;
|
||||
|
|
@ -524,7 +527,7 @@ static int pci_program_bar(uint8_t bus, uint8_t dev, uint8_t fun,
|
|||
pci_config_write32(bus, dev, fun, bar_off, bar_value);
|
||||
if (*is_64bit)
|
||||
pci_config_write32(bus, dev, fun, bar_off + 4, 0x0);
|
||||
*base = bar_value + length;
|
||||
*base = (uint64_t)bar_value + length;
|
||||
PCI_DEBUG_PRINTF("PCI enum: %s bus: %x:%x.%x bar: %d [%x,%x] (0x%x %s %s)\r\n",
|
||||
(is_mmio ? "mm" : "io"), bus, dev, fun, bar_idx, bar_value,
|
||||
bar_value + length, length, (*is_64bit) ? "64bit" : "",
|
||||
|
|
@ -617,14 +620,14 @@ static inline void pci_dump_bridge(uint8_t bus, uint8_t dev, uint8_t fun)
|
|||
static int pci_program_bridge(uint8_t bus, uint8_t dev, uint8_t fun,
|
||||
struct pci_enum_info *info)
|
||||
{
|
||||
uint32_t prefetch_start;
|
||||
uint32_t mem_start;
|
||||
uint32_t io_start;
|
||||
uint64_t prefetch_start;
|
||||
uint64_t mem_start;
|
||||
uint64_t io_start;
|
||||
uint32_t orig_cmd;
|
||||
uint8_t saved_bus;
|
||||
uint32_t saved_mem;
|
||||
uint32_t saved_pf;
|
||||
uint32_t saved_io;
|
||||
uint64_t saved_mem;
|
||||
uint64_t saved_pf;
|
||||
uint64_t saved_io;
|
||||
int ret;
|
||||
|
||||
saved_bus = info->curr_bus_number;
|
||||
|
|
@ -967,16 +970,17 @@ int pci_enum_do(void)
|
|||
ret = pci_enum_bus(0, &enum_info);
|
||||
|
||||
PCI_DEBUG_PRINTF("PCI Memory Mapped I/O range [0x%x,0x%x] (0x%x)\r\n",
|
||||
(uint32_t)PCI_MMIO32_BASE, enum_info.mem,
|
||||
enum_info.mem - PCI_MMIO32_BASE);
|
||||
(uint32_t)PCI_MMIO32_BASE, (uint32_t)enum_info.mem,
|
||||
(uint32_t)(enum_info.mem - PCI_MMIO32_BASE));
|
||||
|
||||
PCI_DEBUG_PRINTF("PCI Memory Mapped I/O range (prefetch) [0x%x,0x%x] (0x%x)\r\n",
|
||||
(uint32_t)PCI_MMIO32_PREFETCH_BASE, enum_info.mem_pf,
|
||||
enum_info.mem_pf - PCI_MMIO32_PREFETCH_BASE);
|
||||
(uint32_t)PCI_MMIO32_PREFETCH_BASE,
|
||||
(uint32_t)enum_info.mem_pf,
|
||||
(uint32_t)(enum_info.mem_pf - PCI_MMIO32_PREFETCH_BASE));
|
||||
|
||||
PCI_DEBUG_PRINTF("PCI I/O range [0x%x,0x%x] (0x%x)\r\n",
|
||||
(uint32_t)PCI_IO32_BASE, enum_info.io,
|
||||
enum_info.io - PCI_IO32_BASE);
|
||||
(uint32_t)PCI_IO32_BASE, (uint32_t)enum_info.io,
|
||||
(uint32_t)(enum_info.io - PCI_IO32_BASE));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ struct test_pci_bar_info {
|
|||
uint32_t upper_mask; /* 64-bit BARs: upper half probe mask (0 = use default 0xFFFFFFFF) */
|
||||
uint8_t has_raw_probe;/* 1=override probe readback with raw_probe (hostile/malformed BAR) */
|
||||
uint32_t raw_probe; /* raw value returned on probe when has_raw_probe is set */
|
||||
uint32_t preset; /* initial BAR register value (previously programmed) */
|
||||
};
|
||||
|
||||
struct test_pci_node {
|
||||
|
|
@ -146,9 +147,19 @@ static void test_pci_dev_set_bar(struct test_pci_topology *t, int node_idx,
|
|||
b->is_prefetch = (type & TEST_PCI_BAR_PF) != 0;
|
||||
}
|
||||
|
||||
static void test_pci_dev_set_bar_preset(struct test_pci_topology *t,
|
||||
int node_idx, int bar_idx,
|
||||
uint32_t value)
|
||||
{
|
||||
ck_assert(node_idx >= 0 && node_idx < t->count);
|
||||
ck_assert(bar_idx >= 0 && bar_idx < TEST_PCI_MAX_BARS);
|
||||
t->nodes[node_idx].bars[bar_idx].preset = value;
|
||||
}
|
||||
|
||||
static void test_pci_commit(struct test_pci_topology *t)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
for (i = 0; i < t->count; i++) {
|
||||
struct test_pci_node *n = &t->nodes[i];
|
||||
if (!n->in_use)
|
||||
|
|
@ -163,6 +174,8 @@ static void test_pci_commit(struct test_pci_topology *t)
|
|||
n->cfg[PCI_CLASS_CODE_BYTE_OFFSET] = 0x06;
|
||||
n->cfg[PCI_SUBCLASS_BYTE_OFFSET] = 0x04;
|
||||
}
|
||||
for (j = 0; j < TEST_PCI_MAX_BARS; j++)
|
||||
memcpy(&n->cfg[PCI_BAR0_OFFSET + j * 4], &n->bars[j].preset, 4);
|
||||
}
|
||||
current_topology = t;
|
||||
}
|
||||
|
|
@ -1740,22 +1753,33 @@ END_TEST
|
|||
START_TEST (test_pool_end_4gib)
|
||||
{
|
||||
struct test_pci_topology t;
|
||||
int dev_node;
|
||||
int dev_node, dev_next;
|
||||
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);
|
||||
test_pci_dev_set_bar(&t, dev_node, 0, 0x00100000, TEST_PCI_BAR_MMIO);
|
||||
dev_next = test_pci_add_dev(&t, 1, 0, 0x9ABC, 0xDEF0, TEST_PCI_ROOT_BUS);
|
||||
/* 1 GB BAR: exactly fills the [0xC0000000, 0x100000000) pool */
|
||||
test_pci_dev_set_bar(&t, dev_node, 0, 0x40000000, TEST_PCI_BAR_MMIO);
|
||||
/* The next device's BAR was programmed by a previous boot */
|
||||
test_pci_dev_set_bar(&t, dev_next, 0, 0x00100000, TEST_PCI_BAR_MMIO);
|
||||
test_pci_dev_set_bar_preset(&t, dev_next, 0, 0x40000000);
|
||||
test_pci_commit(&t);
|
||||
|
||||
ret = pci_enum_do();
|
||||
ck_assert_int_eq(ret, 0);
|
||||
|
||||
/* The BAR is allocated at the pool base */
|
||||
/* The 1 GB BAR is allocated at the pool base */
|
||||
bar_val = pci_config_read32(0, 0, 0, PCI_BAR0_OFFSET);
|
||||
ck_assert_uint_eq(bar_val, 0xC0000000);
|
||||
|
||||
/* The pool is now exhausted exactly at 4 GiB. The allocation
|
||||
* cursor must stay at the pool end, not wrap to 0 and program
|
||||
* the next BAR over address 0: the second BAR is left untouched. */
|
||||
bar_val = pci_config_read32(0, 1, 0, PCI_BAR0_OFFSET);
|
||||
ck_assert_uint_eq(bar_val, 0x40000000);
|
||||
|
||||
test_pci_cleanup(&t);
|
||||
}
|
||||
END_TEST
|
||||
|
|
@ -1922,7 +1946,7 @@ END_TEST
|
|||
/* test_pci_align_check_up_overflow: edge cases for pci_align_check_up */
|
||||
START_TEST(test_pci_align_check_up_overflow)
|
||||
{
|
||||
uint32_t aligned;
|
||||
uint64_t aligned;
|
||||
int ret;
|
||||
|
||||
/* Normal case: already aligned */
|
||||
|
|
|
|||
Loading…
Reference in New Issue