:p
atchew
Login
This series aims to further the ongoing work to introduce support for MPU systems in xen. The patches in this series implement various memory functions and enable the hypervisor timer. Luca Fancellu (3): arm/mpu: Implement copy_from_paddr for MPU systems arm/mpu: Implement vmap functions for MPU arm/mpu: Introduce modify_after_init_mappings Penny Zheng (3): arm/mpu: Implement free_init_memory for MPU systems arm: Use secure hypervisor timer in MPU system arm/mpu: Map domain page in AArch64 MPU systems xen/arch/arm/Kconfig | 1 + xen/arch/arm/include/asm/arm64/sysregs.h | 11 ++ xen/arch/arm/include/asm/mpu/mm.h | 16 +- xen/arch/arm/include/asm/setup.h | 5 + xen/arch/arm/mmu/setup.c | 15 ++ xen/arch/arm/mpu/Makefile | 1 + xen/arch/arm/mpu/domain-page.c | 53 +++++++ xen/arch/arm/mpu/mm.c | 189 ++++++++++++++++++----- xen/arch/arm/mpu/setup.c | 54 ++++++- xen/arch/arm/mpu/vmap.c | 14 +- xen/arch/arm/setup.c | 15 +- 11 files changed, 318 insertions(+), 56 deletions(-) create mode 100644 xen/arch/arm/mpu/domain-page.c -- 2.43.0
From: Luca Fancellu <luca.fancellu@arm.com> Implement the function copy_from_paddr variant for MPU systems, using the map_pages_to_xen/destroy_xen_mappings to temporarily map the memory range to be copied. Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Hari Limaye <hari.limaye@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> Reviewed-by: default avatarMichal Orzel <michal.orzel@amd.com> --- v2: - add Michal R-by --- xen/arch/arm/mpu/setup.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/mpu/setup.c b/xen/arch/arm/mpu/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/setup.c +++ b/xen/arch/arm/mpu/setup.c @@ -XXX,XX +XXX,XX @@ void * __init early_fdt_map(paddr_t fdt_paddr) */ void __init copy_from_paddr(void *dst, paddr_t paddr, unsigned long len) { - BUG_ON("unimplemented"); + paddr_t start_pg = round_pgdown(paddr); + paddr_t end_pg = round_pgup(paddr + len); + unsigned long nr_mfns = (end_pg - start_pg) >> PAGE_SHIFT; + mfn_t mfn = maddr_to_mfn(start_pg); + + if ( map_pages_to_xen(start_pg, mfn, nr_mfns, PAGE_HYPERVISOR_WC) ) + panic("Unable to map range for copy_from_paddr\n"); + + memcpy(dst, maddr_to_virt(paddr), len); + clean_dcache_va_range(dst, len); + + if ( destroy_xen_mappings(start_pg, end_pg) ) + panic("Unable to unmap range for copy_from_paddr\n"); } void __init remove_early_mappings(void) -- 2.43.0
From: Luca Fancellu <luca.fancellu@arm.com> HAS_VMAP is not enabled on MPU systems, but the vmap functions are used in places across common code. In order to keep the existing code and maintain correct functionality, implement the `vmap_contig` and `vunmap` functions for MPU systems. Introduce a helper function `destroy_xen_mapping_containing` to aid with unmapping an entire region when only the start address is known. Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> --- v2: - Rename `destroy_entire_xen_mapping` to `destroy_xen_mapping_containing` - Improve code documentation. - Refactor nested code. - Fix ignored rc error code in `destroy_xen_mapping_containing`. --- xen/arch/arm/include/asm/mpu/mm.h | 10 +++++ xen/arch/arm/mpu/mm.c | 74 ++++++++++++++++++++++++++----- xen/arch/arm/mpu/vmap.c | 14 ++++-- 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/xen/arch/arm/include/asm/mpu/mm.h b/xen/arch/arm/include/asm/mpu/mm.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/mpu/mm.h +++ b/xen/arch/arm/include/asm/mpu/mm.h @@ -XXX,XX +XXX,XX @@ pr_t pr_of_addr(paddr_t base, paddr_t limit, unsigned int flags); int mpumap_contains_region(pr_t *table, uint8_t nr_regions, paddr_t base, paddr_t limit, uint8_t *index); + +/* + * Destroys and frees (if reference count is 0) an entire xen mapping on MPU + * systems where only the start address is known. + * + * @param s Start address of memory region to be destroyed. + * @return: 0 on success, negative on error. + */ +int destroy_xen_mapping_containing(paddr_t s); + #endif /* __ARM_MPU_MM_H__ */ /* diff --git a/xen/arch/arm/mpu/mm.c b/xen/arch/arm/mpu/mm.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/mm.c +++ b/xen/arch/arm/mpu/mm.c @@ -XXX,XX +XXX,XX @@ static void disable_mpu_region_from_index(uint8_t index) write_protection_region(&xen_mpumap[index], index); } +/* + * Free a xen_mpumap entry given the index. A mpu region is actually disabled + * when the refcount is 0 and the region type is MPUMAP_REGION_FOUND. + * + * @param idx Index of the mpumap entry. + * @param region_found_type MPUMAP_REGION_* value. + * @return 0 on success, otherwise negative on error. + */ +static int xen_mpumap_free_entry(uint8_t idx, int region_found_type) +{ + ASSERT(spin_is_locked(&xen_mpumap_lock)); + ASSERT(idx != INVALID_REGION_IDX); + + if ( MPUMAP_REGION_OVERLAP == region_found_type ) + { + printk(XENLOG_ERR "Cannot remove an overlapping region\n"); + return -EINVAL; + } + + if ( xen_mpumap[idx].refcount ) + { + xen_mpumap[idx].refcount -= 1; + return 0; + } + + if ( MPUMAP_REGION_FOUND == region_found_type ) + disable_mpu_region_from_index(idx); + else + { + printk(XENLOG_ERR "Cannot remove a partial region\n"); + return -EINVAL; + } + + return 0; +} + /* * Update the entry in the MPU memory region mapping table (xen_mpumap) for the * given memory range and flags, creating one if none exists. @@ -XXX,XX +XXX,XX @@ static int xen_mpumap_update_entry(paddr_t base, paddr_t limit, return -EINVAL; } - if ( xen_mpumap[idx].refcount == 0 ) - { - if ( MPUMAP_REGION_FOUND == rc ) - disable_mpu_region_from_index(idx); - else - { - printk("Cannot remove a partial region\n"); - return -EINVAL; - } - } - else - xen_mpumap[idx].refcount -= 1; + return xen_mpumap_free_entry(idx, rc); } return 0; @@ -XXX,XX +XXX,XX @@ int destroy_xen_mappings(unsigned long s, unsigned long e) return xen_mpumap_update(s, e, 0); } +int destroy_xen_mapping_containing(paddr_t s) +{ + int rc; + uint8_t idx; + + ASSERT(IS_ALIGNED(s, PAGE_SIZE)); + + spin_lock(&xen_mpumap_lock); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, s, s + PAGE_SIZE, + &idx); + if ( rc == MPUMAP_REGION_NOTFOUND ) + { + printk(XENLOG_ERR "Cannot remove entry that does not exist"); + return -EINVAL; + } + + /* As we are unmapping entire region use MPUMAP_REGION_FOUND instead */ + rc = xen_mpumap_free_entry(idx, MPUMAP_REGION_FOUND); + + spin_unlock(&xen_mpumap_lock); + + return rc; +} + int map_pages_to_xen(unsigned long virt, mfn_t mfn, unsigned long nr_mfns, unsigned int flags) { diff --git a/xen/arch/arm/mpu/vmap.c b/xen/arch/arm/mpu/vmap.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/vmap.c +++ b/xen/arch/arm/mpu/vmap.c @@ -XXX,XX +XXX,XX @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include <xen/bug.h> +#include <xen/mm.h> #include <xen/mm-frame.h> #include <xen/types.h> #include <xen/vmap.h> void *vmap_contig(mfn_t mfn, unsigned int nr) { - BUG_ON("unimplemented"); - return NULL; + paddr_t base = mfn_to_maddr(mfn); + + if ( map_pages_to_xen(base, mfn, nr, PAGE_HYPERVISOR ) ) + return NULL; + + return maddr_to_virt(base); } void vunmap(const void *va) { - BUG_ON("unimplemented"); + paddr_t base = virt_to_maddr(va); + + if ( destroy_xen_mapping_containing(base) ) + panic("Failed to vunmap region\n"); } /* -- 2.43.0
From: Penny Zheng <Penny.Zheng@arm.com> Implement the function `free_init_memory` for MPU systems. In order to support this, the function `modify_xen_mappings` is implemented. On MPU systems, we map the init text and init data sections using separate MPU memory regions. Therefore these are removed separately in `free_init_memory`. Additionally remove warning messages from `is_mm_attr_match` as some permissions can now be updated by `xen_mpumap_update_entry`. Signed-off-by: Penny Zheng <penny.zheng@arm.com> Signed-off-by: Wei Chen <wei.chen@arm.com> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Hari Limaye <hari.limaye@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> --- v2: - Refactor `is_mm_attr_match` to return logical values regarding the permission mismatch. - Improve code documentation. --- xen/arch/arm/include/asm/mpu/mm.h | 6 +- xen/arch/arm/include/asm/setup.h | 2 + xen/arch/arm/mpu/mm.c | 113 +++++++++++++++++++++++------- 3 files changed, 95 insertions(+), 26 deletions(-) diff --git a/xen/arch/arm/include/asm/mpu/mm.h b/xen/arch/arm/include/asm/mpu/mm.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/mpu/mm.h +++ b/xen/arch/arm/include/asm/mpu/mm.h @@ -XXX,XX +XXX,XX @@ #define MPUMAP_REGION_FOUND 1 #define MPUMAP_REGION_INCLUSIVE 2 -#define INVALID_REGION_IDX 0xFFU +#define MPU_ATTR_RO_MISMATCH -1 +#define MPU_ATTR_XN_MISMATCH -2 +#define MPU_ATTR_AI_MISMATCH -3 + +#define INVALID_REGION_IDX 0xFFU extern struct page_info *frame_table; diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -XXX,XX +XXX,XX @@ int map_irq_to_domain(struct domain *d, unsigned int irq, int map_range_to_domain(const struct dt_device_node *dev, uint64_t addr, uint64_t len, void *data); +extern const char __init_data_begin[], __bss_start[], __bss_end[]; + struct init_info { /* Pointer to the stack, used by head.S when entering in C */ diff --git a/xen/arch/arm/mpu/mm.c b/xen/arch/arm/mpu/mm.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/mm.c +++ b/xen/arch/arm/mpu/mm.c @@ -XXX,XX +XXX,XX @@ int mpumap_contains_region(pr_t *table, uint8_t nr_regions, paddr_t base, return MPUMAP_REGION_NOTFOUND; } -static bool is_mm_attr_match(pr_t *region, unsigned int attributes) +static int is_mm_attr_match(pr_t *region, unsigned int attributes) { if ( region->prbar.reg.ro != PAGE_RO_MASK(attributes) ) - { - printk(XENLOG_WARNING - "Mismatched Access Permission attributes (%#x instead of %#x)\n", - region->prbar.reg.ro, PAGE_RO_MASK(attributes)); - return false; - } + return MPU_ATTR_RO_MISMATCH; if ( region->prbar.reg.xn != PAGE_XN_MASK(attributes) ) - { - printk(XENLOG_WARNING - "Mismatched Execute Never attributes (%#x instead of %#x)\n", - region->prbar.reg.xn, PAGE_XN_MASK(attributes)); - return false; - } + return MPU_ATTR_XN_MISMATCH; if ( region->prlar.reg.ai != PAGE_AI_MASK(attributes) ) - { - printk(XENLOG_WARNING - "Mismatched Memory Attribute Index (%#x instead of %#x)\n", - region->prlar.reg.ai, PAGE_AI_MASK(attributes)); - return false; - } + return MPU_ATTR_AI_MISMATCH; - return true; + return 0; } /* Map a frame table to cover physical addresses ps through pe */ @@ -XXX,XX +XXX,XX @@ static int xen_mpumap_update_entry(paddr_t base, paddr_t limit, */ if ( flags_has_page_present && (rc >= MPUMAP_REGION_FOUND) ) { - if ( !is_mm_attr_match(&xen_mpumap[idx], flags) ) + int attr_match = is_mm_attr_match(&xen_mpumap[idx], flags); + + /* We do not support modifying AI attribute. */ + if ( MPU_ATTR_AI_MISMATCH == attr_match ) { - printk("Modifying an existing entry is not supported\n"); + printk(XENLOG_ERR + "Modifying memory attribute is not supported\n"); return -EINVAL; } + /* + * Permissions RO and XN can be changed only by the full region. + * Permissions that match can continue and just increment refcount. + */ + if ( MPU_ATTR_RO_MISMATCH == attr_match || + MPU_ATTR_XN_MISMATCH == attr_match ) + { + if ( rc == MPUMAP_REGION_INCLUSIVE ) + { + printk(XENLOG_ERR + "Cannot modify partial region permissions\n"); + return -EINVAL; + } + + if ( xen_mpumap[idx].refcount != 0 ) + { + printk(XENLOG_ERR + "Cannot modify memory permissions for a region mapped multiple times\n"); + return -EINVAL; + } + + /* Set new permission */ + xen_mpumap[idx].prbar.reg.ro = PAGE_RO_MASK(flags); + xen_mpumap[idx].prbar.reg.xn = PAGE_XN_MASK(flags); + + write_protection_region(&xen_mpumap[idx], idx); + return 0; + } + /* Check for overflow of refcount before incrementing. */ if ( xen_mpumap[idx].refcount == 0xFF ) { @@ -XXX,XX +XXX,XX @@ void __init setup_mm_helper(void) int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) { - BUG_ON("unimplemented"); - return -EINVAL; + return xen_mpumap_update(s, e, nf); } void dump_hyp_walk(vaddr_t addr) @@ -XXX,XX +XXX,XX @@ void dump_hyp_walk(vaddr_t addr) /* Release all __init and __initdata ranges to be reused */ void free_init_memory(void) { - BUG_ON("unimplemented"); + unsigned long inittext_end = (unsigned long)__init_data_begin; + unsigned long len = __init_end - __init_begin; + uint8_t idx; + int rc; + + /* Modify inittext region to be read/write instead of read/execute. */ + rc = modify_xen_mappings((unsigned long)__init_begin, inittext_end, + PAGE_HYPERVISOR_RW); + if ( rc ) + panic("Unable to map RW the init text section (rc = %d)\n", rc); + + /* + * From now on, init will not be used for execution anymore, + * so nuke the instruction cache to remove entries related to init. + */ + invalidate_icache_local(); + + /* + * The initdata region already has read/write permissions so it can just be + * zeroed out. + */ + memset(__init_begin, 0, len); + + rc = destroy_xen_mappings((unsigned long)__init_begin, inittext_end); + if ( rc ) + panic("Unable to remove init text section (rc = %d)\n", rc); + + /* + * The initdata and bss sections are mapped using a single MPU region, so + * modify the start of this region to remove the initdata section. + */ + spin_lock(&xen_mpumap_lock); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, + (unsigned long)__init_data_begin, + (unsigned long)__bss_end, + &idx); + if ( rc < MPUMAP_REGION_FOUND ) + panic("Unable to find bss data section (rc = %d)\n", rc); + + /* bss data section is shrunk and now starts from __bss_start */ + pr_set_base(&xen_mpumap[idx], (unsigned long)__bss_start); + + write_protection_region(&xen_mpumap[idx], idx); + context_sync_mpu(); + + spin_unlock(&xen_mpumap_lock); } void __iomem *ioremap_attr(paddr_t start, size_t len, unsigned int flags) -- 2.43.0
From: Luca Fancellu <luca.fancellu@arm.com> During `init_done`, Xen sets the permissions of all symbols marked with __ro_after_init to be read-only. This does not work on MPU systems at present because part-region modification is not supported. Therefore introduce the function `modify_after_init_mappings` for MMU and MPU, to handle the divergent approaches to setting permissions of __ro_after_init symbols. For MPU systems `modify_xen_mappings` will shrink the RW mapping on one side and extend the RO mapping on the other. This approach prevents wasting an additional region between RW and RO mappings. As the new function is marked with __init, it needs to be called before `free_init_memory`. Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Hari Limaye <hari.limaye@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> --- v2: - No changes --- xen/arch/arm/include/asm/setup.h | 3 +++ xen/arch/arm/mmu/setup.c | 15 ++++++++++++ xen/arch/arm/mpu/mm.c | 2 +- xen/arch/arm/mpu/setup.c | 40 ++++++++++++++++++++++++++++++++ xen/arch/arm/setup.c | 15 ++---------- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -XXX,XX +XXX,XX @@ struct init_info paddr_t consider_modules(paddr_t s, paddr_t e, uint32_t size, paddr_t align, int first_mod); +/* Modify some mappings after the init is done */ +void modify_after_init_mappings(void); + #endif /* * Local variables: diff --git a/xen/arch/arm/mmu/setup.c b/xen/arch/arm/mmu/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mmu/setup.c +++ b/xen/arch/arm/mmu/setup.c @@ -XXX,XX +XXX,XX @@ void __init remove_early_mappings(void) BUG_ON(rc); } +void __init modify_after_init_mappings(void) +{ + /* + * We have finished booting. Mark the section .data.ro_after_init + * read-only. + */ + int rc = modify_xen_mappings((unsigned long)&__ro_after_init_start, + (unsigned long)&__ro_after_init_end, + PAGE_HYPERVISOR_RO); + + if ( rc ) + panic("Unable to mark the .data.ro_after_init section read-only (rc = %d)\n", + rc); +} + /* * After boot, Xen page-tables should not contain mapping that are both * Writable and eXecutables. diff --git a/xen/arch/arm/mpu/mm.c b/xen/arch/arm/mpu/mm.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/mm.c +++ b/xen/arch/arm/mpu/mm.c @@ -XXX,XX +XXX,XX @@ DECLARE_BITMAP(xen_mpumap_mask, MAX_MPU_REGION_NR) \ /* EL2 Xen MPU memory region mapping table. */ pr_t __cacheline_aligned __section(".data") xen_mpumap[MAX_MPU_REGION_NR]; -static DEFINE_SPINLOCK(xen_mpumap_lock); +DEFINE_SPINLOCK(xen_mpumap_lock); static void __init __maybe_unused build_assertions(void) { diff --git a/xen/arch/arm/mpu/setup.c b/xen/arch/arm/mpu/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/setup.c +++ b/xen/arch/arm/mpu/setup.c @@ -XXX,XX +XXX,XX @@ #include <xen/pfn.h> #include <xen/types.h> #include <xen/sizes.h> +#include <xen/spinlock.h> #include <asm/setup.h> static paddr_t __initdata mapped_fdt_base = INVALID_PADDR; static paddr_t __initdata mapped_fdt_limit = INVALID_PADDR; +extern spinlock_t xen_mpumap_lock; + void __init setup_pagetables(void) {} void * __init early_fdt_map(paddr_t fdt_paddr) @@ -XXX,XX +XXX,XX @@ void __init copy_from_paddr(void *dst, paddr_t paddr, unsigned long len) panic("Unable to unmap range for copy_from_paddr\n"); } +void __init modify_after_init_mappings(void) +{ + int rc; + uint8_t idx_rodata; + uint8_t idx_rwdata; + + spin_lock(&xen_mpumap_lock); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, + (unsigned long)_srodata, + (unsigned long)_erodata, + &idx_rodata); + + if ( rc < MPUMAP_REGION_FOUND ) + panic("Unable to find rodata section (rc = %d)\n", rc); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, + (unsigned long)__ro_after_init_start, + (unsigned long)__init_begin, + &idx_rwdata); + + if ( rc < MPUMAP_REGION_FOUND ) + panic("Unable to find rwdata section (rc = %d)\n", rc); + + /* Shrink rwdata section to begin at __ro_after_init_end */ + pr_set_base(&xen_mpumap[idx_rwdata], (unsigned long)__ro_after_init_end); + + /* Extend rodata section to end at __ro_after_init_end */ + pr_set_limit(&xen_mpumap[idx_rodata], (unsigned long)__ro_after_init_end); + + write_protection_region(&xen_mpumap[idx_rwdata], idx_rwdata); + write_protection_region(&xen_mpumap[idx_rodata], idx_rodata); + context_sync_mpu(); + + spin_unlock(&xen_mpumap_lock); +} + void __init remove_early_mappings(void) { int rc = destroy_xen_mappings(round_pgdown(mapped_fdt_base), diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -XXX,XX +XXX,XX @@ domid_t __read_mostly max_init_domid; static __used void noreturn init_done(void) { - int rc; - /* Must be done past setting system_state. */ unregister_init_virtual_region(); - free_init_memory(); + modify_after_init_mappings(); - /* - * We have finished booting. Mark the section .data.ro_after_init - * read-only. - */ - rc = modify_xen_mappings((unsigned long)&__ro_after_init_start, - (unsigned long)&__ro_after_init_end, - PAGE_HYPERVISOR_RO); - if ( rc ) - panic("Unable to mark the .data.ro_after_init section read-only (rc = %d)\n", - rc); + free_init_memory(); startup_cpu_idle_loop(); } -- 2.43.0
From: Penny Zheng <Penny.Zheng@arm.com> As MPU systems only have one secure state, we have to use secure EL2 hypervisor timer for Xen in secure EL2. Signed-off-by: Penny Zheng <penny.zheng@arm.com> Signed-off-by: Wei Chen <wei.chen@arm.com> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> --- v2: - Remove unncessary kconfig attribute. - Remove unncessary hypervisor timer macro. --- xen/arch/arm/include/asm/arm64/sysregs.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/xen/arch/arm/include/asm/arm64/sysregs.h b/xen/arch/arm/include/asm/arm64/sysregs.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/arm64/sysregs.h +++ b/xen/arch/arm/include/asm/arm64/sysregs.h @@ -XXX,XX +XXX,XX @@ #define ZCR_ELx_LEN_SIZE 9 #define ZCR_ELx_LEN_MASK 0x1ff +#ifdef CONFIG_MPU +/* + * The Armv8-R AArch64 architecture always executes code in Secure + * state with EL2 as the highest exception level. + * + * Hypervisor timer registers for Secure EL2. + */ +#define CNTHP_CTL_EL2 CNTHPS_CTL_EL2 +#define CNTHP_CVAL_EL2 CNTHPS_CVAL_EL2 +#endif + #define REGION_TEXT_PRBAR 0x38 /* SH=11 AP=10 XN=00 */ #define REGION_RO_PRBAR 0x3A /* SH=11 AP=10 XN=10 */ #define REGION_DATA_PRBAR 0x32 /* SH=11 AP=00 XN=10 */ -- 2.43.0
From: Penny Zheng <Penny.Zheng@arm.com> In MPU systems, we implement map_domain_page()/unmap_domain_page() through mapping the domain page with a MPU region on demand. Signed-off-by: Penny Zheng <penny.zheng@arm.com> Signed-off-by: Wei Chen <wei.chen@arm.com> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> --- v2: - No changes --- xen/arch/arm/Kconfig | 1 + xen/arch/arm/mpu/Makefile | 1 + xen/arch/arm/mpu/domain-page.c | 53 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 xen/arch/arm/mpu/domain-page.c diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config MMU config MPU bool "MPU" if UNSUPPORTED + select ARCH_MAP_DOMAIN_PAGE if ARM_64 select STATIC_MEMORY help Memory Protection Unit (MPU). Select if you plan to run Xen on ARMv8-R diff --git a/xen/arch/arm/mpu/Makefile b/xen/arch/arm/mpu/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/Makefile +++ b/xen/arch/arm/mpu/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_ARM_32) += arm32/ obj-$(CONFIG_ARM_64) += arm64/ +obj-$(CONFIG_ARM_64) += domain-page.o obj-y += mm.o obj-y += p2m.o obj-y += setup.init.o diff --git a/xen/arch/arm/mpu/domain-page.c b/xen/arch/arm/mpu/domain-page.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/arch/arm/mpu/domain-page.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <xen/bug.h> +#include <xen/domain_page.h> +#include <xen/mm.h> +#include <xen/mm-frame.h> +#include <xen/types.h> + +void *map_domain_page_global(mfn_t mfn) +{ + BUG_ON("unimplemented"); + return NULL; +} + +/* Map a page of domheap memory */ +void *map_domain_page(mfn_t mfn) +{ + paddr_t pa = mfn_to_maddr(mfn); + + if ( map_pages_to_xen((unsigned long)pa, mfn, 1, PAGE_HYPERVISOR_RW) ) + return NULL; + + return maddr_to_virt(pa); +} + +/* Release a mapping taken with map_domain_page() */ +void unmap_domain_page(const void *ptr) +{ + paddr_t base = virt_to_maddr(ptr); + + if ( destroy_xen_mapping_containing(base) ) + panic("Failed to unmap domain page\n"); +} + +mfn_t domain_page_map_to_mfn(const void *ptr) +{ + BUG_ON("unimplemented"); + return INVALID_MFN; +} + +void unmap_domain_page_global(const void *va) +{ + BUG_ON("unimplemented"); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.43.0
This series aims to further the ongoing work to introduce support for MPU systems in xen. The patches in this series implement various memory functions and enable the hypervisor timer. Luca Fancellu (3): arm/mpu: Implement copy_from_paddr for MPU systems arm/mpu: Implement vmap functions for MPU arm/mpu: Introduce modify_after_init_mappings Penny Zheng (3): arm/mpu: Implement free_init_memory for MPU systems arm: Use secure hypervisor timer in MPU system arm/mpu: Map domain page in AArch64 MPU systems xen/arch/arm/Kconfig | 1 + xen/arch/arm/include/asm/arm64/sysregs.h | 11 ++ xen/arch/arm/include/asm/mpu/mm.h | 10 ++ xen/arch/arm/include/asm/setup.h | 5 + xen/arch/arm/mmu/setup.c | 15 ++ xen/arch/arm/mpu/Makefile | 1 + xen/arch/arm/mpu/domain-page.c | 46 +++++ xen/arch/arm/mpu/mm.c | 204 ++++++++++++++++++----- xen/arch/arm/mpu/setup.c | 54 +++++- xen/arch/arm/mpu/vmap.c | 14 +- xen/arch/arm/setup.c | 15 +- 11 files changed, 318 insertions(+), 58 deletions(-) create mode 100644 xen/arch/arm/mpu/domain-page.c -- 2.43.0
From: Luca Fancellu <luca.fancellu@arm.com> Implement the function copy_from_paddr variant for MPU systems, using the map_pages_to_xen/destroy_xen_mappings to temporarily map the memory range to be copied. Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Hari Limaye <hari.limaye@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> Reviewed-by: Michal Orzel <michal.orzel@amd.com> --- v4: - No changes v3: - No changes v2: - add Michal R-by --- xen/arch/arm/mpu/setup.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/mpu/setup.c b/xen/arch/arm/mpu/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/setup.c +++ b/xen/arch/arm/mpu/setup.c @@ -XXX,XX +XXX,XX @@ void * __init early_fdt_map(paddr_t fdt_paddr) */ void __init copy_from_paddr(void *dst, paddr_t paddr, unsigned long len) { - BUG_ON("unimplemented"); + paddr_t start_pg = round_pgdown(paddr); + paddr_t end_pg = round_pgup(paddr + len); + unsigned long nr_mfns = (end_pg - start_pg) >> PAGE_SHIFT; + mfn_t mfn = maddr_to_mfn(start_pg); + + if ( map_pages_to_xen(start_pg, mfn, nr_mfns, PAGE_HYPERVISOR_WC) ) + panic("Unable to map range for copy_from_paddr\n"); + + memcpy(dst, maddr_to_virt(paddr), len); + clean_dcache_va_range(dst, len); + + if ( destroy_xen_mappings(start_pg, end_pg) ) + panic("Unable to unmap range for copy_from_paddr\n"); } void __init remove_early_mappings(void) -- 2.43.0
From: Luca Fancellu <luca.fancellu@arm.com> HAS_VMAP is not enabled on MPU systems, but the vmap functions are used in places across common code. In order to keep the existing code and maintain correct functionality, implement the `vmap_contig` and `vunmap` functions for MPU systems. Introduce a helper function `destroy_xen_mapping_containing` to aid with unmapping an entire region when only the start address is known. Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> --- v4: - Code style fixes v3: - Add additional comments for clarity regarding MPUMAP_REGION checks - Ensure `context_sync_mpu` occurs after `destroy_entire_xen_mapping` - Fix deadlock if `destroy_entire_xen_mapping` is called with an address that does not belong to a region v2: - Rename `destroy_entire_xen_mapping` to `destroy_xen_mapping_containing` - Improve code documentation - Refactor nested code - Fix ignored rc error code in `destroy_xen_mapping_containing` --- xen/arch/arm/include/asm/mpu/mm.h | 10 ++++ xen/arch/arm/mpu/mm.c | 83 ++++++++++++++++++++++++++----- xen/arch/arm/mpu/vmap.c | 14 ++++-- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/xen/arch/arm/include/asm/mpu/mm.h b/xen/arch/arm/include/asm/mpu/mm.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/mpu/mm.h +++ b/xen/arch/arm/include/asm/mpu/mm.h @@ -XXX,XX +XXX,XX @@ pr_t pr_of_addr(paddr_t base, paddr_t limit, unsigned int flags); int mpumap_contains_region(pr_t *table, uint8_t nr_regions, paddr_t base, paddr_t limit, uint8_t *index); + +/* + * Destroys and frees (if reference count is 0) an entire xen mapping on MPU + * systems where only the start address is known. + * + * @param s Start address of memory region to be destroyed. + * @return: 0 on success, negative on error. + */ +int destroy_xen_mapping_containing(paddr_t s); + #endif /* __ARM_MPU_MM_H__ */ /* diff --git a/xen/arch/arm/mpu/mm.c b/xen/arch/arm/mpu/mm.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/mm.c +++ b/xen/arch/arm/mpu/mm.c @@ -XXX,XX +XXX,XX @@ static void disable_mpu_region_from_index(uint8_t index) write_protection_region(&xen_mpumap[index], index); } +/* + * Free a xen_mpumap entry given the index. A mpu region is actually disabled + * when the refcount is 0 and the region type is MPUMAP_REGION_FOUND. + * + * @param idx Index of the mpumap entry. + * @param region_found_type MPUMAP_REGION_* value. + * @return 0 on success, otherwise negative on error. + */ +static int xen_mpumap_free_entry(uint8_t idx, int region_found_type) +{ + ASSERT(spin_is_locked(&xen_mpumap_lock)); + ASSERT(idx != INVALID_REGION_IDX); + ASSERT(MPUMAP_REGION_OVERLAP != region_found_type); + + if ( MPUMAP_REGION_NOTFOUND == region_found_type ) + { + printk(XENLOG_ERR "Cannot remove entry that does not exist\n"); + return -EINVAL; + } + + if ( xen_mpumap[idx].refcount ) + { + xen_mpumap[idx].refcount -= 1; + return 0; + } + + if ( MPUMAP_REGION_FOUND != region_found_type ) + { + printk(XENLOG_ERR "Cannot remove a partial region\n"); + return -EINVAL; + } + + disable_mpu_region_from_index(idx); + + return 0; +} + /* * Update the entry in the MPU memory region mapping table (xen_mpumap) for the * given memory range and flags, creating one if none exists. @@ -XXX,XX +XXX,XX @@ static int xen_mpumap_update_entry(paddr_t base, paddr_t limit, return -EINVAL; } - if ( xen_mpumap[idx].refcount == 0 ) - { - if ( MPUMAP_REGION_FOUND == rc ) - disable_mpu_region_from_index(idx); - else - { - printk("Cannot remove a partial region\n"); - return -EINVAL; - } - } - else - xen_mpumap[idx].refcount -= 1; + return xen_mpumap_free_entry(idx, rc); } return 0; @@ -XXX,XX +XXX,XX @@ int destroy_xen_mappings(unsigned long s, unsigned long e) return xen_mpumap_update(s, e, 0); } +int destroy_xen_mapping_containing(paddr_t s) +{ + int rc; + uint8_t idx; + + ASSERT(IS_ALIGNED(s, PAGE_SIZE)); + + spin_lock(&xen_mpumap_lock); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, s, s + PAGE_SIZE, + &idx); + + /* + * Since only entire regions can be freed using `xen_mpumap_free_entry` we + * must first check the region exists. + */ + if ( MPUMAP_REGION_NOTFOUND == rc ) + { + printk(XENLOG_ERR "Cannot remove entry that does not exist"); + rc = -EINVAL; + goto out; + } + + /* As we are unmapping entire region use MPUMAP_REGION_FOUND instead */ + rc = xen_mpumap_free_entry(idx, MPUMAP_REGION_FOUND); + if ( !rc ) + context_sync_mpu(); + out: + spin_unlock(&xen_mpumap_lock); + + return rc; +} + int map_pages_to_xen(unsigned long virt, mfn_t mfn, unsigned long nr_mfns, unsigned int flags) { diff --git a/xen/arch/arm/mpu/vmap.c b/xen/arch/arm/mpu/vmap.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/vmap.c +++ b/xen/arch/arm/mpu/vmap.c @@ -XXX,XX +XXX,XX @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include <xen/bug.h> +#include <xen/mm.h> #include <xen/mm-frame.h> #include <xen/types.h> #include <xen/vmap.h> void *vmap_contig(mfn_t mfn, unsigned int nr) { - BUG_ON("unimplemented"); - return NULL; + paddr_t base = mfn_to_maddr(mfn); + + if ( map_pages_to_xen(base, mfn, nr, PAGE_HYPERVISOR ) ) + return NULL; + + return maddr_to_virt(base); } void vunmap(const void *va) { - BUG_ON("unimplemented"); + paddr_t base = virt_to_maddr(va); + + if ( destroy_xen_mapping_containing(base) ) + panic("Failed to vunmap region\n"); } /* -- 2.43.0
From: Penny Zheng <Penny.Zheng@arm.com> Implement the function `free_init_memory` for MPU systems. In order to support this, the function `modify_xen_mappings` is implemented. On MPU systems, we map the init text and init data sections using separate MPU memory regions. Therefore these are removed separately in `free_init_memory`. Additionally remove warning messages from `is_mm_attr_match` as some attributes can now be updated by `xen_mpumap_update_entry`. Signed-off-by: Penny Zheng <penny.zheng@arm.com> Signed-off-by: Wei Chen <wei.chen@arm.com> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Hari Limaye <hari.limaye@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> Reviewed-by: Michal Orzel <michal.orzel@amd.com> --- v4: - Refactor printk regarding memory modifications - Add Michal R-By v3: - Refactor MPU_ATTR_* defines v2: - Refactor `is_mm_attr_match` to return logical values regarding the attribute mismatch. - Improve code documentation. --- xen/arch/arm/include/asm/setup.h | 2 + xen/arch/arm/mpu/mm.c | 119 +++++++++++++++++++++++-------- 2 files changed, 93 insertions(+), 28 deletions(-) diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -XXX,XX +XXX,XX @@ int map_irq_to_domain(struct domain *d, unsigned int irq, int map_range_to_domain(const struct dt_device_node *dev, uint64_t addr, uint64_t len, void *data); +extern const char __init_data_begin[], __bss_start[], __bss_end[]; + struct init_info { /* Pointer to the stack, used by head.S when entering in C */ diff --git a/xen/arch/arm/mpu/mm.c b/xen/arch/arm/mpu/mm.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/mm.c +++ b/xen/arch/arm/mpu/mm.c @@ -XXX,XX +XXX,XX @@ #include <asm/setup.h> #include <asm/sysregs.h> +#define MPU_ATTR_XN_RO_MISMATCH -1 +#define MPU_ATTR_AI_MISMATCH -2 + struct page_info *frame_table; /* Maximum number of supported MPU memory regions by the EL2 MPU. */ @@ -XXX,XX +XXX,XX @@ int mpumap_contains_region(pr_t *table, uint8_t nr_regions, paddr_t base, return MPUMAP_REGION_NOTFOUND; } -static bool is_mm_attr_match(pr_t *region, unsigned int attributes) +static int is_mm_attr_match(pr_t *region, unsigned int attributes) { - if ( region->prbar.reg.ro != PAGE_RO_MASK(attributes) ) - { - printk(XENLOG_WARNING - "Mismatched Access Permission attributes (%#x instead of %#x)\n", - region->prbar.reg.ro, PAGE_RO_MASK(attributes)); - return false; - } - - if ( region->prbar.reg.xn != PAGE_XN_MASK(attributes) ) - { - printk(XENLOG_WARNING - "Mismatched Execute Never attributes (%#x instead of %#x)\n", - region->prbar.reg.xn, PAGE_XN_MASK(attributes)); - return false; - } + if ( (region->prbar.reg.xn != PAGE_XN_MASK(attributes)) || + (region->prbar.reg.ro != PAGE_RO_MASK(attributes)) ) + return MPU_ATTR_XN_RO_MISMATCH; if ( region->prlar.reg.ai != PAGE_AI_MASK(attributes) ) - { - printk(XENLOG_WARNING - "Mismatched Memory Attribute Index (%#x instead of %#x)\n", - region->prlar.reg.ai, PAGE_AI_MASK(attributes)); - return false; - } + return MPU_ATTR_AI_MISMATCH; - return true; + return 0; } /* Map a frame table to cover physical addresses ps through pe */ @@ -XXX,XX +XXX,XX @@ static int xen_mpumap_update_entry(paddr_t base, paddr_t limit, */ if ( flags_has_page_present && (rc >= MPUMAP_REGION_FOUND) ) { - if ( !is_mm_attr_match(&xen_mpumap[idx], flags) ) + int attr_match = is_mm_attr_match(&xen_mpumap[idx], flags); + + /* We do not support modifying AI attribute. */ + if ( MPU_ATTR_AI_MISMATCH == attr_match ) { - printk("Modifying an existing entry is not supported\n"); + printk(XENLOG_ERR + "Modifying AI attribute is not supported\n"); return -EINVAL; } + /* + * Attributes RO and XN can be changed only by the full region. + * Attributes that match can continue and just increment refcount. + */ + if ( MPU_ATTR_XN_RO_MISMATCH == attr_match ) + { + if ( rc == MPUMAP_REGION_INCLUSIVE ) + { + printk(XENLOG_ERR + "Cannot modify partial region attributes\n"); + return -EINVAL; + } + + if ( xen_mpumap[idx].refcount != 0 ) + { + printk(XENLOG_ERR + "Cannot modify RO,XN attributes for a region mapped multiple times\n"); + return -EINVAL; + } + + /* Set new attributes */ + xen_mpumap[idx].prbar.reg.ro = PAGE_RO_MASK(flags); + xen_mpumap[idx].prbar.reg.xn = PAGE_XN_MASK(flags); + + write_protection_region(&xen_mpumap[idx], idx); + return 0; + } + /* Check for overflow of refcount before incrementing. */ if ( xen_mpumap[idx].refcount == 0xFF ) { @@ -XXX,XX +XXX,XX @@ void __init setup_mm_helper(void) int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) { - BUG_ON("unimplemented"); - return -EINVAL; + return xen_mpumap_update(s, e, nf); } void dump_hyp_walk(vaddr_t addr) @@ -XXX,XX +XXX,XX @@ void dump_hyp_walk(vaddr_t addr) /* Release all __init and __initdata ranges to be reused */ void free_init_memory(void) { - BUG_ON("unimplemented"); + unsigned long inittext_end = (unsigned long)__init_data_begin; + unsigned long len = __init_end - __init_begin; + uint8_t idx; + int rc; + + /* Modify inittext region to be read/write instead of read/execute. */ + rc = modify_xen_mappings((unsigned long)__init_begin, inittext_end, + PAGE_HYPERVISOR_RW); + if ( rc ) + panic("Unable to map RW the init text section (rc = %d)\n", rc); + + /* + * From now on, init will not be used for execution anymore, + * so nuke the instruction cache to remove entries related to init. + */ + invalidate_icache_local(); + + /* + * The initdata region already has read/write permissions so it can just be + * zeroed out. + */ + memset(__init_begin, 0, len); + + rc = destroy_xen_mappings((unsigned long)__init_begin, inittext_end); + if ( rc ) + panic("Unable to remove init text section (rc = %d)\n", rc); + + /* + * The initdata and bss sections are mapped using a single MPU region, so + * modify the start of this region to remove the initdata section. + */ + spin_lock(&xen_mpumap_lock); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, + (unsigned long)__init_data_begin, + (unsigned long)__bss_end, + &idx); + if ( rc < MPUMAP_REGION_FOUND ) + panic("Unable to find bss data section (rc = %d)\n", rc); + + /* bss data section is shrunk and now starts from __bss_start */ + pr_set_base(&xen_mpumap[idx], (unsigned long)__bss_start); + + write_protection_region(&xen_mpumap[idx], idx); + context_sync_mpu(); + + spin_unlock(&xen_mpumap_lock); } void __iomem *ioremap_attr(paddr_t start, size_t len, unsigned int flags) -- 2.43.0
From: Luca Fancellu <luca.fancellu@arm.com> During `init_done`, Xen sets the permissions of all symbols marked with __ro_after_init to be read-only. This does not work on MPU systems at present because part-region modification is not supported. Therefore introduce the function `modify_after_init_mappings` for MMU and MPU, to handle the divergent approaches to setting permissions of __ro_after_init symbols. For MPU systems `modify_xen_mappings` will shrink the RW mapping on one side and extend the RO mapping on the other. This approach prevents wasting an additional region between RW and RO mappings. As the new function is marked with __init, it needs to be called before `free_init_memory`. Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Hari Limaye <hari.limaye@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> Reviewed-by: Michal Orzel <michal.orzel@amd.com> --- v4: - No changes v3: - Add Michal R-by v2: - No changes --- xen/arch/arm/include/asm/setup.h | 3 +++ xen/arch/arm/mmu/setup.c | 15 ++++++++++++ xen/arch/arm/mpu/mm.c | 2 +- xen/arch/arm/mpu/setup.c | 40 ++++++++++++++++++++++++++++++++ xen/arch/arm/setup.c | 15 ++---------- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/setup.h +++ b/xen/arch/arm/include/asm/setup.h @@ -XXX,XX +XXX,XX @@ struct init_info paddr_t consider_modules(paddr_t s, paddr_t e, uint32_t size, paddr_t align, int first_mod); +/* Modify some mappings after the init is done */ +void modify_after_init_mappings(void); + #endif /* * Local variables: diff --git a/xen/arch/arm/mmu/setup.c b/xen/arch/arm/mmu/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mmu/setup.c +++ b/xen/arch/arm/mmu/setup.c @@ -XXX,XX +XXX,XX @@ void __init remove_early_mappings(void) BUG_ON(rc); } +void __init modify_after_init_mappings(void) +{ + /* + * We have finished booting. Mark the section .data.ro_after_init + * read-only. + */ + int rc = modify_xen_mappings((unsigned long)&__ro_after_init_start, + (unsigned long)&__ro_after_init_end, + PAGE_HYPERVISOR_RO); + + if ( rc ) + panic("Unable to mark the .data.ro_after_init section read-only (rc = %d)\n", + rc); +} + /* * After boot, Xen page-tables should not contain mapping that are both * Writable and eXecutables. diff --git a/xen/arch/arm/mpu/mm.c b/xen/arch/arm/mpu/mm.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/mm.c +++ b/xen/arch/arm/mpu/mm.c @@ -XXX,XX +XXX,XX @@ DECLARE_BITMAP(xen_mpumap_mask, MAX_MPU_REGION_NR) \ /* EL2 Xen MPU memory region mapping table. */ pr_t __cacheline_aligned __section(".data") xen_mpumap[MAX_MPU_REGION_NR]; -static DEFINE_SPINLOCK(xen_mpumap_lock); +DEFINE_SPINLOCK(xen_mpumap_lock); static void __init __maybe_unused build_assertions(void) { diff --git a/xen/arch/arm/mpu/setup.c b/xen/arch/arm/mpu/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/setup.c +++ b/xen/arch/arm/mpu/setup.c @@ -XXX,XX +XXX,XX @@ #include <xen/pfn.h> #include <xen/types.h> #include <xen/sizes.h> +#include <xen/spinlock.h> #include <asm/setup.h> static paddr_t __initdata mapped_fdt_base = INVALID_PADDR; static paddr_t __initdata mapped_fdt_limit = INVALID_PADDR; +extern spinlock_t xen_mpumap_lock; + void __init setup_pagetables(void) {} void * __init early_fdt_map(paddr_t fdt_paddr) @@ -XXX,XX +XXX,XX @@ void __init copy_from_paddr(void *dst, paddr_t paddr, unsigned long len) panic("Unable to unmap range for copy_from_paddr\n"); } +void __init modify_after_init_mappings(void) +{ + int rc; + uint8_t idx_rodata; + uint8_t idx_rwdata; + + spin_lock(&xen_mpumap_lock); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, + (unsigned long)_srodata, + (unsigned long)_erodata, + &idx_rodata); + + if ( rc < MPUMAP_REGION_FOUND ) + panic("Unable to find rodata section (rc = %d)\n", rc); + + rc = mpumap_contains_region(xen_mpumap, max_mpu_regions, + (unsigned long)__ro_after_init_start, + (unsigned long)__init_begin, + &idx_rwdata); + + if ( rc < MPUMAP_REGION_FOUND ) + panic("Unable to find rwdata section (rc = %d)\n", rc); + + /* Shrink rwdata section to begin at __ro_after_init_end */ + pr_set_base(&xen_mpumap[idx_rwdata], (unsigned long)__ro_after_init_end); + + /* Extend rodata section to end at __ro_after_init_end */ + pr_set_limit(&xen_mpumap[idx_rodata], (unsigned long)__ro_after_init_end); + + write_protection_region(&xen_mpumap[idx_rwdata], idx_rwdata); + write_protection_region(&xen_mpumap[idx_rodata], idx_rodata); + context_sync_mpu(); + + spin_unlock(&xen_mpumap_lock); +} + void __init remove_early_mappings(void) { int rc = destroy_xen_mappings(round_pgdown(mapped_fdt_base), diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/setup.c +++ b/xen/arch/arm/setup.c @@ -XXX,XX +XXX,XX @@ domid_t __read_mostly max_init_domid; static __used void noreturn init_done(void) { - int rc; - /* Must be done past setting system_state. */ unregister_init_virtual_region(); - free_init_memory(); + modify_after_init_mappings(); - /* - * We have finished booting. Mark the section .data.ro_after_init - * read-only. - */ - rc = modify_xen_mappings((unsigned long)&__ro_after_init_start, - (unsigned long)&__ro_after_init_end, - PAGE_HYPERVISOR_RO); - if ( rc ) - panic("Unable to mark the .data.ro_after_init section read-only (rc = %d)\n", - rc); + free_init_memory(); startup_cpu_idle_loop(); } -- 2.43.0
From: Penny Zheng <Penny.Zheng@arm.com> As MPU systems only have one secure state, we have to use secure EL2 hypervisor timer for Xen in secure EL2. Signed-off-by: Penny Zheng <penny.zheng@arm.com> Signed-off-by: Wei Chen <wei.chen@arm.com> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> Reviewed-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com> Acked-by: Michal Orzel <michal.orzel@amd.com> --- v4: - No changes v3: - Add Ayan R-by - Add Michal A-by v2: - Remove unncessary kconfig attribute. - Remove unncessary hypervisor timer macro. --- xen/arch/arm/include/asm/arm64/sysregs.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/xen/arch/arm/include/asm/arm64/sysregs.h b/xen/arch/arm/include/asm/arm64/sysregs.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/include/asm/arm64/sysregs.h +++ b/xen/arch/arm/include/asm/arm64/sysregs.h @@ -XXX,XX +XXX,XX @@ #define ZCR_ELx_LEN_SIZE 9 #define ZCR_ELx_LEN_MASK 0x1ff +#ifdef CONFIG_MPU +/* + * The Armv8-R AArch64 architecture always executes code in Secure + * state with EL2 as the highest exception level. + * + * Hypervisor timer registers for Secure EL2. + */ +#define CNTHP_CTL_EL2 CNTHPS_CTL_EL2 +#define CNTHP_CVAL_EL2 CNTHPS_CVAL_EL2 +#endif + #define REGION_TEXT_PRBAR 0x38 /* SH=11 AP=10 XN=00 */ #define REGION_RO_PRBAR 0x3A /* SH=11 AP=10 XN=10 */ #define REGION_DATA_PRBAR 0x32 /* SH=11 AP=00 XN=10 */ -- 2.43.0
From: Penny Zheng <Penny.Zheng@arm.com> In MPU systems, we implement map_domain_page()/unmap_domain_page() through mapping the domain page with a MPU region on demand. Signed-off-by: Penny Zheng <penny.zheng@arm.com> Signed-off-by: Wei Chen <wei.chen@arm.com> Signed-off-by: Luca Fancellu <luca.fancellu@arm.com> Signed-off-by: Harry Ramsey <harry.ramsey@arm.com> --- v4: - Remove duplicate code by having `map_domain_page` and `unmap_domain_page` use `vmap_contig` and `vunmap` v3: - No changes v2: - No changes --- xen/arch/arm/Kconfig | 1 + xen/arch/arm/mpu/Makefile | 1 + xen/arch/arm/mpu/domain-page.c | 46 ++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 xen/arch/arm/mpu/domain-page.c diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/Kconfig +++ b/xen/arch/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config MMU config MPU bool "MPU" if UNSUPPORTED + select ARCH_MAP_DOMAIN_PAGE if ARM_64 select STATIC_MEMORY help Memory Protection Unit (MPU). Select if you plan to run Xen on ARMv8-R diff --git a/xen/arch/arm/mpu/Makefile b/xen/arch/arm/mpu/Makefile index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/mpu/Makefile +++ b/xen/arch/arm/mpu/Makefile @@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_ARM_32) += arm32/ obj-$(CONFIG_ARM_64) += arm64/ +obj-$(CONFIG_ARM_64) += domain-page.o obj-y += mm.o obj-y += p2m.o obj-y += setup.init.o diff --git a/xen/arch/arm/mpu/domain-page.c b/xen/arch/arm/mpu/domain-page.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/xen/arch/arm/mpu/domain-page.c @@ -XXX,XX +XXX,XX @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <xen/bug.h> +#include <xen/domain_page.h> +#include <xen/mm.h> +#include <xen/mm-frame.h> +#include <xen/types.h> +#include <xen/vmap.h> + +void *map_domain_page_global(mfn_t mfn) +{ + BUG_ON("unimplemented"); + return NULL; +} + +/* Map a page of domheap memory */ +void *map_domain_page(mfn_t mfn) +{ + return vmap_contig(mfn, 1); +} + +/* Release a mapping taken with map_domain_page() */ +void unmap_domain_page(const void *ptr) +{ + vunmap(ptr); +} + +mfn_t domain_page_map_to_mfn(const void *ptr) +{ + BUG_ON("unimplemented"); + return INVALID_MFN; +} + +void unmap_domain_page_global(const void *va) +{ + BUG_ON("unimplemented"); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.43.0