|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v3 08/12] xen/riscv: rework G-stage mode handling
Rework G-stage mode handling to make the selected mode descriptor reusable outside of p2m initialization, both for filling CPU nodes in the device tree passed to dom0less guests and for per-domain G-stage mode selection at domain creation time. Promote modes[] from a local __initconst variable inside gstage_mode_detect() to a file-scope static const array, and convert max_gstage_mode from an embedded struct (assigned by value) to a global const pointer into modes[]. This allows referencing both the mode identifier and the mode name after init without copying the descriptor. Remove get_max_supported_mode(); its callers now dereference max_gstage_mode->mode directly. Change struct p2m_domain::mode from an embedded gstage_mode_desc to a const pointer into modes[], so each domain shares the descriptor rather than carrying its own copy. Adjust the modes[] entries in three ways: - Use lowercase names without the "x4" suffix (e.g. "sv39" instead of "Sv39x4"). The DT mmu-type binding [1] does not include the suffix, so the name can now be passed directly to the guest without transformation. The suffix is appended only in the diagnostic printk, where it remains informative. - Use "none" for Bare mode (HGATP_MODE_OFF) to match the DT binding. - Change paging_levels to represent the root page-table level index (i.e. total paging levels minus one) rather than the total count. P2M_ROOT_LEVEL() now returns the correct VPN index directly, without requiring callers to subtract one or use hardcoded offsets. Add gstage_mode[8] to xen_arch_domainconfig so the toolstack can request a specific G-stage mode at domain creation time. Introduce find_gstage_mode() to resolve a mode descriptor by name (case-insensitive), capping the result at max_gstage_mode to prevent requesting a mode the hardware does not support. Update p2m_init() to accept a xen_domctl_createdomain pointer and call find_gstage_mode() instead of hardcoding Sv39x4. Add arch_parse_dom0less_node() in a new dom0less-build.c to read the "mmu-type" DT property from a guest domain node and store it in boot_domain::create_cfg.arch.gstage_mode, falling back to maximum supported mode when the property is absent. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/riscv/cpus.yaml?h=v6.19-rc3#n82 Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx> --- Changes in v3: - New patch. Was taken from another patch series: https://lore.kernel.org/xen-devel/cover.1773157782.git.oleksii.kurochko@xxxxxxxxx/T/#m6eb886bdf718b06b967d1688314b80e7374a8de5 --- xen/arch/riscv/Makefile | 1 + xen/arch/riscv/dom0less-build.c | 30 ++++++++ xen/arch/riscv/include/asm/p2m.h | 11 +-- xen/arch/riscv/p2m.c | 113 +++++++++++++++++++++---------- xen/arch/riscv/vmid.c | 2 +- xen/include/public/arch-riscv.h | 5 ++ 6 files changed, 121 insertions(+), 41 deletions(-) create mode 100644 xen/arch/riscv/dom0less-build.c diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index eecdcbc76867..8f7fd625dddd 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -1,6 +1,7 @@ obj-y += aplic.o obj-y += cpufeature.o obj-y += domain.o +obj-$(CONFIG_DOM0LESS_BOOT) += dom0less-build.init.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += entry.o obj-y += extable.o diff --git a/xen/arch/riscv/dom0less-build.c b/xen/arch/riscv/dom0less-build.c new file mode 100644 index 000000000000..11fa184d54be --- /dev/null +++ b/xen/arch/riscv/dom0less-build.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <xen/bootfdt.h> +#include <xen/device_tree.h> +#include <xen/init.h> + +#include <asm/p2m.h> + +int __init arch_parse_dom0less_node(struct dt_device_node *node, + struct boot_domain *bd) +{ + const char *mmu_type; + + if ( dt_property_read_string(node, "mmu-type", &mmu_type) ) + { + dprintk(XENLOG_WARNING, "mmu-type property is missing in guest domain " + "node. %s will be used as fallback\n", max_gstage_mode->name); + + mmu_type = max_gstage_mode->name; + } + + if ( safe_strcpy(bd->create_cfg.arch.gstage_mode, mmu_type) ) + { + dprintk(XENLOG_ERR, "mmu-type value \"%s\" is too long\n", mmu_type); + + return -EINVAL; + } + + return 0; +} diff --git a/xen/arch/riscv/include/asm/p2m.h b/xen/arch/riscv/include/asm/p2m.h index 54ea67990f06..b5b6a996baeb 100644 --- a/xen/arch/riscv/include/asm/p2m.h +++ b/xen/arch/riscv/include/asm/p2m.h @@ -13,7 +13,7 @@ #define P2M_ROOT_ORDER (ilog2(GSTAGE_ROOT_PAGE_TABLE_SIZE) - PAGE_SHIFT) #define P2M_ROOT_PAGES BIT(P2M_ROOT_ORDER, U) -#define P2M_ROOT_LEVEL(p2m) ((p2m)->mode.paging_levels) +#define P2M_ROOT_LEVEL(p2m) ((p2m)->mode->paging_levels) /* * According to the RISC-V spec: @@ -55,6 +55,8 @@ struct gstage_mode_desc { char name[8]; }; +extern const struct gstage_mode_desc *max_gstage_mode; + /* Per-p2m-table state */ struct p2m_domain { /* @@ -68,7 +70,7 @@ struct p2m_domain { /* The root of the p2m tree. May be concatenated */ struct page_info *root; - struct gstage_mode_desc mode; + const struct gstage_mode_desc *mode; /* Back pointer to domain */ struct domain *domain; @@ -215,9 +217,10 @@ static inline bool arch_acquire_resource_check(struct domain *d) } void guest_mm_init(void); -unsigned char get_max_supported_mode(void); -int p2m_init(struct domain *d); +struct xen_domctl_createdomain; + +int p2m_init(struct domain *d, const struct xen_domctl_createdomain *config); static inline void p2m_write_lock(struct p2m_domain *p2m) { diff --git a/xen/arch/riscv/p2m.c b/xen/arch/riscv/p2m.c index d63697c89a1a..b0773083ff53 100644 --- a/xen/arch/riscv/p2m.c +++ b/xen/arch/riscv/p2m.c @@ -45,12 +45,27 @@ struct p2m_pte_ctx { unsigned int level; /* Paging level at which the PTE resides. */ }; -static struct gstage_mode_desc __ro_after_init max_gstage_mode = { - .mode = HGATP_MODE_OFF, - .paging_levels = 0, - .name = "Bare", +/* Values should be sorted by ->mode in this array */ +static const struct gstage_mode_desc modes[] = { + /* + * Based on the RISC-V spec: + * Bare mode is always supported, regardless of SXLEN. + * When SXLEN=32, the only other valid setting for MODE is Sv32. + * When SXLEN=64, three paged virtual-memory schemes are defined: + * Sv39, Sv48, and Sv57. + */ + { HGATP_MODE_OFF, 0, "none" }, +#ifdef CONFIG_RISCV_32 + { HGATP_MODE_SV32X4, 1, "sv32" }, +#else + { HGATP_MODE_SV39X4, 2, "sv39" }, + { HGATP_MODE_SV48X4, 3, "sv48" }, + { HGATP_MODE_SV57X4, 4, "sv57" }, +#endif }; +const struct gstage_mode_desc * __ro_after_init max_gstage_mode = &modes[0]; + static void p2m_free_page(struct p2m_domain *p2m, struct page_info *pg); static inline void p2m_free_metadata_page(struct p2m_domain *p2m, @@ -63,11 +78,6 @@ static inline void p2m_free_metadata_page(struct p2m_domain *p2m, } } -unsigned char get_max_supported_mode(void) -{ - return max_gstage_mode.mode; -} - /* * If anything is changed here, it may also require updates to * p2m_{get,set}_type(). @@ -148,23 +158,6 @@ static pte_t *p2m_get_root_pointer(struct p2m_domain *p2m, gfn_t gfn) static void __init gstage_mode_detect(void) { - static const struct gstage_mode_desc modes[] __initconst = { - /* - * Based on the RISC-V spec: - * Bare mode is always supported, regardless of SXLEN. - * When SXLEN=32, the only other valid setting for MODE is Sv32. - * When SXLEN=64, three paged virtual-memory schemes are defined: - * Sv39, Sv48, and Sv57. - */ -#ifdef CONFIG_RISCV_32 - { HGATP_MODE_SV32X4, 2, "Sv32x4" } -#else - { HGATP_MODE_SV39X4, 3, "Sv39x4" }, - { HGATP_MODE_SV48X4, 4, "Sv48x4" }, - { HGATP_MODE_SV57X4, 5, "Sv57x4" }, -#endif - }; - for ( unsigned int mode_idx = ARRAY_SIZE(modes); mode_idx-- > 0; ) { unsigned long mode = modes[mode_idx].mode; @@ -173,16 +166,16 @@ static void __init gstage_mode_detect(void) if ( MASK_EXTR(csr_read(CSR_HGATP), HGATP_MODE_MASK) == mode ) { - max_gstage_mode = modes[mode_idx]; + max_gstage_mode = &modes[mode_idx]; break; } } - if ( max_gstage_mode.mode == HGATP_MODE_OFF ) + if ( max_gstage_mode->mode == HGATP_MODE_OFF ) panic("Xen expects that G-stage won't be Bare mode\n"); - printk("Max supported G-stage mode is %s\n", max_gstage_mode.name); + printk("Max supported G-stage mode is %sx4\n", max_gstage_mode->name); csr_write(CSR_HGATP, 0); @@ -283,7 +276,7 @@ static void clear_and_clean_page(struct page_info *page, bool clean_dcache) 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(p2m->mode->mode, HGATP_MODE_MASK) | MASK_INSR(vmid, HGATP_VMID_MASK); } @@ -331,8 +324,40 @@ static int p2m_alloc_root_table(struct p2m_domain *p2m) return 0; } -int p2m_init(struct domain *d) +static const struct gstage_mode_desc * find_gstage_mode(const char *mmu_type) { + for ( unsigned int mode_idx = 0; mode_idx < ARRAY_SIZE(modes); mode_idx++ ) + { + if ( !strcasecmp(mmu_type, modes[mode_idx].name) ) + { + if ( modes[mode_idx].mode == HGATP_MODE_OFF || + modes[mode_idx].mode > max_gstage_mode->mode ) + break; + + return &modes[mode_idx]; + } + } + + ASSERT(modes[0].mode == HGATP_MODE_OFF); + + dprintk(XENLOG_ERR, "Requested G-stage mode (%s) isn't supported\n", + mmu_type); + + /* + * Return the Bare-mode sentinel. p2m_init() will reject it with + * -EINVAL, producing the appropriate domain-creation failure. + */ + return &modes[0]; +} + +int p2m_init(struct domain *d, const struct xen_domctl_createdomain *config) +{ + /* + * TODO: This static is a temporary constraint: all guests must use the + * same MMU mode because p2m_gpa_bits is not yet per-domain. + * Drop this once per-domain p2m_gpa_bits is introduced. + */ + static const struct gstage_mode_desc *m = &modes[0]; struct p2m_domain *p2m = p2m_get_hostp2m(d); /* @@ -341,6 +366,27 @@ int p2m_init(struct domain *d) */ p2m->domain = d; + if ( !config ) + { + dprintk(XENLOG_ERR, "NULL config is passed\n"); + return -EINVAL; + } + + p2m->mode = find_gstage_mode(config->arch.gstage_mode); + + if ( p2m->mode->mode == HGATP_MODE_OFF ) + return -EINVAL; + + if ( m->mode == HGATP_MODE_OFF ) + m = p2m->mode; + + if ( m->mode != p2m->mode->mode ) + { + dprintk(XENLOG_ERR, + "Mode should be the same for all guests at the moment\n"); + return -EINVAL; + } + paging_domain_init(d); rwlock_init(&p2m->lock); @@ -362,11 +408,6 @@ int p2m_init(struct domain *d) # 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; } diff --git a/xen/arch/riscv/vmid.c b/xen/arch/riscv/vmid.c index 8fbcd500f24d..11c7e9d6d6c8 100644 --- a/xen/arch/riscv/vmid.c +++ b/xen/arch/riscv/vmid.c @@ -52,7 +52,7 @@ static DEFINE_PER_CPU(struct vmid_data, vmid_data); static unsigned int vmidlen_detect(void) { unsigned int vmid_bits; - unsigned char gstage_mode = get_max_supported_mode(); + unsigned char gstage_mode = max_gstage_mode->mode; /* * According to the RISC-V Privileged Architecture Spec: diff --git a/xen/include/public/arch-riscv.h b/xen/include/public/arch-riscv.h index 360d8e6871ba..5faf8924d926 100644 --- a/xen/include/public/arch-riscv.h +++ b/xen/include/public/arch-riscv.h @@ -56,6 +56,11 @@ typedef struct vcpu_guest_context vcpu_guest_context_t; DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); struct xen_arch_domainconfig { + /* + * G-stage MMU mode for the guest (e.g. "sv39", "sv48", "sv57"). + * Must be set; an empty string is invalid. + */ + char gstage_mode[8]; }; #endif -- 2.53.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |