iommu++)
static unsigned int iommu_units_count;
-static struct paging amd_iommu_paging[AMD_IOMMU_MAX_PAGE_TABLE_LEVELS];
/*
* Interrupt remapping is not emulated on AMD,
mmio_write64(iommu->mmio_base + AMD_CONTROL_REG, ctrl_reg);
}
-static void amd_iommu_set_next_pt_l4(pt_entry_t pte, unsigned long next_pt)
-{
- *pte = (next_pt & BIT_MASK(51, 12)) | AMD_IOMMU_PTE_PG_MODE(3) |
- AMD_IOMMU_PTE_IR | AMD_IOMMU_PTE_IW | AMD_IOMMU_PTE_P;
-}
-
-static void amd_iommu_set_next_pt_l3(pt_entry_t pte, unsigned long next_pt)
-{
- *pte = (next_pt & BIT_MASK(51, 12)) | AMD_IOMMU_PTE_PG_MODE(2) |
- AMD_IOMMU_PTE_IR | AMD_IOMMU_PTE_IW | AMD_IOMMU_PTE_P;
-}
-
-static void amd_iommu_set_next_pt_l2(pt_entry_t pte, unsigned long next_pt)
-{
- *pte = (next_pt & BIT_MASK(51, 12)) | AMD_IOMMU_PTE_PG_MODE(1) |
- AMD_IOMMU_PTE_IR | AMD_IOMMU_PTE_IW | AMD_IOMMU_PTE_P;
-}
-
-static unsigned long amd_iommu_get_phys_l3(pt_entry_t pte, unsigned long virt)
-{
- if (*pte & AMD_IOMMU_PTE_PG_MODE_MASK)
- return INVALID_PHYS_ADDR;
- return (*pte & BIT_MASK(51, 30)) | (virt & BIT_MASK(29, 0));
-}
-
-static unsigned long amd_iommu_get_phys_l2(pt_entry_t pte, unsigned long virt)
-{
- if (*pte & AMD_IOMMU_PTE_PG_MODE_MASK)
- return INVALID_PHYS_ADDR;
- return (*pte & BIT_MASK(51, 21)) | (virt & BIT_MASK(20, 0));
-}
-
int iommu_init(void)
{
struct jailhouse_iommu *iommu;
iommu_units_count++;
}
- /*
- * Derive amd_iommu_paging from very similar x86_64_paging,
- * replicating all 4 levels.
- */
- memcpy(amd_iommu_paging, x86_64_paging, sizeof(amd_iommu_paging));
- amd_iommu_paging[0].set_next_pt = amd_iommu_set_next_pt_l4;
- amd_iommu_paging[1].set_next_pt = amd_iommu_set_next_pt_l3;
- amd_iommu_paging[2].set_next_pt = amd_iommu_set_next_pt_l2;
- amd_iommu_paging[1].get_phys = amd_iommu_get_phys_l3;
- amd_iommu_paging[2].get_phys = amd_iommu_get_phys_l2;
-
return iommu_cell_init(&root_cell);
}
if (cell->id > 0xffff)
return trace_error(-ERANGE);
- cell->arch.amd_iommu.pg_structs.root_paging = amd_iommu_paging;
- cell->arch.amd_iommu.pg_structs.root_table = page_alloc(&mem_pool, 1);
- if (!cell->arch.amd_iommu.pg_structs.root_table)
- return trace_error(-ENOMEM);
-
return 0;
}
(iommu->cmd_tail_ptr + sizeof(*cmd)) % CMD_BUF_SIZE;
}
-int iommu_map_memory_region(struct cell *cell,
- const struct jailhouse_memory *mem)
+u64 amd_iommu_get_memory_region_flags(const struct jailhouse_memory *mem)
{
unsigned long flags = AMD_IOMMU_PTE_P;
- // HACK for QEMU
- if (iommu_units_count == 0)
- return 0;
-
- /*
- * Check that the address is not outside scope of current page
- * tables. With 4 levels, we only support 48 address bits.
- */
- if (mem->virt_start & BIT_MASK(63, 48))
- return trace_error(-E2BIG);
-
if (!(mem->flags & JAILHOUSE_MEM_DMA))
return 0;
if (mem->flags & JAILHOUSE_MEM_WRITE)
flags |= AMD_IOMMU_PTE_IW;
- return paging_create(&cell->arch.amd_iommu.pg_structs, mem->phys_start,
- mem->size, mem->virt_start, flags, PAGING_COHERENT);
+ return flags;
}
-int iommu_unmap_memory_region(struct cell *cell,
- const struct jailhouse_memory *mem)
+int iommu_map_memory_region(struct cell *cell,
+ const struct jailhouse_memory *mem)
{
/*
- * TODO: This is almost a complete copy of vtd.c counterpart
- * (sans QEMU hack). Think of unification.
+ * Check that the address is not outside the scope of the page tables.
+ * With 4 levels, we only support 48 address bits.
*/
+ if (mem->virt_start & BIT_MASK(63, 48))
+ return trace_error(-E2BIG);
- // HACK for QEMU
- if (iommu_units_count == 0)
- return 0;
-
- if (!(mem->flags & JAILHOUSE_MEM_DMA))
- return 0;
+ /* vcpu_map_memory_region already did the actual work. */
+ return 0;
+}
- return paging_destroy(&cell->arch.amd_iommu.pg_structs, mem->virt_start,
- mem->size, PAGING_COHERENT);
+int iommu_unmap_memory_region(struct cell *cell,
+ const struct jailhouse_memory *mem)
+{
+ /* vcpu_map_memory_region already did the actual work. */
+ return 0;
}
static void amd_iommu_inv_dte(struct amd_iommu *iommu, u16 device_id)
/* Translation information */
dte->raw64[0] = DTE_IR | DTE_IW |
- paging_hvirt2phys(cell->arch.amd_iommu.pg_structs.root_table) |
+ paging_hvirt2phys(cell->arch.svm.npt_iommu_structs.root_table) |
DTE_PAGING_MODE_4_LEVEL | DTE_TRANSLATION_VALID | DTE_VALID;
/* TODO: Interrupt remapping. For now, just forward them unmapped. */
void iommu_cell_exit(struct cell *cell)
{
- /* TODO: Again, this a copy of vtd.c:iommu_cell_exit */
- // HACK for QEMU
- if (iommu_units_count == 0)
- return;
-
- page_free(&mem_pool, cell->arch.amd_iommu.pg_structs.root_table, 1);
}
static void wait_for_zero(volatile u64 *sem, unsigned long mask)
#include <jailhouse/processor.h>
#include <jailhouse/string.h>
#include <jailhouse/utils.h>
+#include <asm/amd_iommu.h>
#include <asm/apic.h>
#include <asm/control.h>
#include <asm/iommu.h>
* combinations of NW and CD bits are prohibited by SVM (see APMv2,
* Sect. 15.5). To handle this, we always keep the NW bit off.
*/
-#define SVM_CR0_ALLOWED_BITS (~X86_CR0_NW)
+#define SVM_CR0_ALLOWED_BITS (~X86_CR0_NW)
/* IOPM size: two 4-K pages + 3 bits */
-#define IOPM_PAGES 3
+#define IOPM_PAGES 3
+
+#define NPT_IOMMU_PAGE_DIR_LEVELS 4
static bool has_avic, has_assists, has_flush_by_asid;
static const struct segment invalid_seg;
-static struct paging npt_paging[NPT_PAGE_DIR_LEVELS];
+static struct paging npt_iommu_paging[NPT_IOMMU_PAGE_DIR_LEVELS];
/* bit cleared: direct access allowed */
// TODO: convert to whitelist
static void svm_set_cell_config(struct cell *cell, struct vmcb *vmcb)
{
vmcb->iopm_base_pa = paging_hvirt2phys(cell->arch.svm.iopm);
- vmcb->n_cr3 = paging_hvirt2phys(cell->arch.svm.npt_structs.root_table);
+ vmcb->n_cr3 =
+ paging_hvirt2phys(cell->arch.svm.npt_iommu_structs.root_table);
}
static void vmcb_setup(struct per_cpu *cpu_data)
unsigned long gphys,
unsigned long flags)
{
- return paging_virt2phys(&cpu_data->cell->arch.svm.npt_structs,
- gphys, flags);
+ return paging_virt2phys(&cpu_data->cell->arch.svm.npt_iommu_structs,
+ gphys, flags);
+}
+
+static void npt_iommu_set_next_pt_l4(pt_entry_t pte, unsigned long next_pt)
+{
+ /*
+ * Merge IOMMU and NPT flags. We need to mark the NTP entries as user
+ * accessible, see APMv2, Section 15.25.5.
+ */
+ *pte = (next_pt & BIT_MASK(51, 12)) | AMD_IOMMU_PTE_PG_MODE(3) |
+ AMD_IOMMU_PTE_IR | AMD_IOMMU_PTE_IW | AMD_IOMMU_PTE_P |
+ PAGE_DEFAULT_FLAGS | PAGE_FLAG_US;
+}
+
+static void npt_iommu_set_next_pt_l3(pt_entry_t pte, unsigned long next_pt)
+{
+ *pte = (next_pt & BIT_MASK(51, 12)) | AMD_IOMMU_PTE_PG_MODE(2) |
+ AMD_IOMMU_PTE_IR | AMD_IOMMU_PTE_IW | AMD_IOMMU_PTE_P |
+ PAGE_DEFAULT_FLAGS | PAGE_FLAG_US;
+}
+
+static void npt_iommu_set_next_pt_l2(pt_entry_t pte, unsigned long next_pt)
+{
+ *pte = (next_pt & BIT_MASK(51, 12)) | AMD_IOMMU_PTE_PG_MODE(1) |
+ AMD_IOMMU_PTE_IR | AMD_IOMMU_PTE_IW | AMD_IOMMU_PTE_P |
+ PAGE_DEFAULT_FLAGS | PAGE_FLAG_US;
+}
+
+static unsigned long npt_iommu_get_phys_l3(pt_entry_t pte, unsigned long virt)
+{
+ if (*pte & AMD_IOMMU_PTE_PG_MODE_MASK)
+ return INVALID_PHYS_ADDR;
+ return (*pte & BIT_MASK(51, 30)) | (virt & BIT_MASK(29, 0));
}
-static void npt_set_next_pt(pt_entry_t pte, unsigned long next_pt)
+static unsigned long npt_iommu_get_phys_l2(pt_entry_t pte, unsigned long virt)
{
- /* See APMv2, Section 15.25.5 */
- *pte = (next_pt & BIT_MASK(51, 12)) | PAGE_DEFAULT_FLAGS | PAGE_FLAG_US;
+ if (*pte & AMD_IOMMU_PTE_PG_MODE_MASK)
+ return INVALID_PHYS_ADDR;
+ return (*pte & BIT_MASK(51, 21)) | (virt & BIT_MASK(20, 0));
}
int vcpu_vendor_init(void)
{
struct paging_structures parking_pt;
unsigned long vm_cr;
- int err, n;
+ int err;
err = svm_check_features();
if (err)
/* SVM disabled in BIOS */
return trace_error(-EPERM);
- /* Nested paging is the same as the native one */
- memcpy(npt_paging, x86_64_paging, sizeof(npt_paging));
- for (n = 0; n < NPT_PAGE_DIR_LEVELS; n++)
- npt_paging[n].set_next_pt = npt_set_next_pt;
+ /*
+ * Nested paging is almost the same as the native one. However, we
+ * need to override some handlers in order to reuse the page table for
+ * the IOMMU as well.
+ */
+ memcpy(npt_iommu_paging, x86_64_paging, sizeof(npt_iommu_paging));
+ npt_iommu_paging[0].set_next_pt = npt_iommu_set_next_pt_l4;
+ npt_iommu_paging[1].set_next_pt = npt_iommu_set_next_pt_l3;
+ npt_iommu_paging[2].set_next_pt = npt_iommu_set_next_pt_l2;
+ npt_iommu_paging[1].get_phys = npt_iommu_get_phys_l3;
+ npt_iommu_paging[2].get_phys = npt_iommu_get_phys_l2;
/* Map guest parking code (shared between cells and CPUs) */
- parking_pt.root_paging = npt_paging;
+ parking_pt.root_paging = npt_iommu_paging;
parking_pt.root_table = parked_mode_npt = page_alloc(&mem_pool, 1);
if (!parked_mode_npt)
return -ENOMEM;
return err;
/* build root NPT of cell */
- cell->arch.svm.npt_structs.root_paging = npt_paging;
- cell->arch.svm.npt_structs.root_table =
+ cell->arch.svm.npt_iommu_structs.root_paging = npt_iommu_paging;
+ cell->arch.svm.npt_iommu_structs.root_table =
(page_table_t)cell->arch.root_table_page;
if (!has_avic) {
* Map xAPIC as is; reads are passed, writes are trapped.
*/
flags = PAGE_READONLY_FLAGS | PAGE_FLAG_US | PAGE_FLAG_DEVICE;
- err = paging_create(&cell->arch.svm.npt_structs, XAPIC_BASE,
- PAGE_SIZE, XAPIC_BASE,
- flags,
- PAGING_NON_COHERENT);
+ err = paging_create(&cell->arch.svm.npt_iommu_structs,
+ XAPIC_BASE, PAGE_SIZE, XAPIC_BASE,
+ flags, PAGING_NON_COHERENT);
} else {
flags = PAGE_DEFAULT_FLAGS | PAGE_FLAG_DEVICE;
- err = paging_create(&cell->arch.svm.npt_structs,
+ err = paging_create(&cell->arch.svm.npt_iommu_structs,
paging_hvirt2phys(avic_page),
PAGE_SIZE, XAPIC_BASE,
- flags,
- PAGING_NON_COHERENT);
+ flags, PAGING_NON_COHERENT);
}
if (err)
goto err_free_iopm;
if (mem->flags & JAILHOUSE_MEM_COMM_REGION)
phys_start = paging_hvirt2phys(&cell->comm_page);
- return paging_create(&cell->arch.svm.npt_structs, phys_start, mem->size,
- mem->virt_start, flags, PAGING_NON_COHERENT);
+ flags |= amd_iommu_get_memory_region_flags(mem);
+
+ /*
+ * As we also manipulate the IOMMU page table, changes need to be
+ * coherent.
+ */
+ return paging_create(&cell->arch.svm.npt_iommu_structs, phys_start,
+ mem->size, mem->virt_start, flags,
+ PAGING_COHERENT);
}
int vcpu_unmap_memory_region(struct cell *cell,
const struct jailhouse_memory *mem)
{
- return paging_destroy(&cell->arch.svm.npt_structs, mem->virt_start,
- mem->size, PAGING_NON_COHERENT);
+ return paging_destroy(&cell->arch.svm.npt_iommu_structs,
+ mem->virt_start, mem->size, PAGING_COHERENT);
}
void vcpu_vendor_cell_exit(struct cell *cell)
{
- paging_destroy(&cell->arch.svm.npt_structs, XAPIC_BASE, PAGE_SIZE,
- PAGING_NON_COHERENT);
+ paging_destroy(&cell->arch.svm.npt_iommu_structs, XAPIC_BASE,
+ PAGE_SIZE, PAGING_NON_COHERENT);
page_free(&mem_pool, cell->arch.svm.iopm, 3);
}