While working on some EFI boot changes for Linux/x86, I noticed that TCG
deviates from bare metal when it comes to how it handles the value of
the stack pointer register RSP when dropping out of long mode.

On bare metal, RSP is truncated to 32 bits, even if the code that runs
in 32-bit protected mode never uses the stack at all (and uses a long
jump rather than long return to switch back to long mode). This means
64-bit code cannot rely on RSP surviving any excursions into 32-bit
protected mode (with paging disabled).

Let's align TCG with this behavior, so that code that relies on RSP
retaining its value does not inadvertently work while bare metal does
not.

Observed on Intel Ice Lake cores.

Cc: Paolo Bonzini <pbonz...@redhat.com>
Cc: Richard Henderson <richard.hender...@linaro.org>
Cc: Eduardo Habkost <edua...@habkost.net>
Link: https://lore.kernel.org/all/20230711091453.2543622-11-a...@kernel.org/
Signed-off-by: Ard Biesheuvel <a...@kernel.org>
---
I used this patch locally to reproduce an issue that was reported on Ice
Lake but didn't trigger in my QEMU testing.

Hints welcome on where the architectural behavior is specified, and in
particular, whether or not other 64-bit GPRs can be relied upon to
preserve their full 64-bit length values.

 target/i386/helper.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/target/i386/helper.c b/target/i386/helper.c
index 89aa696c6d53d68c..a338da23a87746ed 100644
--- a/target/i386/helper.c
+++ b/target/i386/helper.c
@@ -149,6 +149,7 @@ void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0)
         env->efer &= ~MSR_EFER_LMA;
         env->hflags &= ~(HF_LMA_MASK | HF_CS64_MASK);
         env->eip &= 0xffffffff;
+        env->regs[R_ESP] &= 0xffffffff;
     }
 #endif
     env->cr[0] = new_cr0 | CR0_ET_MASK;
-- 
2.39.2


Reply via email to