x86/sev: Carve out the SVSM code into a separate compilation unit

Move the SVSM-related machinery into a separate compilation unit in
order to keep sev/core.c slim and "on-topic".

No functional changes.

Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://patch.msgid.link/20251204124809.31783-4-bp@kernel.org
This commit is contained in:
Borislav Petkov (AMD) 2025-12-04 13:48:08 +01:00
parent f01c6489ad
commit e21279b73e
4 changed files with 392 additions and 378 deletions

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += core.o noinstr.o vc-handle.o
obj-y += core.o noinstr.o vc-handle.o svsm.o
# Clang 14 and older may fail to respect __no_sanitize_undefined when inlining
UBSAN_SANITIZE_noinstr.o := n

View File

@ -55,40 +55,6 @@ SYM_PIC_ALIAS(sev_hv_features);
u64 sev_secrets_pa __ro_after_init;
SYM_PIC_ALIAS(sev_secrets_pa);
/* For early boot SVSM communication */
struct svsm_ca boot_svsm_ca_page __aligned(PAGE_SIZE);
SYM_PIC_ALIAS(boot_svsm_ca_page);
/*
* SVSM related information:
* During boot, the page tables are set up as identity mapped and later
* changed to use kernel virtual addresses. Maintain separate virtual and
* physical addresses for the CAA to allow SVSM functions to be used during
* early boot, both with identity mapped virtual addresses and proper kernel
* virtual addresses.
*/
u64 boot_svsm_caa_pa __ro_after_init;
SYM_PIC_ALIAS(boot_svsm_caa_pa);
DEFINE_PER_CPU(struct svsm_ca *, svsm_caa);
DEFINE_PER_CPU(u64, svsm_caa_pa);
static inline struct svsm_ca *svsm_get_caa(void)
{
if (sev_cfg.use_cas)
return this_cpu_read(svsm_caa);
else
return rip_rel_ptr(&boot_svsm_ca_page);
}
static inline u64 svsm_get_caa_pa(void)
{
if (sev_cfg.use_cas)
return this_cpu_read(svsm_caa_pa);
else
return boot_svsm_caa_pa;
}
/* AP INIT values as documented in the APM2 section "Processor Initialization State" */
#define AP_INIT_CS_LIMIT 0xffff
#define AP_INIT_DS_LIMIT 0xffff
@ -218,95 +184,6 @@ static u64 __init get_jump_table_addr(void)
return ret;
}
static int svsm_perform_ghcb_protocol(struct ghcb *ghcb, struct svsm_call *call)
{
struct es_em_ctxt ctxt;
u8 pending = 0;
vc_ghcb_invalidate(ghcb);
/*
* Fill in protocol and format specifiers. This can be called very early
* in the boot, so use rip-relative references as needed.
*/
ghcb->protocol_version = ghcb_version;
ghcb->ghcb_usage = GHCB_DEFAULT_USAGE;
ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_SNP_RUN_VMPL);
ghcb_set_sw_exit_info_1(ghcb, 0);
ghcb_set_sw_exit_info_2(ghcb, 0);
sev_es_wr_ghcb_msr(__pa(ghcb));
svsm_issue_call(call, &pending);
if (pending)
return -EINVAL;
switch (verify_exception_info(ghcb, &ctxt)) {
case ES_OK:
break;
case ES_EXCEPTION:
vc_forward_exception(&ctxt);
fallthrough;
default:
return -EINVAL;
}
return svsm_process_result_codes(call);
}
static int svsm_perform_call_protocol(struct svsm_call *call)
{
struct ghcb_state state;
unsigned long flags;
struct ghcb *ghcb;
int ret;
flags = native_local_irq_save();
if (sev_cfg.ghcbs_initialized)
ghcb = __sev_get_ghcb(&state);
else if (boot_ghcb)
ghcb = boot_ghcb;
else
ghcb = NULL;
do {
ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call)
: __pi_svsm_perform_msr_protocol(call);
} while (ret == -EAGAIN);
if (sev_cfg.ghcbs_initialized)
__sev_put_ghcb(&state);
native_local_irq_restore(flags);
return ret;
}
static inline void __pval_terminate(u64 pfn, bool action, unsigned int page_size,
int ret, u64 svsm_ret)
{
WARN(1, "PVALIDATE failure: pfn: 0x%llx, action: %u, size: %u, ret: %d, svsm_ret: 0x%llx\n",
pfn, action, page_size, ret, svsm_ret);
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
}
static void svsm_pval_terminate(struct svsm_pvalidate_call *pc, int ret, u64 svsm_ret)
{
unsigned int page_size;
bool action;
u64 pfn;
pfn = pc->entry[pc->cur_index].pfn;
action = pc->entry[pc->cur_index].action;
page_size = pc->entry[pc->cur_index].page_size;
__pval_terminate(pfn, action, page_size, ret, svsm_ret);
}
static void pval_pages(struct snp_psc_desc *desc)
{
struct psc_entry *e;
@ -343,152 +220,6 @@ static void pval_pages(struct snp_psc_desc *desc)
}
}
static u64 svsm_build_ca_from_pfn_range(u64 pfn, u64 pfn_end, bool action,
struct svsm_pvalidate_call *pc)
{
struct svsm_pvalidate_entry *pe;
/* Nothing in the CA yet */
pc->num_entries = 0;
pc->cur_index = 0;
pe = &pc->entry[0];
while (pfn < pfn_end) {
pe->page_size = RMP_PG_SIZE_4K;
pe->action = action;
pe->ignore_cf = 0;
pe->rsvd = 0;
pe->pfn = pfn;
pe++;
pfn++;
pc->num_entries++;
if (pc->num_entries == SVSM_PVALIDATE_MAX_COUNT)
break;
}
return pfn;
}
static int svsm_build_ca_from_psc_desc(struct snp_psc_desc *desc, unsigned int desc_entry,
struct svsm_pvalidate_call *pc)
{
struct svsm_pvalidate_entry *pe;
struct psc_entry *e;
/* Nothing in the CA yet */
pc->num_entries = 0;
pc->cur_index = 0;
pe = &pc->entry[0];
e = &desc->entries[desc_entry];
while (desc_entry <= desc->hdr.end_entry) {
pe->page_size = e->pagesize ? RMP_PG_SIZE_2M : RMP_PG_SIZE_4K;
pe->action = e->operation == SNP_PAGE_STATE_PRIVATE;
pe->ignore_cf = 0;
pe->rsvd = 0;
pe->pfn = e->gfn;
pe++;
e++;
desc_entry++;
pc->num_entries++;
if (pc->num_entries == SVSM_PVALIDATE_MAX_COUNT)
break;
}
return desc_entry;
}
static void svsm_pval_pages(struct snp_psc_desc *desc)
{
struct svsm_pvalidate_entry pv_4k[VMGEXIT_PSC_MAX_ENTRY];
unsigned int i, pv_4k_count = 0;
struct svsm_pvalidate_call *pc;
struct svsm_call call = {};
unsigned long flags;
bool action;
u64 pc_pa;
int ret;
/*
* This can be called very early in the boot, use native functions in
* order to avoid paravirt issues.
*/
flags = native_local_irq_save();
/*
* The SVSM calling area (CA) can support processing 510 entries at a
* time. Loop through the Page State Change descriptor until the CA is
* full or the last entry in the descriptor is reached, at which time
* the SVSM is invoked. This repeats until all entries in the descriptor
* are processed.
*/
call.caa = svsm_get_caa();
pc = (struct svsm_pvalidate_call *)call.caa->svsm_buffer;
pc_pa = svsm_get_caa_pa() + offsetof(struct svsm_ca, svsm_buffer);
/* Protocol 0, Call ID 1 */
call.rax = SVSM_CORE_CALL(SVSM_CORE_PVALIDATE);
call.rcx = pc_pa;
for (i = 0; i <= desc->hdr.end_entry;) {
i = svsm_build_ca_from_psc_desc(desc, i, pc);
do {
ret = svsm_perform_call_protocol(&call);
if (!ret)
continue;
/*
* Check if the entry failed because of an RMP mismatch (a
* PVALIDATE at 2M was requested, but the page is mapped in
* the RMP as 4K).
*/
if (call.rax_out == SVSM_PVALIDATE_FAIL_SIZEMISMATCH &&
pc->entry[pc->cur_index].page_size == RMP_PG_SIZE_2M) {
/* Save this entry for post-processing at 4K */
pv_4k[pv_4k_count++] = pc->entry[pc->cur_index];
/* Skip to the next one unless at the end of the list */
pc->cur_index++;
if (pc->cur_index < pc->num_entries)
ret = -EAGAIN;
else
ret = 0;
}
} while (ret == -EAGAIN);
if (ret)
svsm_pval_terminate(pc, ret, call.rax_out);
}
/* Process any entries that failed to be validated at 2M and validate them at 4K */
for (i = 0; i < pv_4k_count; i++) {
u64 pfn, pfn_end;
action = pv_4k[i].action;
pfn = pv_4k[i].pfn;
pfn_end = pfn + 512;
while (pfn < pfn_end) {
pfn = svsm_build_ca_from_pfn_range(pfn, pfn_end, action, pc);
ret = svsm_perform_call_protocol(&call);
if (ret)
svsm_pval_terminate(pc, ret, call.rax_out);
}
}
native_local_irq_restore(flags);
}
static void pvalidate_pages(struct snp_psc_desc *desc)
{
struct psc_entry *e;
@ -1589,56 +1320,6 @@ static int __init report_snp_info(void)
}
arch_initcall(report_snp_info);
static void update_attest_input(struct svsm_call *call, struct svsm_attest_call *input)
{
/* If (new) lengths have been returned, propagate them up */
if (call->rcx_out != call->rcx)
input->manifest_buf.len = call->rcx_out;
if (call->rdx_out != call->rdx)
input->certificates_buf.len = call->rdx_out;
if (call->r8_out != call->r8)
input->report_buf.len = call->r8_out;
}
int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call,
struct svsm_attest_call *input)
{
struct svsm_attest_call *ac;
unsigned long flags;
u64 attest_call_pa;
int ret;
if (!snp_vmpl)
return -EINVAL;
local_irq_save(flags);
call->caa = svsm_get_caa();
ac = (struct svsm_attest_call *)call->caa->svsm_buffer;
attest_call_pa = svsm_get_caa_pa() + offsetof(struct svsm_ca, svsm_buffer);
*ac = *input;
/*
* Set input registers for the request and set RDX and R8 to known
* values in order to detect length values being returned in them.
*/
call->rax = call_id;
call->rcx = attest_call_pa;
call->rdx = -1;
call->r8 = -1;
ret = svsm_perform_call_protocol(call);
update_attest_input(call, input);
local_irq_restore(flags);
return ret;
}
EXPORT_SYMBOL_GPL(snp_issue_svsm_attest_req);
static int snp_issue_guest_request(struct snp_guest_req *req)
{
struct snp_req_data *input = &req->input;
@ -1703,64 +1384,6 @@ static int snp_issue_guest_request(struct snp_guest_req *req)
return ret;
}
/**
* snp_svsm_vtpm_probe() - Probe if SVSM provides a vTPM device
*
* Check that there is SVSM and that it supports at least TPM_SEND_COMMAND
* which is the only request used so far.
*
* Return: true if the platform provides a vTPM SVSM device, false otherwise.
*/
static bool snp_svsm_vtpm_probe(void)
{
struct svsm_call call = {};
/* The vTPM device is available only if a SVSM is present */
if (!snp_vmpl)
return false;
call.caa = svsm_get_caa();
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_QUERY);
if (svsm_perform_call_protocol(&call))
return false;
/* Check platform commands contains TPM_SEND_COMMAND - platform command 8 */
return call.rcx_out & BIT_ULL(8);
}
/**
* snp_svsm_vtpm_send_command() - Execute a vTPM operation on SVSM
* @buffer: A buffer used to both send the command and receive the response.
*
* Execute a SVSM_VTPM_CMD call as defined by
* "Secure VM Service Module for SEV-SNP Guests" Publication # 58019 Revision: 1.00
*
* All command request/response buffers have a common structure as specified by
* the following table:
* Byte Size     In/Out    Description
* Offset    (Bytes)
* 0x000     4          In        Platform command
 *                         Out       Platform command response size
*
* Each command can build upon this common request/response structure to create
* a structure specific to the command. See include/linux/tpm_svsm.h for more
* details.
*
* Return: 0 on success, -errno on failure
*/
int snp_svsm_vtpm_send_command(u8 *buffer)
{
struct svsm_call call = {};
call.caa = svsm_get_caa();
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_CMD);
call.rcx = __pa(buffer);
return svsm_perform_call_protocol(&call);
}
EXPORT_SYMBOL_GPL(snp_svsm_vtpm_send_command);
static struct platform_device sev_guest_device = {
.name = "sev-guest",
.id = -1,

View File

@ -66,6 +66,9 @@ extern u64 boot_svsm_caa_pa;
enum es_result verify_exception_info(struct ghcb *ghcb, struct es_em_ctxt *ctxt);
void vc_forward_exception(struct es_em_ctxt *ctxt);
void svsm_pval_pages(struct snp_psc_desc *desc);
int svsm_perform_call_protocol(struct svsm_call *call);
bool snp_svsm_vtpm_probe(void);
static inline u64 sev_es_rd_ghcb_msr(void)
{
@ -87,4 +90,30 @@ enum es_result sev_es_ghcb_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt
u64 get_hv_features(void);
const struct snp_cpuid_table *snp_cpuid_get_table(void);
static inline struct svsm_ca *svsm_get_caa(void)
{
if (sev_cfg.use_cas)
return this_cpu_read(svsm_caa);
else
return rip_rel_ptr(&boot_svsm_ca_page);
}
static inline u64 svsm_get_caa_pa(void)
{
if (sev_cfg.use_cas)
return this_cpu_read(svsm_caa_pa);
else
return boot_svsm_caa_pa;
}
static inline void __pval_terminate(u64 pfn, bool action, unsigned int page_size,
int ret, u64 svsm_ret)
{
WARN(1, "PVALIDATE failure: pfn: 0x%llx, action: %u, size: %u, ret: %d, svsm_ret: 0x%llx\n",
pfn, action, page_size, ret, svsm_ret);
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
}
#endif /* __X86_COCO_SEV_INTERNAL_H__ */

362
arch/x86/coco/sev/svsm.c Normal file
View File

@ -0,0 +1,362 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SVSM support code
*/
#include <linux/types.h>
#include <asm/sev.h>
#include "internal.h"
/* For early boot SVSM communication */
struct svsm_ca boot_svsm_ca_page __aligned(PAGE_SIZE);
SYM_PIC_ALIAS(boot_svsm_ca_page);
/*
* SVSM related information:
* During boot, the page tables are set up as identity mapped and later
* changed to use kernel virtual addresses. Maintain separate virtual and
* physical addresses for the CAA to allow SVSM functions to be used during
* early boot, both with identity mapped virtual addresses and proper kernel
* virtual addresses.
*/
u64 boot_svsm_caa_pa __ro_after_init;
SYM_PIC_ALIAS(boot_svsm_caa_pa);
DEFINE_PER_CPU(struct svsm_ca *, svsm_caa);
DEFINE_PER_CPU(u64, svsm_caa_pa);
static int svsm_perform_ghcb_protocol(struct ghcb *ghcb, struct svsm_call *call)
{
struct es_em_ctxt ctxt;
u8 pending = 0;
vc_ghcb_invalidate(ghcb);
/*
* Fill in protocol and format specifiers. This can be called very early
* in the boot, so use rip-relative references as needed.
*/
ghcb->protocol_version = ghcb_version;
ghcb->ghcb_usage = GHCB_DEFAULT_USAGE;
ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_SNP_RUN_VMPL);
ghcb_set_sw_exit_info_1(ghcb, 0);
ghcb_set_sw_exit_info_2(ghcb, 0);
sev_es_wr_ghcb_msr(__pa(ghcb));
svsm_issue_call(call, &pending);
if (pending)
return -EINVAL;
switch (verify_exception_info(ghcb, &ctxt)) {
case ES_OK:
break;
case ES_EXCEPTION:
vc_forward_exception(&ctxt);
fallthrough;
default:
return -EINVAL;
}
return svsm_process_result_codes(call);
}
int svsm_perform_call_protocol(struct svsm_call *call)
{
struct ghcb_state state;
unsigned long flags;
struct ghcb *ghcb;
int ret;
flags = native_local_irq_save();
if (sev_cfg.ghcbs_initialized)
ghcb = __sev_get_ghcb(&state);
else if (boot_ghcb)
ghcb = boot_ghcb;
else
ghcb = NULL;
do {
ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call)
: __pi_svsm_perform_msr_protocol(call);
} while (ret == -EAGAIN);
if (sev_cfg.ghcbs_initialized)
__sev_put_ghcb(&state);
native_local_irq_restore(flags);
return ret;
}
static u64 svsm_build_ca_from_pfn_range(u64 pfn, u64 pfn_end, bool action,
struct svsm_pvalidate_call *pc)
{
struct svsm_pvalidate_entry *pe;
/* Nothing in the CA yet */
pc->num_entries = 0;
pc->cur_index = 0;
pe = &pc->entry[0];
while (pfn < pfn_end) {
pe->page_size = RMP_PG_SIZE_4K;
pe->action = action;
pe->ignore_cf = 0;
pe->rsvd = 0;
pe->pfn = pfn;
pe++;
pfn++;
pc->num_entries++;
if (pc->num_entries == SVSM_PVALIDATE_MAX_COUNT)
break;
}
return pfn;
}
static int svsm_build_ca_from_psc_desc(struct snp_psc_desc *desc, unsigned int desc_entry,
struct svsm_pvalidate_call *pc)
{
struct svsm_pvalidate_entry *pe;
struct psc_entry *e;
/* Nothing in the CA yet */
pc->num_entries = 0;
pc->cur_index = 0;
pe = &pc->entry[0];
e = &desc->entries[desc_entry];
while (desc_entry <= desc->hdr.end_entry) {
pe->page_size = e->pagesize ? RMP_PG_SIZE_2M : RMP_PG_SIZE_4K;
pe->action = e->operation == SNP_PAGE_STATE_PRIVATE;
pe->ignore_cf = 0;
pe->rsvd = 0;
pe->pfn = e->gfn;
pe++;
e++;
desc_entry++;
pc->num_entries++;
if (pc->num_entries == SVSM_PVALIDATE_MAX_COUNT)
break;
}
return desc_entry;
}
static void svsm_pval_terminate(struct svsm_pvalidate_call *pc, int ret, u64 svsm_ret)
{
unsigned int page_size;
bool action;
u64 pfn;
pfn = pc->entry[pc->cur_index].pfn;
action = pc->entry[pc->cur_index].action;
page_size = pc->entry[pc->cur_index].page_size;
__pval_terminate(pfn, action, page_size, ret, svsm_ret);
}
void svsm_pval_pages(struct snp_psc_desc *desc)
{
struct svsm_pvalidate_entry pv_4k[VMGEXIT_PSC_MAX_ENTRY];
unsigned int i, pv_4k_count = 0;
struct svsm_pvalidate_call *pc;
struct svsm_call call = {};
unsigned long flags;
bool action;
u64 pc_pa;
int ret;
/*
* This can be called very early in the boot, use native functions in
* order to avoid paravirt issues.
*/
flags = native_local_irq_save();
/*
* The SVSM calling area (CA) can support processing 510 entries at a
* time. Loop through the Page State Change descriptor until the CA is
* full or the last entry in the descriptor is reached, at which time
* the SVSM is invoked. This repeats until all entries in the descriptor
* are processed.
*/
call.caa = svsm_get_caa();
pc = (struct svsm_pvalidate_call *)call.caa->svsm_buffer;
pc_pa = svsm_get_caa_pa() + offsetof(struct svsm_ca, svsm_buffer);
/* Protocol 0, Call ID 1 */
call.rax = SVSM_CORE_CALL(SVSM_CORE_PVALIDATE);
call.rcx = pc_pa;
for (i = 0; i <= desc->hdr.end_entry;) {
i = svsm_build_ca_from_psc_desc(desc, i, pc);
do {
ret = svsm_perform_call_protocol(&call);
if (!ret)
continue;
/*
* Check if the entry failed because of an RMP mismatch (a
* PVALIDATE at 2M was requested, but the page is mapped in
* the RMP as 4K).
*/
if (call.rax_out == SVSM_PVALIDATE_FAIL_SIZEMISMATCH &&
pc->entry[pc->cur_index].page_size == RMP_PG_SIZE_2M) {
/* Save this entry for post-processing at 4K */
pv_4k[pv_4k_count++] = pc->entry[pc->cur_index];
/* Skip to the next one unless at the end of the list */
pc->cur_index++;
if (pc->cur_index < pc->num_entries)
ret = -EAGAIN;
else
ret = 0;
}
} while (ret == -EAGAIN);
if (ret)
svsm_pval_terminate(pc, ret, call.rax_out);
}
/* Process any entries that failed to be validated at 2M and validate them at 4K */
for (i = 0; i < pv_4k_count; i++) {
u64 pfn, pfn_end;
action = pv_4k[i].action;
pfn = pv_4k[i].pfn;
pfn_end = pfn + 512;
while (pfn < pfn_end) {
pfn = svsm_build_ca_from_pfn_range(pfn, pfn_end, action, pc);
ret = svsm_perform_call_protocol(&call);
if (ret)
svsm_pval_terminate(pc, ret, call.rax_out);
}
}
native_local_irq_restore(flags);
}
static void update_attest_input(struct svsm_call *call, struct svsm_attest_call *input)
{
/* If (new) lengths have been returned, propagate them up */
if (call->rcx_out != call->rcx)
input->manifest_buf.len = call->rcx_out;
if (call->rdx_out != call->rdx)
input->certificates_buf.len = call->rdx_out;
if (call->r8_out != call->r8)
input->report_buf.len = call->r8_out;
}
int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call,
struct svsm_attest_call *input)
{
struct svsm_attest_call *ac;
unsigned long flags;
u64 attest_call_pa;
int ret;
if (!snp_vmpl)
return -EINVAL;
local_irq_save(flags);
call->caa = svsm_get_caa();
ac = (struct svsm_attest_call *)call->caa->svsm_buffer;
attest_call_pa = svsm_get_caa_pa() + offsetof(struct svsm_ca, svsm_buffer);
*ac = *input;
/*
* Set input registers for the request and set RDX and R8 to known
* values in order to detect length values being returned in them.
*/
call->rax = call_id;
call->rcx = attest_call_pa;
call->rdx = -1;
call->r8 = -1;
ret = svsm_perform_call_protocol(call);
update_attest_input(call, input);
local_irq_restore(flags);
return ret;
}
EXPORT_SYMBOL_GPL(snp_issue_svsm_attest_req);
/**
* snp_svsm_vtpm_send_command() - Execute a vTPM operation on SVSM
* @buffer: A buffer used to both send the command and receive the response.
*
* Execute a SVSM_VTPM_CMD call as defined by
* "Secure VM Service Module for SEV-SNP Guests" Publication # 58019 Revision: 1.00
*
* All command request/response buffers have a common structure as specified by
* the following table:
* Byte Size     In/Out    Description
* Offset    (Bytes)
* 0x000     4          In        Platform command
 *                         Out       Platform command response size
*
* Each command can build upon this common request/response structure to create
* a structure specific to the command. See include/linux/tpm_svsm.h for more
* details.
*
* Return: 0 on success, -errno on failure
*/
int snp_svsm_vtpm_send_command(u8 *buffer)
{
struct svsm_call call = {};
call.caa = svsm_get_caa();
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_CMD);
call.rcx = __pa(buffer);
return svsm_perform_call_protocol(&call);
}
EXPORT_SYMBOL_GPL(snp_svsm_vtpm_send_command);
/**
* snp_svsm_vtpm_probe() - Probe if SVSM provides a vTPM device
*
* Check that there is SVSM and that it supports at least TPM_SEND_COMMAND
* which is the only request used so far.
*
* Return: true if the platform provides a vTPM SVSM device, false otherwise.
*/
bool snp_svsm_vtpm_probe(void)
{
struct svsm_call call = {};
/* The vTPM device is available only if a SVSM is present */
if (!snp_vmpl)
return false;
call.caa = svsm_get_caa();
call.rax = SVSM_VTPM_CALL(SVSM_VTPM_QUERY);
if (svsm_perform_call_protocol(&call))
return false;
/* Check platform commands contains TPM_SEND_COMMAND - platform command 8 */
return call.rcx_out & BIT_ULL(8);
}