diff mbox series

[v2,34/40] xen/mpu: free init memory in MPU system

Message ID 20230113052914.3845596-35-Penny.Zheng@arm.com (mailing list archive)
State New, archived
Headers show
Series xen/arm: Add Armv8-R64 MPU support to Xen - Part#1 | expand

Commit Message

Penny Zheng Jan. 13, 2023, 5:29 a.m. UTC
This commit implements free_init_memory in MPU system, trying to keep
the same strategy with MMU system.

In order to inserting BRK instruction into init code section, which
aims to provok a fault on purpose, we should change init code section
permission to RW at first.
Function modify_xen_mappings is introduced to modify permission of the
existing valid MPU memory region.

Then we nuke the instruction cache to remove entries related to init
text.
At last, we destroy these two MPU memory regions referring init text and
init data using destroy_xen_mappings.

Signed-off-by: Penny Zheng <penny.zheng@arm.com>
Signed-off-by: Wei Chen <wei.chen@arm.com>
---
 xen/arch/arm/mm_mpu.c | 85 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 83 insertions(+), 2 deletions(-)

Comments

Julien Grall Feb. 9, 2023, 11:27 a.m. UTC | #1
Hi Penny,

On 13/01/2023 05:29, Penny Zheng wrote:
> This commit implements free_init_memory in MPU system, trying to keep
> the same strategy with MMU system.
> 
> In order to inserting BRK instruction into init code section, which
> aims to provok a fault on purpose, we should change init code section
> permission to RW at first.
> Function modify_xen_mappings is introduced to modify permission of the
> existing valid MPU memory region.
> 
> Then we nuke the instruction cache to remove entries related to init
> text.
> At last, we destroy these two MPU memory regions referring init text and
> init data using destroy_xen_mappings.
> 
> Signed-off-by: Penny Zheng <penny.zheng@arm.com>
> Signed-off-by: Wei Chen <wei.chen@arm.com>
> ---
>   xen/arch/arm/mm_mpu.c | 85 ++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 83 insertions(+), 2 deletions(-)
> 
> diff --git a/xen/arch/arm/mm_mpu.c b/xen/arch/arm/mm_mpu.c
> index 0b720004ee..de0c7d919a 100644
> --- a/xen/arch/arm/mm_mpu.c
> +++ b/xen/arch/arm/mm_mpu.c
> @@ -20,6 +20,7 @@
>    */
>   
>   #include <xen/init.h>
> +#include <xen/kernel.h>
>   #include <xen/libfdt/libfdt.h>
>   #include <xen/mm.h>
>   #include <xen/page-size.h>
> @@ -77,6 +78,8 @@ static const unsigned int mpu_section_mattr[MSINFO_MAX] = {
>       REGION_HYPERVISOR_BOOT,
>   };
>   
> +extern char __init_data_begin[], __init_end[];

Now we have two places define __init_end as extern. Can this instead be 
defined in setup.h?

> +
>   /* Write a MPU protection region */
>   #define WRITE_PROTECTION_REGION(sel, pr, prbar_el2, prlar_el2) ({       \
>       uint64_t _sel = sel;                                                \
> @@ -443,8 +446,41 @@ static int xen_mpumap_update_entry(paddr_t base, paddr_t limit,
>       if ( rc == MPUMAP_REGION_OVERLAP )
>           return -EINVAL;
>   
> +    /* We are updating the permission. */
> +    if ( (flags & _REGION_PRESENT) && (rc == MPUMAP_REGION_FOUND ||
> +                                       rc == MPUMAP_REGION_INCLUSIVE) )
> +    {
> +
> +        /*
> +         * Currently, we only support modifying a *WHOLE* MPU memory region,
> +         * part-region modification is not supported, as in worst case, it will
> +         * lead to three fragments in result after modification.
> +         * part-region modification will be introduced only when actual usage
> +         * come
> +         */
> +        if ( rc == MPUMAP_REGION_INCLUSIVE )
> +        {
> +            region_printk("mpu: part-region modification is not supported\n");
> +            return -EINVAL;
> +        }
> +
> +        /* We don't allow changing memory attributes. */
> +        if (xen_mpumap[idx].prlar.reg.ai != REGION_AI_MASK(flags) )
> +        {
> +            region_printk("Modifying memory attributes is not allowed (0x%x -> 0x%x).\n",
> +                          xen_mpumap[idx].prlar.reg.ai, REGION_AI_MASK(flags));
> +            return -EINVAL;
> +        }
> +
> +        /* Set new permission */
> +        xen_mpumap[idx].prbar.reg.ap = REGION_AP_MASK(flags);
> +        xen_mpumap[idx].prbar.reg.xn = REGION_XN_MASK(flags);
> +
> +        access_protection_region(false, NULL, (const pr_t*)(&xen_mpumap[idx]),
> +                                 idx);
> +    }
>       /* We are inserting a mapping => Create new region. */
> -    if ( flags & _REGION_PRESENT )
> +    else if ( flags & _REGION_PRESENT )
>       {
>           if ( rc != MPUMAP_REGION_FAILED )
>               return -EINVAL;
> @@ -831,11 +867,56 @@ void mmu_init_secondary_cpu(void)
>   
>   int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int flags)
>   {
> -    return -ENOSYS;
> +    ASSERT(IS_ALIGNED(s, PAGE_SIZE));
> +    ASSERT(IS_ALIGNED(e, PAGE_SIZE));
> +    ASSERT(s <= e);
> +    return xen_mpumap_update(s, e, flags);
>   }
>   
>   void free_init_memory(void)
>   {
> +    /* Kernel init text section. */
> +    paddr_t init_text = virt_to_maddr(_sinittext);
> +    paddr_t init_text_end = round_pgup(virt_to_maddr(_einittext));
> +    /* Kernel init data. */
> +    paddr_t init_data = virt_to_maddr(__init_data_begin);
> +    paddr_t init_data_end = round_pgup(virt_to_maddr(__init_end));
> +    unsigned long init_section[4] = {(unsigned long)init_text,
> +                                     (unsigned long)init_text_end,
> +                                     (unsigned long)init_data,
> +                                     (unsigned long)init_data_end};
> +    unsigned int nr_init = 2;

At first, it wasn't  obvious what's the 2 meant here. It also seems you 
expect the number to be in-sync with the one above.

I don't think the genericity is necessary here. But if you want it, then 
it would be better to use an array of structure (begin/end) so you can 
use ARRAY_SIZE() afterwards and avoid magic like "i * 2".

> +    uint32_t insn = AARCH64_BREAK_FAULT;

AMD is also working on 32-bit ARMv8R support. When it is easy (like) 
here it would best to avoid making the assumption about 64-bit only.

That said, to me it feels like a big part of this code could be shared 
with the MMU version.

> +    unsigned int i = 0, j = 0;
> +
> +    /* Change kernel init text section to RW. */
> +    modify_xen_mappings((unsigned long)init_text,
> +                        (unsigned long)init_text_end, REGION_HYPERVISOR_RW);
> +
> +    /*
> +     * 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();
> +
> +    /* Destroy two MPU memory regions referring init text and init data. */
> +    for ( ; i < nr_init; i++ )
> +    {
> +        uint32_t *p;
> +        unsigned int nr;
> +        int rc;
> +
> +        i = 2 * i;

... avoid such magic.

> +        p = (uint32_t *)init_section[i];
> +        nr = (init_section[i + 1] - init_section[i]) / sizeof(uint32_t);
> +
> +        for ( ; j < nr ; j++ )
> +            *(p + j) = insn;
> +
> +        rc = destroy_xen_mappings(init_section[i], init_section[i + 1]);
> +        if ( rc < 0 )
> +            panic("Unable to remove the init section (rc = %d)\n", rc);
> +    }
>   }
>   
>   int xenmem_add_to_physmap_one(

Cheers,
diff mbox series

Patch

diff --git a/xen/arch/arm/mm_mpu.c b/xen/arch/arm/mm_mpu.c
index 0b720004ee..de0c7d919a 100644
--- a/xen/arch/arm/mm_mpu.c
+++ b/xen/arch/arm/mm_mpu.c
@@ -20,6 +20,7 @@ 
  */
 
 #include <xen/init.h>
+#include <xen/kernel.h>
 #include <xen/libfdt/libfdt.h>
 #include <xen/mm.h>
 #include <xen/page-size.h>
@@ -77,6 +78,8 @@  static const unsigned int mpu_section_mattr[MSINFO_MAX] = {
     REGION_HYPERVISOR_BOOT,
 };
 
+extern char __init_data_begin[], __init_end[];
+
 /* Write a MPU protection region */
 #define WRITE_PROTECTION_REGION(sel, pr, prbar_el2, prlar_el2) ({       \
     uint64_t _sel = sel;                                                \
@@ -443,8 +446,41 @@  static int xen_mpumap_update_entry(paddr_t base, paddr_t limit,
     if ( rc == MPUMAP_REGION_OVERLAP )
         return -EINVAL;
 
+    /* We are updating the permission. */
+    if ( (flags & _REGION_PRESENT) && (rc == MPUMAP_REGION_FOUND ||
+                                       rc == MPUMAP_REGION_INCLUSIVE) )
+    {
+
+        /*
+         * Currently, we only support modifying a *WHOLE* MPU memory region,
+         * part-region modification is not supported, as in worst case, it will
+         * lead to three fragments in result after modification.
+         * part-region modification will be introduced only when actual usage
+         * come
+         */
+        if ( rc == MPUMAP_REGION_INCLUSIVE )
+        {
+            region_printk("mpu: part-region modification is not supported\n");
+            return -EINVAL;
+        }
+
+        /* We don't allow changing memory attributes. */
+        if (xen_mpumap[idx].prlar.reg.ai != REGION_AI_MASK(flags) )
+        {
+            region_printk("Modifying memory attributes is not allowed (0x%x -> 0x%x).\n",
+                          xen_mpumap[idx].prlar.reg.ai, REGION_AI_MASK(flags));
+            return -EINVAL;
+        }
+
+        /* Set new permission */
+        xen_mpumap[idx].prbar.reg.ap = REGION_AP_MASK(flags);
+        xen_mpumap[idx].prbar.reg.xn = REGION_XN_MASK(flags);
+
+        access_protection_region(false, NULL, (const pr_t*)(&xen_mpumap[idx]),
+                                 idx);
+    }
     /* We are inserting a mapping => Create new region. */
-    if ( flags & _REGION_PRESENT )
+    else if ( flags & _REGION_PRESENT )
     {
         if ( rc != MPUMAP_REGION_FAILED )
             return -EINVAL;
@@ -831,11 +867,56 @@  void mmu_init_secondary_cpu(void)
 
 int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int flags)
 {
-    return -ENOSYS;
+    ASSERT(IS_ALIGNED(s, PAGE_SIZE));
+    ASSERT(IS_ALIGNED(e, PAGE_SIZE));
+    ASSERT(s <= e);
+    return xen_mpumap_update(s, e, flags);
 }
 
 void free_init_memory(void)
 {
+    /* Kernel init text section. */
+    paddr_t init_text = virt_to_maddr(_sinittext);
+    paddr_t init_text_end = round_pgup(virt_to_maddr(_einittext));
+    /* Kernel init data. */
+    paddr_t init_data = virt_to_maddr(__init_data_begin);
+    paddr_t init_data_end = round_pgup(virt_to_maddr(__init_end));
+    unsigned long init_section[4] = {(unsigned long)init_text,
+                                     (unsigned long)init_text_end,
+                                     (unsigned long)init_data,
+                                     (unsigned long)init_data_end};
+    unsigned int nr_init = 2;
+    uint32_t insn = AARCH64_BREAK_FAULT;
+    unsigned int i = 0, j = 0;
+
+    /* Change kernel init text section to RW. */
+    modify_xen_mappings((unsigned long)init_text,
+                        (unsigned long)init_text_end, REGION_HYPERVISOR_RW);
+
+    /*
+     * 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();
+
+    /* Destroy two MPU memory regions referring init text and init data. */
+    for ( ; i < nr_init; i++ )
+    {
+        uint32_t *p;
+        unsigned int nr;
+        int rc;
+
+        i = 2 * i;
+        p = (uint32_t *)init_section[i];
+        nr = (init_section[i + 1] - init_section[i]) / sizeof(uint32_t);
+
+        for ( ; j < nr ; j++ )
+            *(p + j) = insn;
+
+        rc = destroy_xen_mappings(init_section[i], init_section[i + 1]);
+        if ( rc < 0 )
+            panic("Unable to remove the init section (rc = %d)\n", rc);
+    }
 }
 
 int xenmem_add_to_physmap_one(