|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v4 8/9] arm/mem_access: Add short-descriptor based gpt
This commit adds functionality to walk the guest's page tables using the
short-descriptor translation table format for both ARMv7 and ARMv8. The
implementation is based on ARM DDI 0487B-a J1-6002 and ARM DDI 0406C-b
B3-1506.
Signed-off-by: Sergej Proskurin <proskurin@xxxxxxxxxxxxx>
---
Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx>
Cc: Julien Grall <julien.grall@xxxxxxx>
---
v3: Move the implementation to ./xen/arch/arm/guest_copy.c.
Use defines instead of hardcoded values.
Cosmetic fixes & Added more coments.
v4: Adjusted the names of short-descriptor data-types.
Adapt the function to the new parameter of type "struct vcpu *".
Cosmetic fixes.
---
xen/arch/arm/guest_walk.c | 167 ++++++++++++++++++++++++++++++++++++++-
xen/include/asm-arm/guest_walk.h | 16 ++++
2 files changed, 181 insertions(+), 2 deletions(-)
diff --git a/xen/arch/arm/guest_walk.c b/xen/arch/arm/guest_walk.c
index c37c595157..9cc167af49 100644
--- a/xen/arch/arm/guest_walk.c
+++ b/xen/arch/arm/guest_walk.c
@@ -18,6 +18,7 @@
#include <xen/sched.h>
#include <xen/domain_page.h>
#include <asm/guest_walk.h>
+#include <asm/short-desc.h>
/*
* The function guest_walk_sd translates a given GVA into an IPA using the
@@ -30,8 +31,170 @@ static int guest_walk_sd(const struct vcpu *v,
vaddr_t gva, paddr_t *ipa,
unsigned int *perms)
{
- /* Not implemented yet. */
- return -EFAULT;
+ bool disabled = true;
+ int64_t ttbr;
+ paddr_t mask, paddr;
+ short_desc_t pte, *table;
+ struct page_info *page;
+ register_t ttbcr = READ_SYSREG(TCR_EL1);
+ unsigned int level = 0, n = ttbcr & TTBCR_N_MASK;
+ struct domain *d = v->domain;
+
+ const paddr_t offsets[2] = {
+ ((paddr_t)(gva >> 20) & ((1ULL << (12 - n)) - 1)),
+ ((paddr_t)(gva >> 12) & ((1ULL << 8) - 1))
+ };
+
+ mask = ((1ULL << BITS_PER_WORD) - 1) &
+ ~((1ULL << (BITS_PER_WORD - n)) - 1);
+
+ if ( n == 0 || !(gva & mask) )
+ {
+ /* Use TTBR0 for GVA to IPA translation. */
+ ttbr = READ_SYSREG64(TTBR0_EL1);
+
+ /* If TTBCR.PD0 is set, translations using TTBR0 are disabled. */
+ disabled = ttbcr & TTBCR_PD0;
+ }
+ else
+ {
+ /* Use TTBR1 for GVA to IPA translation. */
+ ttbr = READ_SYSREG64(TTBR1_EL1);
+
+ /* If TTBCR.PD1 is set, translations using TTBR1 are disabled. */
+ disabled = ttbcr & TTBCR_PD1;
+
+ /*
+ * TTBR1 translation always works like n==0 TTBR0 translation (ARM DDI
+ * 0487B.a J1-6003).
+ */
+ n = 0;
+ }
+
+ if ( disabled )
+ return -EFAULT;
+
+ /*
+ * The address of the descriptor for the initial lookup has the following
+ * format: [ttbr<31:14-n>:gva<31-n:20>:00] (ARM DDI 0487B.a J1-6003). In
+ * this way, the first lookup level might comprise up to four consecutive
+ * pages. To avoid mapping all of the pages, we simply map the page that is
+ * needed by the first level translation by incorporating up to 2 MSBs of
+ * the GVA.
+ */
+ mask = (1ULL << (14 - n)) - 1;
+ paddr = (ttbr & ~mask) | (offsets[level] << 2);
+
+ page = get_page_from_gfn(d, paddr_to_pfn(paddr), NULL, P2M_ALLOC);
+ if ( !page )
+ return -EFAULT;
+
+ table = __map_domain_page(page);
+
+ /*
+ * Consider that the first level address translation does not need to be
+ * page-aligned if n > 2.
+ */
+ if ( n > 2 )
+ {
+ /* Make sure that we consider the bits ttbr<12:14-n> if n > 2. */
+ mask = ((1ULL << 12) - 1) & ~((1ULL << (14 - n)) - 1);
+ table = (short_desc_t *)((unsigned long)table | (unsigned long)(ttbr &
mask));
+ }
+
+ /*
+ * As we have considered up to 2 MSBs of the GVA for mapping the first
+ * level translation table, we need to make sure that we limit the table
+ * offset that is is indexed by GVA<31-n:20> to max 10 bits to avoid
+ * exceeding the page size limit.
+ */
+ mask = ((1ULL << 10) - 1);
+ pte = table[offsets[level] & mask];
+
+ unmap_domain_page(table);
+ put_page(page);
+
+ switch ( pte.walk.dt )
+ {
+ case L1DESC_INVALID:
+ return -EFAULT;
+
+ case L1DESC_PAGE_TABLE:
+ level++;
+
+ page = get_page_from_gfn(d, (pte.walk.base >> 2), NULL, P2M_ALLOC);
+ if ( !page )
+ return -EFAULT;
+
+ table = __map_domain_page(page);
+ /*
+ * The second level translation table is addressed by PTE<31:10>. Hence
+ * it does not need to be page aligned. Make sure that we also consider
+ * the bits PTE<11:10>.
+ */
+ table = (short_desc_t *)((unsigned long)table | ((pte.walk.base & 0x3)
<< 10));
+
+ pte = table[offsets[level]];
+
+ unmap_domain_page(table);
+ put_page(page);
+
+ if ( pte.walk.dt == L2DESC_INVALID )
+ return -EFAULT;
+
+ if ( pte.pg.page ) /* Small page. */
+ {
+ mask = (1ULL << PAGE_SHIFT_4K) - 1;
+
+ *ipa = (pte.pg.base << PAGE_SHIFT_4K) | (gva & mask);
+
+ /* Set execute permissions associated with the small page. */
+ if ( !pte.pg.xn )
+ *perms = GV2M_EXEC;
+ }
+ else /* Large page. */
+ {
+ mask = (1ULL << PAGE_SHIFT_64K) - 1;
+
+ *ipa = (pte.lpg.base << PAGE_SHIFT_64K) | (gva & mask);
+
+ /* Set execute permissions associated with the large page. */
+ if ( !pte.lpg.xn )
+ *perms = GV2M_EXEC;
+ }
+
+ /* Set permissions so that the caller can check the flags by herself.
*/
+ if ( !pte.pg.ro )
+ *perms |= GV2M_WRITE;
+
+ break;
+
+ case L1DESC_SECTION:
+ case L1DESC_SECTION_PXN:
+ if ( !pte.sec.supersec ) /* Section */
+ {
+ mask = (1ULL << L1DESC_SECTION_SHIFT) - 1;
+
+ *ipa = (pte.sec.base << L1DESC_SECTION_SHIFT) | (gva & mask);
+ }
+ else /* Supersection */
+ {
+ mask = (1ULL << L1DESC_SUPERSECTION_SHIFT) - 1;
+
+ *ipa = gva & mask;
+ *ipa |= (paddr_t)(pte.supersec.base) << L1DESC_SUPERSECTION_SHIFT;
+ *ipa |= (paddr_t)(pte.supersec.extbase1) <<
L1DESC_SUPERSECTION_EXT_BASE1_SHIFT;
+ *ipa |= (paddr_t)(pte.supersec.extbase2) <<
L1DESC_SUPERSECTION_EXT_BASE2_SHIFT;
+ }
+
+ /* Set permissions so that the caller can check the flags by herself.
*/
+ if ( !pte.sec.ro )
+ *perms = GV2M_WRITE;
+ if ( !pte.sec.xn )
+ *perms |= GV2M_EXEC;
+ }
+
+ return 0;
}
/*
diff --git a/xen/include/asm-arm/guest_walk.h b/xen/include/asm-arm/guest_walk.h
index 4ed8476e08..d0bed0c7a2 100644
--- a/xen/include/asm-arm/guest_walk.h
+++ b/xen/include/asm-arm/guest_walk.h
@@ -1,6 +1,22 @@
#ifndef _XEN_GUEST_WALK_H
#define _XEN_GUEST_WALK_H
+/* First level translation table descriptor types used by the AArch32
+ * short-descriptor translation table format. */
+#define L1DESC_INVALID (0)
+#define L1DESC_PAGE_TABLE (1)
+#define L1DESC_SECTION (2)
+#define L1DESC_SECTION_PXN (3)
+
+/* Defines for section and supersection shifts. */
+#define L1DESC_SECTION_SHIFT (20)
+#define L1DESC_SUPERSECTION_SHIFT (24)
+#define L1DESC_SUPERSECTION_EXT_BASE1_SHIFT (32)
+#define L1DESC_SUPERSECTION_EXT_BASE2_SHIFT (36)
+
+/* Second level translation table descriptor types. */
+#define L2DESC_INVALID (0)
+
/* Walk the guest's page tables in software. */
int guest_walk_tables(const struct vcpu *v,
vaddr_t gva,
--
2.12.2
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |