When allocating bar, overflow can occur. So add overflow check and don't allocate bar if overflowed. Overflow check is ugly, but necessary. Another suggested way is to change related variables u64 from u32 thus overflow can't occur because the related value are all u32 addressable. Anyway even with u64, it is necessary to the resulted value > max_u32.
Signed-off-by: Isaku Yamahata yamahata@valinux.co.jp --- src/pciinit.c | 48 ++++++++++++++++++++++++++++++++++-------------- 1 files changed, 34 insertions(+), 14 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index f75e552..9be895c 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -49,6 +49,12 @@ static void pci_set_io_region_addr(u16 bdf, int region_num, u32 addr) dprintf(1, "region %d: 0x%08x\n", region_num, addr); }
+static inline int pci_bar_overflow(u32 addr, u32 size) +{ + return (ALIGN(addr, size) < addr) || + (ALIGN(addr, size) + size < ALIGN(addr, size)); +} + /* * return value * 0: 32bit BAR @@ -78,7 +84,8 @@ static int pci_bios_allocate_region(u16 bdf, int region_num) if (val != 0) { if (val & PCI_BASE_ADDRESS_SPACE_IO) { paddr = &pci_bios_io_addr; - if (ALIGN(*paddr, size) + size >= 64 * 1024) { + if (pci_bar_overflow(*paddr, size) || + ALIGN(*paddr, size) + size >= 64 * 1024) { dprintf(1, "io region of (bdf 0x%x bar %d) can't be mapped.\n", bdf, region_num); @@ -90,7 +97,8 @@ static int pci_bios_allocate_region(u16 bdf, int region_num) /* If pci_bios_prefmem_addr == 0, keep old behaviour */ pci_bios_prefmem_addr != 0) { paddr = &pci_bios_prefmem_addr; - if (ALIGN(*paddr, size) + size >= BUILD_PCIPREFMEM_END) { + if (pci_bar_overflow(*paddr, size) || + ALIGN(*paddr, size) + size >= BUILD_PCIPREFMEM_END) { dprintf(1, "prefmem region of (bdf 0x%x bar %d) can't be mapped. " "decrease BUILD_PCIMEM_SIZE and recompile. size %x\n", @@ -99,7 +107,8 @@ static int pci_bios_allocate_region(u16 bdf, int region_num) } } else { paddr = &pci_bios_mem_addr; - if (ALIGN(*paddr, size) + size >= BUILD_PCIMEM_END) { + if (pci_bar_overflow(*paddr, size) || + ALIGN(*paddr, size) + size >= BUILD_PCIMEM_END) { dprintf(1, "mem region of (bdf 0x%x bar %d) can't be mapped. " "increase BUILD_PCIMEM_SIZE and recompile. size %x\n", @@ -152,6 +161,12 @@ static const struct pci_device_id pci_isa_bridge_tbl[] = { PCI_DEVICE_END };
+static inline int pci_addr_overflow(u32 old, u32 base, u32 addr, u32 end) +{ + /* if without overflow, old <= base <= addr <= end */ + return (old > base) || (base > addr) || (addr > end); +} + #define PCI_IO_ALIGN 4096 #define PCI_IO_SHIFT 8 #define PCI_MEMORY_ALIGN (1UL << 20) @@ -184,36 +199,41 @@ static void pci_bios_init_device_bridge(u16 bdf, void *arg) pci_bios_init_device_in_bus(secbus); }
- pci_bios_io_addr = ALIGN(pci_bios_io_addr, PCI_IO_ALIGN); - pci_bios_mem_addr = ALIGN(pci_bios_mem_addr, PCI_MEMORY_ALIGN); - pci_bios_prefmem_addr = - ALIGN(pci_bios_prefmem_addr, PCI_PREF_MEMORY_ALIGN); - - u32 io_end = pci_bios_io_addr; - if (io_end == io_base) { + u32 io_end = ALIGN(pci_bios_io_addr, PCI_IO_ALIGN); + if (pci_addr_overflow(io_old, io_base, pci_bios_io_addr, io_end) || + io_end == io_base) { pci_bios_io_addr = io_old; io_base = 0xffff; io_end = 1; + } else { + pci_bios_io_addr = io_end; } pci_config_writeb(bdf, PCI_IO_BASE, io_base >> PCI_IO_SHIFT); pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); pci_config_writeb(bdf, PCI_IO_LIMIT, (io_end - 1) >> PCI_IO_SHIFT); pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0);
- u32 mem_end = pci_bios_mem_addr; - if (mem_end == mem_base) { + u32 mem_end = ALIGN(pci_bios_mem_addr, PCI_MEMORY_ALIGN); + if (pci_addr_overflow(mem_old, mem_base, pci_bios_mem_addr, mem_end) || + mem_end == mem_base) { pci_bios_mem_addr = mem_old; mem_base = 0xffffffff; mem_end = 1; + } else { + pci_bios_io_addr = mem_end; } pci_config_writew(bdf, PCI_MEMORY_BASE, mem_base >> PCI_MEMORY_SHIFT); pci_config_writew(bdf, PCI_MEMORY_LIMIT, (mem_end -1) >> PCI_MEMORY_SHIFT);
- u32 prefmem_end = pci_bios_prefmem_addr; - if (prefmem_end == prefmem_base) { + u32 prefmem_end = ALIGN(pci_bios_prefmem_addr, PCI_PREF_MEMORY_ALIGN); + if (pci_addr_overflow(prefmem_old, prefmem_base, + pci_bios_prefmem_addr, prefmem_end) || + prefmem_end == prefmem_base) { pci_bios_prefmem_addr = prefmem_old; prefmem_base = 0xffffffff; prefmem_end = 1; + } else { + pci_bios_prefmem_addr = prefmem_end; } pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, prefmem_base >> PCI_PREF_MEMORY_SHIFT);