[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH RFC] xen/pci: detect when BARs overlap RAM


  • To: <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Roger Pau Monne <roger.pau@xxxxxxxxxx>
  • Date: Wed, 26 Jan 2022 13:26:48 +0100
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=NNg0t/iqnwe5xcQUDMVyki8mesYjjduVX3ijL7UpRvQ=; b=e2aztB84l8vmtWzmj/VO45N7AujyoWdQM4UHO5fio/+cwphI/tLlLdfuOkudCyPah4mcxHK+STVw6VDUBnryiDPIyNaUkAUXxlCRGYew8sMcJAQ9/a/nwknohIQo7eOPck4qZvBno0HaMLwajXfDMMSmARamiveKa0fHzOSiPaGkdUPjLBKmp3KN1yqIXyjGfgFkG4UPc0ySUoTPSMdgO8f3z2IIL5phN/QMvxiHiabRDAQ+Pe9SWwmiNckaXW8vqLDEULqFKjLWsh4t2VdVbnG8JYI+dt/L6OHT0n5xQ+3tZhieR+hYqe2UhfOz8Lct37c40E+ErCvEdu8WiBnLTw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=epXZWpLE+vdnmwmW37vw/947o+lKz9/5HO4jqDqeE/q4m9eYaiLUXKWu6cVeISmpwx+2c6eqWcnu2Z0Bht/d7K00vR58wGZvDRGHBl8oOfvFifbk3LIUPh/f8sbx7GPLb1+eAk/YTc/4lLBijvEpXLGXwl2xE1b0Zw4mOEn+BI+WulhJeDZdXCcxVeq93Tbuy60BpaGXaeaEu5w25aHNJ9JtZmxrUxUkPzVWhYmzWuJ5EEC+AAh8XHo3O8qOAeRr1bSftt9w+5e16+YeE9RtxiGIl0cH4SGgyRSJwd63v6qf/UPSpaVkw7g5fPArvNMkxio9GAj3mqAeCf5cR2CagA==
  • Authentication-results: esa3.hc3370-68.iphmx.com; dkim=pass (signature verified) header.i=@citrix.onmicrosoft.com
  • Cc: Roger Pau Monne <roger.pau@xxxxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, George Dunlap <george.dunlap@xxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Wei Liu <wl@xxxxxxx>, Paul Durrant <paul@xxxxxxx>
  • Delivery-date: Wed, 26 Jan 2022 12:27:31 +0000
  • Ironport-data: A9a23:4RWOqqroFuqhcAKYxzkJwzrecBVeBmJSYxIvgKrLsJaIsI4StFCzt garIBmCb/uLNmP9KIhwaI7gphwO6pTSztJrS1A5pC81F3sUopuZCYyVIHmrMnLJJKUvbq7GA +byyDXkBJppJpMJjk71atANlZT4vE2xbuKU5NTsY0idfic5Dndx4f5fs7Rh2NQw2ILnW1nlV e7a+KUzBnf0g1aYDUpMg06zgEsHUCPa4W5wUvQWPJinjXeG/5UnJMt3yZKZdhMUdrJ8DO+iL 9sv+Znilo/vE7XBPfv++lrzWhVirrc/pmFigFIOM0SpqkAqSiDfTs/XnRfTAKtao2zhojx/9 DlCnbDhSC1qAIDLo/0YDSN6LHBeFL0c2KCSdBBTseTLp6HHW37lwvEoB0AqJ4wIvO1wBAmi9 9RBdmpLNErawbvrnvTrEYGAhex6RCXvFJkYtXx6iynQEN4tQIzZQrWM7thdtNs1rp4UQqeFO 5JEAdZpRB3kJDJAZAoQNKgRxO2i3nvEfyAJ8XvA8MLb5ECMlVcsgdABKuH9eNaHWMFUlUawv X/d8iLyBRRyHNaS0yaf+3SgwOrGhzrmWZk6Hae9sPVthTW73mUODDUGWF39puO24mahX/pPJ kpS/TAhxYAp71CiRNT5Wxy+oVaHswQaVt4WFPc1gCmdx6yR7wuHC2wsSj9adMdgpMIwXSYt1 FKCg5XuHzMHmIOSTXWR57KFtwSYMCIeLXIBTSIcRA5D6N7myKkxkxbOQ9BLAKOzyNrvFlnNL yui9XZkwe9J1IhSivv9rQuvby+QSobhYl80zEbpc2We9wYnXqyaNqyB+QL78qMVRGqGdWWps H8BksmYyekBC5CRiSCAKNkw8KGVC+Wta2OF3wM2d3U133H0oiP4I9gMiN1rDBoxaq45lSnVj Fg/UO+7zLtaJzOUYKB+eOpd4Ox6nPG7RbwJuh05B+eig6SdlifbrUmChmbKhggBdXTAd4lla P93lu73VR4n5VxPlmbeegvk+eZDKtoC7W3SX4vn6B+szKCTYnWYIZ9cbgfVN7tksv/Y/VWKm zq6Cyds408OOAEZSnKPmbP/0HhQdSRrbXwIg5E/mhG/zvpORzh6Vq65LUIJcI15haVF/tokD VnmMnK0PGHX3CWdQS3TMygLQOq2Af5X8CxnVQRxYwfA8yVzMO6HsfZEH7NqLOZPyQCW5aMuJ xXzU5/eUq0np/Wu02l1UKQRW6Q7JE303lrfZnT8CNX9FrY5LzH0FhbfVlKH3AEFDzattNt4p Lul1wjBRoEESRgkB8HTAM9DBXvr1ZTEsO4tDUbOPPdJf0DgrNpjJyDr16dlKMAQMxTTgDCd0 l/OUxsfoODMpa4z8cXI2v/Y/9v4TbMmExoIBXTf4Ja3KTLeojipz7hfXbvaZjvaTm71pvmvP L0H0/HmPfQbt19WqI4gQa1zxKcz6oK39b9XxwhpBlvRaFGvBu8yK3WKx5AX5KZM2qVYqU29X UfWootWPrCAOcXEFl8NJVV6MrTfhK9MwjSLtKY7OkT34iNz7YGra0QKMknekjFZIZt0LJghn bUrtvkJ5lHtkREtKNuH0HxZrjzeMnwaXqw7nZgGG4u32BEzw1RPbJGAWC/75JaDN4dFPkUwe 2LGgaPDg/JXx1bYcmp1Hn/IhLIPiZMLsRFM7VkDO1XWxYaV2q5phEVcoWYtUwBY7hRbyOYia GFkOnp8KbiK4zo11tNIWHqhGl0ZCRCUkqArJ4DlSIENo5GUa1Hw
  • Ironport-hdrordr: A9a23:x6vjG6j7SsoU7Dh4yUIw//TGYnBQXzZ13DAbv31ZSRFFG/FwyP rAoB1L73PJYWgqNU3I+ergBEGBKUmskaKdhrNhR4tKPTOWw1dASbsN0WKM+UyHJ8STzJ8+6U 4CSdkANDSTNykCsS+S2mDReLxBsbq6GciT9JvjJhxWPGZXgs9bnmJE4lHxKDwKeOAKP+txKL Osou584xawc3Ueacq2QlEDQuj4vtXO0LbrewQPCRIL4BSHyWrA0s+wLzGomjMlFx9fy7Yr9m bI1yT/+6WYqvm+jjvRzXXa4Zh6kMbojvFDGMuPoM4ILSiEsHfhWK1RH5m5+BwlquCm71gn1P HKvhcbJsx2r0jce2mkyCGdrzXI4XIL0TvP2FWYiXzsrYjSXzQhEfdMgopfb1/w91cglMsU6t MG40up875sST/QliX04NbFEztwkFCvnHYkmekPy1RCTIolbqNLp4B3xjIZLH45JlO11GkbKp guMCmFj8wmMW9yLkqp9FWH+ebcEUjaRXy9Mws/Us/86UkioJk29Tpb+CUlpAZwyHsKceg32w 31CNUXqFhwdL5nUUsEPpZ4fSKWMB27ffueChPlHbzYfJt3SE4l7aSHpYkI2A==
  • Ironport-sdr: OJCFHdIi+NS5MSxqD/Ja+6G9HDrbUPzeSViqJEvbd6zRArVFAolZJAWcP2LuC3gKjhHH7fSAbY I12pe07F0IKlygrYwYbTamBcZB1puK/3st+tIAnqomtWspUJbayFnpdk9QOsu+ghPZ9Hnjpc47 Ov5e1sPOZ0MkmVnx18Q5v/Gl6/hOpoOUsWYGGhd6XargEpE3Oln0BeoJ8Kx0k7H5//EDB47TRj gVUBOohP+bvzAYmTuNxdPCBiGMDEbp5oc/l54faqeachTaMIZFxExg2lVruTvnIYIUa+ErDQ7x OokPDJOi7WEyQNfxXX3jkh1u
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

One of the boxes where I was attempting to boot Xen in PVH dom0 mode
has quirky firmware, as it will handover with a device with memory
decoding enabled and a BAR of size 4K at address 0. Such BAR overlaps
with a RAM range on the e820.

This interacts badly with the dom0 PVH build, as BARs will be setup on
the p2m before RAM, so if there's a BAR positioned over a RAM region
it will trigger a domain crash when the dom0 builder attempts to
populate that region with a regular RAM page.

It's in general a very bad idea to have a BAR overlapping with a RAM
region, so add some sanity checks for devices that are added with
memory decoding enabled in order to assure that BARs are not placed on
top of memory regions. If overlaps are detected just disable the
memory decoding bit for the device and expect the hardware domain to
properly position the BAR.

Signed-off-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
---
RFC because:
 - Not sure the best way to implement is_iomem_range on Arm. BARs can
   be quite big, so iterating over every possible page is not ideal.
 - is_iomem_page cannot be used for this purpose on x86, because all
   the low 1MB will return true due to belonging to dom_io.
 - VF BARs are not checked. Should we also check them and disable VF
   if overlaps in a followup patch?
---
 xen/arch/arm/mm.c             | 11 ++++++
 xen/arch/x86/mm.c             | 20 +++++++++++
 xen/drivers/passthrough/pci.c | 68 ++++++++++++++++++++++++++++++++++-
 xen/include/xen/mm.h          |  2 ++
 4 files changed, 100 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
index eea926d823..fa4cee64c7 100644
--- a/xen/arch/arm/mm.c
+++ b/xen/arch/arm/mm.c
@@ -1625,6 +1625,17 @@ bool is_iomem_page(mfn_t mfn)
     return !mfn_valid(mfn);
 }
 
+bool is_iomem_range(uint64_t start, uint64_t size)
+{
+    unsigned int i;
+
+    for ( i = 0; i < PFN_UP(size); i++ )
+        if ( !is_iomem_page(_mfn(PFN_DOWN(start) + i)) )
+            return false;
+
+    return true;
+}
+
 void clear_and_clean_page(struct page_info *page)
 {
     void *p = __map_domain_page(page);
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 1397f83e41..03699b2227 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -479,6 +479,26 @@ unsigned int page_get_ram_type(mfn_t mfn)
     return type ?: RAM_TYPE_UNKNOWN;
 }
 
+bool is_iomem_range(uint64_t start, uint64_t size)
+{
+    unsigned int i;
+
+    for ( i = 0; i < e820.nr_map; i++ )
+    {
+        struct e820entry *entry = &e820.map[i];
+
+        if ( entry->type != E820_RAM && entry->type != E820_ACPI &&
+             entry->type != E820_NVS )
+            continue;
+
+        if ( start < entry->addr + entry->size &&
+             entry->addr < start + size )
+            return false;
+    }
+
+    return true;
+}
+
 unsigned long domain_get_maximum_gpfn(struct domain *d)
 {
     if ( is_hvm_domain(d) )
diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
index 0d8ab2e716..032df71393 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -233,6 +233,7 @@ static void check_pdev(const struct pci_dev *pdev)
      PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT | \
      PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY)
     u16 val;
+    unsigned int nbars = 0, rom_pos = 0, i;
 
     if ( command_mask )
     {
@@ -251,6 +252,8 @@ static void check_pdev(const struct pci_dev *pdev)
     switch ( pci_conf_read8(pdev->sbdf, PCI_HEADER_TYPE) & 0x7f )
     {
     case PCI_HEADER_TYPE_BRIDGE:
+        nbars = PCI_HEADER_BRIDGE_NR_BARS;
+        rom_pos = PCI_ROM_ADDRESS1;
         if ( !bridge_ctl_mask )
             break;
         val = pci_conf_read16(pdev->sbdf, PCI_BRIDGE_CONTROL);
@@ -267,11 +270,74 @@ static void check_pdev(const struct pci_dev *pdev)
         }
         break;
 
+    case PCI_HEADER_TYPE_NORMAL:
+        nbars = PCI_HEADER_NORMAL_NR_BARS;
+        rom_pos = PCI_ROM_ADDRESS;
+        break;
+
     case PCI_HEADER_TYPE_CARDBUS:
         /* TODO */
         break;
     }
 #undef PCI_STATUS_CHECK
+
+    /* Check if BARs overlap with RAM regions. */
+    val = pci_conf_read16(pdev->sbdf, PCI_COMMAND);
+    if ( !(val & PCI_COMMAND_MEMORY) || pdev->ignore_bars )
+        return;
+
+    pci_conf_write16(pdev->sbdf, PCI_COMMAND, val & ~PCI_COMMAND_MEMORY);
+    for ( i = 0; i < nbars; )
+    {
+        uint64_t addr, size;
+        unsigned int reg = PCI_BASE_ADDRESS_0 + i * 4;
+        int rc = 1;
+
+        if ( (pci_conf_read32(pdev->sbdf, reg) & PCI_BASE_ADDRESS_SPACE) !=
+             PCI_BASE_ADDRESS_SPACE_MEMORY )
+            goto next;
+
+        rc = pci_size_mem_bar(pdev->sbdf, reg, &addr, &size,
+                              (i == nbars - 1) ? PCI_BAR_LAST : 0);
+        if ( rc < 0 )
+            return;
+        if ( size && !is_iomem_range(addr, size) )
+        {
+            /*
+             * Return without enabling memory decoding if BAR overlaps with
+             * RAM region.
+             */
+            printk(XENLOG_WARNING
+                   "%pp: disabled: BAR%u [%" PRIx64 ", %" PRIx64
+                   ") overlaps with RAM\n",
+                   &pdev->sbdf, i, addr, addr + size);
+            return;
+        }
+
+ next:
+        ASSERT(rc > 0);
+        i += rc;
+    }
+
+    if ( pci_conf_read32(pdev->sbdf, rom_pos) & PCI_ROM_ADDRESS_ENABLE )
+    {
+        uint64_t addr, size;
+        int rc = pci_size_mem_bar(pdev->sbdf, rom_pos, &addr, &size,
+                                  PCI_BAR_ROM);
+
+        if ( rc < 0 )
+            return;
+        if ( size && !is_iomem_range(addr, size) )
+        {
+            printk(XENLOG_WARNING
+                   "%pp: disabled: ROM BAR [%" PRIx64 ", %" PRIx64
+                   ") overlaps with RAM\n",
+                   &pdev->sbdf, addr, addr + size);
+            return;
+        }
+    }
+
+    pci_conf_write16(pdev->sbdf, PCI_COMMAND, val);
 }
 
 static void apply_quirks(struct pci_dev *pdev)
@@ -399,8 +465,8 @@ static struct pci_dev *alloc_pdev(struct pci_seg *pseg, u8 
bus, u8 devfn)
             break;
     }
 
-    check_pdev(pdev);
     apply_quirks(pdev);
+    check_pdev(pdev);
 
     return pdev;
 }
diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h
index 5db26ed477..764dcad5b3 100644
--- a/xen/include/xen/mm.h
+++ b/xen/include/xen/mm.h
@@ -554,6 +554,8 @@ int __must_check steal_page(struct domain *d, struct 
page_info *page,
 int page_is_ram_type(unsigned long mfn, unsigned long mem_type);
 /* Returns the page type(s). */
 unsigned int page_get_ram_type(mfn_t mfn);
+/* Check if a range is in IO suitable memory. */
+bool is_iomem_range(uint64_t start, uint64_t size);
 
 /* Prepare/destroy a ring for a dom0 helper. Helper with talk
  * with Xen on behalf of this domain. */
-- 
2.34.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.