mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-27 06:33:08 -04: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)))
|
if (copy_from_user(¶ms, u64_to_user_ptr(argp->data), sizeof(params)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
sev->policy = params.policy;
|
||||||
|
|
||||||
memset(&start, 0, sizeof(start));
|
memset(&start, 0, sizeof(start));
|
||||||
|
|
||||||
dh_blob = NULL;
|
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)
|
if (params.policy & SNP_POLICY_MASK_SINGLE_SOCKET)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
sev->policy = params.policy;
|
||||||
|
|
||||||
sev->snp_context = snp_context_create(kvm, argp);
|
sev->snp_context = snp_context_create(kvm, argp);
|
||||||
if (!sev->snp_context)
|
if (!sev->snp_context)
|
||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
@@ -4922,3 +4926,97 @@ int sev_private_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn)
|
|||||||
|
|
||||||
return level;
|
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_logical_id:", control->avic_logical_id);
|
||||||
pr_err("%-20s%016llx\n", "avic_physical_id:", control->avic_physical_id);
|
pr_err("%-20s%016llx\n", "avic_physical_id:", control->avic_physical_id);
|
||||||
pr_err("%-20s%016llx\n", "vmsa_pa:", control->vmsa_pa);
|
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("VMCB State Save Area:\n");
|
||||||
pr_err("%-5s s: %04x a: %04x l: %08x b: %016llx\n",
|
pr_err("%-5s s: %04x a: %04x l: %08x b: %016llx\n",
|
||||||
"es:",
|
"es:",
|
||||||
@@ -3512,6 +3521,10 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
|
|||||||
pr_err("%-15s %016llx %-13s %016llx\n",
|
pr_err("%-15s %016llx %-13s %016llx\n",
|
||||||
"excp_from:", save->last_excp_from,
|
"excp_from:", save->last_excp_from,
|
||||||
"excp_to:", save->last_excp_to);
|
"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)
|
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 asid; /* ASID used for this guest */
|
||||||
unsigned int handle; /* SEV firmware handle */
|
unsigned int handle; /* SEV firmware handle */
|
||||||
int fd; /* SEV device fd */
|
int fd; /* SEV device fd */
|
||||||
|
unsigned long policy;
|
||||||
unsigned long pages_locked; /* Number of pages locked */
|
unsigned long pages_locked; /* Number of pages locked */
|
||||||
struct list_head regions_list; /* List of registered regions */
|
struct list_head regions_list; /* List of registered regions */
|
||||||
u64 ap_jump_table; /* SEV-ES AP Jump Table address */
|
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 */
|
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_svm {
|
||||||
struct kvm kvm;
|
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);
|
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);
|
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);
|
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
|
#else
|
||||||
static inline struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)
|
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;
|
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
|
#endif
|
||||||
|
|
||||||
/* vmenter.S */
|
/* vmenter.S */
|
||||||
|
|||||||
Reference in New Issue
Block a user