mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 16:56:26 +00:00
- Do the proper memory conversion of guest memory in order to be able to kexec
kernels in SNP guests along with other adjustments and cleanups to that effect - Start converting and moving functionality from the sev-guest driver into core code with the purpose of supporting the secure TSC SNP feature where the hypervisor cannot influence the TSC exposed to the guest anymore - Add a "nosnp" cmdline option in order to be able to disable SNP support in the hypervisor and thus free-up resources which are not going to be used - Cleanups -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmc7ZToACgkQEsHwGGHe VUp61hAArA8taJaGUSdoe3sN60yRWCTe30QiDLvUrDGqmPHbBnDpdYsoaZujkQMI 334piSWWu/pB6meO93uwv8X/ZO0ryOw46RK3szTz/RhBB5pTO3NbAj1zMF5q2KUy a+SYbZffV+qBUEpGujGrqrwT7X3U70yCKJFaZQOGvyYFzo+kyx6euqlYP+StOD+D ph7SDrXv0N0uU/2OiwCzF0cKvAuNHG2Cfn3kqSKvcZ+NWF3BKmw1IkgFA9f05P+j mOkc+1jCbi26b94MSJHSL33iRtbD0NgUzT9F2tw9Qszw1BQ5Er30Y45ywoudAhsn VrpMhBwWRCUdakQ2PsI7O8WB4gnBdWpEuzS2Ssqa1akB+pggH2xQzVb5EznmbzlS gz/SqUP75ijTT/oGh+C/hKAES3pmO4pH48J7llOKzb8YpoxxzjSEVb2pVbLzNdIV +it12Cap0lW+CTNGF4p2TbuKXKkE1LiGya1JMymQiZL8quCBYJIQUttiBvBg8Ac1 oCw2DXQZsjDw55Hwwhr95J4FuY4+iQd+o1GgRDQ4MEqaYFEfdcFRA1YCbMHgiAzu NOGwjrQ2PB5xGST34qobGtk7Xt2nIilDvl5K5Co2E4s14NLrlBHo2uq33d0unlIZ BJMrHG/IWNjuHbKl/vM05fuiKEIvpL5qTKz7oVL6tX8Zphf6ljU= =C431 -----END PGP SIGNATURE----- Merge tag 'x86_sev_for_v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull x86 SEV updates from Borislav Petkov: - Do the proper memory conversion of guest memory in order to be able to kexec kernels in SNP guests along with other adjustments and cleanups to that effect - Start converting and moving functionality from the sev-guest driver into core code with the purpose of supporting the secure TSC SNP feature where the hypervisor cannot influence the TSC exposed to the guest anymore - Add a "nosnp" cmdline option in order to be able to disable SNP support in the hypervisor and thus free-up resources which are not going to be used - Cleanups [ Reminding myself about the endless TLA's again: SEV is the AMD Secure Encrypted Virtualization - Linus ] * tag 'x86_sev_for_v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/sev: Cleanup vc_handle_msr() x86/sev: Convert shared memory back to private on kexec x86/mm: Refactor __set_clr_pte_enc() x86/boot: Skip video memory access in the decompressor for SEV-ES/SNP virt: sev-guest: Carve out SNP message context structure virt: sev-guest: Reduce the scope of SNP command mutex virt: sev-guest: Consolidate SNP guest messaging parameters to a struct x86/sev: Cache the secrets page address x86/sev: Handle failures from snp_init() virt: sev-guest: Use AES GCM crypto library x86/virt: Provide "nosnp" boot option for sev kernel command line x86/virt: Move SEV-specific parsing into arch/x86/virt/svm
This commit is contained in:
commit
55db8eb456
@ -305,3 +305,8 @@ The available options are:
|
||||
|
||||
debug
|
||||
Enable debug messages.
|
||||
|
||||
nosnp
|
||||
Do not enable SEV-SNP (applies to host/hypervisor only). Setting
|
||||
'nosnp' avoids the RMP check overhead in memory accesses when
|
||||
users do not want to run SEV-SNP guests.
|
||||
|
@ -385,6 +385,19 @@ static void parse_mem_encrypt(struct setup_header *hdr)
|
||||
hdr->xloadflags |= XLF_MEM_ENCRYPTION;
|
||||
}
|
||||
|
||||
static void early_sev_detect(void)
|
||||
{
|
||||
/*
|
||||
* Accessing video memory causes guest termination because
|
||||
* the boot stage2 #VC handler of SEV-ES/SNP guests does not
|
||||
* support MMIO handling and kexec -c adds screen_info to the
|
||||
* boot parameters passed to the kexec kernel, which causes
|
||||
* console output to be dumped to both video and serial.
|
||||
*/
|
||||
if (sev_status & MSR_AMD64_SEV_ES_ENABLED)
|
||||
lines = cols = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The compressed kernel image (ZO), has been moved so that its position
|
||||
* is against the end of the buffer used to hold the uncompressed kernel
|
||||
@ -440,6 +453,8 @@ asmlinkage __visible void *extract_kernel(void *rmode, unsigned char *output)
|
||||
*/
|
||||
early_tdx_detect();
|
||||
|
||||
early_sev_detect();
|
||||
|
||||
console_init();
|
||||
|
||||
/*
|
||||
|
@ -92,6 +92,9 @@ static struct ghcb *boot_ghcb __section(".data");
|
||||
/* Bitmap of SEV features supported by the hypervisor */
|
||||
static u64 sev_hv_features __ro_after_init;
|
||||
|
||||
/* Secrets page physical address from the CC blob */
|
||||
static u64 secrets_pa __ro_after_init;
|
||||
|
||||
/* #VC handler runtime per-CPU data */
|
||||
struct sev_es_runtime_data {
|
||||
struct ghcb ghcb_page;
|
||||
@ -141,33 +144,6 @@ static DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa);
|
||||
static DEFINE_PER_CPU(struct svsm_ca *, svsm_caa);
|
||||
static DEFINE_PER_CPU(u64, svsm_caa_pa);
|
||||
|
||||
struct sev_config {
|
||||
__u64 debug : 1,
|
||||
|
||||
/*
|
||||
* Indicates when the per-CPU GHCB has been created and registered
|
||||
* and thus can be used by the BSP instead of the early boot GHCB.
|
||||
*
|
||||
* For APs, the per-CPU GHCB is created before they are started
|
||||
* and registered upon startup, so this flag can be used globally
|
||||
* for the BSP and APs.
|
||||
*/
|
||||
ghcbs_initialized : 1,
|
||||
|
||||
/*
|
||||
* Indicates when the per-CPU SVSM CA is to be used instead of the
|
||||
* boot SVSM CA.
|
||||
*
|
||||
* For APs, the per-CPU SVSM CA is created as part of the AP
|
||||
* bringup, so this flag can be used globally for the BSP and APs.
|
||||
*/
|
||||
use_cas : 1,
|
||||
|
||||
__reserved : 61;
|
||||
};
|
||||
|
||||
static struct sev_config sev_cfg __read_mostly;
|
||||
|
||||
static __always_inline bool on_vc_stack(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long sp = regs->sp;
|
||||
@ -722,45 +698,13 @@ void noinstr __sev_es_nmi_complete(void)
|
||||
__sev_put_ghcb(&state);
|
||||
}
|
||||
|
||||
static u64 __init get_secrets_page(void)
|
||||
{
|
||||
u64 pa_data = boot_params.cc_blob_address;
|
||||
struct cc_blob_sev_info info;
|
||||
void *map;
|
||||
|
||||
/*
|
||||
* The CC blob contains the address of the secrets page, check if the
|
||||
* blob is present.
|
||||
*/
|
||||
if (!pa_data)
|
||||
return 0;
|
||||
|
||||
map = early_memremap(pa_data, sizeof(info));
|
||||
if (!map) {
|
||||
pr_err("Unable to locate SNP secrets page: failed to map the Confidential Computing blob.\n");
|
||||
return 0;
|
||||
}
|
||||
memcpy(&info, map, sizeof(info));
|
||||
early_memunmap(map, sizeof(info));
|
||||
|
||||
/* smoke-test the secrets page passed */
|
||||
if (!info.secrets_phys || info.secrets_len != PAGE_SIZE)
|
||||
return 0;
|
||||
|
||||
return info.secrets_phys;
|
||||
}
|
||||
|
||||
static u64 __init get_snp_jump_table_addr(void)
|
||||
{
|
||||
struct snp_secrets_page *secrets;
|
||||
void __iomem *mem;
|
||||
u64 pa, addr;
|
||||
u64 addr;
|
||||
|
||||
pa = get_secrets_page();
|
||||
if (!pa)
|
||||
return 0;
|
||||
|
||||
mem = ioremap_encrypted(pa, PAGE_SIZE);
|
||||
mem = ioremap_encrypted(secrets_pa, PAGE_SIZE);
|
||||
if (!mem) {
|
||||
pr_err("Unable to locate AP jump table address: failed to map the SNP secrets page.\n");
|
||||
return 0;
|
||||
@ -1010,6 +954,137 @@ void snp_accept_memory(phys_addr_t start, phys_addr_t end)
|
||||
set_pages_state(vaddr, npages, SNP_PAGE_STATE_PRIVATE);
|
||||
}
|
||||
|
||||
static void set_pte_enc(pte_t *kpte, int level, void *va)
|
||||
{
|
||||
struct pte_enc_desc d = {
|
||||
.kpte = kpte,
|
||||
.pte_level = level,
|
||||
.va = va,
|
||||
.encrypt = true
|
||||
};
|
||||
|
||||
prepare_pte_enc(&d);
|
||||
set_pte_enc_mask(kpte, d.pfn, d.new_pgprot);
|
||||
}
|
||||
|
||||
static void unshare_all_memory(void)
|
||||
{
|
||||
unsigned long addr, end, size, ghcb;
|
||||
struct sev_es_runtime_data *data;
|
||||
unsigned int npages, level;
|
||||
bool skipped_addr;
|
||||
pte_t *pte;
|
||||
int cpu;
|
||||
|
||||
/* Unshare the direct mapping. */
|
||||
addr = PAGE_OFFSET;
|
||||
end = PAGE_OFFSET + get_max_mapped();
|
||||
|
||||
while (addr < end) {
|
||||
pte = lookup_address(addr, &level);
|
||||
size = page_level_size(level);
|
||||
npages = size / PAGE_SIZE;
|
||||
skipped_addr = false;
|
||||
|
||||
if (!pte || !pte_decrypted(*pte) || pte_none(*pte)) {
|
||||
addr += size;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that all the per-CPU GHCBs are made private at the
|
||||
* end of the unsharing loop so that the switch to the slower
|
||||
* MSR protocol happens last.
|
||||
*/
|
||||
for_each_possible_cpu(cpu) {
|
||||
data = per_cpu(runtime_data, cpu);
|
||||
ghcb = (unsigned long)&data->ghcb_page;
|
||||
|
||||
if (addr <= ghcb && ghcb <= addr + size) {
|
||||
skipped_addr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipped_addr) {
|
||||
set_pte_enc(pte, level, (void *)addr);
|
||||
snp_set_memory_private(addr, npages);
|
||||
}
|
||||
addr += size;
|
||||
}
|
||||
|
||||
/* Unshare all bss decrypted memory. */
|
||||
addr = (unsigned long)__start_bss_decrypted;
|
||||
end = (unsigned long)__start_bss_decrypted_unused;
|
||||
npages = (end - addr) >> PAGE_SHIFT;
|
||||
|
||||
for (; addr < end; addr += PAGE_SIZE) {
|
||||
pte = lookup_address(addr, &level);
|
||||
if (!pte || !pte_decrypted(*pte) || pte_none(*pte))
|
||||
continue;
|
||||
|
||||
set_pte_enc(pte, level, (void *)addr);
|
||||
}
|
||||
addr = (unsigned long)__start_bss_decrypted;
|
||||
snp_set_memory_private(addr, npages);
|
||||
|
||||
__flush_tlb_all();
|
||||
}
|
||||
|
||||
/* Stop new private<->shared conversions */
|
||||
void snp_kexec_begin(void)
|
||||
{
|
||||
if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
|
||||
return;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_KEXEC_CORE))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Crash kernel ends up here with interrupts disabled: can't wait for
|
||||
* conversions to finish.
|
||||
*
|
||||
* If race happened, just report and proceed.
|
||||
*/
|
||||
if (!set_memory_enc_stop_conversion())
|
||||
pr_warn("Failed to stop shared<->private conversions\n");
|
||||
}
|
||||
|
||||
void snp_kexec_finish(void)
|
||||
{
|
||||
struct sev_es_runtime_data *data;
|
||||
unsigned int level, cpu;
|
||||
unsigned long size;
|
||||
struct ghcb *ghcb;
|
||||
pte_t *pte;
|
||||
|
||||
if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
|
||||
return;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_KEXEC_CORE))
|
||||
return;
|
||||
|
||||
unshare_all_memory();
|
||||
|
||||
/*
|
||||
* Switch to using the MSR protocol to change per-CPU GHCBs to
|
||||
* private. All the per-CPU GHCBs have been switched back to private,
|
||||
* so can't do any more GHCB calls to the hypervisor beyond this point
|
||||
* until the kexec'ed kernel starts running.
|
||||
*/
|
||||
boot_ghcb = NULL;
|
||||
sev_cfg.ghcbs_initialized = false;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
data = per_cpu(runtime_data, cpu);
|
||||
ghcb = &data->ghcb_page;
|
||||
pte = lookup_address((unsigned long)ghcb, &level);
|
||||
size = page_level_size(level);
|
||||
set_pte_enc(pte, level, (void *)ghcb);
|
||||
snp_set_memory_private((unsigned long)ghcb, (size / PAGE_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
static int snp_set_vmsa(void *va, void *caa, int apic_id, bool make_vmsa)
|
||||
{
|
||||
int ret;
|
||||
@ -1331,35 +1406,39 @@ int __init sev_es_efi_map_ghcbs(pgd_t *pgd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Writes to the SVSM CAA MSR are ignored */
|
||||
static enum es_result __vc_handle_msr_caa(struct pt_regs *regs, bool write)
|
||||
{
|
||||
if (write)
|
||||
return ES_OK;
|
||||
|
||||
regs->ax = lower_32_bits(this_cpu_read(svsm_caa_pa));
|
||||
regs->dx = upper_32_bits(this_cpu_read(svsm_caa_pa));
|
||||
|
||||
return ES_OK;
|
||||
}
|
||||
|
||||
static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||
{
|
||||
struct pt_regs *regs = ctxt->regs;
|
||||
enum es_result ret;
|
||||
u64 exit_info_1;
|
||||
bool write;
|
||||
|
||||
/* Is it a WRMSR? */
|
||||
exit_info_1 = (ctxt->insn.opcode.bytes[1] == 0x30) ? 1 : 0;
|
||||
write = ctxt->insn.opcode.bytes[1] == 0x30;
|
||||
|
||||
if (regs->cx == MSR_SVSM_CAA) {
|
||||
/* Writes to the SVSM CAA msr are ignored */
|
||||
if (exit_info_1)
|
||||
return ES_OK;
|
||||
|
||||
regs->ax = lower_32_bits(this_cpu_read(svsm_caa_pa));
|
||||
regs->dx = upper_32_bits(this_cpu_read(svsm_caa_pa));
|
||||
|
||||
return ES_OK;
|
||||
}
|
||||
if (regs->cx == MSR_SVSM_CAA)
|
||||
return __vc_handle_msr_caa(regs, write);
|
||||
|
||||
ghcb_set_rcx(ghcb, regs->cx);
|
||||
if (exit_info_1) {
|
||||
if (write) {
|
||||
ghcb_set_rax(ghcb, regs->ax);
|
||||
ghcb_set_rdx(ghcb, regs->dx);
|
||||
}
|
||||
|
||||
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_MSR, exit_info_1, 0);
|
||||
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_MSR, write, 0);
|
||||
|
||||
if ((ret == ES_OK) && (!exit_info_1)) {
|
||||
if ((ret == ES_OK) && !write) {
|
||||
regs->ax = ghcb->save.rax;
|
||||
regs->dx = ghcb->save.rdx;
|
||||
}
|
||||
@ -2300,6 +2379,11 @@ bool __head snp_init(struct boot_params *bp)
|
||||
if (!cc_info)
|
||||
return false;
|
||||
|
||||
if (cc_info->secrets_phys && cc_info->secrets_len == PAGE_SIZE)
|
||||
secrets_pa = cc_info->secrets_phys;
|
||||
else
|
||||
return false;
|
||||
|
||||
setup_cpuid_table(cc_info);
|
||||
|
||||
svsm_setup(cc_info);
|
||||
@ -2374,23 +2458,6 @@ static int __init report_snp_info(void)
|
||||
}
|
||||
arch_initcall(report_snp_info);
|
||||
|
||||
static int __init init_sev_config(char *str)
|
||||
{
|
||||
char *s;
|
||||
|
||||
while ((s = strsep(&str, ","))) {
|
||||
if (!strcmp(s, "debug")) {
|
||||
sev_cfg.debug = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
pr_info("SEV command-line option '%s' was not recognized\n", s);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("sev=", init_sev_config);
|
||||
|
||||
static void update_attest_input(struct svsm_call *call, struct svsm_attest_call *input)
|
||||
{
|
||||
/* If (new) lengths have been returned, propagate them up */
|
||||
@ -2441,7 +2508,8 @@ int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snp_issue_svsm_attest_req);
|
||||
|
||||
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
|
||||
int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
|
||||
struct snp_guest_request_ioctl *rio)
|
||||
{
|
||||
struct ghcb_state state;
|
||||
struct es_em_ctxt ctxt;
|
||||
@ -2465,12 +2533,12 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
|
||||
|
||||
vc_ghcb_invalidate(ghcb);
|
||||
|
||||
if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
|
||||
if (req->exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
|
||||
ghcb_set_rax(ghcb, input->data_gpa);
|
||||
ghcb_set_rbx(ghcb, input->data_npages);
|
||||
}
|
||||
|
||||
ret = sev_es_ghcb_hv_call(ghcb, &ctxt, exit_code, input->req_gpa, input->resp_gpa);
|
||||
ret = sev_es_ghcb_hv_call(ghcb, &ctxt, req->exit_code, input->req_gpa, input->resp_gpa);
|
||||
if (ret)
|
||||
goto e_put;
|
||||
|
||||
@ -2485,7 +2553,7 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct sn
|
||||
|
||||
case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
|
||||
/* Number of expected pages are returned in RBX */
|
||||
if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
|
||||
if (req->exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
|
||||
input->data_npages = ghcb_get_rbx(ghcb);
|
||||
ret = -ENOSPC;
|
||||
break;
|
||||
@ -2513,16 +2581,11 @@ static struct platform_device sev_guest_device = {
|
||||
static int __init snp_init_platform_device(void)
|
||||
{
|
||||
struct sev_guest_platform_data data;
|
||||
u64 gpa;
|
||||
|
||||
if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
|
||||
return -ENODEV;
|
||||
|
||||
gpa = get_secrets_page();
|
||||
if (!gpa)
|
||||
return -ENODEV;
|
||||
|
||||
data.secrets_gpa = gpa;
|
||||
data.secrets_gpa = secrets_pa;
|
||||
if (platform_device_add_data(&sev_guest_device, &data, sizeof(data)))
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -220,4 +220,31 @@ struct snp_psc_desc {
|
||||
#define GHCB_ERR_INVALID_INPUT 5
|
||||
#define GHCB_ERR_INVALID_EVENT 6
|
||||
|
||||
struct sev_config {
|
||||
__u64 debug : 1,
|
||||
|
||||
/*
|
||||
* Indicates when the per-CPU GHCB has been created and registered
|
||||
* and thus can be used by the BSP instead of the early boot GHCB.
|
||||
*
|
||||
* For APs, the per-CPU GHCB is created before they are started
|
||||
* and registered upon startup, so this flag can be used globally
|
||||
* for the BSP and APs.
|
||||
*/
|
||||
ghcbs_initialized : 1,
|
||||
|
||||
/*
|
||||
* Indicates when the per-CPU SVSM CA is to be used instead of the
|
||||
* boot SVSM CA.
|
||||
*
|
||||
* For APs, the per-CPU SVSM CA is created as part of the AP
|
||||
* bringup, so this flag can be used globally for the BSP and APs.
|
||||
*/
|
||||
use_cas : 1,
|
||||
|
||||
__reserved : 61;
|
||||
};
|
||||
|
||||
extern struct sev_config sev_cfg;
|
||||
|
||||
#endif
|
||||
|
@ -120,6 +120,9 @@ struct snp_req_data {
|
||||
};
|
||||
|
||||
#define MAX_AUTHTAG_LEN 32
|
||||
#define AUTHTAG_LEN 16
|
||||
#define AAD_LEN 48
|
||||
#define MSG_HDR_VER 1
|
||||
|
||||
/* See SNP spec SNP_GUEST_REQUEST section for the structure */
|
||||
enum msg_type {
|
||||
@ -171,6 +174,19 @@ struct sev_guest_platform_data {
|
||||
u64 secrets_gpa;
|
||||
};
|
||||
|
||||
struct snp_guest_req {
|
||||
void *req_buf;
|
||||
size_t req_sz;
|
||||
|
||||
void *resp_buf;
|
||||
size_t resp_sz;
|
||||
|
||||
u64 exit_code;
|
||||
unsigned int vmpck_id;
|
||||
u8 msg_version;
|
||||
u8 msg_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* The secrets page contains 96-bytes of reserved field that can be used by
|
||||
* the guest OS. The guest OS uses the area to save the message sequence
|
||||
@ -218,6 +234,27 @@ struct snp_secrets_page {
|
||||
u8 rsvd4[3744];
|
||||
} __packed;
|
||||
|
||||
struct snp_msg_desc {
|
||||
/* request and response are in unencrypted memory */
|
||||
struct snp_guest_msg *request, *response;
|
||||
|
||||
/*
|
||||
* Avoid information leakage by double-buffering shared messages
|
||||
* in fields that are in regular encrypted memory.
|
||||
*/
|
||||
struct snp_guest_msg secret_request, secret_response;
|
||||
|
||||
struct snp_secrets_page *secrets;
|
||||
struct snp_req_data input;
|
||||
|
||||
void *certs_data;
|
||||
|
||||
struct aesgcm_ctx *ctx;
|
||||
|
||||
u32 *os_area_msg_seqno;
|
||||
u8 *vmpck;
|
||||
};
|
||||
|
||||
/*
|
||||
* The SVSM Calling Area (CA) related structures.
|
||||
*/
|
||||
@ -285,6 +322,22 @@ struct svsm_attest_call {
|
||||
u8 rsvd[4];
|
||||
};
|
||||
|
||||
/* PTE descriptor used for the prepare_pte_enc() operations. */
|
||||
struct pte_enc_desc {
|
||||
pte_t *kpte;
|
||||
int pte_level;
|
||||
bool encrypt;
|
||||
/* pfn of the kpte above */
|
||||
unsigned long pfn;
|
||||
/* physical address of @pfn */
|
||||
unsigned long pa;
|
||||
/* virtual address of @pfn */
|
||||
void *va;
|
||||
/* memory covered by the pte */
|
||||
unsigned long size;
|
||||
pgprot_t new_pgprot;
|
||||
};
|
||||
|
||||
/*
|
||||
* SVSM protocol structure
|
||||
*/
|
||||
@ -392,13 +445,18 @@ void snp_set_wakeup_secondary_cpu(void);
|
||||
bool snp_init(struct boot_params *bp);
|
||||
void __noreturn snp_abort(void);
|
||||
void snp_dmi_setup(void);
|
||||
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
|
||||
int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
|
||||
struct snp_guest_request_ioctl *rio);
|
||||
int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, struct svsm_attest_call *input);
|
||||
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
|
||||
u64 snp_get_unsupported_features(u64 status);
|
||||
u64 sev_get_status(void);
|
||||
void sev_show_status(void);
|
||||
void snp_update_svsm_ca(void);
|
||||
int prepare_pte_enc(struct pte_enc_desc *d);
|
||||
void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot);
|
||||
void snp_kexec_finish(void);
|
||||
void snp_kexec_begin(void);
|
||||
|
||||
#else /* !CONFIG_AMD_MEM_ENCRYPT */
|
||||
|
||||
@ -422,7 +480,8 @@ static inline void snp_set_wakeup_secondary_cpu(void) { }
|
||||
static inline bool snp_init(struct boot_params *bp) { return false; }
|
||||
static inline void snp_abort(void) { }
|
||||
static inline void snp_dmi_setup(void) { }
|
||||
static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
|
||||
static inline int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
|
||||
struct snp_guest_request_ioctl *rio)
|
||||
{
|
||||
return -ENOTTY;
|
||||
}
|
||||
@ -435,6 +494,10 @@ static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
|
||||
static inline u64 sev_get_status(void) { return 0; }
|
||||
static inline void sev_show_status(void) { }
|
||||
static inline void snp_update_svsm_ca(void) { }
|
||||
static inline int prepare_pte_enc(struct pte_enc_desc *d) { return 0; }
|
||||
static inline void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot) { }
|
||||
static inline void snp_kexec_finish(void) { }
|
||||
static inline void snp_kexec_begin(void) { }
|
||||
|
||||
#endif /* CONFIG_AMD_MEM_ENCRYPT */
|
||||
|
||||
|
@ -311,59 +311,82 @@ static int amd_enc_status_change_finish(unsigned long vaddr, int npages, bool en
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init __set_clr_pte_enc(pte_t *kpte, int level, bool enc)
|
||||
int prepare_pte_enc(struct pte_enc_desc *d)
|
||||
{
|
||||
pgprot_t old_prot, new_prot;
|
||||
unsigned long pfn, pa, size;
|
||||
pte_t new_pte;
|
||||
pgprot_t old_prot;
|
||||
|
||||
pfn = pg_level_to_pfn(level, kpte, &old_prot);
|
||||
if (!pfn)
|
||||
return;
|
||||
d->pfn = pg_level_to_pfn(d->pte_level, d->kpte, &old_prot);
|
||||
if (!d->pfn)
|
||||
return 1;
|
||||
|
||||
new_prot = old_prot;
|
||||
if (enc)
|
||||
pgprot_val(new_prot) |= _PAGE_ENC;
|
||||
d->new_pgprot = old_prot;
|
||||
if (d->encrypt)
|
||||
pgprot_val(d->new_pgprot) |= _PAGE_ENC;
|
||||
else
|
||||
pgprot_val(new_prot) &= ~_PAGE_ENC;
|
||||
pgprot_val(d->new_pgprot) &= ~_PAGE_ENC;
|
||||
|
||||
/* If prot is same then do nothing. */
|
||||
if (pgprot_val(old_prot) == pgprot_val(new_prot))
|
||||
return;
|
||||
if (pgprot_val(old_prot) == pgprot_val(d->new_pgprot))
|
||||
return 1;
|
||||
|
||||
pa = pfn << PAGE_SHIFT;
|
||||
size = page_level_size(level);
|
||||
d->pa = d->pfn << PAGE_SHIFT;
|
||||
d->size = page_level_size(d->pte_level);
|
||||
|
||||
/*
|
||||
* We are going to perform in-place en-/decryption and change the
|
||||
* physical page attribute from C=1 to C=0 or vice versa. Flush the
|
||||
* caches to ensure that data gets accessed with the correct C-bit.
|
||||
* In-place en-/decryption and physical page attribute change
|
||||
* from C=1 to C=0 or vice versa will be performed. Flush the
|
||||
* caches to ensure that data gets accessed with the correct
|
||||
* C-bit.
|
||||
*/
|
||||
clflush_cache_range(__va(pa), size);
|
||||
if (d->va)
|
||||
clflush_cache_range(d->va, d->size);
|
||||
else
|
||||
clflush_cache_range(__va(d->pa), d->size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_pte_enc_mask(pte_t *kpte, unsigned long pfn, pgprot_t new_prot)
|
||||
{
|
||||
pte_t new_pte;
|
||||
|
||||
/* Change the page encryption mask. */
|
||||
new_pte = pfn_pte(pfn, new_prot);
|
||||
set_pte_atomic(kpte, new_pte);
|
||||
}
|
||||
|
||||
static void __init __set_clr_pte_enc(pte_t *kpte, int level, bool enc)
|
||||
{
|
||||
struct pte_enc_desc d = {
|
||||
.kpte = kpte,
|
||||
.pte_level = level,
|
||||
.encrypt = enc
|
||||
};
|
||||
|
||||
if (prepare_pte_enc(&d))
|
||||
return;
|
||||
|
||||
/* Encrypt/decrypt the contents in-place */
|
||||
if (enc) {
|
||||
sme_early_encrypt(pa, size);
|
||||
sme_early_encrypt(d.pa, d.size);
|
||||
} else {
|
||||
sme_early_decrypt(pa, size);
|
||||
sme_early_decrypt(d.pa, d.size);
|
||||
|
||||
/*
|
||||
* ON SNP, the page state in the RMP table must happen
|
||||
* before the page table updates.
|
||||
*/
|
||||
early_snp_set_memory_shared((unsigned long)__va(pa), pa, 1);
|
||||
early_snp_set_memory_shared((unsigned long)__va(d.pa), d.pa, 1);
|
||||
}
|
||||
|
||||
/* Change the page encryption mask. */
|
||||
new_pte = pfn_pte(pfn, new_prot);
|
||||
set_pte_atomic(kpte, new_pte);
|
||||
set_pte_enc_mask(kpte, d.pfn, d.new_pgprot);
|
||||
|
||||
/*
|
||||
* If page is set encrypted in the page table, then update the RMP table to
|
||||
* add this page as private.
|
||||
*/
|
||||
if (enc)
|
||||
early_snp_set_memory_private((unsigned long)__va(pa), pa, 1);
|
||||
early_snp_set_memory_private((unsigned long)__va(d.pa), d.pa, 1);
|
||||
}
|
||||
|
||||
static int __init early_set_memory_enc_dec(unsigned long vaddr,
|
||||
@ -467,6 +490,8 @@ void __init sme_early_init(void)
|
||||
x86_platform.guest.enc_status_change_finish = amd_enc_status_change_finish;
|
||||
x86_platform.guest.enc_tlb_flush_required = amd_enc_tlb_flush_required;
|
||||
x86_platform.guest.enc_cache_flush_required = amd_enc_cache_flush_required;
|
||||
x86_platform.guest.enc_kexec_begin = snp_kexec_begin;
|
||||
x86_platform.guest.enc_kexec_finish = snp_kexec_finish;
|
||||
|
||||
/*
|
||||
* AMD-SEV-ES intercepts the RDMSR to read the X2APIC ID in the
|
||||
|
@ -495,10 +495,10 @@ void __head sme_enable(struct boot_params *bp)
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
unsigned long feature_mask;
|
||||
unsigned long me_mask;
|
||||
bool snp;
|
||||
bool snp_en;
|
||||
u64 msr;
|
||||
|
||||
snp = snp_init(bp);
|
||||
snp_en = snp_init(bp);
|
||||
|
||||
/* Check for the SME/SEV support leaf */
|
||||
eax = 0x80000000;
|
||||
@ -531,8 +531,11 @@ void __head sme_enable(struct boot_params *bp)
|
||||
RIP_REL_REF(sev_status) = msr = __rdmsr(MSR_AMD64_SEV);
|
||||
feature_mask = (msr & MSR_AMD64_SEV_ENABLED) ? AMD_SEV_BIT : AMD_SME_BIT;
|
||||
|
||||
/* The SEV-SNP CC blob should never be present unless SEV-SNP is enabled. */
|
||||
if (snp && !(msr & MSR_AMD64_SEV_SNP_ENABLED))
|
||||
/*
|
||||
* Any discrepancies between the presence of a CC blob and SNP
|
||||
* enablement abort the guest.
|
||||
*/
|
||||
if (snp_en ^ !!(msr & MSR_AMD64_SEV_SNP_ENABLED))
|
||||
snp_abort();
|
||||
|
||||
/* Check if memory encryption is enabled */
|
||||
|
@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_KVM_AMD_SEV) += sev.o
|
||||
obj-$(CONFIG_CPU_SUP_AMD) += cmdline.o
|
||||
|
45
arch/x86/virt/svm/cmdline.c
Normal file
45
arch/x86/virt/svm/cmdline.c
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* AMD SVM-SEV command line parsing support
|
||||
*
|
||||
* Copyright (C) 2023 - 2024 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Michael Roth <michael.roth@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/cpufeature.h>
|
||||
|
||||
#include <asm/sev-common.h>
|
||||
|
||||
struct sev_config sev_cfg __read_mostly;
|
||||
|
||||
static int __init init_sev_config(char *str)
|
||||
{
|
||||
char *s;
|
||||
|
||||
while ((s = strsep(&str, ","))) {
|
||||
if (!strcmp(s, "debug")) {
|
||||
sev_cfg.debug = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(s, "nosnp")) {
|
||||
if (!cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) {
|
||||
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
|
||||
cc_platform_clear(CC_ATTR_HOST_SEV_SNP);
|
||||
continue;
|
||||
} else {
|
||||
goto warn;
|
||||
}
|
||||
}
|
||||
|
||||
warn:
|
||||
pr_info("SEV command-line option '%s' was not recognized\n", s);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("sev=", init_sev_config);
|
@ -2,9 +2,7 @@ config SEV_GUEST
|
||||
tristate "AMD SEV Guest driver"
|
||||
default m
|
||||
depends on AMD_MEM_ENCRYPT
|
||||
select CRYPTO
|
||||
select CRYPTO_AEAD2
|
||||
select CRYPTO_GCM
|
||||
select CRYPTO_LIB_AESGCM
|
||||
select TSM_REPORTS
|
||||
help
|
||||
SEV-SNP firmware provides the guest a mechanism to communicate with
|
||||
|
@ -17,8 +17,7 @@
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/tsm.h>
|
||||
#include <crypto/aead.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <crypto/gcm.h>
|
||||
#include <linux/psp-sev.h>
|
||||
#include <linux/sockptr.h>
|
||||
#include <linux/cleanup.h>
|
||||
@ -31,44 +30,23 @@
|
||||
#include <asm/sev.h>
|
||||
|
||||
#define DEVICE_NAME "sev-guest"
|
||||
#define AAD_LEN 48
|
||||
#define MSG_HDR_VER 1
|
||||
|
||||
#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
|
||||
#define SNP_REQ_RETRY_DELAY (2*HZ)
|
||||
|
||||
#define SVSM_MAX_RETRIES 3
|
||||
|
||||
struct snp_guest_crypto {
|
||||
struct crypto_aead *tfm;
|
||||
u8 *iv, *authtag;
|
||||
int iv_len, a_len;
|
||||
};
|
||||
|
||||
struct snp_guest_dev {
|
||||
struct device *dev;
|
||||
struct miscdevice misc;
|
||||
|
||||
void *certs_data;
|
||||
struct snp_guest_crypto *crypto;
|
||||
/* request and response are in unencrypted memory */
|
||||
struct snp_guest_msg *request, *response;
|
||||
struct snp_msg_desc *msg_desc;
|
||||
|
||||
/*
|
||||
* Avoid information leakage by double-buffering shared messages
|
||||
* in fields that are in regular encrypted memory.
|
||||
*/
|
||||
struct snp_guest_msg secret_request, secret_response;
|
||||
|
||||
struct snp_secrets_page *secrets;
|
||||
struct snp_req_data input;
|
||||
union {
|
||||
struct snp_report_req report;
|
||||
struct snp_derived_key_req derived_key;
|
||||
struct snp_ext_report_req ext_report;
|
||||
} req;
|
||||
u32 *os_area_msg_seqno;
|
||||
u8 *vmpck;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -85,12 +63,12 @@ MODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP.
|
||||
/* Mutex to serialize the shared buffer access and command handling. */
|
||||
static DEFINE_MUTEX(snp_cmd_mutex);
|
||||
|
||||
static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
|
||||
static bool is_vmpck_empty(struct snp_msg_desc *mdesc)
|
||||
{
|
||||
char zero_key[VMPCK_KEY_LEN] = {0};
|
||||
|
||||
if (snp_dev->vmpck)
|
||||
return !memcmp(snp_dev->vmpck, zero_key, VMPCK_KEY_LEN);
|
||||
if (mdesc->vmpck)
|
||||
return !memcmp(mdesc->vmpck, zero_key, VMPCK_KEY_LEN);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -112,30 +90,30 @@ static bool is_vmpck_empty(struct snp_guest_dev *snp_dev)
|
||||
* vulnerable. If the sequence number were incremented for a fresh IV the ASP
|
||||
* will reject the request.
|
||||
*/
|
||||
static void snp_disable_vmpck(struct snp_guest_dev *snp_dev)
|
||||
static void snp_disable_vmpck(struct snp_msg_desc *mdesc)
|
||||
{
|
||||
dev_alert(snp_dev->dev, "Disabling VMPCK%d communication key to prevent IV reuse.\n",
|
||||
pr_alert("Disabling VMPCK%d communication key to prevent IV reuse.\n",
|
||||
vmpck_id);
|
||||
memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN);
|
||||
snp_dev->vmpck = NULL;
|
||||
memzero_explicit(mdesc->vmpck, VMPCK_KEY_LEN);
|
||||
mdesc->vmpck = NULL;
|
||||
}
|
||||
|
||||
static inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
|
||||
static inline u64 __snp_get_msg_seqno(struct snp_msg_desc *mdesc)
|
||||
{
|
||||
u64 count;
|
||||
|
||||
lockdep_assert_held(&snp_cmd_mutex);
|
||||
|
||||
/* Read the current message sequence counter from secrets pages */
|
||||
count = *snp_dev->os_area_msg_seqno;
|
||||
count = *mdesc->os_area_msg_seqno;
|
||||
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
/* Return a non-zero on success */
|
||||
static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
|
||||
static u64 snp_get_msg_seqno(struct snp_msg_desc *mdesc)
|
||||
{
|
||||
u64 count = __snp_get_msg_seqno(snp_dev);
|
||||
u64 count = __snp_get_msg_seqno(mdesc);
|
||||
|
||||
/*
|
||||
* The message sequence counter for the SNP guest request is a 64-bit
|
||||
@ -146,20 +124,20 @@ static u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev)
|
||||
* invalid number and will fail the message request.
|
||||
*/
|
||||
if (count >= UINT_MAX) {
|
||||
dev_err(snp_dev->dev, "request message sequence counter overflow\n");
|
||||
pr_err("request message sequence counter overflow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev)
|
||||
static void snp_inc_msg_seqno(struct snp_msg_desc *mdesc)
|
||||
{
|
||||
/*
|
||||
* The counter is also incremented by the PSP, so increment it by 2
|
||||
* and save in secrets page.
|
||||
*/
|
||||
*snp_dev->os_area_msg_seqno += 2;
|
||||
*mdesc->os_area_msg_seqno += 2;
|
||||
}
|
||||
|
||||
static inline struct snp_guest_dev *to_snp_dev(struct file *file)
|
||||
@ -169,139 +147,38 @@ static inline struct snp_guest_dev *to_snp_dev(struct file *file)
|
||||
return container_of(dev, struct snp_guest_dev, misc);
|
||||
}
|
||||
|
||||
static struct snp_guest_crypto *init_crypto(struct snp_guest_dev *snp_dev, u8 *key, size_t keylen)
|
||||
static struct aesgcm_ctx *snp_init_crypto(u8 *key, size_t keylen)
|
||||
{
|
||||
struct snp_guest_crypto *crypto;
|
||||
struct aesgcm_ctx *ctx;
|
||||
|
||||
crypto = kzalloc(sizeof(*crypto), GFP_KERNEL_ACCOUNT);
|
||||
if (!crypto)
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
crypto->tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
|
||||
if (IS_ERR(crypto->tfm))
|
||||
goto e_free;
|
||||
|
||||
if (crypto_aead_setkey(crypto->tfm, key, keylen))
|
||||
goto e_free_crypto;
|
||||
|
||||
crypto->iv_len = crypto_aead_ivsize(crypto->tfm);
|
||||
crypto->iv = kmalloc(crypto->iv_len, GFP_KERNEL_ACCOUNT);
|
||||
if (!crypto->iv)
|
||||
goto e_free_crypto;
|
||||
|
||||
if (crypto_aead_authsize(crypto->tfm) > MAX_AUTHTAG_LEN) {
|
||||
if (crypto_aead_setauthsize(crypto->tfm, MAX_AUTHTAG_LEN)) {
|
||||
dev_err(snp_dev->dev, "failed to set authsize to %d\n", MAX_AUTHTAG_LEN);
|
||||
goto e_free_iv;
|
||||
}
|
||||
if (aesgcm_expandkey(ctx, key, keylen, AUTHTAG_LEN)) {
|
||||
pr_err("Crypto context initialization failed\n");
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
crypto->a_len = crypto_aead_authsize(crypto->tfm);
|
||||
crypto->authtag = kmalloc(crypto->a_len, GFP_KERNEL_ACCOUNT);
|
||||
if (!crypto->authtag)
|
||||
goto e_free_iv;
|
||||
|
||||
return crypto;
|
||||
|
||||
e_free_iv:
|
||||
kfree(crypto->iv);
|
||||
e_free_crypto:
|
||||
crypto_free_aead(crypto->tfm);
|
||||
e_free:
|
||||
kfree(crypto);
|
||||
|
||||
return NULL;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void deinit_crypto(struct snp_guest_crypto *crypto)
|
||||
static int verify_and_dec_payload(struct snp_msg_desc *mdesc, struct snp_guest_req *req)
|
||||
{
|
||||
crypto_free_aead(crypto->tfm);
|
||||
kfree(crypto->iv);
|
||||
kfree(crypto->authtag);
|
||||
kfree(crypto);
|
||||
}
|
||||
|
||||
static int enc_dec_message(struct snp_guest_crypto *crypto, struct snp_guest_msg *msg,
|
||||
u8 *src_buf, u8 *dst_buf, size_t len, bool enc)
|
||||
{
|
||||
struct snp_guest_msg_hdr *hdr = &msg->hdr;
|
||||
struct scatterlist src[3], dst[3];
|
||||
DECLARE_CRYPTO_WAIT(wait);
|
||||
struct aead_request *req;
|
||||
int ret;
|
||||
|
||||
req = aead_request_alloc(crypto->tfm, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* AEAD memory operations:
|
||||
* +------ AAD -------+------- DATA -----+---- AUTHTAG----+
|
||||
* | msg header | plaintext | hdr->authtag |
|
||||
* | bytes 30h - 5Fh | or | |
|
||||
* | | cipher | |
|
||||
* +------------------+------------------+----------------+
|
||||
*/
|
||||
sg_init_table(src, 3);
|
||||
sg_set_buf(&src[0], &hdr->algo, AAD_LEN);
|
||||
sg_set_buf(&src[1], src_buf, hdr->msg_sz);
|
||||
sg_set_buf(&src[2], hdr->authtag, crypto->a_len);
|
||||
|
||||
sg_init_table(dst, 3);
|
||||
sg_set_buf(&dst[0], &hdr->algo, AAD_LEN);
|
||||
sg_set_buf(&dst[1], dst_buf, hdr->msg_sz);
|
||||
sg_set_buf(&dst[2], hdr->authtag, crypto->a_len);
|
||||
|
||||
aead_request_set_ad(req, AAD_LEN);
|
||||
aead_request_set_tfm(req, crypto->tfm);
|
||||
aead_request_set_callback(req, 0, crypto_req_done, &wait);
|
||||
|
||||
aead_request_set_crypt(req, src, dst, len, crypto->iv);
|
||||
ret = crypto_wait_req(enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req), &wait);
|
||||
|
||||
aead_request_free(req);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __enc_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
|
||||
void *plaintext, size_t len)
|
||||
{
|
||||
struct snp_guest_crypto *crypto = snp_dev->crypto;
|
||||
struct snp_guest_msg_hdr *hdr = &msg->hdr;
|
||||
|
||||
memset(crypto->iv, 0, crypto->iv_len);
|
||||
memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
|
||||
|
||||
return enc_dec_message(crypto, msg, plaintext, msg->payload, len, true);
|
||||
}
|
||||
|
||||
static int dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg,
|
||||
void *plaintext, size_t len)
|
||||
{
|
||||
struct snp_guest_crypto *crypto = snp_dev->crypto;
|
||||
struct snp_guest_msg_hdr *hdr = &msg->hdr;
|
||||
|
||||
/* Build IV with response buffer sequence number */
|
||||
memset(crypto->iv, 0, crypto->iv_len);
|
||||
memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno));
|
||||
|
||||
return enc_dec_message(crypto, msg, msg->payload, plaintext, len, false);
|
||||
}
|
||||
|
||||
static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz)
|
||||
{
|
||||
struct snp_guest_crypto *crypto = snp_dev->crypto;
|
||||
struct snp_guest_msg *resp_msg = &snp_dev->secret_response;
|
||||
struct snp_guest_msg *req_msg = &snp_dev->secret_request;
|
||||
struct snp_guest_msg *resp_msg = &mdesc->secret_response;
|
||||
struct snp_guest_msg *req_msg = &mdesc->secret_request;
|
||||
struct snp_guest_msg_hdr *req_msg_hdr = &req_msg->hdr;
|
||||
struct snp_guest_msg_hdr *resp_msg_hdr = &resp_msg->hdr;
|
||||
struct aesgcm_ctx *ctx = mdesc->ctx;
|
||||
u8 iv[GCM_AES_IV_SIZE] = {};
|
||||
|
||||
pr_debug("response [seqno %lld type %d version %d sz %d]\n",
|
||||
resp_msg_hdr->msg_seqno, resp_msg_hdr->msg_type, resp_msg_hdr->msg_version,
|
||||
resp_msg_hdr->msg_sz);
|
||||
|
||||
/* Copy response from shared memory to encrypted memory. */
|
||||
memcpy(resp_msg, snp_dev->response, sizeof(*resp_msg));
|
||||
memcpy(resp_msg, mdesc->response, sizeof(*resp_msg));
|
||||
|
||||
/* Verify that the sequence counter is incremented by 1 */
|
||||
if (unlikely(resp_msg_hdr->msg_seqno != (req_msg_hdr->msg_seqno + 1)))
|
||||
@ -316,29 +193,35 @@ static int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload,
|
||||
* If the message size is greater than our buffer length then return
|
||||
* an error.
|
||||
*/
|
||||
if (unlikely((resp_msg_hdr->msg_sz + crypto->a_len) > sz))
|
||||
if (unlikely((resp_msg_hdr->msg_sz + ctx->authsize) > req->resp_sz))
|
||||
return -EBADMSG;
|
||||
|
||||
/* Decrypt the payload */
|
||||
return dec_payload(snp_dev, resp_msg, payload, resp_msg_hdr->msg_sz + crypto->a_len);
|
||||
memcpy(iv, &resp_msg_hdr->msg_seqno, min(sizeof(iv), sizeof(resp_msg_hdr->msg_seqno)));
|
||||
if (!aesgcm_decrypt(ctx, req->resp_buf, resp_msg->payload, resp_msg_hdr->msg_sz,
|
||||
&resp_msg_hdr->algo, AAD_LEN, iv, resp_msg_hdr->authtag))
|
||||
return -EBADMSG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type,
|
||||
void *payload, size_t sz)
|
||||
static int enc_payload(struct snp_msg_desc *mdesc, u64 seqno, struct snp_guest_req *req)
|
||||
{
|
||||
struct snp_guest_msg *msg = &snp_dev->secret_request;
|
||||
struct snp_guest_msg *msg = &mdesc->secret_request;
|
||||
struct snp_guest_msg_hdr *hdr = &msg->hdr;
|
||||
struct aesgcm_ctx *ctx = mdesc->ctx;
|
||||
u8 iv[GCM_AES_IV_SIZE] = {};
|
||||
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
hdr->algo = SNP_AEAD_AES_256_GCM;
|
||||
hdr->hdr_version = MSG_HDR_VER;
|
||||
hdr->hdr_sz = sizeof(*hdr);
|
||||
hdr->msg_type = type;
|
||||
hdr->msg_version = version;
|
||||
hdr->msg_type = req->msg_type;
|
||||
hdr->msg_version = req->msg_version;
|
||||
hdr->msg_seqno = seqno;
|
||||
hdr->msg_vmpck = vmpck_id;
|
||||
hdr->msg_sz = sz;
|
||||
hdr->msg_vmpck = req->vmpck_id;
|
||||
hdr->msg_sz = req->req_sz;
|
||||
|
||||
/* Verify the sequence number is non-zero */
|
||||
if (!hdr->msg_seqno)
|
||||
@ -347,10 +230,17 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
|
||||
pr_debug("request [seqno %lld type %d version %d sz %d]\n",
|
||||
hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
|
||||
|
||||
return __enc_payload(snp_dev, msg, payload, sz);
|
||||
if (WARN_ON((req->req_sz + ctx->authsize) > sizeof(msg->payload)))
|
||||
return -EBADMSG;
|
||||
|
||||
memcpy(iv, &hdr->msg_seqno, min(sizeof(iv), sizeof(hdr->msg_seqno)));
|
||||
aesgcm_encrypt(ctx, msg->payload, req->req_buf, req->req_sz, &hdr->algo,
|
||||
AAD_LEN, iv, hdr->authtag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
|
||||
static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
|
||||
struct snp_guest_request_ioctl *rio)
|
||||
{
|
||||
unsigned long req_start = jiffies;
|
||||
@ -365,7 +255,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
|
||||
* sequence number must be incremented or the VMPCK must be deleted to
|
||||
* prevent reuse of the IV.
|
||||
*/
|
||||
rc = snp_issue_guest_request(exit_code, &snp_dev->input, rio);
|
||||
rc = snp_issue_guest_request(req, &mdesc->input, rio);
|
||||
switch (rc) {
|
||||
case -ENOSPC:
|
||||
/*
|
||||
@ -375,8 +265,8 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
|
||||
* order to increment the sequence number and thus avoid
|
||||
* IV reuse.
|
||||
*/
|
||||
override_npages = snp_dev->input.data_npages;
|
||||
exit_code = SVM_VMGEXIT_GUEST_REQUEST;
|
||||
override_npages = mdesc->input.data_npages;
|
||||
req->exit_code = SVM_VMGEXIT_GUEST_REQUEST;
|
||||
|
||||
/*
|
||||
* Override the error to inform callers the given extended
|
||||
@ -415,7 +305,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
|
||||
* structure and any failure will wipe the VMPCK, preventing further
|
||||
* use anyway.
|
||||
*/
|
||||
snp_inc_msg_seqno(snp_dev);
|
||||
snp_inc_msg_seqno(mdesc);
|
||||
|
||||
if (override_err) {
|
||||
rio->exitinfo2 = override_err;
|
||||
@ -431,29 +321,35 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
|
||||
}
|
||||
|
||||
if (override_npages)
|
||||
snp_dev->input.data_npages = override_npages;
|
||||
mdesc->input.data_npages = override_npages;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
|
||||
struct snp_guest_request_ioctl *rio, u8 type,
|
||||
void *req_buf, size_t req_sz, void *resp_buf,
|
||||
u32 resp_sz)
|
||||
static int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
|
||||
struct snp_guest_request_ioctl *rio)
|
||||
{
|
||||
u64 seqno;
|
||||
int rc;
|
||||
|
||||
guard(mutex)(&snp_cmd_mutex);
|
||||
|
||||
/* Check if the VMPCK is not empty */
|
||||
if (is_vmpck_empty(mdesc)) {
|
||||
pr_err_ratelimited("VMPCK is disabled\n");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
/* Get message sequence and verify that its a non-zero */
|
||||
seqno = snp_get_msg_seqno(snp_dev);
|
||||
seqno = snp_get_msg_seqno(mdesc);
|
||||
if (!seqno)
|
||||
return -EIO;
|
||||
|
||||
/* Clear shared memory's response for the host to populate. */
|
||||
memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
|
||||
memset(mdesc->response, 0, sizeof(struct snp_guest_msg));
|
||||
|
||||
/* Encrypt the userspace provided payload in snp_dev->secret_request. */
|
||||
rc = enc_payload(snp_dev, seqno, rio->msg_version, type, req_buf, req_sz);
|
||||
/* Encrypt the userspace provided payload in mdesc->secret_request. */
|
||||
rc = enc_payload(mdesc, seqno, req);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -461,27 +357,26 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
|
||||
* Write the fully encrypted request to the shared unencrypted
|
||||
* request page.
|
||||
*/
|
||||
memcpy(snp_dev->request, &snp_dev->secret_request,
|
||||
sizeof(snp_dev->secret_request));
|
||||
memcpy(mdesc->request, &mdesc->secret_request,
|
||||
sizeof(mdesc->secret_request));
|
||||
|
||||
rc = __handle_guest_request(snp_dev, exit_code, rio);
|
||||
rc = __handle_guest_request(mdesc, req, rio);
|
||||
if (rc) {
|
||||
if (rc == -EIO &&
|
||||
rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
|
||||
return rc;
|
||||
|
||||
dev_alert(snp_dev->dev,
|
||||
"Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
|
||||
rc, rio->exitinfo2);
|
||||
pr_alert("Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
|
||||
rc, rio->exitinfo2);
|
||||
|
||||
snp_disable_vmpck(snp_dev);
|
||||
snp_disable_vmpck(mdesc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = verify_and_dec_payload(snp_dev, resp_buf, resp_sz);
|
||||
rc = verify_and_dec_payload(mdesc, req);
|
||||
if (rc) {
|
||||
dev_alert(snp_dev->dev, "Detected unexpected decode failure from ASP. rc: %d\n", rc);
|
||||
snp_disable_vmpck(snp_dev);
|
||||
pr_alert("Detected unexpected decode failure from ASP. rc: %d\n", rc);
|
||||
snp_disable_vmpck(mdesc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -495,13 +390,12 @@ struct snp_req_resp {
|
||||
|
||||
static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
|
||||
{
|
||||
struct snp_guest_crypto *crypto = snp_dev->crypto;
|
||||
struct snp_report_req *report_req = &snp_dev->req.report;
|
||||
struct snp_msg_desc *mdesc = snp_dev->msg_desc;
|
||||
struct snp_report_resp *report_resp;
|
||||
struct snp_guest_req req = {};
|
||||
int rc, resp_len;
|
||||
|
||||
lockdep_assert_held(&snp_cmd_mutex);
|
||||
|
||||
if (!arg->req_data || !arg->resp_data)
|
||||
return -EINVAL;
|
||||
|
||||
@ -513,13 +407,21 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
|
||||
* response payload. Make sure that it has enough space to cover the
|
||||
* authtag.
|
||||
*/
|
||||
resp_len = sizeof(report_resp->data) + crypto->a_len;
|
||||
resp_len = sizeof(report_resp->data) + mdesc->ctx->authsize;
|
||||
report_resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
|
||||
if (!report_resp)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg, SNP_MSG_REPORT_REQ,
|
||||
report_req, sizeof(*report_req), report_resp->data, resp_len);
|
||||
req.msg_version = arg->msg_version;
|
||||
req.msg_type = SNP_MSG_REPORT_REQ;
|
||||
req.vmpck_id = vmpck_id;
|
||||
req.req_buf = report_req;
|
||||
req.req_sz = sizeof(*report_req);
|
||||
req.resp_buf = report_resp->data;
|
||||
req.resp_sz = resp_len;
|
||||
req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
|
||||
|
||||
rc = snp_send_guest_request(mdesc, &req, arg);
|
||||
if (rc)
|
||||
goto e_free;
|
||||
|
||||
@ -534,14 +436,13 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
|
||||
static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg)
|
||||
{
|
||||
struct snp_derived_key_req *derived_key_req = &snp_dev->req.derived_key;
|
||||
struct snp_guest_crypto *crypto = snp_dev->crypto;
|
||||
struct snp_derived_key_resp derived_key_resp = {0};
|
||||
struct snp_msg_desc *mdesc = snp_dev->msg_desc;
|
||||
struct snp_guest_req req = {};
|
||||
int rc, resp_len;
|
||||
/* Response data is 64 bytes and max authsize for GCM is 16 bytes. */
|
||||
u8 buf[64 + 16];
|
||||
|
||||
lockdep_assert_held(&snp_cmd_mutex);
|
||||
|
||||
if (!arg->req_data || !arg->resp_data)
|
||||
return -EINVAL;
|
||||
|
||||
@ -550,7 +451,7 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
|
||||
* response payload. Make sure that it has enough space to cover the
|
||||
* authtag.
|
||||
*/
|
||||
resp_len = sizeof(derived_key_resp.data) + crypto->a_len;
|
||||
resp_len = sizeof(derived_key_resp.data) + mdesc->ctx->authsize;
|
||||
if (sizeof(buf) < resp_len)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -558,8 +459,16 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
|
||||
sizeof(*derived_key_req)))
|
||||
return -EFAULT;
|
||||
|
||||
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg, SNP_MSG_KEY_REQ,
|
||||
derived_key_req, sizeof(*derived_key_req), buf, resp_len);
|
||||
req.msg_version = arg->msg_version;
|
||||
req.msg_type = SNP_MSG_KEY_REQ;
|
||||
req.vmpck_id = vmpck_id;
|
||||
req.req_buf = derived_key_req;
|
||||
req.req_sz = sizeof(*derived_key_req);
|
||||
req.resp_buf = buf;
|
||||
req.resp_sz = resp_len;
|
||||
req.exit_code = SVM_VMGEXIT_GUEST_REQUEST;
|
||||
|
||||
rc = snp_send_guest_request(mdesc, &req, arg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -579,13 +488,12 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
|
||||
|
||||
{
|
||||
struct snp_ext_report_req *report_req = &snp_dev->req.ext_report;
|
||||
struct snp_guest_crypto *crypto = snp_dev->crypto;
|
||||
struct snp_msg_desc *mdesc = snp_dev->msg_desc;
|
||||
struct snp_report_resp *report_resp;
|
||||
struct snp_guest_req req = {};
|
||||
int ret, npages = 0, resp_len;
|
||||
sockptr_t certs_address;
|
||||
|
||||
lockdep_assert_held(&snp_cmd_mutex);
|
||||
|
||||
if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data))
|
||||
return -EINVAL;
|
||||
|
||||
@ -614,7 +522,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
|
||||
* the host. If host does not supply any certs in it, then copy
|
||||
* zeros to indicate that certificate data was not provided.
|
||||
*/
|
||||
memset(snp_dev->certs_data, 0, report_req->certs_len);
|
||||
memset(mdesc->certs_data, 0, report_req->certs_len);
|
||||
npages = report_req->certs_len >> PAGE_SHIFT;
|
||||
cmd:
|
||||
/*
|
||||
@ -622,19 +530,27 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
|
||||
* response payload. Make sure that it has enough space to cover the
|
||||
* authtag.
|
||||
*/
|
||||
resp_len = sizeof(report_resp->data) + crypto->a_len;
|
||||
resp_len = sizeof(report_resp->data) + mdesc->ctx->authsize;
|
||||
report_resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT);
|
||||
if (!report_resp)
|
||||
return -ENOMEM;
|
||||
|
||||
snp_dev->input.data_npages = npages;
|
||||
ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg, SNP_MSG_REPORT_REQ,
|
||||
&report_req->data, sizeof(report_req->data),
|
||||
report_resp->data, resp_len);
|
||||
mdesc->input.data_npages = npages;
|
||||
|
||||
req.msg_version = arg->msg_version;
|
||||
req.msg_type = SNP_MSG_REPORT_REQ;
|
||||
req.vmpck_id = vmpck_id;
|
||||
req.req_buf = &report_req->data;
|
||||
req.req_sz = sizeof(report_req->data);
|
||||
req.resp_buf = report_resp->data;
|
||||
req.resp_sz = resp_len;
|
||||
req.exit_code = SVM_VMGEXIT_EXT_GUEST_REQUEST;
|
||||
|
||||
ret = snp_send_guest_request(mdesc, &req, arg);
|
||||
|
||||
/* If certs length is invalid then copy the returned length */
|
||||
if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) {
|
||||
report_req->certs_len = snp_dev->input.data_npages << PAGE_SHIFT;
|
||||
report_req->certs_len = mdesc->input.data_npages << PAGE_SHIFT;
|
||||
|
||||
if (copy_to_sockptr(io->req_data, report_req, sizeof(*report_req)))
|
||||
ret = -EFAULT;
|
||||
@ -643,7 +559,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
|
||||
if (ret)
|
||||
goto e_free;
|
||||
|
||||
if (npages && copy_to_sockptr(certs_address, snp_dev->certs_data, report_req->certs_len)) {
|
||||
if (npages && copy_to_sockptr(certs_address, mdesc->certs_data, report_req->certs_len)) {
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
@ -673,15 +589,6 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
|
||||
if (!input.msg_version)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&snp_cmd_mutex);
|
||||
|
||||
/* Check if the VMPCK is not empty */
|
||||
if (is_vmpck_empty(snp_dev)) {
|
||||
dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
|
||||
mutex_unlock(&snp_cmd_mutex);
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
switch (ioctl) {
|
||||
case SNP_GET_REPORT:
|
||||
ret = get_report(snp_dev, &input);
|
||||
@ -703,8 +610,6 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&snp_cmd_mutex);
|
||||
|
||||
if (input.exitinfo2 && copy_to_user(argp, &input, sizeof(input)))
|
||||
return -EFAULT;
|
||||
|
||||
@ -819,8 +724,6 @@ static int sev_svsm_report_new(struct tsm_report *report, void *data)
|
||||
man_len = SZ_4K;
|
||||
certs_len = SEV_FW_BLOB_MAX_SIZE;
|
||||
|
||||
guard(mutex)(&snp_cmd_mutex);
|
||||
|
||||
if (guid_is_null(&desc->service_guid)) {
|
||||
call_id = SVSM_ATTEST_CALL(SVSM_ATTEST_SERVICES);
|
||||
} else {
|
||||
@ -955,14 +858,6 @@ static int sev_report_new(struct tsm_report *report, void *data)
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
guard(mutex)(&snp_cmd_mutex);
|
||||
|
||||
/* Check if the VMPCK is not empty */
|
||||
if (is_vmpck_empty(snp_dev)) {
|
||||
dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cert_table = buf + report_size;
|
||||
struct snp_ext_report_req ext_req = {
|
||||
.data = { .vmpl = desc->privlevel },
|
||||
@ -1088,6 +983,7 @@ static int __init sev_guest_probe(struct platform_device *pdev)
|
||||
struct snp_secrets_page *secrets;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct snp_guest_dev *snp_dev;
|
||||
struct snp_msg_desc *mdesc;
|
||||
struct miscdevice *misc;
|
||||
void __iomem *mapping;
|
||||
int ret;
|
||||
@ -1112,43 +1008,47 @@ static int __init sev_guest_probe(struct platform_device *pdev)
|
||||
if (!snp_dev)
|
||||
goto e_unmap;
|
||||
|
||||
mdesc = devm_kzalloc(&pdev->dev, sizeof(struct snp_msg_desc), GFP_KERNEL);
|
||||
if (!mdesc)
|
||||
goto e_unmap;
|
||||
|
||||
/* Adjust the default VMPCK key based on the executing VMPL level */
|
||||
if (vmpck_id == -1)
|
||||
vmpck_id = snp_vmpl;
|
||||
|
||||
ret = -EINVAL;
|
||||
snp_dev->vmpck = get_vmpck(vmpck_id, secrets, &snp_dev->os_area_msg_seqno);
|
||||
if (!snp_dev->vmpck) {
|
||||
mdesc->vmpck = get_vmpck(vmpck_id, secrets, &mdesc->os_area_msg_seqno);
|
||||
if (!mdesc->vmpck) {
|
||||
dev_err(dev, "Invalid VMPCK%d communication key\n", vmpck_id);
|
||||
goto e_unmap;
|
||||
}
|
||||
|
||||
/* Verify that VMPCK is not zero. */
|
||||
if (is_vmpck_empty(snp_dev)) {
|
||||
if (is_vmpck_empty(mdesc)) {
|
||||
dev_err(dev, "Empty VMPCK%d communication key\n", vmpck_id);
|
||||
goto e_unmap;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, snp_dev);
|
||||
snp_dev->dev = dev;
|
||||
snp_dev->secrets = secrets;
|
||||
mdesc->secrets = secrets;
|
||||
|
||||
/* Allocate the shared page used for the request and response message. */
|
||||
snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
|
||||
if (!snp_dev->request)
|
||||
mdesc->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
|
||||
if (!mdesc->request)
|
||||
goto e_unmap;
|
||||
|
||||
snp_dev->response = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
|
||||
if (!snp_dev->response)
|
||||
mdesc->response = alloc_shared_pages(dev, sizeof(struct snp_guest_msg));
|
||||
if (!mdesc->response)
|
||||
goto e_free_request;
|
||||
|
||||
snp_dev->certs_data = alloc_shared_pages(dev, SEV_FW_BLOB_MAX_SIZE);
|
||||
if (!snp_dev->certs_data)
|
||||
mdesc->certs_data = alloc_shared_pages(dev, SEV_FW_BLOB_MAX_SIZE);
|
||||
if (!mdesc->certs_data)
|
||||
goto e_free_response;
|
||||
|
||||
ret = -EIO;
|
||||
snp_dev->crypto = init_crypto(snp_dev, snp_dev->vmpck, VMPCK_KEY_LEN);
|
||||
if (!snp_dev->crypto)
|
||||
mdesc->ctx = snp_init_crypto(mdesc->vmpck, VMPCK_KEY_LEN);
|
||||
if (!mdesc->ctx)
|
||||
goto e_free_cert_data;
|
||||
|
||||
misc = &snp_dev->misc;
|
||||
@ -1156,10 +1056,10 @@ static int __init sev_guest_probe(struct platform_device *pdev)
|
||||
misc->name = DEVICE_NAME;
|
||||
misc->fops = &snp_guest_fops;
|
||||
|
||||
/* initial the input address for guest request */
|
||||
snp_dev->input.req_gpa = __pa(snp_dev->request);
|
||||
snp_dev->input.resp_gpa = __pa(snp_dev->response);
|
||||
snp_dev->input.data_gpa = __pa(snp_dev->certs_data);
|
||||
/* Initialize the input addresses for guest request */
|
||||
mdesc->input.req_gpa = __pa(mdesc->request);
|
||||
mdesc->input.resp_gpa = __pa(mdesc->response);
|
||||
mdesc->input.data_gpa = __pa(mdesc->certs_data);
|
||||
|
||||
/* Set the privlevel_floor attribute based on the vmpck_id */
|
||||
sev_tsm_ops.privlevel_floor = vmpck_id;
|
||||
@ -1174,17 +1074,20 @@ static int __init sev_guest_probe(struct platform_device *pdev)
|
||||
|
||||
ret = misc_register(misc);
|
||||
if (ret)
|
||||
goto e_free_cert_data;
|
||||
goto e_free_ctx;
|
||||
|
||||
snp_dev->msg_desc = mdesc;
|
||||
dev_info(dev, "Initialized SEV guest driver (using VMPCK%d communication key)\n", vmpck_id);
|
||||
return 0;
|
||||
|
||||
e_free_ctx:
|
||||
kfree(mdesc->ctx);
|
||||
e_free_cert_data:
|
||||
free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
|
||||
free_shared_pages(mdesc->certs_data, SEV_FW_BLOB_MAX_SIZE);
|
||||
e_free_response:
|
||||
free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
|
||||
free_shared_pages(mdesc->response, sizeof(struct snp_guest_msg));
|
||||
e_free_request:
|
||||
free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
|
||||
free_shared_pages(mdesc->request, sizeof(struct snp_guest_msg));
|
||||
e_unmap:
|
||||
iounmap(mapping);
|
||||
return ret;
|
||||
@ -1193,11 +1096,12 @@ static int __init sev_guest_probe(struct platform_device *pdev)
|
||||
static void __exit sev_guest_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snp_guest_dev *snp_dev = platform_get_drvdata(pdev);
|
||||
struct snp_msg_desc *mdesc = snp_dev->msg_desc;
|
||||
|
||||
free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE);
|
||||
free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg));
|
||||
free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
|
||||
deinit_crypto(snp_dev->crypto);
|
||||
free_shared_pages(mdesc->certs_data, SEV_FW_BLOB_MAX_SIZE);
|
||||
free_shared_pages(mdesc->response, sizeof(struct snp_guest_msg));
|
||||
free_shared_pages(mdesc->request, sizeof(struct snp_guest_msg));
|
||||
kfree(mdesc->ctx);
|
||||
misc_deregister(&snp_dev->misc);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user