aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Roth <michael.roth@amd.com>2023-12-06 09:58:21 -0600
committerStefan Hajnoczi <stefanha@redhat.com>2023-12-06 14:34:11 -0500
commit5746f70d685be1e0156e6e7f0d4a1e9bdca2e2e8 (patch)
treebe12671d0c712f6993177532581b9c512fddaad4
parentd451e32ce8e1eef1b7d05d9f532113e9618f1fc1 (diff)
downloadqemu-5746f70d685be1e0156e6e7f0d4a1e9bdca2e2e8.zip
qemu-5746f70d685be1e0156e6e7f0d4a1e9bdca2e2e8.tar.gz
qemu-5746f70d685be1e0156e6e7f0d4a1e9bdca2e2e8.tar.bz2
i386/sev: Avoid SEV-ES crash due to missing MSR_EFER_LMA bit
Commit 7191f24c7fcf ("accel/kvm/kvm-all: Handle register access errors") added error checking for KVM_SET_SREGS/KVM_SET_SREGS2. In doing so, it exposed a long-running bug in current KVM support for SEV-ES where the kernel assumes that MSR_EFER_LMA will be set explicitly by the guest kernel, in which case EFER write traps would result in KVM eventually seeing MSR_EFER_LMA get set and recording it in such a way that it would be subsequently visible when accessing it via KVM_GET_SREGS/etc. However, guest kernels currently rely on MSR_EFER_LMA getting set automatically when MSR_EFER_LME is set and paging is enabled via CR0_PG_MASK. As a result, the EFER write traps don't actually expose the MSR_EFER_LMA bit, even though it is set internally, and when QEMU subsequently tries to pass this EFER value back to KVM via KVM_SET_SREGS* it will fail various sanity checks and return -EINVAL, which is now considered fatal due to the aforementioned QEMU commit. This can be addressed by inferring the MSR_EFER_LMA bit being set when paging is enabled and MSR_EFER_LME is set, and synthesizing it to ensure the expected bits are all present in subsequent handling on the host side. Ultimately, this handling will be implemented in the host kernel, but to avoid breaking QEMU's SEV-ES support when using older host kernels, the same handling can be done in QEMU just after fetching the register values via KVM_GET_SREGS*. Implement that here. Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Akihiko Odaki <akihiko.odaki@daynix.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Cc: Lara Lazier <laramglazier@gmail.com> Cc: Vitaly Kuznetsov <vkuznets@redhat.com> Cc: Maxim Levitsky <mlevitsk@redhat.com> Cc: <kvm@vger.kernel.org> Fixes: 7191f24c7fcf ("accel/kvm/kvm-all: Handle register access errors") Signed-off-by: Michael Roth <michael.roth@amd.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Message-ID: <20231206155821.1194551-1-michael.roth@amd.com>
-rw-r--r--target/i386/kvm/kvm.c8
1 files changed, 8 insertions, 0 deletions
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c
index 11b8177..4ce8055 100644
--- a/target/i386/kvm/kvm.c
+++ b/target/i386/kvm/kvm.c
@@ -3643,6 +3643,10 @@ static int kvm_get_sregs(X86CPU *cpu)
env->cr[4] = sregs.cr4;
env->efer = sregs.efer;
+ if (sev_es_enabled() && env->efer & MSR_EFER_LME &&
+ env->cr[0] & CR0_PG_MASK) {
+ env->efer |= MSR_EFER_LMA;
+ }
/* changes to apic base and cr8/tpr are read back via kvm_arch_post_run */
x86_update_hflags(env);
@@ -3682,6 +3686,10 @@ static int kvm_get_sregs2(X86CPU *cpu)
env->cr[4] = sregs.cr4;
env->efer = sregs.efer;
+ if (sev_es_enabled() && env->efer & MSR_EFER_LME &&
+ env->cr[0] & CR0_PG_MASK) {
+ env->efer |= MSR_EFER_LMA;
+ }
env->pdptrs_valid = sregs.flags & KVM_SREGS2_FLAGS_PDPTRS_VALID;