Introduce gstage_mode_detect() and guest_mm_init() to probe supported G-stage paging modes at boot. The function iterates over possible HGATP modes (Sv32x4 on RV32, Sv39x4/Sv48x4/Sv57x4 on RV64) and selects the largest supported MMU translation address mode by programming CSR_HGATP and reading it back.
The selected mode is stored in max_gstage_mode (marked __ro_after_init) and reported via printk. If no supported mode is found, Xen panics since Bare mode is not expected to be used. Finally, CSR_HGATP is cleared and a local_hfence_gvma_all() is issued to avoid any potential speculative pollution of the TLB, as required by the RISC-V spec. The following issue starts to occur: ./<riscv>/asm/flushtlb.h:37:55: error: 'struct page_info' declared inside parameter list will not be visible outside of this definition or declaration [-Werror] 37 | static inline void page_set_tlbflush_timestamp(struct page_info *page) To resolve it, forward declaration of struct page_info is added to <asm/flushtlb.h. Signed-off-by: Oleksii Kurochko <[email protected]> --- Changes in V6: - Sort items properly in riscv/Makefile. - Move the call of local_hfence_gvma_all() from gstage_mode_detect() to pre_gstage_init() to avoid a redundancy when vmid_init() will be introduced. - Rename pre_gstage_init() to more generic name guest_mm_init(). - s/gstage_mode/max_gstage_mode - Reverse direction of gstage mode detection to find a maximum supported mode to understand what modes are supported. (lower modes are supported automatically) - Introduce struct gstage_mode_desc. - Introduce get_max_supported_mode(). --- Changes in V5: - Add static and __initconst for local variable modes[] in gstage_mode_detect(). - Change type for gstage_mode from 'unsigned long' to 'unsigned char'. - Update the comment inisde defintion if modes[] variable in gstage_mode_detect(): - Add information about Bare mode. - Drop "a paged virtual-memory scheme described in Section 10.3" as it isn't relevant here. - Drop printing of function name when chosen G-stage mode message is printed. - Drop the call of gstage_mode_detect() from start_xen(). It will be added into p2m_init() when the latter will be introduced. - Introduce pre_gstage_init(). - make gstage_mode_detect() static. --- Changes in V4: - New patch. --- xen/arch/riscv/Makefile | 1 + xen/arch/riscv/include/asm/flushtlb.h | 7 ++ xen/arch/riscv/include/asm/p2m.h | 9 ++ xen/arch/riscv/include/asm/riscv_encoding.h | 5 + xen/arch/riscv/p2m.c | 106 ++++++++++++++++++++ xen/arch/riscv/setup.c | 3 + 6 files changed, 131 insertions(+) create mode 100644 xen/arch/riscv/p2m.c diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index e2b8aa42c8..5a6f8c115d 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -6,6 +6,7 @@ obj-y += imsic.o obj-y += intc.o obj-y += irq.o obj-y += mm.o +obj-y += p2m.o obj-y += pt.o obj-$(CONFIG_RISCV_64) += riscv64/ obj-y += sbi.o diff --git a/xen/arch/riscv/include/asm/flushtlb.h b/xen/arch/riscv/include/asm/flushtlb.h index 51c8f753c5..e70badae0c 100644 --- a/xen/arch/riscv/include/asm/flushtlb.h +++ b/xen/arch/riscv/include/asm/flushtlb.h @@ -7,6 +7,13 @@ #include <asm/sbi.h> +struct page_info; + +static inline void local_hfence_gvma_all(void) +{ + asm volatile ( "hfence.gvma zero, zero" ::: "memory" ); +} + /* Flush TLB of local processor for address va. */ static inline void flush_tlb_one_local(vaddr_t va) { diff --git a/xen/arch/riscv/include/asm/p2m.h b/xen/arch/riscv/include/asm/p2m.h index e43c559e0c..3776b98303 100644 --- a/xen/arch/riscv/include/asm/p2m.h +++ b/xen/arch/riscv/include/asm/p2m.h @@ -8,6 +8,12 @@ #define paddr_bits PADDR_BITS +struct gstage_mode_desc { + unsigned char mode; + unsigned int paging_levels; + char name[8]; +}; + /* * List of possible type for each page in the p2m entry. * The number of available bit per page in the pte for this purpose is 2 bits. @@ -88,6 +94,9 @@ static inline bool arch_acquire_resource_check(struct domain *d) return false; } +void guest_mm_init(void); +unsigned char get_max_supported_mode(void); + #endif /* ASM__RISCV__P2M_H */ /* diff --git a/xen/arch/riscv/include/asm/riscv_encoding.h b/xen/arch/riscv/include/asm/riscv_encoding.h index fd27f74cb7..e0a5e8b58b 100644 --- a/xen/arch/riscv/include/asm/riscv_encoding.h +++ b/xen/arch/riscv/include/asm/riscv_encoding.h @@ -131,10 +131,13 @@ #define HGATP_MODE_SV32X4 _UL(1) #define HGATP_MODE_SV39X4 _UL(8) #define HGATP_MODE_SV48X4 _UL(9) +#define HGATP_MODE_SV57X4 _UL(10) +#define HGATP32_MODE_MASK _UL(0x80000000) #define HGATP32_VMID_MASK _UL(0x1FC00000) #define HGATP32_PPN_MASK _UL(0x003FFFFF) +#define HGATP64_MODE_MASK _ULL(0xF000000000000000) #define HGATP64_VMID_MASK _ULL(0x03FFF00000000000) #define HGATP64_PPN_MASK _ULL(0x00000FFFFFFFFFFF) @@ -164,6 +167,7 @@ #define HGATP_PPN_MASK HGATP64_PPN_MASK #define HGATP_VMID_MASK HGATP64_VMID_MASK +#define HGATP_MODE_MASK HGATP64_MODE_MASK #else #define MSTATUS_SD MSTATUS32_SD #define SSTATUS_SD SSTATUS32_SD @@ -173,6 +177,7 @@ #define HGATP_PPN_MASK HGATP32_PPN_MASK #define HGATP_VMID_MASK HGATP32_VMID_MASK +#define HGATP_MODE_MASK HGATP32_MODE_MASK #endif #define TOPI_IID_SHIFT 16 diff --git a/xen/arch/riscv/p2m.c b/xen/arch/riscv/p2m.c new file mode 100644 index 0000000000..7bb0fc0ab4 --- /dev/null +++ b/xen/arch/riscv/p2m.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <xen/init.h> +#include <xen/lib.h> +#include <xen/macros.h> +#include <xen/sections.h> + +#include <asm/csr.h> +#include <asm/flushtlb.h> +#include <asm/p2m.h> +#include <asm/riscv_encoding.h> + +static struct gstage_mode_desc __ro_after_init max_gstage_mode = { + .mode = HGATP_MODE_OFF, + .paging_levels = 0, + .name = "Bare", +}; + +unsigned char get_max_supported_mode(void) +{ + return max_gstage_mode.mode; +} + +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 + }; + + unsigned int mode_idx; + + for ( mode_idx = ARRAY_SIZE(modes); mode_idx-- > 0; ) + { + unsigned long mode = modes[mode_idx].mode; + + csr_write(CSR_HGATP, MASK_INSR(mode, HGATP_MODE_MASK)); + + if ( MASK_EXTR(csr_read(CSR_HGATP), HGATP_MODE_MASK) == mode ) + { + max_gstage_mode.mode = modes[mode_idx].mode; + max_gstage_mode.paging_levels = modes[mode_idx].paging_levels; + safe_strcpy(max_gstage_mode.name, modes[mode_idx].name); + + break; + } + } + + 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); + + csr_write(CSR_HGATP, 0); + + /* local_hfence_gvma_all() will be called at the end of guest_mm_init. */ +} + +void __init guest_mm_init(void) +{ + gstage_mode_detect(); + + /* + * As gstage_mode_detect() is changing CSR_HGATP, it is necessary to flush + * guest TLB because: + * + * From RISC-V spec: + * Speculative executions of the address-translation algorithm behave as + * non-speculative executions of the algorithm do, except that they must + * not set the dirty bit for a PTE, they must not trigger an exception, + * and they must not create address-translation cache entries if those + * entries would have been invalidated by any SFENCE.VMA instruction + * executed by the hart since the speculative execution of the algorithm + * began. + * + * Also, despite of the fact here it is mentioned that when V=0 two-stage + * address translation is inactivated: + * The current virtualization mode, denoted V, indicates whether the hart + * is currently executing in a guest. When V=1, the hart is either in + * virtual S-mode (VS-mode), or in virtual U-mode (VU-mode) atop a guest + * OS running in VS-mode. When V=0, the hart is either in M-mode, in + * HS-mode, or in U-mode atop an OS running in HS-mode. The + * virtualization mode also indicates whether two-stage address + * translation is active (V=1) or inactive (V=0). + * But on the same side, writing to hgatp register activates it: + * The hgatp register is considered active for the purposes of + * the address-translation algorithm unless the effective privilege mode + * is U and hstatus.HU=0. + * + * Thereby it leaves some room for speculation even in this stage of boot, + * so it could be that we polluted local TLB so flush all guest TLB. + */ + local_hfence_gvma_all(); +} diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c index 483cdd7e17..8f46f1a1de 100644 --- a/xen/arch/riscv/setup.c +++ b/xen/arch/riscv/setup.c @@ -22,6 +22,7 @@ #include <asm/early_printk.h> #include <asm/fixmap.h> #include <asm/intc.h> +#include <asm/p2m.h> #include <asm/sbi.h> #include <asm/setup.h> #include <asm/traps.h> @@ -148,6 +149,8 @@ void __init noreturn start_xen(unsigned long bootcpu_id, console_init_postirq(); + guest_mm_init(); + printk("All set up\n"); machine_halt(); -- 2.51.1
