Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/tdx: Add MSR support for TDX guests

Use hypercall to emulate MSR read/write for the TDX platform.

There are two viable approaches for doing MSRs in a TD guest:

1. Execute the RDMSR/WRMSR instructions like most VMs and bare metal
do. Some will succeed, others will cause a #VE. All of those that
cause a #VE will be handled with a TDCALL.
2. Use paravirt infrastructure. The paravirt hook has to keep a list
of which MSRs would cause a #VE and use a TDCALL. All other MSRs
execute RDMSR/WRMSR instructions directly.

The second option can be ruled out because the list of MSRs was
challenging to maintain. That leaves option #1 as the only viable
solution for the minimal TDX support.

Kernel relies on the exception fixup machinery to handle MSR access
errors. #VE handler uses the same exception fixup code as #GP. It
covers MSR accesses along with other types of fixups.

For performance-critical MSR writes (like TSC_DEADLINE), future patches
will replace the WRMSR/#VE sequence with the direct TDCALL.

RDMSR and WRMSR specification details can be found in
Guest-Host-Communication Interface (GHCI) for Intel Trust Domain
Extensions (Intel TDX) specification, sec titled "TDG.VP.
VMCALL<Instruction.RDMSR>" and "TDG.VP.VMCALL<Instruction.WRMSR>".

Co-developed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Reviewed-by: Tony Luck <tony.luck@intel.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20220405232939.73860-10-kirill.shutemov@linux.intel.com

authored by

Kirill A. Shutemov and committed by
Dave Hansen
ae87f609 bfe6ed0c

+42
+42
arch/x86/coco/tdx/tdx.c
··· 142 142 WARN_ONCE(1, "HLT instruction emulation failed\n"); 143 143 } 144 144 145 + static bool read_msr(struct pt_regs *regs) 146 + { 147 + struct tdx_hypercall_args args = { 148 + .r10 = TDX_HYPERCALL_STANDARD, 149 + .r11 = hcall_func(EXIT_REASON_MSR_READ), 150 + .r12 = regs->cx, 151 + }; 152 + 153 + /* 154 + * Emulate the MSR read via hypercall. More info about ABI 155 + * can be found in TDX Guest-Host-Communication Interface 156 + * (GHCI), section titled "TDG.VP.VMCALL<Instruction.RDMSR>". 157 + */ 158 + if (__tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT)) 159 + return false; 160 + 161 + regs->ax = lower_32_bits(args.r11); 162 + regs->dx = upper_32_bits(args.r11); 163 + return true; 164 + } 165 + 166 + static bool write_msr(struct pt_regs *regs) 167 + { 168 + struct tdx_hypercall_args args = { 169 + .r10 = TDX_HYPERCALL_STANDARD, 170 + .r11 = hcall_func(EXIT_REASON_MSR_WRITE), 171 + .r12 = regs->cx, 172 + .r13 = (u64)regs->dx << 32 | regs->ax, 173 + }; 174 + 175 + /* 176 + * Emulate the MSR write via hypercall. More info about ABI 177 + * can be found in TDX Guest-Host-Communication Interface 178 + * (GHCI) section titled "TDG.VP.VMCALL<Instruction.WRMSR>". 179 + */ 180 + return !__tdx_hypercall(&args, 0); 181 + } 182 + 145 183 void tdx_get_ve_info(struct ve_info *ve) 146 184 { 147 185 struct tdx_module_output out; ··· 216 178 switch (ve->exit_reason) { 217 179 case EXIT_REASON_HLT: 218 180 return handle_halt(); 181 + case EXIT_REASON_MSR_READ: 182 + return read_msr(regs); 183 + case EXIT_REASON_MSR_WRITE: 184 + return write_msr(regs); 219 185 default: 220 186 pr_warn("Unexpected #VE: %lld\n", ve->exit_reason); 221 187 return false;