Skip to content

Commit 6bc6f7d

Browse files
tlendackybp3tk0v
authored andcommitted
x86/sev: Use the GHCB protocol when available for SNP CPUID requests
SNP retrieves the majority of CPUID information from the SNP CPUID page. But there are times when that information needs to be supplemented by the hypervisor, for example, obtaining the initial APIC ID of the vCPU from leaf 1. The current implementation uses the MSR protocol to retrieve the data from the hypervisor, even when a GHCB exists. The problem arises when an NMI arrives on return from the VMGEXIT. The NMI will be immediately serviced and may generate a #VC requiring communication with the hypervisor. Since a GHCB exists in this case, it will be used. As part of using the GHCB, the #VC handler will write the GHCB physical address into the GHCB MSR and the #VC will be handled. When the NMI completes, processing resumes at the site of the VMGEXIT which is expecting to read the GHCB MSR and find a CPUID MSR protocol response. Since the NMI handling overwrote the GHCB MSR response, the guest will see an invalid reply from the hypervisor and self-terminate. Fix this problem by using the GHCB when it is available. Any NMI received is properly handled because the GHCB contents are copied into a backup page and restored on NMI exit, thus preserving the active GHCB request or result. [ bp: Touchups. ] Fixes: ee0bfa0 ("x86/compressed/64: Add support for SEV-SNP CPUID table in #VC handlers") Signed-off-by: Tom Lendacky <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Cc: <[email protected]> Link: https://lore.kernel.org/r/a5856fa1ebe3879de91a8f6298b6bbd901c61881.1690578565.git.thomas.lendacky@amd.com
1 parent 8a749fd commit 6bc6f7d

File tree

1 file changed

+55
-14
lines changed

1 file changed

+55
-14
lines changed

arch/x86/kernel/sev-shared.c

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ static int __sev_cpuid_hv(u32 fn, int reg_idx, u32 *reg)
256256
return 0;
257257
}
258258

259-
static int sev_cpuid_hv(struct cpuid_leaf *leaf)
259+
static int __sev_cpuid_hv_msr(struct cpuid_leaf *leaf)
260260
{
261261
int ret;
262262

@@ -279,6 +279,45 @@ static int sev_cpuid_hv(struct cpuid_leaf *leaf)
279279
return ret;
280280
}
281281

282+
static int __sev_cpuid_hv_ghcb(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf)
283+
{
284+
u32 cr4 = native_read_cr4();
285+
int ret;
286+
287+
ghcb_set_rax(ghcb, leaf->fn);
288+
ghcb_set_rcx(ghcb, leaf->subfn);
289+
290+
if (cr4 & X86_CR4_OSXSAVE)
291+
/* Safe to read xcr0 */
292+
ghcb_set_xcr0(ghcb, xgetbv(XCR_XFEATURE_ENABLED_MASK));
293+
else
294+
/* xgetbv will cause #UD - use reset value for xcr0 */
295+
ghcb_set_xcr0(ghcb, 1);
296+
297+
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_CPUID, 0, 0);
298+
if (ret != ES_OK)
299+
return ret;
300+
301+
if (!(ghcb_rax_is_valid(ghcb) &&
302+
ghcb_rbx_is_valid(ghcb) &&
303+
ghcb_rcx_is_valid(ghcb) &&
304+
ghcb_rdx_is_valid(ghcb)))
305+
return ES_VMM_ERROR;
306+
307+
leaf->eax = ghcb->save.rax;
308+
leaf->ebx = ghcb->save.rbx;
309+
leaf->ecx = ghcb->save.rcx;
310+
leaf->edx = ghcb->save.rdx;
311+
312+
return ES_OK;
313+
}
314+
315+
static int sev_cpuid_hv(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf)
316+
{
317+
return ghcb ? __sev_cpuid_hv_ghcb(ghcb, ctxt, leaf)
318+
: __sev_cpuid_hv_msr(leaf);
319+
}
320+
282321
/*
283322
* This may be called early while still running on the initial identity
284323
* mapping. Use RIP-relative addressing to obtain the correct address
@@ -388,19 +427,20 @@ snp_cpuid_get_validated_func(struct cpuid_leaf *leaf)
388427
return false;
389428
}
390429

391-
static void snp_cpuid_hv(struct cpuid_leaf *leaf)
430+
static void snp_cpuid_hv(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf)
392431
{
393-
if (sev_cpuid_hv(leaf))
432+
if (sev_cpuid_hv(ghcb, ctxt, leaf))
394433
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID_HV);
395434
}
396435

397-
static int snp_cpuid_postprocess(struct cpuid_leaf *leaf)
436+
static int snp_cpuid_postprocess(struct ghcb *ghcb, struct es_em_ctxt *ctxt,
437+
struct cpuid_leaf *leaf)
398438
{
399439
struct cpuid_leaf leaf_hv = *leaf;
400440

401441
switch (leaf->fn) {
402442
case 0x1:
403-
snp_cpuid_hv(&leaf_hv);
443+
snp_cpuid_hv(ghcb, ctxt, &leaf_hv);
404444

405445
/* initial APIC ID */
406446
leaf->ebx = (leaf_hv.ebx & GENMASK(31, 24)) | (leaf->ebx & GENMASK(23, 0));
@@ -419,7 +459,7 @@ static int snp_cpuid_postprocess(struct cpuid_leaf *leaf)
419459
break;
420460
case 0xB:
421461
leaf_hv.subfn = 0;
422-
snp_cpuid_hv(&leaf_hv);
462+
snp_cpuid_hv(ghcb, ctxt, &leaf_hv);
423463

424464
/* extended APIC ID */
425465
leaf->edx = leaf_hv.edx;
@@ -467,7 +507,7 @@ static int snp_cpuid_postprocess(struct cpuid_leaf *leaf)
467507
}
468508
break;
469509
case 0x8000001E:
470-
snp_cpuid_hv(&leaf_hv);
510+
snp_cpuid_hv(ghcb, ctxt, &leaf_hv);
471511

472512
/* extended APIC ID */
473513
leaf->eax = leaf_hv.eax;
@@ -488,7 +528,7 @@ static int snp_cpuid_postprocess(struct cpuid_leaf *leaf)
488528
* Returns -EOPNOTSUPP if feature not enabled. Any other non-zero return value
489529
* should be treated as fatal by caller.
490530
*/
491-
static int snp_cpuid(struct cpuid_leaf *leaf)
531+
static int snp_cpuid(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf)
492532
{
493533
const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table();
494534

@@ -522,7 +562,7 @@ static int snp_cpuid(struct cpuid_leaf *leaf)
522562
return 0;
523563
}
524564

525-
return snp_cpuid_postprocess(leaf);
565+
return snp_cpuid_postprocess(ghcb, ctxt, leaf);
526566
}
527567

528568
/*
@@ -544,14 +584,14 @@ void __init do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code)
544584
leaf.fn = fn;
545585
leaf.subfn = subfn;
546586

547-
ret = snp_cpuid(&leaf);
587+
ret = snp_cpuid(NULL, NULL, &leaf);
548588
if (!ret)
549589
goto cpuid_done;
550590

551591
if (ret != -EOPNOTSUPP)
552592
goto fail;
553593

554-
if (sev_cpuid_hv(&leaf))
594+
if (__sev_cpuid_hv_msr(&leaf))
555595
goto fail;
556596

557597
cpuid_done:
@@ -848,14 +888,15 @@ static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
848888
return ret;
849889
}
850890

851-
static int vc_handle_cpuid_snp(struct pt_regs *regs)
891+
static int vc_handle_cpuid_snp(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
852892
{
893+
struct pt_regs *regs = ctxt->regs;
853894
struct cpuid_leaf leaf;
854895
int ret;
855896

856897
leaf.fn = regs->ax;
857898
leaf.subfn = regs->cx;
858-
ret = snp_cpuid(&leaf);
899+
ret = snp_cpuid(ghcb, ctxt, &leaf);
859900
if (!ret) {
860901
regs->ax = leaf.eax;
861902
regs->bx = leaf.ebx;
@@ -874,7 +915,7 @@ static enum es_result vc_handle_cpuid(struct ghcb *ghcb,
874915
enum es_result ret;
875916
int snp_cpuid_ret;
876917

877-
snp_cpuid_ret = vc_handle_cpuid_snp(regs);
918+
snp_cpuid_ret = vc_handle_cpuid_snp(ghcb, ctxt);
878919
if (!snp_cpuid_ret)
879920
return ES_OK;
880921
if (snp_cpuid_ret != -EOPNOTSUPP)

0 commit comments

Comments
 (0)