diff --git a/arch/x86/include/asm/csv.h b/arch/x86/include/asm/csv.h index e2fcaf4ded5f7..18ddf881a6fca 100644 --- a/arch/x86/include/asm/csv.h +++ b/arch/x86/include/asm/csv.h @@ -66,6 +66,7 @@ void __init csv_early_update_memory_dec(u64 vaddr, u64 pages); void __init csv_early_memory_enc_dec(u64 vaddr, u64 size, bool enc); void csv_memory_enc_dec(u64 vaddr, u64 pages, bool enc); +int csv3_issue_request_report(phys_addr_t paddr, size_t size); #else /* !CONFIG_HYGON_CSV */ @@ -79,6 +80,7 @@ static inline void __init csv_early_memory_enc_dec(u64 vaddr, u64 size, bool enc) { } static inline void csv_memory_enc_dec(u64 vaddr, u64 pages, bool enc) { } +static inline int csv3_issue_request_report(phys_addr_t paddr, size_t size) { return -EIO; } #endif /* CONFIG_HYGON_CSV */ diff --git a/arch/x86/kernel/csv-shared.c b/arch/x86/kernel/csv-shared.c index 0763195764daf..9383f5d0a4765 100644 --- a/arch/x86/kernel/csv-shared.c +++ b/arch/x86/kernel/csv-shared.c @@ -76,12 +76,33 @@ * CSV3_SECURE_CMD_UPDATE_SECURE_CALL_TABLE: * CSV3 guest wants to change the secure call pages. * The secure processor re-init the secure call context. + * + * CSV3_SECURE_CMD_REQ_REPORT: + * CSV3 guest wants to request attestation report. + * The secure processor will update the request message buffer and respond + * buffer to indicate the result of this request. */ enum csv3_secure_command_type { - CSV3_SECURE_CMD_ENC = 1, + /* The secure call request should below CSV3_SECURE_CMD_ACK */ + CSV3_SECURE_CMD_ENC = 0x1, CSV3_SECURE_CMD_DEC, CSV3_SECURE_CMD_RESET, CSV3_SECURE_CMD_UPDATE_SECURE_CALL_TABLE, + CSV3_SECURE_CMD_REQ_REPORT = 0x7, + + /* SECURE_CMD_ACK indicates secure call request can be handled */ + CSV3_SECURE_CMD_ACK = 0x6b, + + /* + * The following values are the error code of the secure call + * when firmware can't handling the specific secure call command + * as expected. + */ + CSV3_SECURE_CMD_ERROR_INTERNAL = 0x6c, + CSV3_SECURE_CMD_ERROR_INVALID_COMMAND = 0x6d, + CSV3_SECURE_CMD_ERROR_INVALID_PARAM = 0x6e, + CSV3_SECURE_CMD_ERROR_INVALID_ADDRESS = 0x6f, + CSV3_SECURE_CMD_ERROR_INVALID_LENGTH = 0x70, }; /* diff --git a/arch/x86/kernel/csv.c b/arch/x86/kernel/csv.c index 4f80c97798deb..84a76ae3c0625 100644 --- a/arch/x86/kernel/csv.c +++ b/arch/x86/kernel/csv.c @@ -285,3 +285,76 @@ void csv_memory_enc_dec(u64 vaddr, u64 pages, bool enc) __csv3_memory_enc_dec(csv3_secure_call, vaddr & PAGE_MASK, pages, enc); } + +static void print_secure_call_error(enum csv3_secure_command_type code) +{ + switch (code) { + case CSV3_SECURE_CMD_ACK: + pr_debug("secure call: handled\n"); + break; + case CSV3_SECURE_CMD_ERROR_INTERNAL: + pr_err("secure call: internal error\n"); + break; + case CSV3_SECURE_CMD_ERROR_INVALID_COMMAND: + pr_err("secure call: unsupported cmd\n"); + break; + case CSV3_SECURE_CMD_ERROR_INVALID_PARAM: + pr_err("secure call: invalid param\n"); + break; + case CSV3_SECURE_CMD_ERROR_INVALID_ADDRESS: + pr_err("secure call: invalid address\n"); + break; + case CSV3_SECURE_CMD_ERROR_INVALID_LENGTH: + pr_err("secure call: invalid length\n"); + break; + default: + pr_err("secure call: shouldn't reach here\n"); + break; + } +} + +int csv3_issue_request_report(phys_addr_t paddr, size_t size) +{ + struct secure_call_pages *sc_page_info; + struct csv3_secure_call_cmd *sc_wr, *sc_rd; + unsigned long flags; + int sc_page_idx; + enum csv3_secure_command_type sc_return_code; + + /* + * secure call pages needs to access with IRQs disabled because it is + * using a per-CPU data. + */ + local_irq_save(flags); + + sc_page_info = this_cpu_read(secure_call_data); + sc_page_idx = this_cpu_read(secure_call_page_idx); + + sc_wr = sc_page_idx ? &sc_page_info->page_a : &sc_page_info->page_b; + sc_rd = sc_page_idx ? &sc_page_info->page_b : &sc_page_info->page_a; + + sc_wr->cmd_type = CSV3_SECURE_CMD_REQ_REPORT; + sc_wr->nums = 1; + sc_wr->unused = 0; + sc_wr->entry[0].base_address = (u64)paddr; + sc_wr->entry[0].size = size; + + /* + * Write command in sc_wr must be done before retrieve status code + * from sc_rd, and it's ensured by the smp_mb below. + */ + smp_mb(); + + sc_return_code = sc_rd->cmd_type; + + this_cpu_write(secure_call_page_idx, sc_page_idx ^ 1); + + /* Leave per-CPU data access */ + local_irq_restore(flags); + + /* Print return code of the secure call */ + print_secure_call_error(sc_return_code); + + return sc_return_code == CSV3_SECURE_CMD_ACK ? 0 : -EIO; +} +EXPORT_SYMBOL_GPL(csv3_issue_request_report); diff --git a/arch/x86/mm/mem_encrypt_hygon.c b/arch/x86/mm/mem_encrypt_hygon.c index 1871850cbb604..52ec3fa041feb 100644 --- a/arch/x86/mm/mem_encrypt_hygon.c +++ b/arch/x86/mm/mem_encrypt_hygon.c @@ -130,6 +130,7 @@ bool noinstr csv3_active(void) else return false; } +EXPORT_SYMBOL_GPL(csv3_active); /******************************************************************************/ /**************************** CSV3 CMA interfaces *****************************/ diff --git a/drivers/virt/coco/csv-guest/csv-guest.c b/drivers/virt/coco/csv-guest/csv-guest.c index 7db8177637ce1..6a77c68b19b4a 100644 --- a/drivers/virt/coco/csv-guest/csv-guest.c +++ b/drivers/virt/coco/csv-guest/csv-guest.c @@ -12,21 +12,28 @@ #include #include #include +#include #include +#include + #include "csv-guest.h" -static long csv_get_report(void __user *argp) +/* Mutex to serialize the command handling. */ +static DEFINE_MUTEX(csv_cmd_mutex); + +static int csv_get_report(unsigned long arg) { u8 *csv_report; long ret; struct csv_report_req req; - if (copy_from_user(&req, argp, sizeof(struct csv_report_req))) + if (copy_from_user(&req, (void __user *)arg, + sizeof(struct csv_report_req))) return -EFAULT; - if (req.len < CSV_REPORT_INPUT_DATA_LEN) + if (req.len < CSV_REPORT_INPUT_DATA_LEN || !req.report_data) return -EINVAL; csv_report = kzalloc(req.len, GFP_KERNEL); @@ -54,14 +61,124 @@ static long csv_get_report(void __user *argp) return ret; } +static int csv3_get_report(unsigned long arg) +{ + struct csv_report_req input; + struct page *page = NULL; + struct csv3_data_attestation_report *cmd_buff = NULL; + void *req_buff = NULL; + void *resp_buff = NULL; + int ret; + + if (copy_from_user(&input, (void __user *)arg, sizeof(input))) + return -EFAULT; + + if (!input.len || !input.report_data) + return -EINVAL; + + /* Use alloc_page for alignment */ + page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!page) + return -ENOMEM; + cmd_buff = (struct csv3_data_attestation_report *)page_address(page); + + /* + * Query the firmware to get minimum length of request buffer and + * respond buffer. + */ + ret = csv3_issue_request_report(__pa(cmd_buff), sizeof(*cmd_buff)); + + /* + * The input.len must be the maxinum length of the req and resp buffer + * at least, otherwise return with error. + */ + if (input.len < max(cmd_buff->req_len, cmd_buff->resp_len)) { + ret = -EINVAL; + goto err; + } + + /* Use alloc_page for alignment */ + page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!page) { + ret = -ENOMEM; + goto err; + } + req_buff = page_address(page); + + /* Use alloc_page for alignment */ + page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); + if (!page) { + ret = -ENOMEM; + goto err; + } + resp_buff = page_address(page); + + /* Copy user's input data */ + if (copy_from_user(req_buff, input.report_data, cmd_buff->req_len)) { + ret = -EFAULT; + goto err; + } + + /* + * The req_len and resp_len fields has already been filled by firmware + * when we query the lengths from firmware. + */ + cmd_buff->req_gpa = __pa(req_buff); + cmd_buff->resp_gpa = __pa(resp_buff); + + ret = csv3_issue_request_report(__pa(cmd_buff), sizeof(*cmd_buff)); + if (ret || (!ret && cmd_buff->fw_error_code)) { + pr_err("%s: fail to generate report, fw_error:%#x ret:%d\n", + __func__, cmd_buff->fw_error_code, ret); + ret = -EIO; + goto err; + } + + /* Copy attestation report to user */ + if (copy_to_user(input.report_data, resp_buff, cmd_buff->resp_len)) + ret = -EFAULT; + +err: + if (resp_buff) + free_page((unsigned long)resp_buff); + if (req_buff) + free_page((unsigned long)req_buff); + if (cmd_buff) + free_page((unsigned long)cmd_buff); + + return ret; +} + +static int get_report(unsigned long arg) +{ + int ret = -ENOTTY; + + lockdep_assert_held(&csv_cmd_mutex); + + if (csv3_active()) + ret = csv3_get_report(arg); + else if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) + ret = csv_get_report(arg); + return ret; +} + static long csv_guest_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + int ret = -ENOTTY; + + mutex_lock(&csv_cmd_mutex); + switch (cmd) { case CSV_CMD_GET_REPORT: - return csv_get_report((void __user *)arg); + ret = get_report(arg); + break; default: - return -ENOTTY; + break; } + + mutex_unlock(&csv_cmd_mutex); + + return ret; } static const struct file_operations csv_guest_fops = { diff --git a/include/linux/psp-hygon.h b/include/linux/psp-hygon.h index 1888d9d725925..f1f7a23c606ea 100644 --- a/include/linux/psp-hygon.h +++ b/include/linux/psp-hygon.h @@ -306,6 +306,28 @@ struct csv3_data_dbg_read_mem { u32 size; /* In */ } __packed; +/** + * struct csv3_data_attestation_report - ATTESTATION secure call command parameters + * + * @handle: handle of the VM to process + * @resp_gpa: guest physical address to save the generated report + * @resp_length: length of the generated report + * @req_gpa: guest physical address of the input for the report + * @req_length: length of the input for the report + * @fw_error_code: firmware status code when generating the report + */ +struct csv3_data_attestation_report { + u32 handle; /* Out */ + u32 reserved1; + u64 resp_gpa; /* In */ + u8 reserved2[16]; + u32 resp_len; /* In/Out */ + u32 reserved3; + u64 req_gpa; /* In */ + u32 req_len; /* In,Out */ + u32 fw_error_code; /* Out */ +} __packed; + /** * struct csv3_data_send_encrypt_data - SEND_ENCRYPT_DATA command parameters * diff --git a/include/uapi/linux/psp-hygon.h b/include/uapi/linux/psp-hygon.h index 0e65afbeea3c2..7bb6a4f8b6e02 100644 --- a/include/uapi/linux/psp-hygon.h +++ b/include/uapi/linux/psp-hygon.h @@ -55,4 +55,17 @@ struct csv_user_data_download_firmware { __u32 length; /* In */ } __packed; +/** + * struct csv_guest_user_data_attestation - ATTESTATION command parameters + * + * @user_data: user specified data for the attestation report + * @mnonce: user's random nonce + * @hash: sm3 hash of the @user_data and @mnonce + */ +struct csv_guest_user_data_attestation { + __u8 user_data[64]; /* In */ + __u8 monce[16]; /* In */ + __u8 hash[32]; /* In */ +} __packed; + #endif /* __PSP_HYGON_USER_H__ */