From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=45316 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PEBoI-0005SK-GI for qemu-devel@nongnu.org; Thu, 04 Nov 2010 22:13:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PEBoH-00062l-2y for qemu-devel@nongnu.org; Thu, 04 Nov 2010 22:13:46 -0400 Received: from smtp-out1.slb.compass.net.nz ([203.152.101.168]:42767) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PEBoG-00062B-L0 for qemu-devel@nongnu.org; Thu, 04 Nov 2010 22:13:45 -0400 From: Alexey Korolev Content-Type: text/plain; charset="UTF-8" Date: Fri, 5 Nov 2010 15:13:38 +1300 Message-ID: <1288923218.28768.67.camel@nzhmlwks0057> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] [PATCH] [Seabios] Over 4GB address ranges for 64bit PCI BARs List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: yamahata@valinux.co.jp, Stephen Donnelly , qemu-devel@nongnu.org Hi, We have seen some issues with 64bit PCI address and large BAR region supp= ort in=20 seabios.=20 On attempting to register a 64bit BAR of 1GB size we had some strange fai= lures in qemu. After some debugging we find out that source of the issue was in= =20 seabios PCI enumeration code.=20 The issue takes place because of u32 overflow in pciinit.c.=20 =E2=80=A6......... if (ALIGN(*paddr, size) + size >=3D BUILD_PCIPREFMEM_END) { dprintf(1, "prefmem region of (bdf 0x%x bar %d) can't be ma= pped. " "decrease BUILD_PCIMEM_SIZE and recompile. size = %x\n", bdf, region_num, BUILD_PCIPREFMEM_SIZE); size =3D 0; } =E2=80=A6........... if (size > 0) { *paddr =3D ALIGN(*paddr, size); pci_set_io_region_addr(bdf, region_num, *paddr); *paddr +=3D size; } If size is greater than 0xFFFFFFFF =E2=80=93 BUILD_PCIPREFMEM_START (256M= B),=20 this call ALIGN(*paddr, size) will always return 0. The protection test f= ails, size remains > 0, and guest memory mapping will be corrupted. We have found that a very similar problem was discussed previously: http://www.mail-archive.com/qemu-devel@nongnu.org/msg38090.html The discussion also touches on the question of 64bit addressing for PCI B= ARs. In the current implementation regardless of whether the PCI BAR type is 6= 4bit or not the addressable range will be limited to the first 4GB. We also want to h= ave "true" 64bit addressable range for PCI BARs. So to solve these issues some changes were made.=20 Tracking for overflows has been added as well as support for 64bit range.= =20 To support the full 64bit address range a pci_bios_64bit_addr variable ha= s been added.=20 The 64bit range can be used only if BAR has PCI_BASE_ADDRESS_MEM_TYPE_64 attribute and the given region doesn't fit in first 4GB. The patch has b= een tested on Linux and BSD guest OS's and it appears to work fine.=20 Signed-off-by Alexey Korolev Signed-off-by Stephen Donelly --- pciinit.c | 83 ++++++++++++++++++++++++++++++++-----------------------= ------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/src/pciinit.c b/src/pciinit.c index 0346423..86c8137 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -20,6 +20,7 @@ static void pci_bios_init_device_in_bus(int bus); static u32 pci_bios_io_addr; static u32 pci_bios_mem_addr; static u32 pci_bios_prefmem_addr; +static u64 pci_bios_64bit_addr; /* host irqs corresponding to PCI irqs A-D */ const u8 pci_irqs[4] =3D { 10, 10, 11, 11 @@ -56,9 +57,11 @@ static int pci_bios_allocate_region(u16 bdf, int regio= n_num) { u32 *paddr; u32 ofs =3D pci_bar(bdf, region_num); - u32 old =3D pci_config_readl(bdf, ofs); u32 mask; + u32 limit_addr; + int is_64bit; + if (region_num =3D=3D PCI_ROM_SLOT) { mask =3D PCI_ROM_ADDRESS_MASK; pci_config_writel(bdf, ofs, mask); @@ -73,50 +76,49 @@ static int pci_bios_allocate_region(u16 bdf, int regi= on_num) pci_config_writel(bdf, ofs, old); =20 u32 size =3D (~(val & mask)) + 1; - if (val !=3D 0) { - if (val & PCI_BASE_ADDRESS_SPACE_IO) { - paddr =3D &pci_bios_io_addr; - if (ALIGN(*paddr, size) + size >=3D 64 * 1024) { - dprintf(1, - "io region of (bdf 0x%x bar %d) can't be mapped.= \n", - bdf, region_num); - size =3D 0; - } - } else if ((val & PCI_BASE_ADDRESS_MEM_PREFETCH) && - /* keep behaviour on bus =3D 0 */ - pci_bdf_to_bus(bdf) !=3D 0 && - /* If pci_bios_prefmem_addr =3D=3D 0, keep old behaviou= r */ - pci_bios_prefmem_addr !=3D 0) { - paddr =3D &pci_bios_prefmem_addr; - if (ALIGN(*paddr, size) + size >=3D BUILD_PCIPREFMEM_END) { - dprintf(1, - "prefmem region of (bdf 0x%x bar %d) can't be ma= pped. " - "decrease BUILD_PCIMEM_SIZE and recompile. size = %x\n", - bdf, region_num, BUILD_PCIPREFMEM_SIZE); - size =3D 0; - } - } else { - paddr =3D &pci_bios_mem_addr; - if (ALIGN(*paddr, size) + size >=3D BUILD_PCIMEM_END) { + if (!val || !size) + return 0; + + is_64bit =3D !(val & PCI_BASE_ADDRESS_SPACE_IO) && + (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) =3D=3D PCI_BASE_ADDRESS_M= EM_TYPE_64; + + if (val & PCI_BASE_ADDRESS_SPACE_IO) { + paddr =3D &pci_bios_io_addr; + limit_addr =3D 64 * 1024; + } else if ((val & PCI_BASE_ADDRESS_MEM_PREFETCH) && + /* keep behaviour on bus =3D 0 */ + pci_bdf_to_bus(bdf) !=3D 0 && + /* If pci_bios_prefmem_addr =3D=3D 0, keep old behaviour */ + pci_bios_prefmem_addr !=3D 0) { + paddr =3D &pci_bios_prefmem_addr; + limit_addr =3D BUILD_PCIPREFMEM_END; + } else { + paddr =3D &pci_bios_mem_addr; + limit_addr =3D BUILD_PCIMEM_END; + } + + /* The region is out of allowed 32bit mapping range */ + if ((ALIGN(*paddr, size) + size >=3D limit_addr) || + (ALIGN(*paddr, size) + size <=3D *paddr)) { + if (!is_64bit) { dprintf(1, "mem region of (bdf 0x%x bar %d) can't be mapped= . " - "increase BUILD_PCIMEM_SIZE and recompile. size = %x\n", - bdf, region_num, BUILD_PCIMEM_SIZE); - size =3D 0; + "Region size %x\n", bdf, region_num, size); + return 0; } - } - if (size > 0) { - *paddr =3D ALIGN(*paddr, size); - pci_set_io_region_addr(bdf, region_num, *paddr); - *paddr +=3D size; - } + pci_bios_64bit_addr =3D ALIGN(pci_bios_64bit_addr, size); + pci_set_io_region_addr(bdf, region_num, (u32)pci_bios_64bit_= addr); + pci_set_io_region_addr(bdf, region_num + 1, + (u32)(pci_bios_64bit_addr >> 32)); + pci_bios_64bit_addr +=3D size; + return is_64bit; } =20 - int is_64bit =3D !(val & PCI_BASE_ADDRESS_SPACE_IO) && - (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) =3D=3D PCI_BASE_ADDRESS_M= EM_TYPE_64; - if (is_64bit && size > 0) { - pci_config_writel(bdf, ofs + 4, 0); - } + *paddr =3D ALIGN(*paddr, size); + pci_set_io_region_addr(bdf, region_num, *paddr); + if (is_64bit) + pci_set_io_region_addr(bdf, region_num + 1, 0); + *paddr +=3D size; return is_64bit; } =20 @@ -409,6 +411,7 @@ pci_setup(void) pci_bios_io_addr =3D 0xc000; pci_bios_mem_addr =3D BUILD_PCIMEM_START; pci_bios_prefmem_addr =3D BUILD_PCIPREFMEM_START; + pci_bios_64bit_addr =3D ALIGN(RamSizeOver4G + 0x100000000ull, 0x1000= 00000ull); =20 pci_bios_init_bus(); =20