Message ID | 20200211135256.24617-15-joro@8bytes.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Linux as SEV-ES Guest Support | expand |
On Tue, Feb 11, 2020 at 5:53 AM Joerg Roedel <joro@8bytes.org> wrote: > > From: Joerg Roedel <jroedel@suse.de> > > Add the first handler for #VC exceptions. At stage 1 there is no GHCB > yet becaue we might still be on the EFI page table and thus can't map > memory unencrypted. > > The stage 1 handler is limited to the MSR based protocol to talk to > the hypervisor and can only support CPUID exit-codes, but that is > enough to get to stage 2. > > Signed-off-by: Joerg Roedel <jroedel@suse.de> > --- > arch/x86/boot/compressed/Makefile | 1 + > arch/x86/boot/compressed/idt_64.c | 4 ++ > arch/x86/boot/compressed/idt_handlers_64.S | 4 ++ > arch/x86/boot/compressed/misc.h | 1 + > arch/x86/boot/compressed/sev-es.c | 42 ++++++++++++++ > arch/x86/include/asm/msr-index.h | 1 + > arch/x86/include/asm/sev-es.h | 45 +++++++++++++++ > arch/x86/include/asm/trap_defs.h | 1 + > arch/x86/kernel/sev-es-shared.c | 66 ++++++++++++++++++++++ > 9 files changed, 165 insertions(+) > create mode 100644 arch/x86/boot/compressed/sev-es.c > create mode 100644 arch/x86/include/asm/sev-es.h > create mode 100644 arch/x86/kernel/sev-es-shared.c > > diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile > index e6b3e0fc48de..583678c78e1b 100644 > --- a/arch/x86/boot/compressed/Makefile > +++ b/arch/x86/boot/compressed/Makefile > @@ -84,6 +84,7 @@ ifdef CONFIG_X86_64 > vmlinux-objs-y += $(obj)/idt_64.o $(obj)/idt_handlers_64.o > vmlinux-objs-y += $(obj)/mem_encrypt.o > vmlinux-objs-y += $(obj)/pgtable_64.o > + vmlinux-objs-$(CONFIG_AMD_MEM_ENCRYPT) += $(obj)/sev-es.o > endif > > vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o > diff --git a/arch/x86/boot/compressed/idt_64.c b/arch/x86/boot/compressed/idt_64.c > index 84ba57d9d436..bdd20dfd1fd0 100644 > --- a/arch/x86/boot/compressed/idt_64.c > +++ b/arch/x86/boot/compressed/idt_64.c > @@ -31,6 +31,10 @@ void load_stage1_idt(void) > { > boot_idt_desc.address = (unsigned long)boot_idt; > > +#ifdef CONFIG_AMD_MEM_ENCRYPT > + set_idt_entry(X86_TRAP_VC, boot_stage1_vc_handler); > +#endif > + > load_boot_idt(&boot_idt_desc); > } > > diff --git a/arch/x86/boot/compressed/idt_handlers_64.S b/arch/x86/boot/compressed/idt_handlers_64.S > index f7f1ea66dcbf..330eb4e5c8b3 100644 > --- a/arch/x86/boot/compressed/idt_handlers_64.S > +++ b/arch/x86/boot/compressed/idt_handlers_64.S > @@ -71,3 +71,7 @@ SYM_FUNC_END(\name) > .code64 > > EXCEPTION_HANDLER boot_pf_handler do_boot_page_fault error_code=1 > + > +#ifdef CONFIG_AMD_MEM_ENCRYPT > +EXCEPTION_HANDLER boot_stage1_vc_handler no_ghcb_vc_handler error_code=1 > +#endif > diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h > index 4e5bc688f467..0e3508c5c15c 100644 > --- a/arch/x86/boot/compressed/misc.h > +++ b/arch/x86/boot/compressed/misc.h > @@ -141,5 +141,6 @@ extern struct desc_ptr boot_idt_desc; > > /* IDT Entry Points */ > void boot_pf_handler(void); > +void boot_stage1_vc_handler(void); > > #endif /* BOOT_COMPRESSED_MISC_H */ > diff --git a/arch/x86/boot/compressed/sev-es.c b/arch/x86/boot/compressed/sev-es.c > new file mode 100644 > index 000000000000..8d13121a8cf2 > --- /dev/null > +++ b/arch/x86/boot/compressed/sev-es.c > @@ -0,0 +1,42 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * AMD Encrypted Register State Support > + * > + * Author: Joerg Roedel <jroedel@suse.de> > + */ > + > +#include <linux/kernel.h> > + > +#include <asm/sev-es.h> > +#include <asm/msr-index.h> > +#include <asm/ptrace.h> > +#include <asm/svm.h> > + > +#include "misc.h" > + > +static inline u64 read_ghcb_msr(void) > +{ > + unsigned long low, high; > + > + asm volatile("rdmsr\n" : "=a" (low), "=d" (high) : > + "c" (MSR_AMD64_SEV_ES_GHCB)); > + > + return ((high << 32) | low); > +} > + > +static inline void write_ghcb_msr(u64 val) > +{ > + u32 low, high; > + > + low = val & 0xffffffffUL; > + high = val >> 32; > + > + asm volatile("wrmsr\n" : : "c" (MSR_AMD64_SEV_ES_GHCB), > + "a"(low), "d" (high) : "memory"); > +} > + > +#undef __init > +#define __init > + > +/* Include code for early handlers */ > +#include "../../kernel/sev-es-shared.c" > diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h > index ebe1685e92dd..b6139b70db54 100644 > --- a/arch/x86/include/asm/msr-index.h > +++ b/arch/x86/include/asm/msr-index.h > @@ -432,6 +432,7 @@ > #define MSR_AMD64_IBSBRTARGET 0xc001103b > #define MSR_AMD64_IBSOPDATA4 0xc001103d > #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ > +#define MSR_AMD64_SEV_ES_GHCB 0xc0010130 > #define MSR_AMD64_SEV 0xc0010131 > #define MSR_AMD64_SEV_ENABLED_BIT 0 > #define MSR_AMD64_SEV_ENABLED BIT_ULL(MSR_AMD64_SEV_ENABLED_BIT) > diff --git a/arch/x86/include/asm/sev-es.h b/arch/x86/include/asm/sev-es.h > new file mode 100644 > index 000000000000..f524b40aef07 > --- /dev/null > +++ b/arch/x86/include/asm/sev-es.h > @@ -0,0 +1,45 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * AMD Encrypted Register State Support > + * > + * Author: Joerg Roedel <jroedel@suse.de> > + */ > + > +#ifndef __ASM_ENCRYPTED_STATE_H > +#define __ASM_ENCRYPTED_STATE_H > + > +#include <linux/types.h> > + > +#define GHCB_SEV_CPUID_REQ 0x004UL > +#define GHCB_CPUID_REQ_EAX 0 > +#define GHCB_CPUID_REQ_EBX 1 > +#define GHCB_CPUID_REQ_ECX 2 > +#define GHCB_CPUID_REQ_EDX 3 > +#define GHCB_CPUID_REQ(fn, reg) (GHCB_SEV_CPUID_REQ | \ > + (((unsigned long)reg & 3) << 30) | \ > + (((unsigned long)fn) << 32)) > + > +#define GHCB_SEV_CPUID_RESP 0x005UL > +#define GHCB_SEV_TERMINATE 0x100UL > + > +#define GHCB_SEV_GHCB_RESP_CODE(v) ((v) & 0xfff) > +#define VMGEXIT() { asm volatile("rep; vmmcall\n\r"); } > + > +static inline u64 lower_bits(u64 val, unsigned int bits) > +{ > + u64 mask = (1ULL << bits) - 1; > + > + return (val & mask); > +} > + > +static inline u64 copy_lower_bits(u64 out, u64 in, unsigned int bits) > +{ > + u64 mask = (1ULL << bits) - 1; > + > + out &= ~mask; > + out |= lower_bits(in, bits); > + > + return out; > +} > + > +#endif > diff --git a/arch/x86/include/asm/trap_defs.h b/arch/x86/include/asm/trap_defs.h > index 488f82ac36da..af45d65f0458 100644 > --- a/arch/x86/include/asm/trap_defs.h > +++ b/arch/x86/include/asm/trap_defs.h > @@ -24,6 +24,7 @@ enum { > X86_TRAP_AC, /* 17, Alignment Check */ > X86_TRAP_MC, /* 18, Machine Check */ > X86_TRAP_XF, /* 19, SIMD Floating-Point Exception */ > + X86_TRAP_VC = 29, /* 29, VMM Communication Exception */ > X86_TRAP_IRET = 32, /* 32, IRET Exception */ > }; > > diff --git a/arch/x86/kernel/sev-es-shared.c b/arch/x86/kernel/sev-es-shared.c > new file mode 100644 > index 000000000000..7edf2dfac71f > --- /dev/null > +++ b/arch/x86/kernel/sev-es-shared.c > @@ -0,0 +1,66 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * AMD Encrypted Register State Support > + * > + * Author: Joerg Roedel <jroedel@suse.de> > + * > + * This file is not compiled stand-alone. It contains code shared > + * between the pre-decompression boot code and the running Linux kernel > + * and is included directly into both code-bases. > + */ > + > +/* > + * Boot VC Handler - This is the first VC handler during boot, there is no GHCB > + * page yet, so it only supports the MSR based communication with the > + * hypervisor and only the CPUID exit-code. > + */ > +void __init no_ghcb_vc_handler(struct pt_regs *regs) Isn't there a second parameter: unsigned long error_code? --Andy
On Tue, Feb 11, 2020 at 02:23:22PM -0800, Andy Lutomirski wrote: > On Tue, Feb 11, 2020 at 5:53 AM Joerg Roedel <joro@8bytes.org> wrote: > > +void __init no_ghcb_vc_handler(struct pt_regs *regs) > > Isn't there a second parameter: unsigned long error_code? No, the function gets the error-code from regs->orig_ax. This particular function only needs to check for error_code == SVM_EXIT_CPUID, as that is the only one supported when there is no GHCB. Regards, Joerg
> On Feb 12, 2020, at 3:38 AM, Joerg Roedel <joro@8bytes.org> wrote: > > On Tue, Feb 11, 2020 at 02:23:22PM -0800, Andy Lutomirski wrote: >>> On Tue, Feb 11, 2020 at 5:53 AM Joerg Roedel <joro@8bytes.org> wrote: >>> +void __init no_ghcb_vc_handler(struct pt_regs *regs) >> >> Isn't there a second parameter: unsigned long error_code? > > No, the function gets the error-code from regs->orig_ax. This particular > function only needs to check for error_code == SVM_EXIT_CPUID, as that > is the only one supported when there is no GHCB. > Hmm. It might be nice to use the same signature for early handlers as for normal ones. > Regards, > > Joerg
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index e6b3e0fc48de..583678c78e1b 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -84,6 +84,7 @@ ifdef CONFIG_X86_64 vmlinux-objs-y += $(obj)/idt_64.o $(obj)/idt_handlers_64.o vmlinux-objs-y += $(obj)/mem_encrypt.o vmlinux-objs-y += $(obj)/pgtable_64.o + vmlinux-objs-$(CONFIG_AMD_MEM_ENCRYPT) += $(obj)/sev-es.o endif vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o diff --git a/arch/x86/boot/compressed/idt_64.c b/arch/x86/boot/compressed/idt_64.c index 84ba57d9d436..bdd20dfd1fd0 100644 --- a/arch/x86/boot/compressed/idt_64.c +++ b/arch/x86/boot/compressed/idt_64.c @@ -31,6 +31,10 @@ void load_stage1_idt(void) { boot_idt_desc.address = (unsigned long)boot_idt; +#ifdef CONFIG_AMD_MEM_ENCRYPT + set_idt_entry(X86_TRAP_VC, boot_stage1_vc_handler); +#endif + load_boot_idt(&boot_idt_desc); } diff --git a/arch/x86/boot/compressed/idt_handlers_64.S b/arch/x86/boot/compressed/idt_handlers_64.S index f7f1ea66dcbf..330eb4e5c8b3 100644 --- a/arch/x86/boot/compressed/idt_handlers_64.S +++ b/arch/x86/boot/compressed/idt_handlers_64.S @@ -71,3 +71,7 @@ SYM_FUNC_END(\name) .code64 EXCEPTION_HANDLER boot_pf_handler do_boot_page_fault error_code=1 + +#ifdef CONFIG_AMD_MEM_ENCRYPT +EXCEPTION_HANDLER boot_stage1_vc_handler no_ghcb_vc_handler error_code=1 +#endif diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 4e5bc688f467..0e3508c5c15c 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -141,5 +141,6 @@ extern struct desc_ptr boot_idt_desc; /* IDT Entry Points */ void boot_pf_handler(void); +void boot_stage1_vc_handler(void); #endif /* BOOT_COMPRESSED_MISC_H */ diff --git a/arch/x86/boot/compressed/sev-es.c b/arch/x86/boot/compressed/sev-es.c new file mode 100644 index 000000000000..8d13121a8cf2 --- /dev/null +++ b/arch/x86/boot/compressed/sev-es.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Encrypted Register State Support + * + * Author: Joerg Roedel <jroedel@suse.de> + */ + +#include <linux/kernel.h> + +#include <asm/sev-es.h> +#include <asm/msr-index.h> +#include <asm/ptrace.h> +#include <asm/svm.h> + +#include "misc.h" + +static inline u64 read_ghcb_msr(void) +{ + unsigned long low, high; + + asm volatile("rdmsr\n" : "=a" (low), "=d" (high) : + "c" (MSR_AMD64_SEV_ES_GHCB)); + + return ((high << 32) | low); +} + +static inline void write_ghcb_msr(u64 val) +{ + u32 low, high; + + low = val & 0xffffffffUL; + high = val >> 32; + + asm volatile("wrmsr\n" : : "c" (MSR_AMD64_SEV_ES_GHCB), + "a"(low), "d" (high) : "memory"); +} + +#undef __init +#define __init + +/* Include code for early handlers */ +#include "../../kernel/sev-es-shared.c" diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index ebe1685e92dd..b6139b70db54 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -432,6 +432,7 @@ #define MSR_AMD64_IBSBRTARGET 0xc001103b #define MSR_AMD64_IBSOPDATA4 0xc001103d #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ +#define MSR_AMD64_SEV_ES_GHCB 0xc0010130 #define MSR_AMD64_SEV 0xc0010131 #define MSR_AMD64_SEV_ENABLED_BIT 0 #define MSR_AMD64_SEV_ENABLED BIT_ULL(MSR_AMD64_SEV_ENABLED_BIT) diff --git a/arch/x86/include/asm/sev-es.h b/arch/x86/include/asm/sev-es.h new file mode 100644 index 000000000000..f524b40aef07 --- /dev/null +++ b/arch/x86/include/asm/sev-es.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AMD Encrypted Register State Support + * + * Author: Joerg Roedel <jroedel@suse.de> + */ + +#ifndef __ASM_ENCRYPTED_STATE_H +#define __ASM_ENCRYPTED_STATE_H + +#include <linux/types.h> + +#define GHCB_SEV_CPUID_REQ 0x004UL +#define GHCB_CPUID_REQ_EAX 0 +#define GHCB_CPUID_REQ_EBX 1 +#define GHCB_CPUID_REQ_ECX 2 +#define GHCB_CPUID_REQ_EDX 3 +#define GHCB_CPUID_REQ(fn, reg) (GHCB_SEV_CPUID_REQ | \ + (((unsigned long)reg & 3) << 30) | \ + (((unsigned long)fn) << 32)) + +#define GHCB_SEV_CPUID_RESP 0x005UL +#define GHCB_SEV_TERMINATE 0x100UL + +#define GHCB_SEV_GHCB_RESP_CODE(v) ((v) & 0xfff) +#define VMGEXIT() { asm volatile("rep; vmmcall\n\r"); } + +static inline u64 lower_bits(u64 val, unsigned int bits) +{ + u64 mask = (1ULL << bits) - 1; + + return (val & mask); +} + +static inline u64 copy_lower_bits(u64 out, u64 in, unsigned int bits) +{ + u64 mask = (1ULL << bits) - 1; + + out &= ~mask; + out |= lower_bits(in, bits); + + return out; +} + +#endif diff --git a/arch/x86/include/asm/trap_defs.h b/arch/x86/include/asm/trap_defs.h index 488f82ac36da..af45d65f0458 100644 --- a/arch/x86/include/asm/trap_defs.h +++ b/arch/x86/include/asm/trap_defs.h @@ -24,6 +24,7 @@ enum { X86_TRAP_AC, /* 17, Alignment Check */ X86_TRAP_MC, /* 18, Machine Check */ X86_TRAP_XF, /* 19, SIMD Floating-Point Exception */ + X86_TRAP_VC = 29, /* 29, VMM Communication Exception */ X86_TRAP_IRET = 32, /* 32, IRET Exception */ }; diff --git a/arch/x86/kernel/sev-es-shared.c b/arch/x86/kernel/sev-es-shared.c new file mode 100644 index 000000000000..7edf2dfac71f --- /dev/null +++ b/arch/x86/kernel/sev-es-shared.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Encrypted Register State Support + * + * Author: Joerg Roedel <jroedel@suse.de> + * + * This file is not compiled stand-alone. It contains code shared + * between the pre-decompression boot code and the running Linux kernel + * and is included directly into both code-bases. + */ + +/* + * Boot VC Handler - This is the first VC handler during boot, there is no GHCB + * page yet, so it only supports the MSR based communication with the + * hypervisor and only the CPUID exit-code. + */ +void __init no_ghcb_vc_handler(struct pt_regs *regs) +{ + unsigned int fn = lower_bits(regs->ax, 32); + unsigned long exit_code = regs->orig_ax; + unsigned long val; + + /* Only CPUID is supported via MSR protocol */ + if (exit_code != SVM_EXIT_CPUID) + goto fail; + + write_ghcb_msr(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_EAX)); + VMGEXIT(); + val = read_ghcb_msr(); + if (GHCB_SEV_GHCB_RESP_CODE(val) != GHCB_SEV_CPUID_RESP) + goto fail; + regs->ax = copy_lower_bits(regs->ax, val >> 32, 32); + + write_ghcb_msr(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_EBX)); + VMGEXIT(); + val = read_ghcb_msr(); + if (GHCB_SEV_GHCB_RESP_CODE(val) != GHCB_SEV_CPUID_RESP) + goto fail; + regs->bx = copy_lower_bits(regs->bx, val >> 32, 32); + + write_ghcb_msr(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_ECX)); + VMGEXIT(); + val = read_ghcb_msr(); + if (GHCB_SEV_GHCB_RESP_CODE(val) != GHCB_SEV_CPUID_RESP) + goto fail; + regs->cx = copy_lower_bits(regs->cx, val >> 32, 32); + + write_ghcb_msr(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_EDX)); + VMGEXIT(); + val = read_ghcb_msr(); + if (GHCB_SEV_GHCB_RESP_CODE(val) != GHCB_SEV_CPUID_RESP) + goto fail; + regs->dx = copy_lower_bits(regs->dx, val >> 32, 32); + + regs->ip += 2; + + return; + +fail: + write_ghcb_msr(GHCB_SEV_TERMINATE); + VMGEXIT(); + + /* Shouldn't get here - if we do halt the machine */ + while (true) + asm volatile("hlt\n"); +}