@@ -34,6 +34,7 @@
struct map_data {
struct domain *d;
+ const struct vpci_bar *bar;
bool map;
};
@@ -41,26 +42,38 @@ static int cf_check map_range(
unsigned long s, unsigned long e, void *data, unsigned long *c)
{
const struct map_data *map = data;
+ /* Start address of the BAR as seen by the guest. */
+ unsigned long start_gfn = PFN_DOWN(map->bar->guest_addr);
+ /* Physical start address of the BAR. */
+ unsigned long start_mfn = PFN_DOWN(map->bar->addr);
int rc;
for ( ; ; )
{
unsigned long size = e - s + 1;
+ /*
+ * Ranges to be mapped don't always start at the BAR start address, as
+ * there can be holes or partially consumed ranges. Account for the
+ * offset of the current address from the BAR start.
+ */
+ unsigned long map_mfn = start_mfn + s - start_gfn;
+ unsigned long m_end = map_mfn + size - 1;
- if ( !iomem_access_permitted(map->d, s, e) )
+ if ( !iomem_access_permitted(map->d, map_mfn, m_end) )
{
printk(XENLOG_G_WARNING
"%pd denied access to MMIO range [%#lx, %#lx]\n",
- map->d, s, e);
+ map->d, map_mfn, m_end);
return -EPERM;
}
- rc = xsm_iomem_mapping(XSM_HOOK, map->d, s, e, map->map);
+ rc = xsm_iomem_mapping(XSM_HOOK, map->d, map_mfn, m_end,
+ map->map);
if ( rc )
{
printk(XENLOG_G_WARNING
"%pd XSM denied access to MMIO range [%#lx, %#lx]: %d\n",
- map->d, s, e, rc);
+ map->d, map_mfn, m_end, rc);
return rc;
}
@@ -73,8 +86,8 @@ static int cf_check map_range(
* - {un}map_mmio_regions doesn't support preemption.
*/
- rc = map->map ? map_mmio_regions(map->d, _gfn(s), size, _mfn(s))
- : unmap_mmio_regions(map->d, _gfn(s), size, _mfn(s));
+ rc = map->map ? map_mmio_regions(map->d, _gfn(s), size, _mfn(map_mfn))
+ : unmap_mmio_regions(map->d, _gfn(s), size, _mfn(map_mfn));
if ( rc == 0 )
{
*c += size;
@@ -83,8 +96,9 @@ static int cf_check map_range(
if ( rc < 0 )
{
printk(XENLOG_G_WARNING
- "Failed to identity %smap [%lx, %lx] for d%d: %d\n",
- map->map ? "" : "un", s, e, map->d->domain_id, rc);
+ "Failed to %smap [%lx %lx] -> [%lx %lx] for %pd: %d\n",
+ map->map ? "" : "un", s, e, map_mfn,
+ map_mfn + size, map->d, rc);
break;
}
ASSERT(rc < size);
@@ -163,10 +177,6 @@ static void modify_decoding(const struct pci_dev *pdev, uint16_t cmd,
bool vpci_process_pending(struct vcpu *v)
{
struct pci_dev *pdev = v->vpci.pdev;
- struct map_data data = {
- .d = v->domain,
- .map = v->vpci.cmd & PCI_COMMAND_MEMORY,
- };
struct vpci_header *header = NULL;
unsigned int i;
@@ -186,6 +196,11 @@ bool vpci_process_pending(struct vcpu *v)
for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
{
struct vpci_bar *bar = &header->bars[i];
+ struct map_data data = {
+ .d = v->domain,
+ .map = v->vpci.cmd & PCI_COMMAND_MEMORY,
+ .bar = bar,
+ };
int rc;
if ( rangeset_is_empty(bar->mem) )
@@ -236,7 +251,6 @@ bool vpci_process_pending(struct vcpu *v)
static int __init apply_map(struct domain *d, const struct pci_dev *pdev,
uint16_t cmd)
{
- struct map_data data = { .d = d, .map = true };
struct vpci_header *header = &pdev->vpci->header;
int rc = 0;
unsigned int i;
@@ -246,6 +260,7 @@ static int __init apply_map(struct domain *d, const struct pci_dev *pdev,
for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
{
struct vpci_bar *bar = &header->bars[i];
+ struct map_data data = { .d = d, .map = true, .bar = bar };
if ( rangeset_is_empty(bar->mem) )
continue;
@@ -311,12 +326,16 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
* First fill the rangesets with the BAR of this device or with the ROM
* BAR only, depending on whether the guest is toggling the memory decode
* bit of the command register, or the enable bit of the ROM BAR register.
+ *
+ * For non-hardware domain we use guest physical addresses.
*/
for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
{
struct vpci_bar *bar = &header->bars[i];
unsigned long start = PFN_DOWN(bar->addr);
unsigned long end = PFN_DOWN(bar->addr + bar->size - 1);
+ unsigned long start_guest = PFN_DOWN(bar->guest_addr);
+ unsigned long end_guest = PFN_DOWN(bar->guest_addr + bar->size - 1);
if ( !bar->mem )
continue;
@@ -336,11 +355,26 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
continue;
}
- rc = rangeset_add_range(bar->mem, start, end);
+ ASSERT(rangeset_is_empty(bar->mem));
+
+ /*
+ * Make sure that the guest set address has the same page offset
+ * as the physical address on the host or otherwise things won't work as
+ * expected.
+ */
+ if ( PAGE_OFFSET(bar->guest_addr) != PAGE_OFFSET(bar->addr) )
+ {
+ gprintk(XENLOG_G_WARNING,
+ "%pp: can't map BAR%u - offset mismatch: %#lx vs %#lx\n",
+ &pdev->sbdf, i, bar->guest_addr, bar->addr);
+ return -EINVAL;
+ }
+
+ rc = rangeset_add_range(bar->mem, start_guest, end_guest);
if ( rc )
{
printk(XENLOG_G_WARNING "Failed to add [%lx, %lx]: %d\n",
- start, end, rc);
+ start_guest, end_guest, rc);
return rc;
}
@@ -352,12 +386,12 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
if ( rangeset_is_empty(prev_bar->mem) )
continue;
- rc = rangeset_remove_range(prev_bar->mem, start, end);
+ rc = rangeset_remove_range(prev_bar->mem, start_guest, end_guest);
if ( rc )
{
gprintk(XENLOG_WARNING,
"%pp: failed to remove overlapping range [%lx, %lx]: %d\n",
- &pdev->sbdf, start, end, rc);
+ &pdev->sbdf, start_guest, end_guest, rc);
return rc;
}
}
@@ -425,8 +459,8 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
for ( i = 0; i < ARRAY_SIZE(tmp->vpci->header.bars); i++ )
{
const struct vpci_bar *remote_bar = &tmp->vpci->header.bars[i];
- unsigned long start = PFN_DOWN(remote_bar->addr);
- unsigned long end = PFN_DOWN(remote_bar->addr +
+ unsigned long start = PFN_DOWN(remote_bar->guest_addr);
+ unsigned long end = PFN_DOWN(remote_bar->guest_addr +
remote_bar->size - 1);
if ( !remote_bar->enabled )
@@ -513,6 +547,8 @@ static void cf_check bar_write(
struct vpci_bar *bar = data;
bool hi = false;
+ ASSERT(is_hardware_domain(pdev->domain));
+
if ( bar->type == VPCI_BAR_MEM64_HI )
{
ASSERT(reg > PCI_BASE_ADDRESS_0);
@@ -543,6 +579,8 @@ static void cf_check bar_write(
*/
bar->addr &= ~(0xffffffffULL << (hi ? 32 : 0));
bar->addr |= (uint64_t)val << (hi ? 32 : 0);
+ /* Update guest address, so hardware domain BAR is identity mapped. */
+ bar->guest_addr = bar->addr;
/* Make sure Xen writes back the same value for the BAR RO bits. */
if ( !hi )
@@ -639,11 +677,14 @@ static void cf_check rom_write(
}
if ( !rom->enabled )
+ {
/*
* If the ROM BAR is not mapped update the address field so the
* correct address is mapped into the p2m.
*/
rom->addr = val & PCI_ROM_ADDRESS_MASK;
+ rom->guest_addr = rom->addr;
+ }
if ( !header->bars_mapped || rom->enabled == new_enabled )
{
@@ -667,7 +708,10 @@ static void cf_check rom_write(
return;
if ( !new_enabled )
+ {
rom->addr = val & PCI_ROM_ADDRESS_MASK;
+ rom->guest_addr = rom->addr;
+ }
}
static int bar_add_rangeset(const struct pci_dev *pdev, struct vpci_bar *bar,
@@ -862,6 +906,7 @@ static int cf_check init_header(struct pci_dev *pdev)
}
bars[i].addr = addr;
+ bars[i].guest_addr = addr;
bars[i].size = size;
bars[i].prefetchable = val & PCI_BASE_ADDRESS_MEM_PREFETCH;
@@ -884,6 +929,7 @@ static int cf_check init_header(struct pci_dev *pdev)
rom->type = VPCI_BAR_ROM;
rom->size = size;
rom->addr = addr;
+ rom->guest_addr = addr;
header->rom_enabled = pci_conf_read32(pdev->sbdf, rom_reg) &
PCI_ROM_ADDRESS_ENABLE;
@@ -216,7 +216,8 @@ int vpci_msix_arch_print(const struct vpci_msix *msix);
*/
static inline paddr_t vmsix_table_base(const struct vpci *vpci, unsigned int nr)
{
- return vpci->header.bars[vpci->msix->tables[nr] & PCI_MSIX_BIRMASK].addr;
+ return vpci->header.bars[vpci->msix->tables[nr] &
+ PCI_MSIX_BIRMASK].guest_addr;
}
static inline paddr_t vmsix_table_addr(const struct vpci *vpci, unsigned int nr)