[PATCH] target-i386: Walk NPT in guest real mode

Alexander Graf posted 1 patch 2 months, 4 weeks ago
There is a newer version of this series
target/i386/tcg/sysemu/excp_helper.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
[PATCH] target-i386: Walk NPT in guest real mode
Posted by Alexander Graf 2 months, 4 weeks ago
When translating virtual to physical address with a guest CPU that
supports nested paging (NPT), we need to perform every page table walk
access indirectly through the NPT, which we correctly do.

However, we treat real mode (no page table walk) special: In that case,
we currently just skip any walks and translate VA -> PA. With NPT
enabled, we also need to then perform NPT walk to do GVA -> GPA -> HPA
which we fail to do so far.

The net result of that is that TCG VMs with NPT enabled that execute
real mode code (like SeaBIOS) end up with GPA==HPA mappings which means
the guest accesses host code and data. This typically shows as failure
to boot guests.

This patch changes the page walk logic for NPT enabled guests so that we
always perform a GVA -> GPA translation, but simply provide a 1 GiB fake
PTE when in real mode. That way, all remaining logic to walk the NPT
stays and we successfully walk the NPT in real mode.

Fixes: fe441054bb3f0 ("target-i386: Add NPT support")

Signed-off-by: Alexander Graf <graf@amazon.com>
Reported-by: Eduard Vlad <evlad@amazon.de>
---
 target/i386/tcg/sysemu/excp_helper.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c
index 8fb05b1f53..17f45431f6 100644
--- a/target/i386/tcg/sysemu/excp_helper.c
+++ b/target/i386/tcg/sysemu/excp_helper.c
@@ -298,7 +298,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
         /* combine pde and pte nx, user and rw protections */
         ptep &= pte ^ PG_NX_MASK;
         page_size = 4096;
-    } else {
+    } else if (pg_mode) {
         /*
          * Page table level 2
          */
@@ -343,6 +343,12 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
         ptep &= pte | PG_NX_MASK;
         page_size = 4096;
         rsvd_mask = 0;
+    } else {
+        /* No paging (real mode), let's assemble a fake 1:1 1GiB PTE */
+        page_size = 0x40000000;
+        pte = (in->addr & ~(page_size - 1)) | PG_DIRTY_MASK | PG_ACCESSED_MASK;
+        ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK;
+        rsvd_mask = 0;
     }
 
 do_check_protect:
@@ -562,7 +568,7 @@ static bool get_physical_address(CPUX86State *env, vaddr addr,
             addr = (uint32_t)addr;
         }
 
-        if (likely(env->cr[0] & CR0_PG_MASK)) {
+        if (likely(env->cr[0] & CR0_PG_MASK || use_stage2)) {
             in.cr3 = env->cr[3];
             in.mmu_idx = mmu_idx;
             in.ptw_idx = use_stage2 ? MMU_NESTED_IDX : MMU_PHYS_IDX;
-- 
2.40.1




Amazon Web Services Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597
Re: [PATCH] target-i386: Walk NPT in guest real mode
Posted by Richard Henderson 2 months ago
On 8/27/24 15:58, Alexander Graf wrote:
> When translating virtual to physical address with a guest CPU that
> supports nested paging (NPT), we need to perform every page table walk
> access indirectly through the NPT, which we correctly do.
> 
> However, we treat real mode (no page table walk) special: In that case,
> we currently just skip any walks and translate VA -> PA. With NPT
> enabled, we also need to then perform NPT walk to do GVA -> GPA -> HPA
> which we fail to do so far.
> 
> The net result of that is that TCG VMs with NPT enabled that execute
> real mode code (like SeaBIOS) end up with GPA==HPA mappings which means
> the guest accesses host code and data. This typically shows as failure
> to boot guests.
> 
> This patch changes the page walk logic for NPT enabled guests so that we
> always perform a GVA -> GPA translation, but simply provide a 1 GiB fake
> PTE when in real mode. That way, all remaining logic to walk the NPT
> stays and we successfully walk the NPT in real mode.
> 
> Fixes: fe441054bb3f0 ("target-i386: Add NPT support")
> 
> Signed-off-by: Alexander Graf <graf@amazon.com>
> Reported-by: Eduard Vlad <evlad@amazon.de>
> ---
>   target/i386/tcg/sysemu/excp_helper.c | 10 ++++++++--
>   1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c
> index 8fb05b1f53..17f45431f6 100644
> --- a/target/i386/tcg/sysemu/excp_helper.c
> +++ b/target/i386/tcg/sysemu/excp_helper.c
> @@ -298,7 +298,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
>           /* combine pde and pte nx, user and rw protections */
>           ptep &= pte ^ PG_NX_MASK;
>           page_size = 4096;
> -    } else {
> +    } else if (pg_mode) {
>           /*
>            * Page table level 2
>            */
> @@ -343,6 +343,12 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in,
>           ptep &= pte | PG_NX_MASK;
>           page_size = 4096;
>           rsvd_mask = 0;
> +    } else {
> +        /* No paging (real mode), let's assemble a fake 1:1 1GiB PTE */
> +        page_size = 0x40000000;
> +        pte = (in->addr & ~(page_size - 1)) | PG_DIRTY_MASK | PG_ACCESSED_MASK;
> +        ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK;
> +        rsvd_mask = 0;
>       }

It's better to emulate a 4k page. Large pages incur extra overhead in the softmmu tlb, 
because we expect to be able to flush the large page as well.

That said, the patch looks correct.


r~