# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1207815758 -3600
# Node ID 1d3aaa6a8b870805e16dcf162223fb2edd9de26d
# Parent 85848be18ba22814bddeb82a4cfc99e14447cab1
VT-d: Allocates page table pgd, root_entry, iremap and qinval from
domheap rather than xenheap, and get rid of structure page_info in
iommu.c.
Signed-off-by: Weidong Han <weidong.han@xxxxxxxxx>
---
xen/drivers/passthrough/vtd/intremap.c | 51 ++-
xen/drivers/passthrough/vtd/iommu.c | 458 +++++++++------------------------
xen/drivers/passthrough/vtd/iommu.h | 4
xen/drivers/passthrough/vtd/qinval.c | 94 +++---
xen/drivers/passthrough/vtd/utils.c | 40 +-
xen/drivers/passthrough/vtd/x86/vtd.c | 177 ++++++++++++
xen/include/xen/hvm/iommu.h | 2
xen/include/xen/iommu.h | 3
8 files changed, 421 insertions(+), 408 deletions(-)
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/drivers/passthrough/vtd/intremap.c
--- a/xen/drivers/passthrough/vtd/intremap.c Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/drivers/passthrough/vtd/intremap.c Thu Apr 10 09:22:38 2008 +0100
@@ -45,7 +45,7 @@ static void remap_entry_to_ioapic_rte(
static void remap_entry_to_ioapic_rte(
struct iommu *iommu, struct IO_APIC_route_entry *old_rte)
{
- struct iremap_entry *iremap_entry = NULL;
+ struct iremap_entry *iremap_entry = NULL, *iremap_entries;
struct IO_APIC_route_remap_entry *remap_rte;
unsigned int index;
unsigned long flags;
@@ -70,7 +70,9 @@ static void remap_entry_to_ioapic_rte(
spin_lock_irqsave(&ir_ctrl->iremap_lock, flags);
- iremap_entry = &ir_ctrl->iremap[index];
+ iremap_entries =
+ (struct iremap_entry *)map_vtd_domain_page(ir_ctrl->iremap_maddr);
+ iremap_entry = &iremap_entries[index];
old_rte->vector = iremap_entry->lo.vector;
old_rte->delivery_mode = iremap_entry->lo.dlm;
@@ -80,13 +82,14 @@ static void remap_entry_to_ioapic_rte(
old_rte->dest.logical.__reserved_1 = 0;
old_rte->dest.logical.logical_dest = iremap_entry->lo.dst;
+ unmap_vtd_domain_page(iremap_entries);
spin_unlock_irqrestore(&ir_ctrl->iremap_lock, flags);
}
static void ioapic_rte_to_remap_entry(struct iommu *iommu,
int apic_id, struct IO_APIC_route_entry *old_rte)
{
- struct iremap_entry *iremap_entry = NULL;
+ struct iremap_entry *iremap_entry = NULL, *iremap_entries;
struct IO_APIC_route_remap_entry *remap_rte;
unsigned int index;
unsigned long flags;
@@ -103,7 +106,10 @@ static void ioapic_rte_to_remap_entry(st
goto out;
}
- iremap_entry = &(ir_ctrl->iremap[index]);
+ iremap_entries =
+ (struct iremap_entry *)map_vtd_domain_page(ir_ctrl->iremap_maddr);
+ iremap_entry = &iremap_entries[index];
+
if ( *(u64 *)iremap_entry != 0 )
dprintk(XENLOG_WARNING VTDPREFIX,
"Interrupt remapping entry is in use already!\n");
@@ -124,12 +130,13 @@ static void ioapic_rte_to_remap_entry(st
iremap_entry->lo.p = 1; /* finally, set present bit */
ir_ctrl->iremap_index++;
+ unmap_vtd_domain_page(iremap_entries);
iommu_flush_iec_index(iommu, 0, index);
ret = invalidate_sync(iommu);
- /* now construct new ioapic rte entry */
+ /* now construct new ioapic rte entry */
remap_rte->vector = old_rte->vector;
- remap_rte->delivery_mode = 0; /* has to be 0 for remap format */
+ remap_rte->delivery_mode = 0; /* has to be 0 for remap format */
remap_rte->index_15 = index & 0x8000;
remap_rte->index_0_14 = index & 0x7fff;
remap_rte->delivery_status = old_rte->delivery_status;
@@ -154,7 +161,7 @@ io_apic_read_remap_rte(
struct iommu *iommu = ioapic_to_iommu(mp_ioapics[apic].mpc_apicid);
struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu);
- if ( !iommu || !ir_ctrl || !(ir_ctrl->iremap) )
+ if ( !iommu || !ir_ctrl || ir_ctrl->iremap_maddr == 0 )
{
*IO_APIC_BASE(apic) = reg;
return *(IO_APIC_BASE(apic)+4);
@@ -200,7 +207,7 @@ io_apic_write_remap_rte(
struct iommu *iommu = ioapic_to_iommu(mp_ioapics[apic].mpc_apicid);
struct ir_ctrl *ir_ctrl = iommu_ir_ctrl(iommu);
- if ( !iommu || !ir_ctrl || !(ir_ctrl->iremap) )
+ if ( !iommu || !ir_ctrl || ir_ctrl->iremap_maddr == 0 )
{
*IO_APIC_BASE(apic) = reg;
*(IO_APIC_BASE(apic)+4) = value;
@@ -238,32 +245,30 @@ int intremap_setup(struct iommu *iommu)
{
struct ir_ctrl *ir_ctrl;
unsigned long start_time;
- u64 paddr;
if ( !ecap_intr_remap(iommu->ecap) )
return -ENODEV;
ir_ctrl = iommu_ir_ctrl(iommu);
- if ( ir_ctrl->iremap == NULL )
- {
- ir_ctrl->iremap = alloc_xenheap_page();
- if ( ir_ctrl->iremap == NULL )
+ if ( ir_ctrl->iremap_maddr == 0 )
+ {
+ ir_ctrl->iremap_maddr = alloc_pgtable_maddr();
+ if ( ir_ctrl->iremap_maddr == 0 )
{
dprintk(XENLOG_WARNING VTDPREFIX,
- "Cannot allocate memory for ir_ctrl->iremap\n");
- return -ENODEV;
- }
- memset(ir_ctrl->iremap, 0, PAGE_SIZE);
- }
-
- paddr = virt_to_maddr(ir_ctrl->iremap);
+ "Cannot allocate memory for ir_ctrl->iremap_maddr\n");
+ return -ENODEV;
+ }
+ }
+
#if defined(ENABLED_EXTENDED_INTERRUPT_SUPPORT)
/* set extended interrupt mode bit */
- paddr |= ecap_ext_intr(iommu->ecap) ? (1 << IRTA_REG_EIMI_SHIFT) : 0;
+ ir_ctrl->iremap_maddr |=
+ ecap_ext_intr(iommu->ecap) ? (1 << IRTA_REG_EIMI_SHIFT) : 0;
#endif
/* size field = 256 entries per 4K page = 8 - 1 */
- paddr |= 7;
- dmar_writeq(iommu->reg, DMAR_IRTA_REG, paddr);
+ ir_ctrl->iremap_maddr |= 7;
+ dmar_writeq(iommu->reg, DMAR_IRTA_REG, ir_ctrl->iremap_maddr);
/* set SIRTP */
iommu->gcmd |= DMA_GCMD_SIRTP;
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c Thu Apr 10 09:22:38 2008 +0100
@@ -185,71 +185,70 @@ void iommu_flush_cache_page(struct iommu
int nr_iommus;
/* context entry handling */
-static struct context_entry * device_to_context_entry(struct iommu *iommu,
- u8 bus, u8 devfn)
-{
- struct root_entry *root;
- struct context_entry *context;
- unsigned long phy_addr;
+static u64 bus_to_context_maddr(struct iommu *iommu, u8 bus)
+{
+ struct root_entry *root, *root_entries;
unsigned long flags;
+ u64 maddr;
spin_lock_irqsave(&iommu->lock, flags);
- root = &iommu->root_entry[bus];
+ root_entries = (struct root_entry *)map_vtd_domain_page(iommu->root_maddr);
+ root = &root_entries[bus];
if ( !root_present(*root) )
{
- phy_addr = (unsigned long) alloc_xenheap_page();
- if ( !phy_addr )
+ maddr = alloc_pgtable_maddr();
+ if ( maddr == 0 )
{
spin_unlock_irqrestore(&iommu->lock, flags);
- return NULL;
+ return 0;
}
- memset((void *) phy_addr, 0, PAGE_SIZE);
- iommu_flush_cache_page(iommu, (void *)phy_addr);
- phy_addr = virt_to_maddr((void *)phy_addr);
- set_root_value(*root, phy_addr);
+ set_root_value(*root, maddr);
set_root_present(*root);
iommu_flush_cache_entry(iommu, root);
}
- phy_addr = (unsigned long) get_context_addr(*root);
- context = (struct context_entry *)maddr_to_virt(phy_addr);
+ maddr = (u64) get_context_addr(*root);
+ unmap_vtd_domain_page(root_entries);
spin_unlock_irqrestore(&iommu->lock, flags);
- return &context[devfn];
+ return maddr;
}
static int device_context_mapped(struct iommu *iommu, u8 bus, u8 devfn)
{
- struct root_entry *root;
+ struct root_entry *root, *root_entries;
struct context_entry *context;
- unsigned long phy_addr;
+ u64 context_maddr;
int ret;
unsigned long flags;
spin_lock_irqsave(&iommu->lock, flags);
- root = &iommu->root_entry[bus];
+ root_entries = (struct root_entry *)map_vtd_domain_page(iommu->root_maddr);
+ root = &root_entries[bus];
if ( !root_present(*root) )
{
ret = 0;
goto out;
}
- phy_addr = get_context_addr(*root);
- context = (struct context_entry *)maddr_to_virt(phy_addr);
+ context_maddr = get_context_addr(*root);
+ context = (struct context_entry *)map_vtd_domain_page(context_maddr);
ret = context_present(context[devfn]);
+ unmap_vtd_domain_page(context);
out:
+ unmap_vtd_domain_page(root_entries);
spin_unlock_irqrestore(&iommu->lock, flags);
return ret;
}
-static struct page_info *addr_to_dma_page(struct domain *domain, u64 addr)
+static u64 addr_to_dma_page_maddr(struct domain *domain, u64 addr)
{
struct hvm_iommu *hd = domain_hvm_iommu(domain);
struct acpi_drhd_unit *drhd;
struct iommu *iommu;
int addr_width = agaw_to_width(hd->agaw);
- struct dma_pte *parent, *pte = NULL, *pgd;
+ struct dma_pte *parent, *pte = NULL;
int level = agaw_to_level(hd->agaw);
int offset;
unsigned long flags;
- struct page_info *pg = NULL;
+ u64 pte_maddr = 0;
u64 *vaddr = NULL;
drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list);
@@ -257,19 +256,14 @@ static struct page_info *addr_to_dma_pag
addr &= (((u64)1) << addr_width) - 1;
spin_lock_irqsave(&hd->mapping_lock, flags);
- if ( !hd->pgd )
- {
- pgd = (struct dma_pte *)alloc_xenheap_page();
- if ( !pgd )
- {
- spin_unlock_irqrestore(&hd->mapping_lock, flags);
- return NULL;
- }
- memset(pgd, 0, PAGE_SIZE);
- hd->pgd = pgd;
- }
-
- parent = hd->pgd;
+ if ( hd->pgd_maddr == 0 )
+ {
+ hd->pgd_maddr = alloc_pgtable_maddr();
+ if ( hd->pgd_maddr == 0 )
+ return 0;
+ }
+
+ parent = (struct dma_pte *)map_vtd_domain_page(hd->pgd_maddr);
while ( level > 1 )
{
offset = address_level_offset(addr, level);
@@ -277,18 +271,15 @@ static struct page_info *addr_to_dma_pag
if ( dma_pte_addr(*pte) == 0 )
{
- pg = alloc_domheap_page(
- NULL, MEMF_node(domain_to_node(domain)));
- vaddr = map_domain_page(page_to_mfn(pg));
+ u64 maddr = alloc_pgtable_maddr();
+ dma_set_pte_addr(*pte, maddr);
+ vaddr = map_vtd_domain_page(maddr);
if ( !vaddr )
{
+ unmap_vtd_domain_page(parent);
spin_unlock_irqrestore(&hd->mapping_lock, flags);
- return NULL;
+ return 0;
}
- memset(vaddr, 0, PAGE_SIZE);
- iommu_flush_cache_page(iommu, vaddr);
-
- dma_set_pte_addr(*pte, page_to_maddr(pg));
/*
* high level table always sets r/w, last level
@@ -300,21 +291,20 @@ static struct page_info *addr_to_dma_pag
}
else
{
- pg = maddr_to_page(pte->val);
- vaddr = map_domain_page(page_to_mfn(pg));
+ vaddr = map_vtd_domain_page(pte->val);
if ( !vaddr )
{
+ unmap_vtd_domain_page(parent);
spin_unlock_irqrestore(&hd->mapping_lock, flags);
- return NULL;
+ return 0;
}
}
- if ( parent != hd->pgd )
- unmap_domain_page(parent);
-
- if ( level == 2 && vaddr )
+ unmap_vtd_domain_page(parent);
+ if ( level == 2 )
{
- unmap_domain_page(vaddr);
+ pte_maddr = pte->val & PAGE_MASK_4K;
+ unmap_vtd_domain_page(vaddr);
break;
}
@@ -324,43 +314,42 @@ static struct page_info *addr_to_dma_pag
}
spin_unlock_irqrestore(&hd->mapping_lock, flags);
- return pg;
+ return pte_maddr;
}
/* return address's page at specific level */
-static struct page_info *dma_addr_level_page(struct domain *domain,
- u64 addr, int level)
+static u64 dma_addr_level_page_maddr(
+ struct domain *domain, u64 addr, int level)
{
struct hvm_iommu *hd = domain_hvm_iommu(domain);
struct dma_pte *parent, *pte = NULL;
int total = agaw_to_level(hd->agaw);
int offset;
- struct page_info *pg = NULL;
-
- parent = hd->pgd;
+ u64 pg_maddr = hd->pgd_maddr;
+
+ if ( pg_maddr == 0 )
+ return 0;
+
+ parent = (struct dma_pte *)map_vtd_domain_page(pg_maddr);
while ( level <= total )
{
offset = address_level_offset(addr, total);
pte = &parent[offset];
if ( dma_pte_addr(*pte) == 0 )
- {
- if ( parent != hd->pgd )
- unmap_domain_page(parent);
break;
- }
-
- pg = maddr_to_page(pte->val);
- if ( parent != hd->pgd )
- unmap_domain_page(parent);
+
+ pg_maddr = pte->val & PAGE_MASK_4K;
+ unmap_vtd_domain_page(parent);
if ( level == total )
- return pg;
-
- parent = map_domain_page(page_to_mfn(pg));
+ return pg_maddr;
+
+ parent = map_vtd_domain_page(pte->val);
total--;
}
- return NULL;
+ unmap_vtd_domain_page(parent);
+ return 0;
}
static void iommu_flush_write_buffer(struct iommu *iommu)
@@ -639,17 +628,17 @@ static void dma_pte_clear_one(struct dom
{
struct acpi_drhd_unit *drhd;
struct iommu *iommu;
- struct dma_pte *pte = NULL;
- struct page_info *pg = NULL;
+ struct dma_pte *page = NULL, *pte = NULL;
+ u64 pg_maddr;
drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list);
/* get last level pte */
- pg = dma_addr_level_page(domain, addr, 1);
- if ( !pg )
+ pg_maddr = dma_addr_level_page_maddr(domain, addr, 1);
+ if ( pg_maddr == 0 )
return;
- pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg));
- pte += address_level_offset(addr, 1);
+ page = (struct dma_pte *)map_vtd_domain_page(pg_maddr);
+ pte = page + address_level_offset(addr, 1);
if ( pte )
{
dma_clear_pte(*pte);
@@ -665,7 +654,7 @@ static void dma_pte_clear_one(struct dom
iommu_flush_write_buffer(iommu);
}
}
- unmap_domain_page(pte);
+ unmap_vtd_domain_page(page);
}
/* clear last level pte, a tlb flush should be followed */
@@ -695,11 +684,11 @@ void dma_pte_free_pagetable(struct domai
struct hvm_iommu *hd = domain_hvm_iommu(domain);
struct iommu *iommu;
int addr_width = agaw_to_width(hd->agaw);
- struct dma_pte *pte;
+ struct dma_pte *page, *pte;
int total = agaw_to_level(hd->agaw);
int level;
u32 tmp;
- struct page_info *pg = NULL;
+ u64 pg_maddr;
drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list);
iommu = drhd->iommu;
@@ -717,15 +706,15 @@ void dma_pte_free_pagetable(struct domai
while ( tmp < end )
{
- pg = dma_addr_level_page(domain, tmp, level);
- if ( !pg )
+ pg_maddr = dma_addr_level_page_maddr(domain, tmp, level);
+ if ( pg_maddr == 0 )
return;
- pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg));
- pte += address_level_offset(tmp, level);
+ page = (struct dma_pte *)map_vtd_domain_page(pg_maddr);
+ pte = page + address_level_offset(tmp, level);
dma_clear_pte(*pte);
iommu_flush_cache_entry(iommu, pte);
- unmap_domain_page(pte);
- free_domheap_page(pg);
+ unmap_vtd_domain_page(page);
+ free_pgtable_maddr(pg_maddr);
tmp += level_size(level);
}
@@ -735,17 +724,15 @@ void dma_pte_free_pagetable(struct domai
/* free pgd */
if ( start == 0 && end == ((((u64)1) << addr_width) - 1) )
{
- free_xenheap_page((void *)hd->pgd);
- hd->pgd = NULL;
+ free_pgtable_maddr(hd->pgd_maddr);
+ hd->pgd_maddr = 0;
}
}
/* iommu handling */
static int iommu_set_root_entry(struct iommu *iommu)
{
- void *addr;
u32 cmd, sts;
- struct root_entry *root;
unsigned long flags;
if ( iommu == NULL )
@@ -755,25 +742,19 @@ static int iommu_set_root_entry(struct i
return -EINVAL;
}
- if ( unlikely(!iommu->root_entry) )
- {
- root = (struct root_entry *)alloc_xenheap_page();
- if ( root == NULL )
- return -ENOMEM;
-
- memset((u8*)root, 0, PAGE_SIZE);
- iommu_flush_cache_page(iommu, root);
-
- if ( cmpxchg((unsigned long *)&iommu->root_entry,
- 0, (unsigned long)root) != 0 )
- free_xenheap_page((void *)root);
- }
-
- addr = iommu->root_entry;
+ if ( iommu->root_maddr != 0 )
+ {
+ free_pgtable_maddr(iommu->root_maddr);
+ iommu->root_maddr = 0;
+ }
spin_lock_irqsave(&iommu->register_lock, flags);
- dmar_writeq(iommu->reg, DMAR_RTADDR_REG, virt_to_maddr(addr));
+ iommu->root_maddr = alloc_pgtable_maddr();
+ if ( iommu->root_maddr == 0 )
+ return -ENOMEM;
+
+ dmar_writeq(iommu->reg, DMAR_RTADDR_REG, iommu->root_maddr);
cmd = iommu->gcmd | DMA_GCMD_SRTP;
dmar_writel(iommu->reg, DMAR_GCMD_REG, cmd);
@@ -1110,8 +1091,11 @@ static void free_iommu(struct iommu *iom
{
if ( !iommu )
return;
- if ( iommu->root_entry )
- free_xenheap_page((void *)iommu->root_entry);
+ if ( iommu->root_maddr != 0 )
+ {
+ free_pgtable_maddr(iommu->root_maddr);
+ iommu->root_maddr = 0;
+ }
if ( iommu->reg )
iounmap(iommu->reg);
free_intel_iommu(iommu->intel);
@@ -1166,13 +1150,17 @@ static int domain_context_mapping_one(
u8 bus, u8 devfn)
{
struct hvm_iommu *hd = domain_hvm_iommu(domain);
- struct context_entry *context;
+ struct context_entry *context, *context_entries;
unsigned long flags;
int ret = 0;
-
- context = device_to_context_entry(iommu, bus, devfn);
+ u64 maddr;
+
+ maddr = bus_to_context_maddr(iommu, bus);
+ context_entries = (struct context_entry *)map_vtd_domain_page(maddr);
+ context = &context_entries[devfn];
if ( !context )
{
+ unmap_vtd_domain_page(context_entries);
gdprintk(XENLOG_ERR VTDPREFIX,
"domain_context_mapping_one:context == NULL:"
"bdf = %x:%x:%x\n",
@@ -1182,6 +1170,7 @@ static int domain_context_mapping_one(
if ( context_present(*context) )
{
+ unmap_vtd_domain_page(context_entries);
gdprintk(XENLOG_WARNING VTDPREFIX,
"domain_context_mapping_one:context present:bdf=%x:%x:%x\n",
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
@@ -1202,19 +1191,8 @@ static int domain_context_mapping_one(
else
{
#endif
- if ( !hd->pgd )
- {
- struct dma_pte *pgd = (struct dma_pte *)alloc_xenheap_page();
- if ( !pgd )
- {
- spin_unlock_irqrestore(&hd->mapping_lock, flags);
- return -ENOMEM;
- }
- memset(pgd, 0, PAGE_SIZE);
- hd->pgd = pgd;
- }
-
- context_set_address_root(*context, virt_to_maddr(hd->pgd));
+ ASSERT(hd->pgd_maddr != 0);
+ context_set_address_root(*context, hd->pgd_maddr);
context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL);
#ifdef CONTEXT_PASSTHRU
}
@@ -1226,9 +1204,11 @@ static int domain_context_mapping_one(
gdprintk(XENLOG_INFO VTDPREFIX,
"domain_context_mapping_one-%x:%x:%x-*context=%"PRIx64":%"PRIx64
- " hd->pgd=%p\n",
+ " hd->pgd_maddr=%"PRIx64"\n",
bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
- context->hi, context->lo, hd->pgd);
+ context->hi, context->lo, hd->pgd_maddr);
+
+ unmap_vtd_domain_page(context_entries);
if ( iommu_flush_context_device(iommu, domain_iommu_domid(domain),
(((u16)bus) << 8) | devfn,
@@ -1389,12 +1369,16 @@ static int domain_context_unmap_one(
struct iommu *iommu,
u8 bus, u8 devfn)
{
- struct context_entry *context;
+ struct context_entry *context, *context_entries;
unsigned long flags;
-
- context = device_to_context_entry(iommu, bus, devfn);
+ u64 maddr;
+
+ maddr = bus_to_context_maddr(iommu, bus);
+ context_entries = (struct context_entry *)map_vtd_domain_page(maddr);
+ context = &context_entries[devfn];
if ( !context )
{
+ unmap_vtd_domain_page(context_entries);
gdprintk(XENLOG_ERR VTDPREFIX,
"domain_context_unmap_one-%x:%x:%x- context == NULL:return\n",
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
@@ -1403,6 +1387,7 @@ static int domain_context_unmap_one(
if ( !context_present(*context) )
{
+ unmap_vtd_domain_page(context_entries);
gdprintk(XENLOG_WARNING VTDPREFIX,
"domain_context_unmap_one-%x:%x:%x- "
"context NOT present:return\n",
@@ -1420,6 +1405,7 @@ static int domain_context_unmap_one(
iommu_flush_cache_entry(iommu, context);
iommu_flush_context_global(iommu, 0);
iommu_flush_iotlb_global(iommu, 0);
+ unmap_vtd_domain_page(context_entries);
spin_unlock_irqrestore(&iommu->lock, flags);
return 0;
@@ -1575,36 +1561,7 @@ void iommu_domain_teardown(struct domain
return;
iommu_domid_release(d);
-
-#if CONFIG_PAGING_LEVELS == 3
- {
- struct hvm_iommu *hd = domain_hvm_iommu(d);
- int level = agaw_to_level(hd->agaw);
- struct dma_pte *pgd = NULL;
-
- switch ( level )
- {
- case VTD_PAGE_TABLE_LEVEL_3:
- if ( hd->pgd )
- free_xenheap_page((void *)hd->pgd);
- break;
- case VTD_PAGE_TABLE_LEVEL_4:
- if ( hd->pgd )
- {
- pgd = hd->pgd;
- if ( pgd[0].val != 0 )
- free_xenheap_page((void*)maddr_to_virt(
- dma_pte_addr(pgd[0])));
- free_xenheap_page((void *)hd->pgd);
- }
- break;
- default:
- gdprintk(XENLOG_ERR VTDPREFIX,
- "Unsupported p2m table sharing level!\n");
- break;
- }
- }
-#endif
+ iommu_free_pgd(d);
return_devices_to_dom0(d);
}
@@ -1630,8 +1587,8 @@ int intel_iommu_map_page(
{
struct acpi_drhd_unit *drhd;
struct iommu *iommu;
- struct dma_pte *pte = NULL;
- struct page_info *pg = NULL;
+ struct dma_pte *page = NULL, *pte = NULL;
+ u64 pg_maddr;
drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list);
iommu = drhd->iommu;
@@ -1642,15 +1599,15 @@ int intel_iommu_map_page(
return 0;
#endif
- pg = addr_to_dma_page(d, (paddr_t)gfn << PAGE_SHIFT_4K);
- if ( !pg )
+ pg_maddr = addr_to_dma_page_maddr(d, gfn << PAGE_SHIFT_4K);
+ if ( pg_maddr == 0 )
return -ENOMEM;
- pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg));
- pte += gfn & LEVEL_MASK;
+ page = (struct dma_pte *)map_vtd_domain_page(pg_maddr);
+ pte = page + (gfn & LEVEL_MASK);
dma_set_pte_addr(*pte, (paddr_t)mfn << PAGE_SHIFT_4K);
dma_set_pte_prot(*pte, DMA_PTE_READ | DMA_PTE_WRITE);
iommu_flush_cache_entry(iommu, pte);
- unmap_domain_page(pte);
+ unmap_vtd_domain_page(page);
for_each_drhd_unit ( drhd )
{
@@ -1690,9 +1647,9 @@ int iommu_page_mapping(struct domain *do
struct acpi_drhd_unit *drhd;
struct iommu *iommu;
unsigned long start_pfn, end_pfn;
- struct dma_pte *pte = NULL;
+ struct dma_pte *page = NULL, *pte = NULL;
int index;
- struct page_info *pg = NULL;
+ u64 pg_maddr;
drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list);
iommu = drhd->iommu;
@@ -1705,15 +1662,15 @@ int iommu_page_mapping(struct domain *do
index = 0;
while ( start_pfn < end_pfn )
{
- pg = addr_to_dma_page(domain, iova + PAGE_SIZE_4K * index);
- if ( !pg )
+ pg_maddr = addr_to_dma_page_maddr(domain, iova + PAGE_SIZE_4K * index);
+ if ( pg_maddr == 0 )
return -ENOMEM;
- pte = (struct dma_pte *)map_domain_page(page_to_mfn(pg));
- pte += start_pfn & LEVEL_MASK;
+ page = (struct dma_pte *)map_vtd_domain_page(pg_maddr);
+ pte = page + (start_pfn & LEVEL_MASK);
dma_set_pte_addr(*pte, start_pfn << PAGE_SHIFT_4K);
dma_set_pte_prot(*pte, prot);
iommu_flush_cache_entry(iommu, pte);
- unmap_domain_page(pte);
+ unmap_vtd_domain_page(page);
start_pfn++;
index++;
}
@@ -2050,159 +2007,6 @@ int intel_iommu_assign_device(struct dom
return ret;
}
-
-void iommu_set_pgd(struct domain *d)
-{
- struct hvm_iommu *hd = domain_hvm_iommu(d);
- unsigned long p2m_table;
-
- if ( hd->pgd )
- {
- gdprintk(XENLOG_INFO VTDPREFIX,
- "iommu_set_pgd_1: hd->pgd = %p\n", hd->pgd);
- hd->pgd = NULL;
- }
- p2m_table = mfn_x(pagetable_get_mfn(d->arch.phys_table));
-
- if ( paging_mode_hap(d) )
- {
- int level = agaw_to_level(hd->agaw);
- struct dma_pte *dpte = NULL;
- mfn_t pgd_mfn;
-
- switch ( level )
- {
- case VTD_PAGE_TABLE_LEVEL_3:
- dpte = map_domain_page(p2m_table);
- if ( !dma_pte_present(*dpte) )
- {
- gdprintk(XENLOG_ERR VTDPREFIX,
- "iommu_set_pgd: second level wasn't there\n");
- unmap_domain_page(dpte);
- return;
- }
- pgd_mfn = _mfn(dma_pte_addr(*dpte) >> PAGE_SHIFT_4K);
- unmap_domain_page(dpte);
- hd->pgd = maddr_to_virt(pagetable_get_paddr(
- pagetable_from_mfn(pgd_mfn)));
- break;
- case VTD_PAGE_TABLE_LEVEL_4:
- pgd_mfn = _mfn(p2m_table);
- hd->pgd = maddr_to_virt(pagetable_get_paddr(
- pagetable_from_mfn(pgd_mfn)));
- break;
- default:
- gdprintk(XENLOG_ERR VTDPREFIX,
- "iommu_set_pgd:Unsupported p2m table sharing level!\n");
- break;
- }
- }
- else
- {
-#if CONFIG_PAGING_LEVELS == 3
- int level = agaw_to_level(hd->agaw);
- struct dma_pte *pmd = NULL;
- struct dma_pte *pgd = NULL;
- struct dma_pte *pte = NULL;
- l3_pgentry_t *l3e;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&hd->mapping_lock, flags);
- if ( !hd->pgd )
- {
- pgd = (struct dma_pte *)alloc_xenheap_page();
- if ( !pgd )
- {
- spin_unlock_irqrestore(&hd->mapping_lock, flags);
- gdprintk(XENLOG_ERR VTDPREFIX,
- "Allocate pgd memory failed!\n");
- return;
- }
- memset(pgd, 0, PAGE_SIZE);
- hd->pgd = pgd;
- }
-
- l3e = map_domain_page(p2m_table);
- switch ( level )
- {
- case VTD_PAGE_TABLE_LEVEL_3: /* Weybridge */
- /* We only support 8 entries for the PAE L3 p2m table */
- for ( i = 0; i < 8 ; i++ )
- {
- /* Don't create new L2 entry, use ones from p2m table */
- pgd[i].val = l3e[i].l3 | _PAGE_PRESENT | _PAGE_RW;
- }
- break;
-
- case VTD_PAGE_TABLE_LEVEL_4: /* Stoakley */
- /* We allocate one more page for the top vtd page table. */
- pmd = (struct dma_pte *)alloc_xenheap_page();
- if ( !pmd )
- {
- unmap_domain_page(l3e);
- spin_unlock_irqrestore(&hd->mapping_lock, flags);
- gdprintk(XENLOG_ERR VTDPREFIX,
- "Allocate pmd memory failed!\n");
- return;
- }
- memset((u8*)pmd, 0, PAGE_SIZE);
- pte = &pgd[0];
- dma_set_pte_addr(*pte, virt_to_maddr(pmd));
- dma_set_pte_readable(*pte);
- dma_set_pte_writable(*pte);
-
- for ( i = 0; i < 8; i++ )
- {
- /* Don't create new L2 entry, use ones from p2m table */
- pmd[i].val = l3e[i].l3 | _PAGE_PRESENT | _PAGE_RW;
- }
- break;
- default:
- gdprintk(XENLOG_ERR VTDPREFIX,
- "iommu_set_pgd:Unsupported p2m table sharing level!\n");
- break;
- }
- unmap_domain_page(l3e);
- spin_unlock_irqrestore(&hd->mapping_lock, flags);
-#elif CONFIG_PAGING_LEVELS == 4
- int level = agaw_to_level(hd->agaw);
- l3_pgentry_t *l3e;
- mfn_t pgd_mfn;
-
- switch ( level )
- {
- case VTD_PAGE_TABLE_LEVEL_3:
- l3e = map_domain_page(p2m_table);
- if ( (l3e_get_flags(*l3e) & _PAGE_PRESENT) == 0 )
- {
- gdprintk(XENLOG_ERR VTDPREFIX,
- "iommu_set_pgd: second level wasn't there\n");
- unmap_domain_page(l3e);
- return;
- }
- pgd_mfn = _mfn(l3e_get_pfn(*l3e));
- unmap_domain_page(l3e);
- hd->pgd = maddr_to_virt(pagetable_get_paddr(
- pagetable_from_mfn(pgd_mfn)));
- break;
-
- case VTD_PAGE_TABLE_LEVEL_4:
- pgd_mfn = _mfn(p2m_table);
- hd->pgd = maddr_to_virt(pagetable_get_paddr(
- pagetable_from_mfn(pgd_mfn)));
- break;
- default:
- gdprintk(XENLOG_ERR VTDPREFIX,
- "iommu_set_pgd:Unsupported p2m table sharing level!\n");
- break;
- }
-#endif
- }
- gdprintk(XENLOG_INFO VTDPREFIX,
- "iommu_set_pgd: hd->pgd = %p\n", hd->pgd);
-}
-
u8 iommu_state[MAX_IOMMU_REGS * MAX_IOMMUS];
int iommu_suspend(void)
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/drivers/passthrough/vtd/iommu.h
--- a/xen/drivers/passthrough/vtd/iommu.h Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.h Thu Apr 10 09:22:38 2008 +0100
@@ -425,7 +425,7 @@ extern struct list_head acpi_ioapic_unit
extern struct list_head acpi_ioapic_units;
struct qi_ctrl {
- struct qinval_entry *qinval; /* queue invalidation page */
+ u64 qinval_maddr; /* queue invalidation page machine address */
int qinval_index; /* queue invalidation index */
spinlock_t qinval_lock; /* lock for queue invalidation page */
spinlock_t qinval_poll_lock; /* lock for queue invalidation poll addr */
@@ -433,7 +433,7 @@ struct qi_ctrl {
};
struct ir_ctrl {
- struct iremap_entry *iremap; /* interrupt remap table */
+ u64 iremap_maddr; /* interrupt remap table machine address */
int iremap_index; /* interrupt remap index */
spinlock_t iremap_lock; /* lock for irq remappping table */
};
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/drivers/passthrough/vtd/qinval.c
--- a/xen/drivers/passthrough/vtd/qinval.c Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/drivers/passthrough/vtd/qinval.c Thu Apr 10 09:22:38 2008 +0100
@@ -63,13 +63,14 @@ static int gen_cc_inv_dsc(struct iommu *
static int gen_cc_inv_dsc(struct iommu *iommu, int index,
u16 did, u16 source_id, u8 function_mask, u8 granu)
{
- u64 *ptr64;
- unsigned long flags;
- struct qinval_entry * qinval_entry = NULL;
- struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu);
-
- spin_lock_irqsave(&qi_ctrl->qinval_lock, flags);
- qinval_entry = &qi_ctrl->qinval[index];
+ unsigned long flags;
+ struct qinval_entry *qinval_entry = NULL, *qinval_entries;
+ struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu);
+
+ spin_lock_irqsave(&qi_ctrl->qinval_lock, flags);
+ qinval_entries =
+ (struct qinval_entry *)map_vtd_domain_page(qi_ctrl->qinval_maddr);
+ qinval_entry = &qinval_entries[index];
qinval_entry->q.cc_inv_dsc.lo.type = TYPE_INVAL_CONTEXT;
qinval_entry->q.cc_inv_dsc.lo.granu = granu;
qinval_entry->q.cc_inv_dsc.lo.res_1 = 0;
@@ -78,9 +79,10 @@ static int gen_cc_inv_dsc(struct iommu *
qinval_entry->q.cc_inv_dsc.lo.fm = function_mask;
qinval_entry->q.cc_inv_dsc.lo.res_2 = 0;
qinval_entry->q.cc_inv_dsc.hi.res = 0;
- spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags);
-
- ptr64 = (u64 *)qinval_entry;
+
+ unmap_vtd_domain_page(qinval_entries);
+ spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags);
+
return 0;
}
@@ -93,7 +95,7 @@ int queue_invalidate_context(struct iomm
spin_lock_irqsave(&iommu->register_lock, flags);
index = qinval_next_index(iommu);
- if (index == -1)
+ if ( index == -1 )
return -EBUSY;
ret = gen_cc_inv_dsc(iommu, index, did, source_id,
function_mask, granu);
@@ -106,14 +108,16 @@ static int gen_iotlb_inv_dsc(struct iomm
u8 granu, u8 dr, u8 dw, u16 did, u8 am, u8 ih, u64 addr)
{
unsigned long flags;
- struct qinval_entry * qinval_entry = NULL;
+ struct qinval_entry *qinval_entry = NULL, *qinval_entries;
struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu);
if ( index == -1 )
return -1;
spin_lock_irqsave(&qi_ctrl->qinval_lock, flags);
- qinval_entry = &qi_ctrl->qinval[index];
+ qinval_entries =
+ (struct qinval_entry *)map_vtd_domain_page(qi_ctrl->qinval_maddr);
+ qinval_entry = &qinval_entries[index];
qinval_entry->q.iotlb_inv_dsc.lo.type = TYPE_INVAL_IOTLB;
qinval_entry->q.iotlb_inv_dsc.lo.granu = granu;
qinval_entry->q.iotlb_inv_dsc.lo.dr = 0;
@@ -127,6 +131,7 @@ static int gen_iotlb_inv_dsc(struct iomm
qinval_entry->q.iotlb_inv_dsc.hi.res_1 = 0;
qinval_entry->q.iotlb_inv_dsc.hi.addr = addr;
+ unmap_vtd_domain_page(qinval_entries);
spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags);
return 0;
}
@@ -151,15 +156,16 @@ static int gen_wait_dsc(struct iommu *io
static int gen_wait_dsc(struct iommu *iommu, int index,
u8 iflag, u8 sw, u8 fn, u32 sdata, volatile u32 *saddr)
{
- u64 *ptr64;
- unsigned long flags;
- struct qinval_entry * qinval_entry = NULL;
+ unsigned long flags;
+ struct qinval_entry *qinval_entry = NULL, *qinval_entries;
struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu);
if ( index == -1 )
return -1;
spin_lock_irqsave(&qi_ctrl->qinval_lock, flags);
- qinval_entry = &qi_ctrl->qinval[index];
+ qinval_entries =
+ (struct qinval_entry *)map_vtd_domain_page(qi_ctrl->qinval_maddr);
+ qinval_entry = &qinval_entries[index];
qinval_entry->q.inv_wait_dsc.lo.type = TYPE_INVAL_WAIT;
qinval_entry->q.inv_wait_dsc.lo.iflag = iflag;
qinval_entry->q.inv_wait_dsc.lo.sw = sw;
@@ -168,8 +174,8 @@ static int gen_wait_dsc(struct iommu *io
qinval_entry->q.inv_wait_dsc.lo.sdata = sdata;
qinval_entry->q.inv_wait_dsc.hi.res_1 = 0;
qinval_entry->q.inv_wait_dsc.hi.saddr = virt_to_maddr(saddr) >> 2;
- spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags);
- ptr64 = (u64 *)qinval_entry;
+ unmap_vtd_domain_page(qinval_entries);
+ spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags);
return 0;
}
@@ -185,7 +191,7 @@ static int queue_invalidate_wait(struct
spin_lock_irqsave(&qi_ctrl->qinval_poll_lock, flags);
spin_lock_irqsave(&iommu->register_lock, flags);
index = qinval_next_index(iommu);
- if (*saddr == 1)
+ if ( *saddr == 1 )
*saddr = 0;
ret = gen_wait_dsc(iommu, index, iflag, sw, fn, sdata, saddr);
ret |= qinval_update_qtail(iommu, index);
@@ -196,8 +202,10 @@ static int queue_invalidate_wait(struct
{
/* In case all wait descriptor writes to same addr with same data */
start_time = jiffies;
- while ( *saddr != 1 ) {
- if (time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT)) {
+ while ( *saddr != 1 )
+ {
+ if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) )
+ {
print_qi_regs(iommu);
panic("queue invalidate wait descriptor was not executed\n");
}
@@ -213,7 +221,7 @@ int invalidate_sync(struct iommu *iommu)
int ret = -1;
struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu);
- if (qi_ctrl->qinval)
+ if ( qi_ctrl->qinval_maddr == 0 )
{
ret = queue_invalidate_wait(iommu,
0, 1, 1, 1, &qi_ctrl->qinval_poll_status);
@@ -226,14 +234,16 @@ static int gen_dev_iotlb_inv_dsc(struct
u32 max_invs_pend, u16 sid, u16 size, u64 addr)
{
unsigned long flags;
- struct qinval_entry * qinval_entry = NULL;
+ struct qinval_entry *qinval_entry = NULL, *qinval_entries;
struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu);
if ( index == -1 )
return -1;
spin_lock_irqsave(&qi_ctrl->qinval_lock, flags);
- qinval_entry = &qi_ctrl->qinval[index];
+ qinval_entries =
+ (struct qinval_entry *)map_vtd_domain_page(qi_ctrl->qinval_maddr);
+ qinval_entry = &qinval_entries[index];
qinval_entry->q.dev_iotlb_inv_dsc.lo.type = TYPE_INVAL_DEVICE_IOTLB;
qinval_entry->q.dev_iotlb_inv_dsc.lo.res_1 = 0;
qinval_entry->q.dev_iotlb_inv_dsc.lo.max_invs_pend = max_invs_pend;
@@ -244,6 +254,7 @@ static int gen_dev_iotlb_inv_dsc(struct
qinval_entry->q.dev_iotlb_inv_dsc.hi.size = size;
qinval_entry->q.dev_iotlb_inv_dsc.hi.addr = addr;
+ unmap_vtd_domain_page(qinval_entries);
spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags);
return 0;
}
@@ -268,14 +279,16 @@ static int gen_iec_inv_dsc(struct iommu
u8 granu, u8 im, u16 iidx)
{
unsigned long flags;
- struct qinval_entry * qinval_entry = NULL;
+ struct qinval_entry *qinval_entry = NULL, *qinval_entries;
struct qi_ctrl *qi_ctrl = iommu_qi_ctrl(iommu);
if ( index == -1 )
return -1;
spin_lock_irqsave(&qi_ctrl->qinval_lock, flags);
- qinval_entry = &qi_ctrl->qinval[index];
+ qinval_entries =
+ (struct qinval_entry *)map_vtd_domain_page(qi_ctrl->qinval_maddr);
+ qinval_entry = &qinval_entries[index];
qinval_entry->q.iec_inv_dsc.lo.type = TYPE_INVAL_IEC;
qinval_entry->q.iec_inv_dsc.lo.granu = granu;
qinval_entry->q.iec_inv_dsc.lo.res_1 = 0;
@@ -284,6 +297,7 @@ static int gen_iec_inv_dsc(struct iommu
qinval_entry->q.iec_inv_dsc.lo.res_2 = 0;
qinval_entry->q.iec_inv_dsc.hi.res = 0;
+ unmap_vtd_domain_page(qinval_entries);
spin_unlock_irqrestore(&qi_ctrl->qinval_lock, flags);
return 0;
}
@@ -349,7 +363,7 @@ static int flush_context_qi(
did = 0;
}
- if (qi_ctrl->qinval)
+ if ( qi_ctrl->qinval_maddr != 0 )
{
ret = queue_invalidate_context(iommu, did, sid, fm,
type >> DMA_CCMD_INVL_GRANU_OFFSET);
@@ -382,7 +396,8 @@ static int flush_iotlb_qi(
did = 0;
}
- if (qi_ctrl->qinval) {
+ if ( qi_ctrl->qinval_maddr != 0 )
+ {
/* use queued invalidation */
if (cap_write_drain(iommu->cap))
dw = 1;
@@ -400,7 +415,6 @@ int qinval_setup(struct iommu *iommu)
int qinval_setup(struct iommu *iommu)
{
unsigned long start_time;
- u64 paddr;
u32 status = 0;
struct qi_ctrl *qi_ctrl;
struct iommu_flush *flush;
@@ -411,15 +425,14 @@ int qinval_setup(struct iommu *iommu)
if ( !ecap_queued_inval(iommu->ecap) )
return -ENODEV;
- if (qi_ctrl->qinval == NULL) {
- qi_ctrl->qinval = alloc_xenheap_page();
- if (qi_ctrl->qinval == NULL)
- panic("Cannot allocate memory for qi_ctrl->qinval\n");
- memset((u8*)qi_ctrl->qinval, 0, PAGE_SIZE_4K);
+ if ( qi_ctrl->qinval_maddr == 0 )
+ {
+ qi_ctrl->qinval_maddr = alloc_pgtable_maddr();
+ if ( qi_ctrl->qinval_maddr == 0 )
+ panic("Cannot allocate memory for qi_ctrl->qinval_maddr\n");
flush->context = flush_context_qi;
flush->iotlb = flush_iotlb_qi;
}
- paddr = virt_to_maddr(qi_ctrl->qinval);
/* Setup Invalidation Queue Address(IQA) register with the
* address of the page we just allocated. QS field at
@@ -428,7 +441,7 @@ int qinval_setup(struct iommu *iommu)
* registers are automatically reset to 0 with write
* to IQA register.
*/
- dmar_writeq(iommu->reg, DMAR_IQA_REG, paddr);
+ dmar_writeq(iommu->reg, DMAR_IQA_REG, qi_ctrl->qinval_maddr);
/* enable queued invalidation hardware */
iommu->gcmd |= DMA_GCMD_QIE;
@@ -436,11 +449,12 @@ int qinval_setup(struct iommu *iommu)
/* Make sure hardware complete it */
start_time = jiffies;
- while (1) {
+ while ( 1 )
+ {
status = dmar_readl(iommu->reg, DMAR_GSTS_REG);
- if (status & DMA_GSTS_QIES)
+ if ( status & DMA_GSTS_QIES )
break;
- if (time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT))
+ if ( time_after(jiffies, start_time + DMAR_OPERATION_TIMEOUT) )
panic("Cannot set QIE field for queue invalidation\n");
cpu_relax();
}
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/drivers/passthrough/vtd/utils.c
--- a/xen/drivers/passthrough/vtd/utils.c Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/drivers/passthrough/vtd/utils.c Thu Apr 10 09:22:38 2008 +0100
@@ -25,6 +25,7 @@
#include "../pci-direct.h"
#include "../pci_regs.h"
#include "msi.h"
+#include "vtd.h"
#define INTEL 0x8086
#define SEABURG 0x4000
@@ -243,7 +244,7 @@ u32 get_level_index(unsigned long gmfn,
}
void print_vtd_entries(
- struct domain *d,
+ struct domain *d,
struct iommu *iommu,
int bus, int devfn,
unsigned long gmfn)
@@ -261,37 +262,40 @@ void print_vtd_entries(
printk("print_vtd_entries: domain_id = %x bdf = %x:%x:%x gmfn = %lx\n",
d->domain_id, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), gmfn);
- if ( hd->pgd == NULL )
- {
- printk(" hg->pgd == NULL\n");
+ if ( hd->pgd_maddr == 0 )
+ {
+ printk(" hd->pgd_maddr == 0\n");
return;
}
- printk(" d->pgd = %p virt_to_maddr(hd->pgd) = %lx\n",
- hd->pgd, virt_to_maddr(hd->pgd));
+ printk(" hd->pgd_maddr = %"PRIx64"\n", hd->pgd_maddr);
for_each_drhd_unit ( drhd )
{
printk("---- print_vtd_entries %d ----\n", i++);
- root_entry = iommu->root_entry;
- if ( root_entry == NULL )
- {
- printk(" root_entry == NULL\n");
- continue;
- }
-
+ if ( iommu->root_maddr == 0 )
+ {
+ printk(" iommu->root_maddr = 0\n");
+ continue;
+ }
+
+ root_entry =
+ (struct root_entry *)map_vtd_domain_page(iommu->root_maddr);
+
printk(" root_entry = %p\n", root_entry);
printk(" root_entry[%x] = %"PRIx64"\n", bus, root_entry[bus].val);
if ( !root_present(root_entry[bus]) )
{
+ unmap_vtd_domain_page(root_entry);
printk(" root_entry[%x] not present\n", bus);
continue;
}
ctxt_entry =
- maddr_to_virt((root_entry[bus].val >> PAGE_SHIFT) << PAGE_SHIFT);
+ (struct context_entry *)map_vtd_domain_page(root_entry[bus].val);
if ( ctxt_entry == NULL )
{
+ unmap_vtd_domain_page(root_entry);
printk(" ctxt_entry == NULL\n");
continue;
}
@@ -301,6 +305,8 @@ void print_vtd_entries(
devfn, ctxt_entry[devfn].hi, ctxt_entry[devfn].lo);
if ( !context_present(ctxt_entry[devfn]) )
{
+ unmap_vtd_domain_page(ctxt_entry);
+ unmap_vtd_domain_page(root_entry);
printk(" ctxt_entry[%x] not present\n", devfn);
continue;
}
@@ -308,6 +314,8 @@ void print_vtd_entries(
if ( level != VTD_PAGE_TABLE_LEVEL_3 &&
level != VTD_PAGE_TABLE_LEVEL_4)
{
+ unmap_vtd_domain_page(ctxt_entry);
+ unmap_vtd_domain_page(root_entry);
printk("Unsupported VTD page table level (%d)!\n", level);
continue;
}
@@ -319,6 +327,8 @@ void print_vtd_entries(
printk(" l%d = %p\n", level, l);
if ( l == NULL )
{
+ unmap_vtd_domain_page(ctxt_entry);
+ unmap_vtd_domain_page(root_entry);
printk(" l%d == NULL\n", level);
break;
}
@@ -329,6 +339,8 @@ void print_vtd_entries(
pte.val = l[l_index];
if ( !dma_pte_present(pte) )
{
+ unmap_vtd_domain_page(ctxt_entry);
+ unmap_vtd_domain_page(root_entry);
printk(" l%d[%x] not present\n", level, l_index);
break;
}
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/drivers/passthrough/vtd/x86/vtd.c
--- a/xen/drivers/passthrough/vtd/x86/vtd.c Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/drivers/passthrough/vtd/x86/vtd.c Thu Apr 10 09:22:38 2008 +0100
@@ -20,6 +20,7 @@
#include <xen/sched.h>
#include <xen/domain_page.h>
+#include <asm/paging.h>
#include <xen/iommu.h>
#include "../iommu.h"
#include "../dmar.h"
@@ -124,3 +125,179 @@ void hvm_dpci_isairq_eoi(struct domain *
}
}
}
+
+void iommu_set_pgd(struct domain *d)
+{
+ struct hvm_iommu *hd = domain_hvm_iommu(d);
+ unsigned long p2m_table;
+ int level = agaw_to_level(hd->agaw);
+ l3_pgentry_t *l3e;
+
+ p2m_table = mfn_x(pagetable_get_mfn(d->arch.phys_table));
+
+ if ( paging_mode_hap(d) )
+ {
+ int level = agaw_to_level(hd->agaw);
+ struct dma_pte *dpte = NULL;
+ mfn_t pgd_mfn;
+
+ switch ( level )
+ {
+ case VTD_PAGE_TABLE_LEVEL_3:
+ dpte = map_domain_page(p2m_table);
+ if ( !dma_pte_present(*dpte) )
+ {
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "iommu_set_pgd: second level wasn't there\n");
+ unmap_domain_page(dpte);
+ return;
+ }
+ pgd_mfn = _mfn(dma_pte_addr(*dpte) >> PAGE_SHIFT_4K);
+ hd->pgd_maddr = mfn_x(pgd_mfn) << PAGE_SHIFT_4K;
+ unmap_domain_page(dpte);
+ break;
+ case VTD_PAGE_TABLE_LEVEL_4:
+ pgd_mfn = _mfn(p2m_table);
+ hd->pgd_maddr = mfn_x(pgd_mfn) << PAGE_SHIFT_4K;
+ break;
+ default:
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "iommu_set_pgd:Unsupported p2m table sharing level!\n");
+ break;
+ }
+ }
+ else
+ {
+#if CONFIG_PAGING_LEVELS == 3
+ struct dma_pte *pte = NULL, *pgd_vaddr = NULL, *pmd_vaddr = NULL;
+ int i;
+ u64 pmd_maddr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hd->mapping_lock, flags);
+ hd->pgd_maddr = alloc_pgtable_maddr();
+ if ( hd->pgd_maddr == 0 )
+ {
+ spin_unlock_irqrestore(&hd->mapping_lock, flags);
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "Allocate pgd memory failed!\n");
+ return;
+ }
+
+ pgd_vaddr = map_vtd_domain_page(hd->pgd_maddr);
+ l3e = map_domain_page(p2m_table);
+ switch ( level )
+ {
+ case VTD_PAGE_TABLE_LEVEL_3: /* Weybridge */
+ /* We only support 8 entries for the PAE L3 p2m table */
+ for ( i = 0; i < 8 ; i++ )
+ {
+ /* Don't create new L2 entry, use ones from p2m table */
+ pgd_vaddr[i].val = l3e[i].l3 | _PAGE_PRESENT | _PAGE_RW;
+ }
+ break;
+
+ case VTD_PAGE_TABLE_LEVEL_4: /* Stoakley */
+ /* We allocate one more page for the top vtd page table. */
+ pmd_maddr = alloc_pgtable_maddr();
+ if ( pmd_maddr == 0 )
+ {
+ unmap_vtd_domain_page(pgd_vaddr);
+ unmap_domain_page(l3e);
+ spin_unlock_irqrestore(&hd->mapping_lock, flags);
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "Allocate pmd memory failed!\n");
+ return;
+ }
+
+ pte = &pgd_vaddr[0];
+ dma_set_pte_addr(*pte, pmd_maddr);
+ dma_set_pte_readable(*pte);
+ dma_set_pte_writable(*pte);
+
+ pmd_vaddr = map_vtd_domain_page(pmd_maddr);
+ for ( i = 0; i < 8; i++ )
+ {
+ /* Don't create new L2 entry, use ones from p2m table */
+ pmd_vaddr[i].val = l3e[i].l3 | _PAGE_PRESENT | _PAGE_RW;
+ }
+
+ unmap_vtd_domain_page(pmd_vaddr);
+ break;
+ default:
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "iommu_set_pgd:Unsupported p2m table sharing level!\n");
+ break;
+ }
+
+ unmap_vtd_domain_page(pgd_vaddr);
+ unmap_domain_page(l3e);
+ spin_unlock_irqrestore(&hd->mapping_lock, flags);
+
+#elif CONFIG_PAGING_LEVELS == 4
+ mfn_t pgd_mfn;
+
+ switch ( level )
+ {
+ case VTD_PAGE_TABLE_LEVEL_3:
+ l3e = map_domain_page(p2m_table);
+ if ( (l3e_get_flags(*l3e) & _PAGE_PRESENT) == 0 )
+ {
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "iommu_set_pgd: second level wasn't there\n");
+ unmap_domain_page(l3e);
+ return;
+ }
+
+ pgd_mfn = _mfn(l3e_get_pfn(*l3e));
+ hd->pgd_maddr = mfn_x(pgd_mfn) << PAGE_SHIFT_4K;
+ unmap_domain_page(l3e);
+ break;
+ case VTD_PAGE_TABLE_LEVEL_4:
+ pgd_mfn = _mfn(p2m_table);
+ hd->pgd_maddr = mfn_x(pgd_mfn) << PAGE_SHIFT_4K;
+ break;
+ default:
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "iommu_set_pgd:Unsupported p2m table sharing level!\n");
+ break;
+ }
+#endif
+ }
+}
+
+void iommu_free_pgd(struct domain *d)
+{
+#if CONFIG_PAGING_LEVELS == 3
+ struct hvm_iommu *hd = domain_hvm_iommu(d);
+ int level = agaw_to_level(hd->agaw);
+ struct dma_pte *pgd_vaddr = NULL;
+
+ switch ( level )
+ {
+ case VTD_PAGE_TABLE_LEVEL_3:
+ if ( hd->pgd_maddr != 0 )
+ {
+ free_pgtable_maddr(hd->pgd_maddr);
+ hd->pgd_maddr = 0;
+ }
+ break;
+ case VTD_PAGE_TABLE_LEVEL_4:
+ if ( hd->pgd_maddr != 0 )
+ {
+ pgd_vaddr = (struct dma_pte*)map_vtd_domain_page(hd->pgd_maddr);
+ if ( pgd_vaddr[0].val != 0 )
+ free_pgtable_maddr(pgd_vaddr[0].val);
+ unmap_vtd_domain_page(pgd_vaddr);
+ free_pgtable_maddr(hd->pgd_maddr);
+ hd->pgd_maddr = 0;
+ }
+ break;
+ default:
+ gdprintk(XENLOG_ERR VTDPREFIX,
+ "Unsupported p2m table sharing level!\n");
+ break;
+ }
+#endif
+}
+
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/include/xen/hvm/iommu.h
--- a/xen/include/xen/hvm/iommu.h Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/include/xen/hvm/iommu.h Thu Apr 10 09:22:38 2008 +0100
@@ -38,7 +38,7 @@ struct hvm_iommu {
struct hvm_iommu {
spinlock_t iommu_list_lock; /* protect iommu specific lists */
struct list_head pdev_list; /* direct accessed pci devices */
- struct dma_pte *pgd; /* io page directory root */
+ u64 pgd_maddr; /* io page directory machine address */
spinlock_t mapping_lock; /* io page table lock */
int agaw; /* adjusted guest address width, 0 is level 2 30-bit */
struct list_head g2m_ioport_list; /* guest to machine ioport mapping */
diff -r 85848be18ba2 -r 1d3aaa6a8b87 xen/include/xen/iommu.h
--- a/xen/include/xen/iommu.h Thu Apr 10 09:20:07 2008 +0100
+++ b/xen/include/xen/iommu.h Thu Apr 10 09:22:38 2008 +0100
@@ -67,7 +67,7 @@ struct iommu {
u64 ecap;
spinlock_t lock; /* protect context, domain ids */
spinlock_t register_lock; /* protect iommu register handling */
- struct root_entry *root_entry; /* virtual address */
+ u64 root_maddr; /* root entry machine address */
unsigned int vector;
struct intel_iommu *intel;
};
@@ -85,6 +85,7 @@ int iommu_unmap_page(struct domain *d, u
int iommu_unmap_page(struct domain *d, unsigned long gfn);
void iommu_flush(struct domain *d, unsigned long gfn, u64 *p2m_entry);
void iommu_set_pgd(struct domain *d);
+void iommu_free_pgd(struct domain *d);
void iommu_domain_teardown(struct domain *d);
int hvm_do_IRQ_dpci(struct domain *d, unsigned int irq);
int dpci_ioport_intercept(ioreq_t *p);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|