826eb0ff89
but allow discarding BTF information when loading modules, so that upgrades which are otherwise ABI compatible still work. this allows using BTF information when matching and available, while degrading gracefully if the currently running kernel is not identical to the one that module was built for. in case of a mismatch, the kernel will log a warning when loading the module, for example: Jan 30 13:57:58 test kernel: BPF: type_id=184 bits_offset=4096 Jan 30 13:57:58 test kernel: BPF: Jan 30 13:57:58 test kernel: BPF: Invalid name Jan 30 13:57:58 test kernel: BPF: Jan 30 13:57:58 test kernel: failed to validate module [bonding] BTF: -22 Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
285 lines
8.4 KiB
Diff
285 lines
8.4 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Maxim Levitsky <mlevitsk@redhat.com>
|
|
Date: Wed, 3 Aug 2022 18:50:05 +0300
|
|
Subject: [PATCH] KVM: x86: emulator/smm: add structs for KVM's smram layout
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Those structs will be used to read/write the smram state image.
|
|
|
|
Also document the differences between KVM's SMRAM layout and SMRAM
|
|
layout that is used by real Intel/AMD cpus.
|
|
|
|
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
|
|
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
|
|
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
|
|
---
|
|
arch/x86/kvm/emulate.c | 6 +
|
|
arch/x86/kvm/kvm_emulate.h | 218 +++++++++++++++++++++++++++++++++++++
|
|
arch/x86/kvm/x86.c | 1 +
|
|
3 files changed, 225 insertions(+)
|
|
|
|
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
|
|
index 4f7f5117ec7a..470dd4453b01 100644
|
|
--- a/arch/x86/kvm/emulate.c
|
|
+++ b/arch/x86/kvm/emulate.c
|
|
@@ -5856,3 +5856,9 @@ bool emulator_can_use_gpa(struct x86_emulate_ctxt *ctxt)
|
|
|
|
return true;
|
|
}
|
|
+
|
|
+void __init kvm_emulator_init(void)
|
|
+{
|
|
+ __check_smram32_offsets();
|
|
+ __check_smram64_offsets();
|
|
+}
|
|
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
|
|
index 89246446d6aa..dd0ae61e44a1 100644
|
|
--- a/arch/x86/kvm/kvm_emulate.h
|
|
+++ b/arch/x86/kvm/kvm_emulate.h
|
|
@@ -13,6 +13,7 @@
|
|
#define _ASM_X86_KVM_X86_EMULATE_H
|
|
|
|
#include <asm/desc_defs.h>
|
|
+#include <linux/build_bug.h>
|
|
#include "fpu.h"
|
|
|
|
struct x86_emulate_ctxt;
|
|
@@ -503,6 +504,223 @@ enum x86_intercept {
|
|
nr_x86_intercepts
|
|
};
|
|
|
|
+
|
|
+/* 32 bit KVM's emulated SMM layout. Loosely based on Intel's layout */
|
|
+
|
|
+struct kvm_smm_seg_state_32 {
|
|
+ u32 flags;
|
|
+ u32 limit;
|
|
+ u32 base;
|
|
+} __packed;
|
|
+
|
|
+struct kvm_smram_state_32 {
|
|
+ u32 reserved1[62];
|
|
+ u32 smbase;
|
|
+ u32 smm_revision;
|
|
+ u32 reserved2[5];
|
|
+ u32 cr4; /* CR4 is not present in Intel/AMD SMRAM image */
|
|
+ u32 reserved3[5];
|
|
+
|
|
+ /*
|
|
+ * Segment state is not present/documented in the Intel/AMD SMRAM image
|
|
+ * Instead this area on Intel/AMD contains IO/HLT restart flags.
|
|
+ */
|
|
+ struct kvm_smm_seg_state_32 ds;
|
|
+ struct kvm_smm_seg_state_32 fs;
|
|
+ struct kvm_smm_seg_state_32 gs;
|
|
+ struct kvm_smm_seg_state_32 idtr; /* IDTR has only base and limit */
|
|
+ struct kvm_smm_seg_state_32 tr;
|
|
+ u32 reserved;
|
|
+ struct kvm_smm_seg_state_32 gdtr; /* GDTR has only base and limit */
|
|
+ struct kvm_smm_seg_state_32 ldtr;
|
|
+ struct kvm_smm_seg_state_32 es;
|
|
+ struct kvm_smm_seg_state_32 cs;
|
|
+ struct kvm_smm_seg_state_32 ss;
|
|
+
|
|
+ u32 es_sel;
|
|
+ u32 cs_sel;
|
|
+ u32 ss_sel;
|
|
+ u32 ds_sel;
|
|
+ u32 fs_sel;
|
|
+ u32 gs_sel;
|
|
+ u32 ldtr_sel;
|
|
+ u32 tr_sel;
|
|
+
|
|
+ u32 dr7;
|
|
+ u32 dr6;
|
|
+ u32 gprs[8]; /* GPRS in the "natural" X86 order (EAX/ECX/EDX.../EDI) */
|
|
+ u32 eip;
|
|
+ u32 eflags;
|
|
+ u32 cr3;
|
|
+ u32 cr0;
|
|
+} __packed;
|
|
+
|
|
+
|
|
+static inline void __check_smram32_offsets(void)
|
|
+{
|
|
+#define __CHECK_SMRAM32_OFFSET(field, offset) \
|
|
+ ASSERT_STRUCT_OFFSET(struct kvm_smram_state_32, field, offset - 0xFE00)
|
|
+
|
|
+ __CHECK_SMRAM32_OFFSET(reserved1, 0xFE00);
|
|
+ __CHECK_SMRAM32_OFFSET(smbase, 0xFEF8);
|
|
+ __CHECK_SMRAM32_OFFSET(smm_revision, 0xFEFC);
|
|
+ __CHECK_SMRAM32_OFFSET(reserved2, 0xFF00);
|
|
+ __CHECK_SMRAM32_OFFSET(cr4, 0xFF14);
|
|
+ __CHECK_SMRAM32_OFFSET(reserved3, 0xFF18);
|
|
+ __CHECK_SMRAM32_OFFSET(ds, 0xFF2C);
|
|
+ __CHECK_SMRAM32_OFFSET(fs, 0xFF38);
|
|
+ __CHECK_SMRAM32_OFFSET(gs, 0xFF44);
|
|
+ __CHECK_SMRAM32_OFFSET(idtr, 0xFF50);
|
|
+ __CHECK_SMRAM32_OFFSET(tr, 0xFF5C);
|
|
+ __CHECK_SMRAM32_OFFSET(gdtr, 0xFF6C);
|
|
+ __CHECK_SMRAM32_OFFSET(ldtr, 0xFF78);
|
|
+ __CHECK_SMRAM32_OFFSET(es, 0xFF84);
|
|
+ __CHECK_SMRAM32_OFFSET(cs, 0xFF90);
|
|
+ __CHECK_SMRAM32_OFFSET(ss, 0xFF9C);
|
|
+ __CHECK_SMRAM32_OFFSET(es_sel, 0xFFA8);
|
|
+ __CHECK_SMRAM32_OFFSET(cs_sel, 0xFFAC);
|
|
+ __CHECK_SMRAM32_OFFSET(ss_sel, 0xFFB0);
|
|
+ __CHECK_SMRAM32_OFFSET(ds_sel, 0xFFB4);
|
|
+ __CHECK_SMRAM32_OFFSET(fs_sel, 0xFFB8);
|
|
+ __CHECK_SMRAM32_OFFSET(gs_sel, 0xFFBC);
|
|
+ __CHECK_SMRAM32_OFFSET(ldtr_sel, 0xFFC0);
|
|
+ __CHECK_SMRAM32_OFFSET(tr_sel, 0xFFC4);
|
|
+ __CHECK_SMRAM32_OFFSET(dr7, 0xFFC8);
|
|
+ __CHECK_SMRAM32_OFFSET(dr6, 0xFFCC);
|
|
+ __CHECK_SMRAM32_OFFSET(gprs, 0xFFD0);
|
|
+ __CHECK_SMRAM32_OFFSET(eip, 0xFFF0);
|
|
+ __CHECK_SMRAM32_OFFSET(eflags, 0xFFF4);
|
|
+ __CHECK_SMRAM32_OFFSET(cr3, 0xFFF8);
|
|
+ __CHECK_SMRAM32_OFFSET(cr0, 0xFFFC);
|
|
+#undef __CHECK_SMRAM32_OFFSET
|
|
+}
|
|
+
|
|
+
|
|
+/* 64 bit KVM's emulated SMM layout. Based on AMD64 layout */
|
|
+
|
|
+struct kvm_smm_seg_state_64 {
|
|
+ u16 selector;
|
|
+ u16 attributes;
|
|
+ u32 limit;
|
|
+ u64 base;
|
|
+};
|
|
+
|
|
+struct kvm_smram_state_64 {
|
|
+
|
|
+ struct kvm_smm_seg_state_64 es;
|
|
+ struct kvm_smm_seg_state_64 cs;
|
|
+ struct kvm_smm_seg_state_64 ss;
|
|
+ struct kvm_smm_seg_state_64 ds;
|
|
+ struct kvm_smm_seg_state_64 fs;
|
|
+ struct kvm_smm_seg_state_64 gs;
|
|
+ struct kvm_smm_seg_state_64 gdtr; /* GDTR has only base and limit*/
|
|
+ struct kvm_smm_seg_state_64 ldtr;
|
|
+ struct kvm_smm_seg_state_64 idtr; /* IDTR has only base and limit*/
|
|
+ struct kvm_smm_seg_state_64 tr;
|
|
+
|
|
+ /* I/O restart and auto halt restart are not implemented by KVM */
|
|
+ u64 io_restart_rip;
|
|
+ u64 io_restart_rcx;
|
|
+ u64 io_restart_rsi;
|
|
+ u64 io_restart_rdi;
|
|
+ u32 io_restart_dword;
|
|
+ u32 reserved1;
|
|
+ u8 io_inst_restart;
|
|
+ u8 auto_hlt_restart;
|
|
+ u8 reserved2[6];
|
|
+
|
|
+ u64 efer;
|
|
+
|
|
+ /*
|
|
+ * Two fields below are implemented on AMD only, to store
|
|
+ * SVM guest vmcb address if the #SMI was received while in the guest mode.
|
|
+ */
|
|
+ u64 svm_guest_flag;
|
|
+ u64 svm_guest_vmcb_gpa;
|
|
+ u64 svm_guest_virtual_int; /* unknown purpose, not implemented */
|
|
+
|
|
+ u32 reserved3[3];
|
|
+ u32 smm_revison;
|
|
+ u32 smbase;
|
|
+ u32 reserved4[5];
|
|
+
|
|
+ /* ssp and svm_* fields below are not implemented by KVM */
|
|
+ u64 ssp;
|
|
+ u64 svm_guest_pat;
|
|
+ u64 svm_host_efer;
|
|
+ u64 svm_host_cr4;
|
|
+ u64 svm_host_cr3;
|
|
+ u64 svm_host_cr0;
|
|
+
|
|
+ u64 cr4;
|
|
+ u64 cr3;
|
|
+ u64 cr0;
|
|
+ u64 dr7;
|
|
+ u64 dr6;
|
|
+ u64 rflags;
|
|
+ u64 rip;
|
|
+ u64 gprs[16]; /* GPRS in a reversed "natural" X86 order (R15/R14/../RCX/RAX.) */
|
|
+};
|
|
+
|
|
+
|
|
+static inline void __check_smram64_offsets(void)
|
|
+{
|
|
+#define __CHECK_SMRAM64_OFFSET(field, offset) \
|
|
+ ASSERT_STRUCT_OFFSET(struct kvm_smram_state_64, field, offset - 0xFE00)
|
|
+
|
|
+ __CHECK_SMRAM64_OFFSET(es, 0xFE00);
|
|
+ __CHECK_SMRAM64_OFFSET(cs, 0xFE10);
|
|
+ __CHECK_SMRAM64_OFFSET(ss, 0xFE20);
|
|
+ __CHECK_SMRAM64_OFFSET(ds, 0xFE30);
|
|
+ __CHECK_SMRAM64_OFFSET(fs, 0xFE40);
|
|
+ __CHECK_SMRAM64_OFFSET(gs, 0xFE50);
|
|
+ __CHECK_SMRAM64_OFFSET(gdtr, 0xFE60);
|
|
+ __CHECK_SMRAM64_OFFSET(ldtr, 0xFE70);
|
|
+ __CHECK_SMRAM64_OFFSET(idtr, 0xFE80);
|
|
+ __CHECK_SMRAM64_OFFSET(tr, 0xFE90);
|
|
+ __CHECK_SMRAM64_OFFSET(io_restart_rip, 0xFEA0);
|
|
+ __CHECK_SMRAM64_OFFSET(io_restart_rcx, 0xFEA8);
|
|
+ __CHECK_SMRAM64_OFFSET(io_restart_rsi, 0xFEB0);
|
|
+ __CHECK_SMRAM64_OFFSET(io_restart_rdi, 0xFEB8);
|
|
+ __CHECK_SMRAM64_OFFSET(io_restart_dword, 0xFEC0);
|
|
+ __CHECK_SMRAM64_OFFSET(reserved1, 0xFEC4);
|
|
+ __CHECK_SMRAM64_OFFSET(io_inst_restart, 0xFEC8);
|
|
+ __CHECK_SMRAM64_OFFSET(auto_hlt_restart, 0xFEC9);
|
|
+ __CHECK_SMRAM64_OFFSET(reserved2, 0xFECA);
|
|
+ __CHECK_SMRAM64_OFFSET(efer, 0xFED0);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_guest_flag, 0xFED8);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_guest_vmcb_gpa, 0xFEE0);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_guest_virtual_int, 0xFEE8);
|
|
+ __CHECK_SMRAM64_OFFSET(reserved3, 0xFEF0);
|
|
+ __CHECK_SMRAM64_OFFSET(smm_revison, 0xFEFC);
|
|
+ __CHECK_SMRAM64_OFFSET(smbase, 0xFF00);
|
|
+ __CHECK_SMRAM64_OFFSET(reserved4, 0xFF04);
|
|
+ __CHECK_SMRAM64_OFFSET(ssp, 0xFF18);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_guest_pat, 0xFF20);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_host_efer, 0xFF28);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_host_cr4, 0xFF30);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_host_cr3, 0xFF38);
|
|
+ __CHECK_SMRAM64_OFFSET(svm_host_cr0, 0xFF40);
|
|
+ __CHECK_SMRAM64_OFFSET(cr4, 0xFF48);
|
|
+ __CHECK_SMRAM64_OFFSET(cr3, 0xFF50);
|
|
+ __CHECK_SMRAM64_OFFSET(cr0, 0xFF58);
|
|
+ __CHECK_SMRAM64_OFFSET(dr7, 0xFF60);
|
|
+ __CHECK_SMRAM64_OFFSET(dr6, 0xFF68);
|
|
+ __CHECK_SMRAM64_OFFSET(rflags, 0xFF70);
|
|
+ __CHECK_SMRAM64_OFFSET(rip, 0xFF78);
|
|
+ __CHECK_SMRAM64_OFFSET(gprs, 0xFF80);
|
|
+#undef __CHECK_SMRAM64_OFFSET
|
|
+}
|
|
+
|
|
+union kvm_smram {
|
|
+ struct kvm_smram_state_64 smram64;
|
|
+ struct kvm_smram_state_32 smram32;
|
|
+ u8 bytes[512];
|
|
+};
|
|
+
|
|
+void __init kvm_emulator_init(void);
|
|
+
|
|
+
|
|
/* Host execution mode. */
|
|
#if defined(CONFIG_X86_32)
|
|
#define X86EMUL_MODE_HOST X86EMUL_MODE_PROT32
|
|
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
|
|
index acca85b10545..20aec64e3521 100644
|
|
--- a/arch/x86/kvm/x86.c
|
|
+++ b/arch/x86/kvm/x86.c
|
|
@@ -13730,6 +13730,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_vmgexit_msr_protocol_exit);
|
|
static int __init kvm_x86_init(void)
|
|
{
|
|
kvm_mmu_x86_module_init();
|
|
+ kvm_emulator_init();
|
|
return 0;
|
|
}
|
|
module_init(kvm_x86_init);
|