xen/riscv: add root page table allocation

Introduce support for allocating and initializing the root page table
required for RISC-V stage-2 address translation.

To implement root page table allocation the following is introduced:
- p2m_get_clean_page() and p2m_alloc_root_table(), p2m_allocate_root()
  helpers to allocate and zero a 16 KiB root page table, as mandated
  by the RISC-V privileged specification for Sv32x4/Sv39x4/Sv48x4/Sv57x4
  modes.
- Update p2m_init() to inititialize p2m_root_order.
- Add maddr_to_page() and page_to_maddr() macros for easier address
  manipulation.
- Introduce paging_ret_to_domheap() to return some pages before
  allocate 16 KiB pages for root page table.
- Allocate root p2m table after p2m pool is initialized.
- Add construct_hgatp() to construct the hgatp register value based on
  p2m->root, p2m->hgatp_mode and VMID.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Oleksii Kurochko authored and Jan Beulich committed 2025-12-18 09:59:40 +01:00
1 parent 3810f15521
commit 824ec51384
6 files changed
+206 -29

No files matched your search

+4
View File
@@ -167,6 +167,10 @@ extern struct page_info *frametable_virt_start;
#define mfn_to_page(mfn) (frametable_virt_start + mfn_x(mfn))
#define page_to_mfn(pg) _mfn((pg) - frametable_virt_start)
/* Convert between machine addresses and page-info structures. */
#define maddr_to_page(ma) mfn_to_page(maddr_to_mfn(ma))
#define page_to_maddr(pg) mfn_to_maddr(page_to_mfn(pg))
static inline void *page_to_virt(const struct page_info *pg)
{
return mfn_to_virt(mfn_x(page_to_mfn(pg)));
+17
View File
@@ -2,6 +2,7 @@
#ifndef ASM__RISCV__P2M_H
#define ASM__RISCV__P2M_H
#include <xen/bitops.h>
#include <xen/errno.h>
#include <xen/mm.h>
#include <xen/rwlock.h>
@@ -9,6 +10,9 @@
#include <asm/page-bits.h>
#define P2M_ROOT_ORDER (ilog2(GSTAGE_ROOT_PAGE_TABLE_SIZE) - PAGE_SHIFT)
#define P2M_ROOT_PAGES BIT(P2M_ROOT_ORDER, U)
#define paddr_bits PADDR_BITS
/* Get host p2m table */
@@ -30,6 +34,11 @@ struct p2m_domain {
/* Pages used to construct the p2m */
struct page_list_head pages;
/* The root of the p2m tree. May be concatenated */
struct page_info *root;
struct gstage_mode_desc mode;
/* Back pointer to domain */
struct domain *domain;
@@ -43,6 +52,12 @@ struct p2m_domain {
* shattered), call p2m_tlb_flush_sync().
*/
bool need_flush;
/*
* Indicate if it is required to clean the cache when writing an entry or
* when a page is needed to be fully cleared and cleaned.
*/
bool clean_dcache;
};
/*
@@ -130,6 +145,8 @@ unsigned char get_max_supported_mode(void);
int p2m_init(struct domain *d);
unsigned long construct_hgatp(const struct p2m_domain *p2m, uint16_t vmid);
#endif /* ASM__RISCV__P2M_H */
/*
+3
View File
@@ -10,4 +10,7 @@ int paging_domain_init(struct domain *d);
int paging_freelist_adjust(struct domain *d, unsigned long pages,
bool *preempted);
int paging_ret_to_domheap(struct domain *d, unsigned int nr_pages);
int paging_refill_from_domheap(struct domain *d, unsigned int nr_pages);
#endif /* ASM_RISCV_PAGING_H */
@@ -180,6 +180,8 @@
#define HGATP_MODE_MASK HGATP32_MODE_MASK
#endif
#define GSTAGE_ROOT_PAGE_TABLE_SIZE KB(16)
#define TOPI_IID_SHIFT 16
#define TOPI_IID_MASK 0xfff
#define TOPI_IPRIO_MASK 0xff
+98 -1
View File
@@ -3,6 +3,7 @@
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/macros.h>
#include <xen/domain_page.h>
#include <xen/mm.h>
#include <xen/paging.h>
#include <xen/rwlock.h>
@@ -109,6 +110,74 @@ void __init guest_mm_init(void)
local_hfence_gvma_all();
}
static void clear_and_clean_page(struct page_info *page, bool clean_dcache)
{
void *p = __map_domain_page(page);
clear_page(p);
/*
* If the IOMMU doesn't support coherent walks and the p2m tables are
* shared between the CPU and IOMMU, it is necessary to clean the
* d-cache.
*/
if ( clean_dcache )
clean_dcache_va_range(p, PAGE_SIZE);
unmap_domain_page(p);
}
unsigned long construct_hgatp(const struct p2m_domain *p2m, uint16_t vmid)
{
return MASK_INSR(mfn_x(page_to_mfn(p2m->root)), HGATP_PPN_MASK) |
MASK_INSR(p2m->mode.mode, HGATP_MODE_MASK) |
MASK_INSR(vmid, HGATP_VMID_MASK);
}
static int p2m_alloc_root_table(struct p2m_domain *p2m)
{
struct domain *d = p2m->domain;
struct page_info *page;
int rc;
/*
* Return back P2M_ROOT_PAGES to assure the root table memory is also
* accounted against the P2M pool of the domain.
*/
if ( (rc = paging_ret_to_domheap(d, P2M_ROOT_PAGES)) )
return rc;
/*
* As mentioned in the Priviliged Architecture Spec (version 20240411)
* in Section 18.5.1, for the paged virtual-memory schemes (Sv32x4,
* Sv39x4, Sv48x4, and Sv57x4), the root page table is 16 KiB and must
* be aligned to a 16-KiB boundary.
*/
page = alloc_domheap_pages(d, P2M_ROOT_ORDER, MEMF_no_owner);
if ( !page )
{
/*
* If allocation of root table pages fails, the pages acquired above
* must be returned to the freelist to maintain proper freelist
* balance.
*/
paging_refill_from_domheap(d, P2M_ROOT_PAGES);
return -ENOMEM;
}
for ( unsigned int i = 0; i < P2M_ROOT_PAGES; i++ )
{
clear_and_clean_page(page + i, p2m->clean_dcache);
page_list_add(page + i, &p2m->pages);
}
p2m->root = page;
return 0;
}
int p2m_init(struct domain *d)
{
struct p2m_domain *p2m = p2m_get_hostp2m(d);
@@ -124,6 +193,24 @@ int p2m_init(struct domain *d)
rwlock_init(&p2m->lock);
INIT_PAGE_LIST_HEAD(&p2m->pages);
/*
* Currently, the infrastructure required to enable CONFIG_HAS_PASSTHROUGH
* is not ready for RISC-V support.
*
* When CONFIG_HAS_PASSTHROUGH=y, p2m->clean_dcache must be properly
* initialized.
* At the moment, it defaults to false because the p2m structure is
* zero-initialized.
*/
#ifdef CONFIG_HAS_PASSTHROUGH
# error "Add init of p2m->clean_dcache"
#endif
/* TODO: don't hardcode used for a domain g-stage mode. */
p2m->mode.mode = HGATP_MODE_SV39X4;
p2m->mode.paging_levels = 2;
safe_strcpy(p2m->mode.name, "Sv39x4");
return 0;
}
@@ -134,10 +221,20 @@ int p2m_init(struct domain *d)
*/
int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted)
{
struct p2m_domain *p2m = p2m_get_hostp2m(d);
int rc;
if ( (rc = paging_freelist_adjust(d, pages, preempted)) )
return rc;
return 0;
/*
* First, initialize p2m pool. Then allocate the root
* table so that the necessary pages can be returned from the p2m pool,
* since the root table must be allocated using alloc_domheap_pages(...)
* to meet its specific requirements.
*/
if ( !p2m->root )
rc = p2m_alloc_root_table(p2m);
return rc;
}
+82 -28
View File
@@ -4,46 +4,67 @@
#include <xen/sched.h>
#include <xen/spinlock.h>
static int _paging_ret_to_domheap(struct domain *d)
{
struct page_info *page;
ASSERT(spin_is_locked(&d->arch.paging.lock));
/* Return memory to domheap. */
page = page_list_remove_head(&d->arch.paging.freelist);
if( page )
{
d->arch.paging.total_pages--;
free_domheap_page(page);
}
else
{
printk(XENLOG_ERR
"failed to free pages, P2M freelist is empty\n");
return -ENOMEM;
}
return 0;
}
static int _paging_add_to_freelist(struct domain *d)
{
struct page_info *page;
ASSERT(spin_is_locked(&d->arch.paging.lock));
/* Need to allocate more memory from domheap */
page = alloc_domheap_page(d, MEMF_no_owner);
if ( page == NULL )
{
printk(XENLOG_ERR "failed to allocate pages\n");
return -ENOMEM;
}
d->arch.paging.total_pages++;
page_list_add_tail(page, &d->arch.paging.freelist);
return 0;
}
int paging_freelist_adjust(struct domain *d, unsigned long pages,
bool *preempted)
{
struct page_info *pg;
ASSERT(spin_is_locked(&d->arch.paging.lock));
for ( ; ; )
{
int rc = 0;
if ( d->arch.paging.total_pages < pages )
{
/* Need to allocate more memory from domheap */
pg = alloc_domheap_page(d, MEMF_no_owner);
if ( pg == NULL )
{
printk(XENLOG_ERR "Failed to allocate pages.\n");
return -ENOMEM;
}
ACCESS_ONCE(d->arch.paging.total_pages)++;
page_list_add_tail(pg, &d->arch.paging.freelist);
}
rc = _paging_add_to_freelist(d);
else if ( d->arch.paging.total_pages > pages )
{
/* Need to return memory to domheap */
pg = page_list_remove_head(&d->arch.paging.freelist);
if ( pg )
{
ACCESS_ONCE(d->arch.paging.total_pages)--;
free_domheap_page(pg);
}
else
{
printk(XENLOG_ERR
"Failed to free pages, freelist is empty.\n");
return -ENOMEM;
}
}
rc = _paging_ret_to_domheap(d);
else
break;
if ( rc )
return rc;
/* Check to see if we need to yield and try again */
if ( preempted && general_preempt_check() )
{
@@ -55,6 +76,39 @@ int paging_freelist_adjust(struct domain *d, unsigned long pages,
return 0;
}
int paging_refill_from_domheap(struct domain *d, unsigned int nr_pages)
{
ASSERT(spin_is_locked(&d->arch.paging.lock));
for ( unsigned int i = 0; i < nr_pages; i++ )
{
int rc = _paging_add_to_freelist(d);
if ( rc )
return rc;
}
return 0;
}
int paging_ret_to_domheap(struct domain *d, unsigned int nr_pages)
{
ASSERT(spin_is_locked(&d->arch.paging.lock));
if ( d->arch.paging.total_pages < nr_pages )
return false;
for ( unsigned int i = 0; i < nr_pages; i++ )
{
int rc = _paging_ret_to_domheap(d);
if ( rc )
return rc;
}
return 0;
}
/* Domain paging struct initialization. */
int paging_domain_init(struct domain *d)
{
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.