mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
KVM: SVM: Decrypt SEV VMSA in dump_vmcb() if debugging is enabled
An SEV-ES/SEV-SNP VM save area (VMSA) can be decrypted if the guest policy allows debugging. Update the dump_vmcb() routine to output some of the SEV VMSA contents if possible. This can be useful for debug purposes. Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Acked-by: Borislav Petkov (AMD) <bp@alien8.de> Tested-by: Kim Phillips <kim.phillips@amd.com> Link: https://lore.kernel.org/r/ea3b852c295b6f4b200925ed6b6e2c90d9475e71.1742477213.git.thomas.lendacky@amd.com Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
committed by
Sean Christopherson
parent
309d28576f
commit
962e2b6152
@@ -560,6 +560,8 @@ static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
|
||||
if (copy_from_user(¶ms, u64_to_user_ptr(argp->data), sizeof(params)))
|
||||
return -EFAULT;
|
||||
|
||||
sev->policy = params.policy;
|
||||
|
||||
memset(&start, 0, sizeof(start));
|
||||
|
||||
dh_blob = NULL;
|
||||
@@ -2199,6 +2201,8 @@ static int snp_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
|
||||
if (params.policy & SNP_POLICY_MASK_SINGLE_SOCKET)
|
||||
return -EINVAL;
|
||||
|
||||
sev->policy = params.policy;
|
||||
|
||||
sev->snp_context = snp_context_create(kvm, argp);
|
||||
if (!sev->snp_context)
|
||||
return -ENOTTY;
|
||||
@@ -4922,3 +4926,97 @@ int sev_private_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn)
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vcpu_svm *svm = to_svm(vcpu);
|
||||
struct vmcb_save_area *vmsa;
|
||||
struct kvm_sev_info *sev;
|
||||
int error = 0;
|
||||
int ret;
|
||||
|
||||
if (!sev_es_guest(vcpu->kvm))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* If the VMSA has not yet been encrypted, return a pointer to the
|
||||
* current un-encrypted VMSA.
|
||||
*/
|
||||
if (!vcpu->arch.guest_state_protected)
|
||||
return (struct vmcb_save_area *)svm->sev_es.vmsa;
|
||||
|
||||
sev = to_kvm_sev_info(vcpu->kvm);
|
||||
|
||||
/* Check if the SEV policy allows debugging */
|
||||
if (sev_snp_guest(vcpu->kvm)) {
|
||||
if (!(sev->policy & SNP_POLICY_DEBUG))
|
||||
return NULL;
|
||||
} else {
|
||||
if (sev->policy & SEV_POLICY_NODBG)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sev_snp_guest(vcpu->kvm)) {
|
||||
struct sev_data_snp_dbg dbg = {0};
|
||||
|
||||
vmsa = snp_alloc_firmware_page(__GFP_ZERO);
|
||||
if (!vmsa)
|
||||
return NULL;
|
||||
|
||||
dbg.gctx_paddr = __psp_pa(sev->snp_context);
|
||||
dbg.src_addr = svm->vmcb->control.vmsa_pa;
|
||||
dbg.dst_addr = __psp_pa(vmsa);
|
||||
|
||||
ret = sev_do_cmd(SEV_CMD_SNP_DBG_DECRYPT, &dbg, &error);
|
||||
|
||||
/*
|
||||
* Return the target page to a hypervisor page no matter what.
|
||||
* If this fails, the page can't be used, so leak it and don't
|
||||
* try to use it.
|
||||
*/
|
||||
if (snp_page_reclaim(vcpu->kvm, PHYS_PFN(__pa(vmsa))))
|
||||
return NULL;
|
||||
|
||||
if (ret) {
|
||||
pr_err("SEV: SNP_DBG_DECRYPT failed ret=%d, fw_error=%d (%#x)\n",
|
||||
ret, error, error);
|
||||
free_page((unsigned long)vmsa);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
struct sev_data_dbg dbg = {0};
|
||||
struct page *vmsa_page;
|
||||
|
||||
vmsa_page = alloc_page(GFP_KERNEL);
|
||||
if (!vmsa_page)
|
||||
return NULL;
|
||||
|
||||
vmsa = page_address(vmsa_page);
|
||||
|
||||
dbg.handle = sev->handle;
|
||||
dbg.src_addr = svm->vmcb->control.vmsa_pa;
|
||||
dbg.dst_addr = __psp_pa(vmsa);
|
||||
dbg.len = PAGE_SIZE;
|
||||
|
||||
ret = sev_do_cmd(SEV_CMD_DBG_DECRYPT, &dbg, &error);
|
||||
if (ret) {
|
||||
pr_err("SEV: SEV_CMD_DBG_DECRYPT failed ret=%d, fw_error=%d (0x%x)\n",
|
||||
ret, error, error);
|
||||
__free_page(vmsa_page);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return vmsa;
|
||||
}
|
||||
|
||||
void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa)
|
||||
{
|
||||
/* If the VMSA has not yet been encrypted, nothing was allocated */
|
||||
if (!vcpu->arch.guest_state_protected || !vmsa)
|
||||
return;
|
||||
|
||||
free_page((unsigned long)vmsa);
|
||||
}
|
||||
|
||||
@@ -3442,6 +3442,15 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
|
||||
pr_err("%-20s%016llx\n", "avic_logical_id:", control->avic_logical_id);
|
||||
pr_err("%-20s%016llx\n", "avic_physical_id:", control->avic_physical_id);
|
||||
pr_err("%-20s%016llx\n", "vmsa_pa:", control->vmsa_pa);
|
||||
|
||||
if (sev_es_guest(vcpu->kvm)) {
|
||||
save = sev_decrypt_vmsa(vcpu);
|
||||
if (!save)
|
||||
goto no_vmsa;
|
||||
|
||||
save01 = save;
|
||||
}
|
||||
|
||||
pr_err("VMCB State Save Area:\n");
|
||||
pr_err("%-5s s: %04x a: %04x l: %08x b: %016llx\n",
|
||||
"es:",
|
||||
@@ -3512,6 +3521,10 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
|
||||
pr_err("%-15s %016llx %-13s %016llx\n",
|
||||
"excp_from:", save->last_excp_from,
|
||||
"excp_to:", save->last_excp_to);
|
||||
|
||||
no_vmsa:
|
||||
if (sev_es_guest(vcpu->kvm))
|
||||
sev_free_decrypted_vmsa(vcpu, save);
|
||||
}
|
||||
|
||||
static bool svm_check_exit_valid(u64 exit_code)
|
||||
|
||||
@@ -98,6 +98,7 @@ struct kvm_sev_info {
|
||||
unsigned int asid; /* ASID used for this guest */
|
||||
unsigned int handle; /* SEV firmware handle */
|
||||
int fd; /* SEV device fd */
|
||||
unsigned long policy;
|
||||
unsigned long pages_locked; /* Number of pages locked */
|
||||
struct list_head regions_list; /* List of registered regions */
|
||||
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
|
||||
@@ -114,6 +115,9 @@ struct kvm_sev_info {
|
||||
struct mutex guest_req_mutex; /* Must acquire before using bounce buffers */
|
||||
};
|
||||
|
||||
#define SEV_POLICY_NODBG BIT_ULL(0)
|
||||
#define SNP_POLICY_DEBUG BIT_ULL(19)
|
||||
|
||||
struct kvm_svm {
|
||||
struct kvm kvm;
|
||||
|
||||
@@ -783,6 +787,8 @@ void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu);
|
||||
int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
|
||||
void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
|
||||
int sev_private_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn);
|
||||
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu);
|
||||
void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa);
|
||||
#else
|
||||
static inline struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)
|
||||
{
|
||||
@@ -814,6 +820,11 @@ static inline int sev_private_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa) {}
|
||||
#endif
|
||||
|
||||
/* vmenter.S */
|
||||
|
||||
Reference in New Issue
Block a user