diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 68547fc20ac3c928e468223062377b02fdc75ac0..2adf01ebd236d49f7542299d453f22d41118acf5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1359,16 +1359,20 @@ struct kvm_x86_ops { void (*migrate_timers)(struct kvm_vcpu *vcpu); void (*msr_filter_changed)(struct kvm_vcpu *vcpu); - int (*vm_attestation)(struct kvm *kvm, unsigned long gpa, unsigned long len); - int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err); void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector); + int (*arch_hypercall)(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64 a2, u64 a3); + + /* + * Interfaces for HYGON CSV guest + */ + int (*vm_attestation)(struct kvm *kvm, unsigned long gpa, unsigned long len); int (*control_pre_system_reset)(struct kvm *kvm); int (*control_post_system_reset)(struct kvm *kvm); - - int (*arch_hypercall)(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64 a2, u64 a3); + int (*get_hygon_coco_extension)(struct kvm *kvm); + int (*enable_hygon_coco_extension)(struct kvm *kvm, u32 arg); }; struct kvm_x86_nested_ops { diff --git a/arch/x86/include/asm/processor-hygon.h b/arch/x86/include/asm/processor-hygon.h new file mode 100644 index 0000000000000000000000000000000000000000..a19bda3ed00561cec551775e20c2bedaabe64fb1 --- /dev/null +++ b/arch/x86/include/asm/processor-hygon.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The helpers to support Hygon CPU specific code path. + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + */ + +#ifndef _ASM_X86_PROCESSOR_HYGON_H +#define _ASM_X86_PROCESSOR_HYGON_H + +#include + +/* + * helper to determine HYGON CPU + */ +static inline bool is_x86_vendor_hygon(void) +{ + return boot_cpu_data.x86_vendor == X86_VENDOR_HYGON; +} + +#endif /* _ASM_X86_PROCESSOR_HYGON_H */ diff --git a/arch/x86/kvm/svm/csv.c b/arch/x86/kvm/svm/csv.c index 3956c3b08eacedace1b36b572a6f8935b52ae6f6..2751ef92c60ce434acd4a388c71e1fa91b1bf52d 100644 --- a/arch/x86/kvm/svm/csv.c +++ b/arch/x86/kvm/svm/csv.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,9 @@ #undef pr_fmt #define pr_fmt(fmt) "CSV: " fmt +/* Function and variable pointers for hooks */ +struct hygon_kvm_hooks_table hygon_kvm_hooks; + struct encrypt_data_block { struct { u64 npages: 12; @@ -93,6 +97,13 @@ struct kvm_csv_info { struct list_head smr_list; /* List of guest secure memory regions */ unsigned long nodemask; /* Nodemask where CSV guest's memory resides */ + + /* The following 5 fields record the extension status for current VM */ + bool fw_ext_valid; /* if @fw_ext field is valid */ + u32 fw_ext; /* extensions supported by current platform */ + bool kvm_ext_valid; /* if @kvm_ext field is valid */ + u32 kvm_ext; /* extensions supported by KVM */ + u32 inuse_ext; /* extensions inused by current VM */ }; struct kvm_svm_csv { @@ -197,7 +208,7 @@ static int to_csv_pg_level(int level) return ret; } -static bool csv_guest(struct kvm *kvm) +static bool csv3_guest(struct kvm *kvm) { struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; @@ -214,11 +225,37 @@ static int csv_sync_vmsa(struct vcpu_svm *svm) /* Sync registgers per spec. */ save->rax = svm->vcpu.arch.regs[VCPU_REGS_RAX]; + save->rbx = svm->vcpu.arch.regs[VCPU_REGS_RBX]; + save->rcx = svm->vcpu.arch.regs[VCPU_REGS_RCX]; save->rdx = svm->vcpu.arch.regs[VCPU_REGS_RDX]; + save->rsp = svm->vcpu.arch.regs[VCPU_REGS_RSP]; + save->rbp = svm->vcpu.arch.regs[VCPU_REGS_RBP]; + save->rsi = svm->vcpu.arch.regs[VCPU_REGS_RSI]; + save->rdi = svm->vcpu.arch.regs[VCPU_REGS_RDI]; +#ifdef CONFIG_X86_64 + save->r8 = svm->vcpu.arch.regs[VCPU_REGS_R8]; + save->r9 = svm->vcpu.arch.regs[VCPU_REGS_R9]; + save->r10 = svm->vcpu.arch.regs[VCPU_REGS_R10]; + save->r11 = svm->vcpu.arch.regs[VCPU_REGS_R11]; + save->r12 = svm->vcpu.arch.regs[VCPU_REGS_R12]; + save->r13 = svm->vcpu.arch.regs[VCPU_REGS_R13]; + save->r14 = svm->vcpu.arch.regs[VCPU_REGS_R14]; + save->r15 = svm->vcpu.arch.regs[VCPU_REGS_R15]; +#endif save->rip = svm->vcpu.arch.regs[VCPU_REGS_RIP]; + + /* Sync some non-GPR registers before encrypting */ save->xcr0 = svm->vcpu.arch.xcr0; + save->pkru = svm->vcpu.arch.pkru; save->xss = svm->vcpu.arch.ia32_xss; + save->dr6 = svm->vcpu.arch.dr6; + /* + * CSV3 will use a VMSA that is pointed to by the VMCB, not + * the traditional VMSA that is part of the VMCB. Copy the + * traditional VMSA as it has been built so far (in prep + * for LAUNCH_ENCRYPT_VMCB) to be the initial CSV3 state. + */ memcpy(svm->vmsa, save, sizeof(*save)); return 0; } @@ -299,14 +336,14 @@ static bool csv_is_mmio_pfn(kvm_pfn_t pfn) E820_TYPE_RAM); } -static int csv_set_guest_private_memory(struct kvm *kvm) +static int csv3_set_guest_private_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) { struct kvm_memslots *slots = kvm_memslots(kvm); struct kvm_memory_slot *memslot; struct secure_memory_region *smr; struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; - struct csv_data_set_guest_private_memory *set_guest_private_memory; + struct csv3_data_set_guest_private_memory *set_guest_private_memory; struct csv_data_memory_region *regions; nodemask_t nodemask; nodemask_t *nodemask_ptr; @@ -314,7 +351,7 @@ static int csv_set_guest_private_memory(struct kvm *kvm) LIST_HEAD(tmp_list); struct list_head *pos, *q; u32 i = 0, count = 0, remainder; - int ret = 0, error; + int ret = 0; u64 size = 0, nr_smr = 0, nr_pages = 0; u32 smr_entry_shift; @@ -322,9 +359,13 @@ static int csv_set_guest_private_memory(struct kvm *kvm) int npages; struct page *page; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; + /* The smr_list should be initialized only once */ + if (!list_empty(&csv->smr_list)) + return -EFAULT; + nodes_clear(nodemask); for_each_set_bit(i, &csv->nodemask, BITS_PER_LONG) if (i < MAX_NUMNODES) @@ -405,8 +446,8 @@ static int csv_set_guest_private_memory(struct kvm *kvm) set_guest_private_memory->regions_paddr = __sme_pa(regions); /* set secury memory region for launch enrypt data */ - ret = csv_issue_cmd(kvm, CSV_CMD_SET_GUEST_PRIVATE_MEMORY, - set_guest_private_memory, &error); + ret = csv_issue_cmd(kvm, CSV3_CMD_SET_GUEST_PRIVATE_MEMORY, + set_guest_private_memory, &argp->error); if (ret) goto e_free_smr; @@ -438,11 +479,16 @@ static int csv_set_guest_private_memory(struct kvm *kvm) return ret; } -static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) +/** + * csv3_launch_encrypt_data_alt_1 - The legacy handler to encrypt CSV3 + * guest's memory before VMRUN. + */ +static int csv3_launch_encrypt_data_alt_1(struct kvm *kvm, + struct kvm_sev_cmd *argp) { struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; - struct kvm_csv_launch_encrypt_data params; - struct csv_data_launch_encrypt_data *encrypt_data = NULL; + struct kvm_csv3_launch_encrypt_data params; + struct csv3_data_launch_encrypt_data *encrypt_data = NULL; struct encrypt_data_block *blocks = NULL; u8 *data = NULL; u32 offset; @@ -452,9 +498,6 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) unsigned long pfn, pfn_sme_mask; int ret = 0; - if (!csv_guest(kvm)) - return -ENOTTY; - if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) { ret = -EFAULT; @@ -466,10 +509,17 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) goto exit; } - /* Allocate all the guest memory from CMA */ - ret = csv_set_guest_private_memory(kvm); - if (ret) - goto exit; + /* + * If userspace request to invoke CSV3_CMD_SET_GUEST_PRIVATE_MEMORY + * explicitly, we should not calls to csv3_set_guest_private_memory() + * here. + */ + if (!(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM)) { + /* Allocate all the guest memory from CMA */ + ret = csv3_set_guest_private_memory(kvm, argp); + if (ret) + goto exit; + } num_entries = params.len / PAGE_SIZE; num_entries_in_block = ARRAY_SIZE(blocks->entry); @@ -527,7 +577,7 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) } clflush_cache_range(data, params.len); - ret = csv_issue_cmd(kvm, CSV_CMD_LAUNCH_ENCRYPT_DATA, + ret = csv_issue_cmd(kvm, CSV3_CMD_LAUNCH_ENCRYPT_DATA, encrypt_data, &argp->error); kfree(encrypt_data); @@ -539,6 +589,224 @@ static int csv_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) return ret; } +#define MAX_ENTRIES_PER_BLOCK \ + (sizeof(((struct encrypt_data_block *)0)->entry) / \ + sizeof(((struct encrypt_data_block *)0)->entry[0])) +#define MAX_BLOCKS_PER_CSV3_LUP_DATA \ + (sizeof(((struct csv3_data_launch_encrypt_data *)0)->data_blocks) / \ + sizeof(((struct csv3_data_launch_encrypt_data *)0)->data_blocks[0])) +#define MAX_ENTRIES_PER_CSV3_LUP_DATA \ + (MAX_BLOCKS_PER_CSV3_LUP_DATA * MAX_ENTRIES_PER_BLOCK) + +/** + * __csv3_launch_encrypt_data - The helper for handler + * csv3_launch_encrypt_data_alt_2. + */ +static int __csv3_launch_encrypt_data(struct kvm *kvm, + struct kvm_sev_cmd *argp, + struct kvm_csv3_launch_encrypt_data *params, + void *src_buf, + unsigned int start_pgoff, + unsigned int end_pgoff) +{ + struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; + struct csv3_data_launch_encrypt_data *data = NULL; + struct encrypt_data_block *block = NULL; + struct page **pages = NULL; + unsigned long len, remain_len; + unsigned long pfn, pfn_sme_mask, last_pfn; + unsigned int pgoff = start_pgoff; + int i, j; + int ret = -ENOMEM; + + /* Alloc command buffer for CSV3_CMD_LAUNCH_ENCRYPT_DATA command */ + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT); + if (!data) + return -ENOMEM; + + /* Alloc pages for data_blocks[] in the command buffer */ + len = ARRAY_SIZE(data->data_blocks) * sizeof(struct page *); + pages = kzalloc(len, GFP_KERNEL_ACCOUNT); + if (!pages) + goto e_free_data; + + for (i = 0; i < ARRAY_SIZE(data->data_blocks); i++) { + pages[i] = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!pages[i]) + goto e_free_pages; + } + + i = 0; + while (i < ARRAY_SIZE(data->data_blocks) && pgoff < end_pgoff) { + block = (struct encrypt_data_block *)page_to_virt(pages[i]); + + j = 0; + last_pfn = 0; + while (j < ARRAY_SIZE(block->entry) && pgoff < end_pgoff) { + pfn = vmalloc_to_pfn(src_buf + (pgoff << PAGE_SHIFT)); + pfn_sme_mask = __sme_set(pfn << PAGE_SHIFT) >> PAGE_SHIFT; + + /* + * One entry can record a number of contiguous physical + * pages. If the current page is not adjacent to the + * previous physical page, we should record the page to + * the next entry. If entries of current block is used + * up, we should try the next block. + */ + if (last_pfn && (last_pfn + 1 == pfn)) { + block->entry[j].npages++; + } else if (j < (ARRAY_SIZE(block->entry) - 1)) { + /* @last_pfn == 0 means fill in entry[0] */ + if (likely(last_pfn != 0)) + j++; + block->entry[j].pfn = pfn_sme_mask; + block->entry[j].npages = 1; + } else { + break; + } + + /* + * Succeed to record one page, increase the page offset. + * We also record the pfn of current page so that we can + * record the contiguous physical pages into one entry. + */ + last_pfn = pfn; + pgoff++; + } + + i++; + } + + if (pgoff < end_pgoff) { + pr_err("CSV3: Fail to fill in LAUNCH_ENCRYPT_DATA command!\n"); + goto e_free_pages; + } + + len = (end_pgoff - start_pgoff) << PAGE_SHIFT; + clflush_cache_range(src_buf + (start_pgoff << PAGE_SHIFT), len); + + /* Fill in command buffer */ + data->handle = csv->sev->handle; + + if (start_pgoff == 0) { + data->gpa = params->gpa; + len -= params->gpa & ~PAGE_MASK; + } else { + data->gpa = (params->gpa & PAGE_MASK) + (start_pgoff << PAGE_SHIFT); + } + remain_len = params->len - (data->gpa - params->gpa); + + data->length = (len <= remain_len) ? len : remain_len; + + for (j = 0; j < i; j++) + data->data_blocks[j] = __sme_set(page_to_phys(pages[j])); + + /* Issue command */ + ret = csv_issue_cmd(kvm, CSV3_CMD_LAUNCH_ENCRYPT_DATA, data, &argp->error); + +e_free_pages: + for (i = 0; i < ARRAY_SIZE(data->data_blocks); i++) { + if (pages[i]) + __free_page(pages[i]); + } + kfree(pages); +e_free_data: + kfree(data); + + return ret; +} + +/** + * csv3_launch_encrypt_data_alt_2 - The handler to support encrypt CSV3 + * guest's memory before VMRUN. This handler support issue API command + * multiple times, both the GPA and length of the memory region are not + * required to be 4K-aligned. + */ +static int csv3_launch_encrypt_data_alt_2(struct kvm *kvm, + struct kvm_sev_cmd *argp) +{ + struct kvm_csv3_launch_encrypt_data params; + void *buffer = NULL; + unsigned long len; + unsigned int total_pages, start_pgoff, next_pgoff; + int ret = 0; + + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, + sizeof(params))) { + return -EFAULT; + } + + /* Both the GPA and length must be 16 Bytes aligned at least */ + if (!params.len || + !params.uaddr || + !IS_ALIGNED(params.len, 16) || + !IS_ALIGNED(params.gpa, 16)) { + return -EINVAL; + } + + /* + * Alloc buffer to save source data. When we copy source data from + * userspace to the buffer, the data in the first page of the buffer + * should keep the offset as params.gpa. + */ + len = PAGE_ALIGN((params.gpa & ~PAGE_MASK) + params.len); + total_pages = len >> PAGE_SHIFT; + next_pgoff = 0; + + buffer = vzalloc(len); + if (!buffer) + return -ENOMEM; + + if (copy_from_user(buffer + (params.gpa & ~PAGE_MASK), + (void __user *)params.uaddr, params.len)) { + ret = -EFAULT; + goto e_free_buffer; + } + + /* + * If the source data is too large, we should issue command more than + * once. The LAUNCH_ENCRYPT_DATA API updates not only the measurement + * of the data, but also the measurement of the metadata correspond to + * the data. The guest owner is obligated to verify the launch + * measurement, so guest owner must be aware of the launch measurement + * of each LAUNCH_ENCRYPT_DATA API command. If we processing pages more + * than MAX_ENTRIES_PER_CSV3_LUP_DATA in each API command, the guest + * owner could not able to calculate the correct measurement and fail + * to verify the launch measurement. For this reason, we limit the + * maximum number of pages processed by each API command to + * MAX_ENTRIES_PER_CSV3_LUP_DATA. + */ + while (next_pgoff < total_pages) { + start_pgoff = next_pgoff; + next_pgoff += MAX_ENTRIES_PER_CSV3_LUP_DATA; + + if (next_pgoff > total_pages) + next_pgoff = total_pages; + + ret = __csv3_launch_encrypt_data(kvm, argp, ¶ms, + buffer, start_pgoff, next_pgoff); + if (ret) + goto e_free_buffer; + } + +e_free_buffer: + vfree(buffer); + return ret; +} + +static int csv3_launch_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) +{ + struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; + + if (!csv3_guest(kvm)) + return -ENOTTY; + + if (!(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA)) + return csv3_launch_encrypt_data_alt_1(kvm, argp); + + return csv3_launch_encrypt_data_alt_2(kvm, argp); +} + static int csv_launch_encrypt_vmcb(struct kvm *kvm, struct kvm_sev_cmd *argp) { struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; @@ -547,7 +815,7 @@ static int csv_launch_encrypt_vmcb(struct kvm *kvm, struct kvm_sev_cmd *argp) int ret = 0; unsigned long i = 0; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; encrypt_vmcb = kzalloc(sizeof(*encrypt_vmcb), GFP_KERNEL); @@ -623,7 +891,7 @@ static int csv_send_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) int ret = 0; int i; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -775,7 +1043,7 @@ static int csv_send_encrypt_context(struct kvm *kvm, struct kvm_sev_cmd *argp) u32 offset; int ret = 0; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -870,12 +1138,12 @@ static int csv_receive_encrypt_data(struct kvm *kvm, struct kvm_sev_cmd *argp) u32 offset; int ret = 0; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (unlikely(list_empty(&csv->smr_list))) { /* Allocate all the guest memory from CMA */ - ret = csv_set_guest_private_memory(kvm); + ret = csv3_set_guest_private_memory(kvm, argp); if (ret) goto exit; } @@ -990,7 +1258,7 @@ static int csv_receive_encrypt_context(struct kvm *kvm, struct kvm_sev_cmd *argp struct kvm_vcpu *vcpu; unsigned long i; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -1377,7 +1645,7 @@ static void csv_vm_destroy(struct kvm *kvm) struct rb_node *node; unsigned long i = 0; - if (csv_guest(kvm)) { + if (csv3_guest(kvm)) { mutex_lock(&csv->sp_lock); while ((node = rb_first(&csv->sp_mgr.root))) { sp = rb_entry(node, struct shared_page, node); @@ -1400,7 +1668,7 @@ static void csv_vm_destroy(struct kvm *kvm) if (likely(csv_x86_ops.vm_destroy)) csv_x86_ops.vm_destroy(kvm); - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return; /* free secure memory region */ @@ -1444,7 +1712,7 @@ static int csv_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) /* * NPF for csv is dedicated. */ - if (csv_guest(vcpu->kvm) && exit_code == SVM_EXIT_NPF) { + if (csv3_guest(vcpu->kvm) && exit_code == SVM_EXIT_NPF) { gpa_t gpa = __sme_clr(svm->vmcb->control.exit_info_2); u64 error_code = svm->vmcb->control.exit_info_1; @@ -1459,7 +1727,7 @@ static int csv_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) static void csv_guest_memory_reclaimed(struct kvm *kvm) { - if (!csv_guest(kvm)) { + if (!csv3_guest(kvm)) { if (likely(csv_x86_ops.guest_memory_reclaimed)) csv_x86_ops.guest_memory_reclaimed(kvm); } @@ -1470,7 +1738,7 @@ static int csv_handle_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) struct kvm_csv_handle_memory params; int r = -EINVAL; - if (!csv_guest(kvm)) + if (!csv3_guest(kvm)) return -ENOTTY; if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, @@ -1478,7 +1746,7 @@ static int csv_handle_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) return -EFAULT; switch (params.opcode) { - case KVM_CSV_RELEASE_SHARED_MEMORY: + case KVM_CSV3_RELEASE_SHARED_MEMORY: r = csv_unpin_shared_memory(kvm, params.gpa, params.num_pages); break; default: @@ -1488,11 +1756,108 @@ static int csv_handle_memory(struct kvm *kvm, struct kvm_sev_cmd *argp) return r; }; +static int csv_launch_secret(struct kvm *kvm, struct kvm_sev_cmd *argp) +{ + struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info; + struct kvm_csv_info *csv = &to_kvm_svm_csv(kvm)->csv_info; + struct sev_data_launch_secret *data; + struct kvm_sev_launch_secret params; + struct page **pages; + void *blob, *hdr; + unsigned long n, i; + int ret, offset; + + if (!sev_guest(kvm)) + return -ENOTTY; + + if (copy_from_user(¶ms, (void __user *)(uintptr_t)argp->data, sizeof(params))) + return -EFAULT; + + data = kzalloc(sizeof(*data), GFP_KERNEL_ACCOUNT); + if (!data) + return -ENOMEM; + + if (!csv3_guest(kvm) || + !(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET)) { + pages = hygon_kvm_hooks.sev_pin_memory(kvm, params.guest_uaddr, + params.guest_len, &n, 1); + if (IS_ERR(pages)) { + ret = PTR_ERR(pages); + goto e_free_data; + } + + /* + * Flush (on non-coherent CPUs) before LAUNCH_SECRET encrypts pages in + * place; the cache may contain the data that was written unencrypted. + */ + hygon_kvm_hooks.sev_clflush_pages(pages, n); + + /* + * The secret must be copied into contiguous memory region, lets verify + * that userspace memory pages are contiguous before we issue command. + */ + if (hygon_kvm_hooks.get_num_contig_pages(0, pages, n) != n) { + ret = -EINVAL; + goto e_unpin_memory; + } + + offset = params.guest_uaddr & (PAGE_SIZE - 1); + data->guest_address = __sme_page_pa(pages[0]) + offset; + } else { + /* It's gpa for CSV3 guest */ + data->guest_address = params.guest_uaddr; + } + data->guest_len = params.guest_len; + + blob = psp_copy_user_blob(params.trans_uaddr, params.trans_len); + if (IS_ERR(blob)) { + ret = PTR_ERR(blob); + goto e_unpin_memory; + } + + data->trans_address = __psp_pa(blob); + data->trans_len = params.trans_len; + + hdr = psp_copy_user_blob(params.hdr_uaddr, params.hdr_len); + if (IS_ERR(hdr)) { + ret = PTR_ERR(hdr); + goto e_free_blob; + } + data->hdr_address = __psp_pa(hdr); + data->hdr_len = params.hdr_len; + + data->handle = sev->handle; + ret = hygon_kvm_hooks.sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_SECRET, + data, &argp->error); + + kfree(hdr); + +e_free_blob: + kfree(blob); +e_unpin_memory: + if (!csv3_guest(kvm) || + !(csv->inuse_ext & KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET)) { + /* content of memory is updated, mark pages dirty */ + for (i = 0; i < n; i++) { + set_page_dirty_lock(pages[i]); + mark_page_accessed(pages[i]); + } + hygon_kvm_hooks.sev_unpin_memory(kvm, pages, n); + } +e_free_data: + kfree(data); + return ret; +} + static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) { struct kvm_sev_cmd sev_cmd; int r = -EINVAL; + if (!hygon_kvm_hooks.sev_hooks_installed || + !(*hygon_kvm_hooks.sev_enabled)) + return -ENOTTY; + if (!argp) return 0; @@ -1502,30 +1867,36 @@ static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) mutex_lock(&kvm->lock); switch (sev_cmd.id) { - case KVM_CSV_INIT: + case KVM_SEV_LAUNCH_SECRET: + r = csv_launch_secret(kvm, &sev_cmd); + break; + case KVM_CSV3_INIT: r = csv_guest_init(kvm, &sev_cmd); break; - case KVM_CSV_LAUNCH_ENCRYPT_DATA: - r = csv_launch_encrypt_data(kvm, &sev_cmd); + case KVM_CSV3_LAUNCH_ENCRYPT_DATA: + r = csv3_launch_encrypt_data(kvm, &sev_cmd); break; - case KVM_CSV_LAUNCH_ENCRYPT_VMCB: + case KVM_CSV3_LAUNCH_ENCRYPT_VMCB: r = csv_launch_encrypt_vmcb(kvm, &sev_cmd); break; - case KVM_CSV_SEND_ENCRYPT_DATA: + case KVM_CSV3_SEND_ENCRYPT_DATA: r = csv_send_encrypt_data(kvm, &sev_cmd); break; - case KVM_CSV_SEND_ENCRYPT_CONTEXT: + case KVM_CSV3_SEND_ENCRYPT_CONTEXT: r = csv_send_encrypt_context(kvm, &sev_cmd); break; - case KVM_CSV_RECEIVE_ENCRYPT_DATA: + case KVM_CSV3_RECEIVE_ENCRYPT_DATA: r = csv_receive_encrypt_data(kvm, &sev_cmd); break; - case KVM_CSV_RECEIVE_ENCRYPT_CONTEXT: + case KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT: r = csv_receive_encrypt_context(kvm, &sev_cmd); break; - case KVM_CSV_HANDLE_MEMORY: + case KVM_CSV3_HANDLE_MEMORY: r = csv_handle_memory(kvm, &sev_cmd); break; + case KVM_CSV3_SET_GUEST_PRIVATE_MEMORY: + r = csv3_set_guest_private_memory(kvm, &sev_cmd); + break; default: mutex_unlock(&kvm->lock); if (likely(csv_x86_ops.mem_enc_op)) @@ -1542,23 +1913,108 @@ static int csv_mem_enc_op(struct kvm *kvm, void __user *argp) return r; } +/** + * When userspace recognizes these extensions, it is suggested that the userspace + * enables these extensions through KVM_ENABLE_CAP, so that both the userspace + * and KVM can utilize these extensions. + */ +static int csv_get_hygon_coco_extension(struct kvm *kvm) +{ + struct kvm_csv_info *csv; + size_t len = sizeof(uint32_t); + int ret = 0; + + if (!kvm) + return 0; + + csv = &to_kvm_svm_csv(kvm)->csv_info; + + if (csv->fw_ext_valid == false) { + ret = csv_get_extension_info(&csv->fw_ext, &len); + + if (ret == -ENODEV) { + pr_err("Unable to interact with CSV firmware!\n"); + return 0; + } else if (ret == -EINVAL) { + pr_err("Need %ld bytes to record fw extension!\n", len); + return 0; + } + + csv->fw_ext_valid = true; + } + + /* The kvm_ext field of kvm_csv_info is filled in only if the fw_ext + * field of kvm_csv_info is valid. + */ + if (csv->kvm_ext_valid == false) { + if (csv3_guest(kvm)) { + csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM; + if (csv->fw_ext & CSV_EXT_CSV3_MULT_LUP_DATA) + csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA; + if (csv->fw_ext & CSV_EXT_CSV3_INJ_SECRET) + csv->kvm_ext |= KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET; + } + csv->kvm_ext_valid = true; + } + + /* Return extension info only if both fw_ext and kvm_ext fields of + * kvm_csv_info are valid. + */ + pr_debug("%s: fw_ext=%#x kvm_ext=%#x\n", + __func__, csv->fw_ext, csv->kvm_ext); + return (int)csv->kvm_ext; +} + +/** + * Return 0 means KVM accept the negotiation from userspace. Both the + * userspace and KVM should not utilise extensions if failed to negotiate. + */ +static int csv_enable_hygon_coco_extension(struct kvm *kvm, u32 arg) +{ + struct kvm_csv_info *csv; + + if (!kvm) + return -EINVAL; + + csv = &to_kvm_svm_csv(kvm)->csv_info; + + /* Negotiation is accepted only if both the fw_ext and kvm_ext fields + * of kvm_csv_info are valid and the virtual machine is a CSV3 guest. + */ + if (csv->fw_ext_valid && csv->kvm_ext_valid && csv3_guest(kvm)) { + csv->inuse_ext = csv->kvm_ext & arg; + pr_debug("%s: inuse_ext=%#x\n", __func__, csv->inuse_ext); + return csv->inuse_ext; + } + + /* Userspace should not utilise the extensions */ + return -EINVAL; +} + #define CSV_BIT BIT(30) void __init csv_init(struct kvm_x86_ops *ops) { unsigned int eax, ebx, ecx, edx; - if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) + /* + * Hygon CSV is indicated by X86_FEATURE_SEV, return directly if CSV + * is unsupported. + */ + if (!boot_cpu_has(X86_FEATURE_SEV)) return; + memcpy(&csv_x86_ops, ops, sizeof(struct kvm_x86_ops)); + + ops->mem_enc_op = csv_mem_enc_op; + ops->vm_size = sizeof(struct kvm_svm_csv); + ops->get_hygon_coco_extension = csv_get_hygon_coco_extension; + ops->enable_hygon_coco_extension = csv_enable_hygon_coco_extension; + /* Retrieve CSV CPUID information */ cpuid(0x8000001f, &eax, &ebx, &ecx, &edx); - if (eax & CSV_BIT) { - memcpy(&csv_x86_ops, ops, sizeof(struct kvm_x86_ops)); - - ops->mem_enc_op = csv_mem_enc_op; + if (boot_cpu_has(X86_FEATURE_SEV_ES) && (eax & CSV_BIT)) { ops->vm_destroy = csv_vm_destroy; - ops->vm_size = sizeof(struct kvm_svm_csv); ops->handle_exit = csv_handle_exit; ops->guest_memory_reclaimed = csv_guest_memory_reclaimed; } diff --git a/arch/x86/kvm/svm/csv.h b/arch/x86/kvm/svm/csv.h index c70fe083b6bff24bbf490c307601771a161059e8..baade835b9f2f1da13dcd11484e9ee30b0fd94fa 100644 --- a/arch/x86/kvm/svm/csv.h +++ b/arch/x86/kvm/svm/csv.h @@ -12,6 +12,26 @@ #ifdef CONFIG_HYGON_CSV +/* + * Hooks table: a table of function and variable pointers filled in + * when module init. + */ +extern struct hygon_kvm_hooks_table { + bool sev_hooks_installed; + int *sev_enabled; + unsigned long *sev_me_mask; + int (*sev_issue_cmd)(struct kvm *kvm, int id, void *data, int *error); + unsigned long (*get_num_contig_pages)(unsigned long idx, + struct page **inpages, + unsigned long npages); + struct page **(*sev_pin_memory)(struct kvm *kvm, unsigned long uaddr, + unsigned long ulen, unsigned long *n, + int write); + void (*sev_unpin_memory)(struct kvm *kvm, struct page **pages, + unsigned long npages); + void (*sev_clflush_pages)(struct page *pages[], unsigned long npages); +} hygon_kvm_hooks; + void __init csv_init(struct kvm_x86_ops *ops); #else /* !CONFIG_HYGON_CSV */ diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 97df8a375bd567f62d635c7c169bb20a22f4d86a..ccb652975f50b6e7facf492586bc88780fac8806 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -28,6 +28,9 @@ #include "cpuid.h" #include "trace.h" +#include +#include "csv.h" + #define __ex(x) __kvm_handle_fault_on_reboot(x) static u8 sev_enc_bit; @@ -2737,6 +2740,22 @@ void __init sev_set_cpu_caps(void) kvm_cpu_cap_clear(X86_FEATURE_SEV_ES); } +#ifdef CONFIG_HYGON_CSV +/* Code to set all of the function and vaiable pointers */ +void sev_install_hooks(void) +{ + hygon_kvm_hooks.sev_enabled = &sev; + hygon_kvm_hooks.sev_me_mask = &sev_me_mask; + hygon_kvm_hooks.sev_issue_cmd = sev_issue_cmd; + hygon_kvm_hooks.get_num_contig_pages = get_num_contig_pages; + hygon_kvm_hooks.sev_pin_memory = sev_pin_memory; + hygon_kvm_hooks.sev_unpin_memory = sev_unpin_memory; + hygon_kvm_hooks.sev_clflush_pages = sev_clflush_pages; + + hygon_kvm_hooks.sev_hooks_installed = true; +} +#endif + void __init sev_hardware_setup(void) { unsigned int eax, ebx, ecx, edx; @@ -2839,6 +2858,17 @@ void __init sev_hardware_setup(void) out: sev = sev_supported; sev_es = sev_es_supported; + +#ifdef CONFIG_HYGON_CSV + /* Setup resources which are necessary for HYGON CSV */ + if (is_x86_vendor_hygon()) { + /* + * Install sev related function and variable pointers hooks + * no matter @sev is false. + */ + sev_install_hooks(); + } +#endif } void sev_hardware_teardown(void) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index def2fd83633b15baeb54b76e1981999063e1d516..dcb7c1d68efc7dc77276aa5a9dc9ef0cb20af976 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -44,6 +44,8 @@ #include "trace.h" #include "svm.h" + +#include #include "csv.h" #define __ex(x) __kvm_handle_fault_on_reboot(x) @@ -4784,7 +4786,11 @@ static struct kvm_x86_init_ops svm_init_ops __initdata = { static int __init svm_init(void) { __unused_size_checks(); - csv_init(&svm_x86_ops); + + /* Register CSV specific interface for Hygon CPUs */ + if (is_x86_vendor_hygon()) + csv_init(&svm_x86_ops); + return kvm_init(&svm_init_ops, sizeof(struct vcpu_svm), __alignof__(struct vcpu_svm), THIS_MODULE); } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 74f3c35a193dd8d96ebe1e84549b4d4f0ecee16e..1e93b27839e835fd27e4a53977f13c8f7bed1b2e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -80,6 +80,8 @@ #include #include +#include + #define CREATE_TRACE_POINTS #include "trace.h" @@ -4078,6 +4080,18 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = kvm_x86_ops.has_emulated_msr(kvm, MSR_AMD64_SEV_ES_GHCB); break; + case KVM_CAP_HYGON_COCO_EXT: + r = 0; + + /* + * Before running a Hygon confidential guest, the userspace + * should find the advanced extensions of the Hygon CSV + * technology. If the userspace recognize the extensions, it's + * suggested that the userspace to utilise extensions. + */ + if (is_x86_vendor_hygon() && kvm_x86_ops.get_hygon_coco_extension) + r = kvm_x86_ops.get_hygon_coco_extension(kvm); + break; default: break; } @@ -5655,6 +5669,17 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, } mutex_unlock(&kvm->lock); break; + case KVM_CAP_HYGON_COCO_EXT: + r = -EINVAL; + + /* + * The userspace negotiate with KVM to utilise extensions of + * Hygon CSV technology. + */ + if (is_x86_vendor_hygon() && kvm_x86_ops.enable_hygon_coco_extension) + r = kvm_x86_ops.enable_hygon_coco_extension(kvm, + (u32)cap->args[0]); + break; default: r = -EINVAL; break; diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile index 426ef8f2bd47b7714fc68433ef50cf65e34a76b3..a4cc3a6d3fd14bcfaaed269d50ba933c1c46aa47 100644 --- a/drivers/crypto/ccp/Makefile +++ b/drivers/crypto/ccp/Makefile @@ -13,7 +13,8 @@ ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o \ sev-dev.o \ tee-dev.o \ psp-ringbuf.o \ - vpsp.o + vpsp.o \ + hygon/csv-dev.o ccp-$(CONFIG_TDM_DEV_HYGON) += tdm-dev.o diff --git a/drivers/crypto/ccp/hygon/csv-dev.c b/drivers/crypto/ccp/hygon/csv-dev.c new file mode 100644 index 0000000000000000000000000000000000000000..fae8fbb5072cd4a3c31e0d05f7900ded11e7361c --- /dev/null +++ b/drivers/crypto/ccp/hygon/csv-dev.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HYGON CSV interface + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "csv-dev.h" + +/* + * Hygon CSV build info: + * Hygon CSV build info is 32-bit in length other than 8-bit as that + * in AMD SEV. + */ +u32 hygon_csv_build; + +/* + * csv_update_api_version used to update the api version of HYGON CSV + * firmwareat driver side. + * Currently, we only need to update @hygon_csv_build. + */ +void csv_update_api_version(struct sev_user_data_status *status) +{ + if (status) { + hygon_csv_build = (status->flags >> 9) | + ((u32)status->build << 23); + } +} + +int csv_get_extension_info(void *buf, size_t *size) +{ + /* If @hygon_csv_build is 0, this means CSV firmware doesn't exist or + * the psp device doesn't exist. + */ + if (hygon_csv_build == 0) + return -ENODEV; + + /* The caller must provide valid @buf and the @buf must >= 4 bytes in + * size. + */ + if (!buf || !size || *size < sizeof(uint32_t)) { + if (size) + *size = sizeof(uint32_t); + + return -EINVAL; + } + + /* Since firmware with build id 2200, support: + * a. issue LAUNCH_ENCRYPT_DATA command more than once for a + * CSV3 guest. + * b. inject secret to a CSV3 guest. + */ + if (csv_version_greater_or_equal(2200)) { + *(uint32_t *)buf |= CSV_EXT_CSV3_MULT_LUP_DATA; + *(uint32_t *)buf |= CSV_EXT_CSV3_INJ_SECRET; + } + + return 0; +} +EXPORT_SYMBOL_GPL(csv_get_extension_info); diff --git a/drivers/crypto/ccp/hygon/csv-dev.h b/drivers/crypto/ccp/hygon/csv-dev.h new file mode 100644 index 0000000000000000000000000000000000000000..dfdb4261fc525baa7f340b55c98095b2653541d1 --- /dev/null +++ b/drivers/crypto/ccp/hygon/csv-dev.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * HYGON CSV driver interface + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + */ + +#ifndef __CCP_HYGON_CSV_DEV_H__ +#define __CCP_HYGON_CSV_DEV_H__ + +#include + +#include "sp-dev.h" + +extern u32 hygon_csv_build; + +void csv_update_api_version(struct sev_user_data_status *status); + +static inline bool csv_version_greater_or_equal(u32 build) +{ + return hygon_csv_build >= build; +} + +#endif /* __CCP_HYGON_CSV_DEV_H__ */ diff --git a/drivers/crypto/ccp/hygon/sp-dev.h b/drivers/crypto/ccp/hygon/sp-dev.h new file mode 100644 index 0000000000000000000000000000000000000000..dfe6fd50933a6c66c96033daa337f6809674fa66 --- /dev/null +++ b/drivers/crypto/ccp/hygon/sp-dev.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * HYGON Secure Processor interface + * + * Copyright (C) 2024 Hygon Info Technologies Ltd. + * + * Author: Liyang Han + */ + +#ifndef __CCP_HYGON_SP_DEV_H__ +#define __CCP_HYGON_SP_DEV_H__ + +#include + +#ifdef CONFIG_X86_64 +static inline bool is_vendor_hygon(void) +{ + return boot_cpu_data.x86_vendor == X86_VENDOR_HYGON; +} +#else +static inline bool is_vendor_hygon(void) { return false; } +#endif + +#endif /* __CCP_HYGON_SP_DEV_H__ */ diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index aca53b2fd1fbe21d3f0af0cf2883e0e66970a9ee..ccc5950dd6b5a7001b477e8e590e4dd355d5fdf1 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -31,6 +31,8 @@ #include "psp-dev.h" #include "sev-dev.h" +#include "hygon/csv-dev.h" + #define DEVICE_NAME "sev" #define SEV_FW_FILE "amd/sev.fw" #define CSV_FW_FILE "hygon/csv.fw" @@ -85,13 +87,6 @@ static int csv_comm_mode = CSV_COMM_MAILBOX_ON; #define SEV_ES_TMR_SIZE (1024 * 1024) static void *sev_es_tmr; -/* - * Hygon CSV build info: - * Hygon CSV build info is 32-bit in length other than 8-bit as that - * in AMD SEV. - */ -static u32 hygon_csv_build; - static inline bool sev_version_greater_or_equal(u8 maj, u8 min) { struct sev_device *sev = psp_master->sev_data; @@ -105,11 +100,6 @@ static inline bool sev_version_greater_or_equal(u8 maj, u8 min) return false; } -static inline bool csv_version_greater_or_equal(u32 build) -{ - return hygon_csv_build >= build; -} - static void sev_irq_handler(int irq, void *data, unsigned int status) { struct sev_device *sev = data; @@ -168,8 +158,8 @@ static int sev_cmd_buffer_len(int cmd) return sizeof(struct csv_data_hgsc_cert_import); case CSV_CMD_RING_BUFFER: return sizeof(struct csv_data_ring_buffer); - case CSV_CMD_LAUNCH_ENCRYPT_DATA: - return sizeof(struct csv_data_launch_encrypt_data); + case CSV3_CMD_LAUNCH_ENCRYPT_DATA: + return sizeof(struct csv3_data_launch_encrypt_data); case CSV_CMD_LAUNCH_ENCRYPT_VMCB: return sizeof(struct csv_data_launch_encrypt_vmcb); case CSV_CMD_UPDATE_NPT: @@ -178,8 +168,8 @@ static int sev_cmd_buffer_len(int cmd) return sizeof(struct csv_data_set_smr); case CSV_CMD_SET_SMCR: return sizeof(struct csv_data_set_smcr); - case CSV_CMD_SET_GUEST_PRIVATE_MEMORY: - return sizeof(struct csv_data_set_guest_private_memory); + case CSV3_CMD_SET_GUEST_PRIVATE_MEMORY: + return sizeof(struct csv3_data_set_guest_private_memory); case CSV_CMD_DBG_READ_VMSA: return sizeof(struct csv_data_dbg_read_vmsa); case CSV_CMD_DBG_READ_MEM: @@ -929,9 +919,12 @@ static int sev_get_api_version(void) sev->build = status.build; sev->state = status.state; - if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) - hygon_csv_build = (status.flags >> 9) | - ((u32)status.build << 23); + /* + * The api version fields of HYGON CSV firmware are not consistent + * with AMD SEV firmware. + */ + if (is_vendor_hygon()) + csv_update_api_version(&status); return 0; } @@ -1329,10 +1322,19 @@ static int csv_ioctl_do_download_firmware(struct sev_issue_cmd *argp) data->len = input.length; ret = __sev_do_cmd_locked(SEV_CMD_DOWNLOAD_FIRMWARE, data, &argp->error); - if (ret) + if (ret) { pr_err("Failed to update CSV firmware: %#x\n", argp->error); - else + goto err_free_page; + } else { pr_info("CSV firmware update successful\n"); + } + + /* + * Synchronize API version status, and return -EIO if the Hygon PSP fails + * to respond to the PLATFORM_STATUS API. + */ + if (sev_get_api_version()) + ret = -EIO; err_free_page: __free_pages(p, order); diff --git a/include/linux/psp-csv.h b/include/linux/psp-csv.h index 2a899e32ae89a8a5d6efd7f0002ba052339a2024..d8fbb06319a87644347479d4cc970664d4ed99fa 100644 --- a/include/linux/psp-csv.h +++ b/include/linux/psp-csv.h @@ -10,13 +10,18 @@ #include +#define CSV_EXT_CSV3_MULT_LUP_DATA_BIT 0 +#define CSV_EXT_CSV3_MULT_LUP_DATA (1 << CSV_EXT_CSV3_MULT_LUP_DATA_BIT) +#define CSV_EXT_CSV3_INJ_SECRET_BIT 1 +#define CSV_EXT_CSV3_INJ_SECRET (1 << CSV_EXT_CSV3_INJ_SECRET_BIT) + /** * Guest/platform management commands */ enum csv3_cmd { /* Guest launch commands */ - CSV_CMD_SET_GUEST_PRIVATE_MEMORY = 0x200, - CSV_CMD_LAUNCH_ENCRYPT_DATA = 0x201, + CSV3_CMD_SET_GUEST_PRIVATE_MEMORY = 0x200, + CSV3_CMD_LAUNCH_ENCRYPT_DATA = 0x201, CSV_CMD_LAUNCH_ENCRYPT_VMCB = 0x202, /* Guest NPT(Nested Page Table) management commands */ CSV_CMD_UPDATE_NPT = 0x203, @@ -39,14 +44,14 @@ enum csv3_cmd { }; /** - * struct csv_data_launch_encrypt_data - CSV_CMD_LAUNCH_ENCRYPT_DATA command + * struct csv3_data_launch_encrypt_data - CSV3_CMD_LAUNCH_ENCRYPT_DATA command * * @handle: handle of the VM to update * @gpa: guest address where data is copied * @length: len of memory to be encrypted * @data_blocks: memory regions to hold data page address */ -struct csv_data_launch_encrypt_data { +struct csv3_data_launch_encrypt_data { u32 handle; /* In */ u32 reserved; /* In */ u64 gpa; /* In */ @@ -119,14 +124,14 @@ struct csv_data_memory_region { } __packed; /** - * struct csv_data_set_guest_private_memory - CSV_CMD_SET_GUEST_PRIVATE_MEMORY + * struct csv3_data_set_guest_private_memory - CSV3_CMD_SET_GUEST_PRIVATE_MEMORY * command parameters * * @handle: handle assigned to the VM * @nregions: number of memory regions * @regions_paddr: address of memory containing multiple memory regions */ -struct csv_data_set_guest_private_memory { +struct csv3_data_set_guest_private_memory { u32 handle; /* In */ u32 nregions; /* In */ u64 regions_paddr; /* In */ @@ -285,4 +290,25 @@ struct csv_data_receive_encrypt_context { u32 vmcb_block_len; /* In */ } __packed; -#endif +#ifdef CONFIG_CRYPTO_DEV_SP_PSP + +/** + * csv_get_extension_info - collect extension set of the firmware + * + * @buf: The buffer to save extension set + * @size: The size of the @buf + * + * Returns: + * 0 if @buf is filled with extension bitflags + * -%ENODEV if the CSV device is not available + * -%EINVAL if @buf is NULL or @size is too smaller + */ +int csv_get_extension_info(void *buf, size_t *size); + +#else /* !CONFIG_CRYPTO_DEV_SP_PSP */ + +static inline int csv_get_extension_info(void *buf, size_t *size) { return -ENODEV; } + +#endif /* !CONFIG_CRYPTO_DEV_SP_PSP */ + +#endif /* __PSP_CSV_H__ */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 3eb70c7c5c6cf5b665cea78456e5479d3acceafb..2d899f9e6cc2fd1f6f3e97a32619c8f1961ff501 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1099,6 +1099,13 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_X86_NOTIFY_VMEXIT 219 #define KVM_CAP_SEV_ES_GHCB 500 +#define KVM_CAP_HYGON_COCO_EXT 501 +/* support userspace to request firmware to build CSV3 guest's memory space */ +#define KVM_CAP_HYGON_COCO_EXT_CSV3_SET_PRIV_MEM (1 << 0) +/* support request to update CSV3 guest's memory region multiple times */ +#define KVM_CAP_HYGON_COCO_EXT_CSV3_MULT_LUP_DATA (1 << 1) +/* support request to inject secret to CSV3 guest */ +#define KVM_CAP_HYGON_COCO_EXT_CSV3_INJ_SECRET (1 << 2) #define KVM_CAP_ARM_CPU_FEATURE 555 @@ -1922,27 +1929,29 @@ struct kvm_hyperv_eventfd { #define KVM_X86_NOTIFY_VMEXIT_ENABLED (1ULL << 0) #define KVM_X86_NOTIFY_VMEXIT_USER (1ULL << 1) -/* CSV command */ -enum csv_cmd_id { - KVM_CSV_NR_MIN = 0xc0, +/* CSV3 command */ +enum csv3_cmd_id { + KVM_CSV3_NR_MIN = 0xc0, - KVM_CSV_INIT = KVM_CSV_NR_MIN, - KVM_CSV_LAUNCH_ENCRYPT_DATA, - KVM_CSV_LAUNCH_ENCRYPT_VMCB, - KVM_CSV_SEND_ENCRYPT_DATA, - KVM_CSV_SEND_ENCRYPT_CONTEXT, - KVM_CSV_RECEIVE_ENCRYPT_DATA, - KVM_CSV_RECEIVE_ENCRYPT_CONTEXT, - KVM_CSV_HANDLE_MEMORY, + KVM_CSV3_INIT = KVM_CSV3_NR_MIN, + KVM_CSV3_LAUNCH_ENCRYPT_DATA, + KVM_CSV3_LAUNCH_ENCRYPT_VMCB, + KVM_CSV3_SEND_ENCRYPT_DATA, + KVM_CSV3_SEND_ENCRYPT_CONTEXT, + KVM_CSV3_RECEIVE_ENCRYPT_DATA, + KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT, + KVM_CSV3_HANDLE_MEMORY, - KVM_CSV_NR_MAX, + KVM_CSV3_SET_GUEST_PRIVATE_MEMORY = 0xc8, + + KVM_CSV3_NR_MAX, }; struct kvm_csv_init_data { __u64 nodemask; }; -struct kvm_csv_launch_encrypt_data { +struct kvm_csv3_launch_encrypt_data { __u64 gpa; __u64 uaddr; __u32 len; @@ -1980,7 +1989,7 @@ struct kvm_csv_receive_encrypt_context { __u32 trans_len; }; -#define KVM_CSV_RELEASE_SHARED_MEMORY (0x0001) +#define KVM_CSV3_RELEASE_SHARED_MEMORY (0x0001) struct kvm_csv_handle_memory { __u64 gpa;