[Android-virt] [PATCH 3/4] ARM: KVM: Add support for IO mapping at the HYP level

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

 



Moving to the VGIC implies giving access to some ioremapped
devices (the VGIC virtual interface control registers) to the
hypervisor.

Define create_hyp_io_mappings() as the IO aware sibling of
create_hyp_mappings(), and extend the mapping functions
to support a pte mapping backend.

Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
---
 arch/arm/include/asm/kvm_mmu.h |    1 +
 arch/arm/kvm/mmu.c             |   76 ++++++++++++++++++++++++++++++----------
 2 files changed, 58 insertions(+), 19 deletions(-)

diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 7ccd259..422ec45 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -30,6 +30,7 @@
 #define PGD2_ORDER	get_order(PTRS_PER_PGD2 * sizeof(pgd_t))
 
 int create_hyp_mappings(void *from, void *to);
+int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
 void free_hyp_pmds(void);
 
 int kvm_alloc_stage2_pgd(struct kvm *kvm);
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 33d34ea..c37afd0 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -18,6 +18,7 @@
 
 #include <linux/mman.h>
 #include <linux/kvm_host.h>
+#include <linux/io.h>
 #include <trace/events/kvm.h>
 #include <asm/idmap.h>
 #include <asm/pgalloc.h>
@@ -25,6 +26,7 @@
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_asm.h>
 #include <asm/kvm_emulate.h>
+#include <asm/mach/map.h>
 
 #include "trace.h"
 
@@ -74,24 +76,43 @@ void free_hyp_pmds(void)
 	mutex_unlock(&kvm_hyp_pgd_mutex);
 }
 
+/*
+ * Create a HYP pte mapping.
+ *
+ * If pfn_base is NULL, we map kernel pages into HYP with the virtual
+ * address. Otherwise, this is considered an I/O mapping and we map
+ * the physical region starting at *pfn_base to [start, end[.
+ */
 static void create_hyp_pte_mappings(pmd_t *pmd, unsigned long start,
-						unsigned long end)
+				    unsigned long end, unsigned long *pfn_base)
 {
 	pte_t *pte;
-	struct page *page;
 	unsigned long addr;
+	pgprot_t prot;
+
+	if (pfn_base)
+		prot = __pgprot(get_mem_type_prot_pte(MT_DEVICE) | L_PTE_USER);
+	else
+		prot = PAGE_HYP;
 
 	for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) {
 		pte = pte_offset_kernel(pmd, addr);
-		BUG_ON(!virt_addr_valid(addr));
-		page = virt_to_page(addr);
+		if (pfn_base) {
+			BUG_ON(pfn_valid(*pfn_base));
+			set_pte_ext(pte, pfn_pte(*pfn_base, prot), 0);
+			(*pfn_base)++;
+		} else {
+			struct page *page;
+			BUG_ON(!virt_addr_valid(addr));
+			page = virt_to_page(addr);
+			set_pte_ext(pte, mk_pte(page, prot), 0);
+		}
 
-		set_pte_ext(pte, mk_pte(page, PAGE_HYP), 0);
 	}
 }
 
 static int create_hyp_pmd_mappings(pud_t *pud, unsigned long start,
-					       unsigned long end)
+				   unsigned long end, unsigned long *pfn_base)
 {
 	pmd_t *pmd;
 	pte_t *pte;
@@ -112,23 +133,13 @@ static int create_hyp_pmd_mappings(pud_t *pud, unsigned long start,
 		}
 
 		next = pmd_addr_end(addr, end);
-		create_hyp_pte_mappings(pmd, addr, next);
+		create_hyp_pte_mappings(pmd, addr, next, pfn_base);
 	}
 
 	return 0;
 }
 
-/**
- * create_hyp_mappings - map a kernel virtual address range in Hyp mode
- * @from:	The virtual kernel start address of the range
- * @to:		The virtual kernel end address of the range (exclusive)
- *
- * The same virtual address as the kernel virtual address is also used in
- * Hyp-mode mapping to the same underlying physical pages.
- *
- * Note: Wrapping around zero in the "to" address is not supported.
- */
-int create_hyp_mappings(void *from, void *to)
+static int __create_hyp_mappings(void *from, void *to, unsigned long *pfn_base)
 {
 	unsigned long start = (unsigned long)from;
 	unsigned long end = (unsigned long)to;
@@ -158,7 +169,7 @@ int create_hyp_mappings(void *from, void *to)
 		}
 
 		next = pgd_addr_end(addr, end);
-		err = create_hyp_pmd_mappings(pud, addr, next);
+		err = create_hyp_pmd_mappings(pud, addr, next, pfn_base);
 		if (err)
 			goto out;
 	}
@@ -168,6 +179,33 @@ out:
 }
 
 /**
+ * create_hyp_mappings - map a kernel virtual address range in Hyp mode
+ * @from:	The virtual kernel start address of the range
+ * @to:		The virtual kernel end address of the range (exclusive)
+ *
+ * The same virtual address as the kernel virtual address is also used in
+ * Hyp-mode mapping to the same underlying physical pages.
+ *
+ * Note: Wrapping around zero in the "to" address is not supported.
+ */
+int create_hyp_mappings(void *from, void *to)
+{
+	return __create_hyp_mappings(from, to, NULL);
+}
+
+/**
+ * create_hyp_io_mappings - map a physical IO range in Hyp mode
+ * @from:	The virtual HYP start address of the range
+ * @to:		The virtual HYP end address of the range (exclusive)
+ * @addr:	The physical start address which gets mapped
+ */
+int create_hyp_io_mappings(void *from, void *to, phys_addr_t addr)
+{
+	unsigned long pfn = __phys_to_pfn(addr);
+	return __create_hyp_mappings(from, to, &pfn);
+}
+
+/**
  * kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
  * @kvm:	The KVM struct pointer for the VM.
  *
-- 
1.7.10.3





[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux