|
|
ebc4dec |
From 419a4bbc20cf7c5d7d9dedae59fb8049922e6a2c Mon Sep 17 00:00:00 2001
|
|
|
ebc4dec |
From: Henry Wang <Henry.Wang@arm.com>
|
|
|
ebc4dec |
Date: Mon, 6 Jun 2022 06:17:28 +0000
|
|
|
ebc4dec |
Subject: [PATCH 2/4] xen/arm: Construct the P2M pages pool for guests
|
|
|
ebc4dec |
|
|
|
ebc4dec |
This commit constructs the p2m pages pool for guests from the
|
|
|
ebc4dec |
data structure and helper perspective.
|
|
|
ebc4dec |
|
|
|
ebc4dec |
This is implemented by:
|
|
|
ebc4dec |
|
|
|
ebc4dec |
- Adding a `struct paging_domain` which contains a freelist, a
|
|
|
ebc4dec |
counter variable and a spinlock to `struct arch_domain` to
|
|
|
ebc4dec |
indicate the free p2m pages and the number of p2m total pages in
|
|
|
ebc4dec |
the p2m pages pool.
|
|
|
ebc4dec |
|
|
|
ebc4dec |
- Adding a helper `p2m_get_allocation` to get the p2m pool size.
|
|
|
ebc4dec |
|
|
|
ebc4dec |
- Adding a helper `p2m_set_allocation` to set the p2m pages pool
|
|
|
ebc4dec |
size. This helper should be called before allocating memory for
|
|
|
ebc4dec |
a guest.
|
|
|
ebc4dec |
|
|
|
ebc4dec |
- Adding a helper `p2m_teardown_allocation` to free the p2m pages
|
|
|
ebc4dec |
pool. This helper should be called during the xl domain destory.
|
|
|
ebc4dec |
|
|
|
ebc4dec |
This is part of CVE-2022-33747 / XSA-409.
|
|
|
ebc4dec |
|
|
|
ebc4dec |
Signed-off-by: Henry Wang <Henry.Wang@arm.com>
|
|
|
ebc4dec |
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
|
|
|
ebc4dec |
---
|
|
|
ebc4dec |
xen/arch/arm/p2m.c | 88 ++++++++++++++++++++++++++++++++++++
|
|
|
ebc4dec |
xen/include/asm-arm/domain.h | 10 ++++
|
|
|
ebc4dec |
xen/include/asm-arm/p2m.h | 4 ++
|
|
|
ebc4dec |
3 files changed, 102 insertions(+)
|
|
|
ebc4dec |
|
|
|
ebc4dec |
diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
|
|
|
ebc4dec |
index 3bcd1e897e88..79f3d37f5230 100644
|
|
|
ebc4dec |
--- a/xen/arch/arm/p2m.c
|
|
|
ebc4dec |
+++ b/xen/arch/arm/p2m.c
|
|
|
ebc4dec |
@@ -50,6 +50,92 @@ static uint64_t generate_vttbr(uint16_t vmid, mfn_t root_mfn)
|
|
|
ebc4dec |
return (mfn_to_maddr(root_mfn) | ((uint64_t)vmid << 48));
|
|
|
ebc4dec |
}
|
|
|
ebc4dec |
|
|
|
ebc4dec |
+/* Return the size of the pool, rounded up to the nearest MB */
|
|
|
ebc4dec |
+unsigned int p2m_get_allocation(struct domain *d)
|
|
|
ebc4dec |
+{
|
|
|
ebc4dec |
+ unsigned long nr_pages = ACCESS_ONCE(d->arch.paging.p2m_total_pages);
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+ return ROUNDUP(nr_pages, 1 << (20 - PAGE_SHIFT)) >> (20 - PAGE_SHIFT);
|
|
|
ebc4dec |
+}
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+/*
|
|
|
ebc4dec |
+ * Set the pool of pages to the required number of pages.
|
|
|
ebc4dec |
+ * Returns 0 for success, non-zero for failure.
|
|
|
ebc4dec |
+ * Call with d->arch.paging.lock held.
|
|
|
ebc4dec |
+ */
|
|
|
ebc4dec |
+int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted)
|
|
|
ebc4dec |
+{
|
|
|
ebc4dec |
+ struct page_info *pg;
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+ ASSERT(spin_is_locked(&d->arch.paging.lock));
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+ for ( ; ; )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ if ( d->arch.paging.p2m_total_pages < pages )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ /* Need to allocate more memory from domheap */
|
|
|
ebc4dec |
+ pg = alloc_domheap_page(NULL, 0);
|
|
|
ebc4dec |
+ if ( pg == NULL )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ printk(XENLOG_ERR "Failed to allocate P2M pages.\n");
|
|
|
ebc4dec |
+ return -ENOMEM;
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ ACCESS_ONCE(d->arch.paging.p2m_total_pages) =
|
|
|
ebc4dec |
+ d->arch.paging.p2m_total_pages + 1;
|
|
|
ebc4dec |
+ page_list_add_tail(pg, &d->arch.paging.p2m_freelist);
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ else if ( d->arch.paging.p2m_total_pages > pages )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ /* Need to return memory to domheap */
|
|
|
ebc4dec |
+ pg = page_list_remove_head(&d->arch.paging.p2m_freelist);
|
|
|
ebc4dec |
+ if( pg )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ ACCESS_ONCE(d->arch.paging.p2m_total_pages) =
|
|
|
ebc4dec |
+ d->arch.paging.p2m_total_pages - 1;
|
|
|
ebc4dec |
+ free_domheap_page(pg);
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ else
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ printk(XENLOG_ERR
|
|
|
ebc4dec |
+ "Failed to free P2M pages, P2M freelist is empty.\n");
|
|
|
ebc4dec |
+ return -ENOMEM;
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ else
|
|
|
ebc4dec |
+ break;
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+ /* Check to see if we need to yield and try again */
|
|
|
ebc4dec |
+ if ( preempted && general_preempt_check() )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ *preempted = true;
|
|
|
ebc4dec |
+ return -ERESTART;
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+ return 0;
|
|
|
ebc4dec |
+}
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+int p2m_teardown_allocation(struct domain *d)
|
|
|
ebc4dec |
+{
|
|
|
ebc4dec |
+ int ret = 0;
|
|
|
ebc4dec |
+ bool preempted = false;
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+ spin_lock(&d->arch.paging.lock);
|
|
|
ebc4dec |
+ if ( d->arch.paging.p2m_total_pages != 0 )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ ret = p2m_set_allocation(d, 0, &preempted);
|
|
|
ebc4dec |
+ if ( preempted )
|
|
|
ebc4dec |
+ {
|
|
|
ebc4dec |
+ spin_unlock(&d->arch.paging.lock);
|
|
|
ebc4dec |
+ return -ERESTART;
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ ASSERT(d->arch.paging.p2m_total_pages == 0);
|
|
|
ebc4dec |
+ }
|
|
|
ebc4dec |
+ spin_unlock(&d->arch.paging.lock);
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
+ return ret;
|
|
|
ebc4dec |
+}
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
/* Unlock the flush and do a P2M TLB flush if necessary */
|
|
|
ebc4dec |
void p2m_write_unlock(struct p2m_domain *p2m)
|
|
|
ebc4dec |
{
|
|
|
ebc4dec |
@@ -1599,7 +1685,9 @@ int p2m_init(struct domain *d)
|
|
|
ebc4dec |
unsigned int cpu;
|
|
|
ebc4dec |
|
|
|
ebc4dec |
rwlock_init(&p2m->lock);
|
|
|
ebc4dec |
+ spin_lock_init(&d->arch.paging.lock);
|
|
|
ebc4dec |
INIT_PAGE_LIST_HEAD(&p2m->pages);
|
|
|
ebc4dec |
+ INIT_PAGE_LIST_HEAD(&d->arch.paging.p2m_freelist);
|
|
|
ebc4dec |
|
|
|
ebc4dec |
p2m->vmid = INVALID_VMID;
|
|
|
ebc4dec |
|
|
|
ebc4dec |
diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h
|
|
|
ebc4dec |
index 9b3647587a04..c90daa65afa7 100644
|
|
|
ebc4dec |
--- a/xen/include/asm-arm/domain.h
|
|
|
ebc4dec |
+++ b/xen/include/asm-arm/domain.h
|
|
|
ebc4dec |
@@ -40,6 +40,14 @@ struct vtimer {
|
|
|
ebc4dec |
uint64_t cval;
|
|
|
ebc4dec |
};
|
|
|
ebc4dec |
|
|
|
ebc4dec |
+struct paging_domain {
|
|
|
ebc4dec |
+ spinlock_t lock;
|
|
|
ebc4dec |
+ /* Free P2M pages from the pre-allocated P2M pool */
|
|
|
ebc4dec |
+ struct page_list_head p2m_freelist;
|
|
|
ebc4dec |
+ /* Number of pages from the pre-allocated P2M pool */
|
|
|
ebc4dec |
+ unsigned long p2m_total_pages;
|
|
|
ebc4dec |
+};
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
struct arch_domain
|
|
|
ebc4dec |
{
|
|
|
ebc4dec |
#ifdef CONFIG_ARM_64
|
|
|
ebc4dec |
@@ -51,6 +59,8 @@ struct arch_domain
|
|
|
ebc4dec |
|
|
|
ebc4dec |
struct hvm_domain hvm;
|
|
|
ebc4dec |
|
|
|
ebc4dec |
+ struct paging_domain paging;
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
struct vmmio vmmio;
|
|
|
ebc4dec |
|
|
|
ebc4dec |
/* Continuable domain_relinquish_resources(). */
|
|
|
ebc4dec |
diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
|
|
|
ebc4dec |
index b3ba83283e11..c9598740bd02 100644
|
|
|
ebc4dec |
--- a/xen/include/asm-arm/p2m.h
|
|
|
ebc4dec |
+++ b/xen/include/asm-arm/p2m.h
|
|
|
ebc4dec |
@@ -218,6 +218,10 @@ void p2m_restore_state(struct vcpu *n);
|
|
|
ebc4dec |
/* Print debugging/statistial info about a domain's p2m */
|
|
|
ebc4dec |
void p2m_dump_info(struct domain *d);
|
|
|
ebc4dec |
|
|
|
ebc4dec |
+unsigned int p2m_get_allocation(struct domain *d);
|
|
|
ebc4dec |
+int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted);
|
|
|
ebc4dec |
+int p2m_teardown_allocation(struct domain *d);
|
|
|
ebc4dec |
+
|
|
|
ebc4dec |
static inline void p2m_write_lock(struct p2m_domain *p2m)
|
|
|
ebc4dec |
{
|
|
|
ebc4dec |
write_lock(&p2m->lock);
|
|
|
ebc4dec |
--
|
|
|
ebc4dec |
2.37.1
|
|
|
ebc4dec |
|