Re: [PATCH Part1 v5 36/38] virt: Add SEV-SNP guest driver

Borislav Petkov
2021-09-06T17:38:20Z

On Fri, Aug 20, 2021 at 10:19:31AM -0500, Brijesh Singh wrote:
> +===================================================================
> +The Definitive SEV Guest API Documentation
> +===================================================================
> +
> +1. General description
> +======================
> +
> +The SEV API is a set of ioctls that are issued to by the guest or

issued to by?

Issued by the guest or hypervisor, you mean..

> +hypervisor to get or set certain aspect of the SEV virtual machine.
> +The ioctls belong to the following classes:
> +
> + - Hypervisor ioctls: These query and set global attributes which affect the
> + whole SEV firmware. These ioctl is used by platform provision tools.

"These ioctls are used ... "

> +
> + - Guest ioctls: These query and set attribute of the SEV virtual machine.

"... attributes... "

> +
> +2. API description
> +==================
> +
> +This section describes ioctls that can be used to query or set SEV guests.
> +For each ioctl, the following information is provided along with a
> +description:
> +
> + Technology:
> + which SEV techology provides this ioctl. sev, sev-es, sev-snp or all.
> +
> + Type:
> + hypervisor or guest. The ioctl can be used inside the guest or the
> + hypervisor.
> +
> + Parameters:
> + what parameters are accepted by the ioctl.
> +
> + Returns:
> + the return value. General error numbers (ENOMEM, EINVAL)
> + are not detailed, but errors with specific meanings are.
> +
> +The guest ioctl should be called to /dev/sev-guest device. The ioctl accepts

s/called to/issued on a file descriptor of the/

> +struct snp_user_guest_request. The input and output structure is specified
> +through the req_data and resp_data field respectively. If the ioctl fails
> +to execute due to the firmware error, then fw_err code will be set.

"... due to a ... "

> +
> +::
> + struct snp_user_guest_request {

So you said earlier:

> I followed the naming convension you recommended during the initial SEV driver
> developement. IIRC, the main reason for us having to add "user" in it because
> we wanted to distinguious that this structure is not exactly same as the what
> is defined in the SEV-SNP firmware spec.

but looking at the current variant in the code, the structure in the SNP spec is

Table 91. Layout of the CMDBUF_SNP_GUEST_REQUEST Structure

which corresponds to struct snp_guest_request_data so you can call this one:

struct snp_guest_request_ioctl

and then it is perfectly clear what is what.

> + /* Request and response structure address */
> + __u64 req_data;
> + __u64 resp_data;
> +
> + /* firmware error code on failure (see psp-sev.h) */
> + __u64 fw_err;
> + };
> +
> +2.1 SNP_GET_REPORT
> +------------------
> +
> +:Technology: sev-snp
> +:Type: guest ioctl
> +:Parameters (in): struct snp_report_req
> +:Returns (out): struct snp_report_resp on success, -negative on error
> +
> +The SNP_GET_REPORT ioctl can be used to query the attestation report from the
> +SEV-SNP firmware. The ioctl uses the SNP_GUEST_REQUEST (MSG_REPORT_REQ) command
> +provided by the SEV-SNP firmware to query the attestation report.
> +
> +On success, the snp_report_resp.data will contains the report. The report

"... will contain... "

> +format is described in the SEV-SNP specification. See the SEV-SNP specification
> +for further details.

"... which can be found at https://developer.amd.com/sev/."

assuming that URL will keep its validity in the foreseeable future.

> +static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
> +{
> + struct snp_guest_dev *snp_dev = to_snp_dev(file);
> + void __user *argp = (void __user *)arg;
> + struct snp_user_guest_request input;
> + int ret = -ENOTTY;
> +
> + if (copy_from_user(&input, argp, sizeof(input)))
> + return -EFAULT;
> +
> + mutex_lock(&snp_cmd_mutex);
> +
> + switch (ioctl) {
> + case SNP_GET_REPORT: {
> + ret = get_report(snp_dev, &input);
> + break;
> + }

No need for those {} brackets around the case.

> + default:
> + break;
> + }
> +
> + mutex_unlock(&snp_cmd_mutex);
> +
> + if (copy_to_user(argp, &input, sizeof(input)))
> + return -EFAULT;
> +
> + return ret;
> +}
> +
> +static void free_shared_pages(void *buf, size_t sz)
> +{
> + unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
> +
> + /* If fail to restore the encryption mask then leak it. */
> + if (set_memory_encrypted((unsigned long)buf, npages))

Hmm, this sounds like an abnormal condition about which we should at
least warn...

> + return;
> +
> + __free_pages(virt_to_page(buf), get_order(sz));
> +}
> +
> +static void *alloc_shared_pages(size_t sz)
> +{
> + unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
> + struct page *page;
> + int ret;
> +
> + page = alloc_pages(GFP_KERNEL_ACCOUNT, get_order(sz));
> + if (IS_ERR(page))
> + return NULL;
> +
> + ret = set_memory_decrypted((unsigned long)page_address(page), npages);
> + if (ret) {
> + __free_pages(page, get_order(sz));
> + return NULL;
> + }
> +
> + return page_address(page);
> +}
> +
> +static const struct file_operations snp_guest_fops = {
> + .owner = THIS_MODULE,
> + .unlocked_ioctl = snp_guest_ioctl,
> +};
> +
> +static int __init snp_guest_probe(struct platform_device *pdev)
> +{
> + struct snp_guest_platform_data *data;
> + struct device *dev = &pdev->dev;
> + struct snp_guest_dev *snp_dev;
> + struct miscdevice *misc;
> + int ret;
> +
> + if (!dev->platform_data)
> + return -ENODEV;
> +
> + data = (struct snp_guest_platform_data *)dev->platform_data;
> + vmpck_id = data->vmpck_id;
> +
> + snp_dev = devm_kzalloc(&pdev->dev, sizeof(struct snp_guest_dev), GFP_KERNEL);
> + if (!snp_dev)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, snp_dev);
> + snp_dev->dev = dev;
> +
> + snp_dev->crypto = init_crypto(snp_dev, data->vmpck, sizeof(data->vmpck));
> + if (!snp_dev->crypto)
> + return -EIO;

I guess you should put the crypto init...

> +
> + /* Allocate the shared page used for the request and response message. */
> + snp_dev->request = alloc_shared_pages(sizeof(struct snp_guest_msg));
> + if (IS_ERR(snp_dev->request)) {
> + ret = PTR_ERR(snp_dev->request);
> + goto e_free_crypto;
> + }
> +
> + snp_dev->response = alloc_shared_pages(sizeof(struct snp_guest_msg));
> + if (IS_ERR(snp_dev->response)) {
> + ret = PTR_ERR(snp_dev->response);
> + goto e_free_req;
> + }

... here, after the page allocation to save yourself all the setup work
if the shared pages allocation fails.

> +
> + misc = &snp_dev->misc;
> + misc->minor = MISC_DYNAMIC_MINOR;
> + misc->name = DEVICE_NAME;
> + misc->fops = &snp_guest_fops;
> +
> + return misc_register(misc);
> +
> +e_free_req:
> + free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg));
> +
> +e_free_crypto:
> + deinit_crypto(snp_dev->crypto);
> +
> + return ret;
> +}

--
Regards/Gruss,
Boris.