|
@@ -22,11 +22,17 @@
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/hw_random.h>
|
|
|
#include <linux/ccp.h>
|
|
|
+#include <linux/firmware.h>
|
|
|
|
|
|
#include "sp-dev.h"
|
|
|
#include "psp-dev.h"
|
|
|
|
|
|
+#define SEV_VERSION_GREATER_OR_EQUAL(_maj, _min) \
|
|
|
+ ((psp_master->api_major) >= _maj && \
|
|
|
+ (psp_master->api_minor) >= _min)
|
|
|
+
|
|
|
#define DEVICE_NAME "sev"
|
|
|
+#define SEV_FW_FILE "amd/sev.fw"
|
|
|
|
|
|
static DEFINE_MUTEX(sev_cmd_mutex);
|
|
|
static struct sev_misc_dev *misc_dev;
|
|
@@ -112,6 +118,7 @@ static int sev_cmd_buffer_len(int cmd)
|
|
|
case SEV_CMD_RECEIVE_UPDATE_DATA: return sizeof(struct sev_data_receive_update_data);
|
|
|
case SEV_CMD_RECEIVE_UPDATE_VMSA: return sizeof(struct sev_data_receive_update_vmsa);
|
|
|
case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret);
|
|
|
+ case SEV_CMD_DOWNLOAD_FIRMWARE: return sizeof(struct sev_data_download_firmware);
|
|
|
default: return 0;
|
|
|
}
|
|
|
|
|
@@ -378,6 +385,79 @@ void *psp_copy_user_blob(u64 __user uaddr, u32 len)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(psp_copy_user_blob);
|
|
|
|
|
|
+static int sev_get_api_version(void)
|
|
|
+{
|
|
|
+ struct sev_user_data_status *status;
|
|
|
+ int error, ret;
|
|
|
+
|
|
|
+ status = &psp_master->status_cmd_buf;
|
|
|
+ ret = sev_platform_status(status, &error);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(psp_master->dev,
|
|
|
+ "SEV: failed to get status. Error: %#x\n", error);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ psp_master->api_major = status->api_major;
|
|
|
+ psp_master->api_minor = status->api_minor;
|
|
|
+ psp_master->build = status->build;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Don't fail if SEV FW couldn't be updated. Continue with existing SEV FW */
|
|
|
+static int sev_update_firmware(struct device *dev)
|
|
|
+{
|
|
|
+ struct sev_data_download_firmware *data;
|
|
|
+ const struct firmware *firmware;
|
|
|
+ int ret, error, order;
|
|
|
+ struct page *p;
|
|
|
+ u64 data_size;
|
|
|
+
|
|
|
+ ret = request_firmware(&firmware, SEV_FW_FILE, dev);
|
|
|
+ if (ret < 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * SEV FW expects the physical address given to it to be 32
|
|
|
+ * byte aligned. Memory allocated has structure placed at the
|
|
|
+ * beginning followed by the firmware being passed to the SEV
|
|
|
+ * FW. Allocate enough memory for data structure + alignment
|
|
|
+ * padding + SEV FW.
|
|
|
+ */
|
|
|
+ data_size = ALIGN(sizeof(struct sev_data_download_firmware), 32);
|
|
|
+
|
|
|
+ order = get_order(firmware->size + data_size);
|
|
|
+ p = alloc_pages(GFP_KERNEL, order);
|
|
|
+ if (!p) {
|
|
|
+ ret = -1;
|
|
|
+ goto fw_err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Copy firmware data to a kernel allocated contiguous
|
|
|
+ * memory region.
|
|
|
+ */
|
|
|
+ data = page_address(p);
|
|
|
+ memcpy(page_address(p) + data_size, firmware->data, firmware->size);
|
|
|
+
|
|
|
+ data->address = __psp_pa(page_address(p) + data_size);
|
|
|
+ data->len = firmware->size;
|
|
|
+
|
|
|
+ ret = sev_do_cmd(SEV_CMD_DOWNLOAD_FIRMWARE, data, &error);
|
|
|
+ if (ret)
|
|
|
+ dev_dbg(dev, "Failed to update SEV firmware: %#x\n", error);
|
|
|
+ else
|
|
|
+ dev_info(dev, "SEV firmware update successful\n");
|
|
|
+
|
|
|
+ __free_pages(p, order);
|
|
|
+
|
|
|
+fw_err:
|
|
|
+ release_firmware(firmware);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp)
|
|
|
{
|
|
|
struct sev_user_data_pek_cert_import input;
|
|
@@ -750,7 +830,6 @@ EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
|
|
|
|
|
|
void psp_pci_init(void)
|
|
|
{
|
|
|
- struct sev_user_data_status *status;
|
|
|
struct sp_device *sp;
|
|
|
int error, rc;
|
|
|
|
|
@@ -760,6 +839,13 @@ void psp_pci_init(void)
|
|
|
|
|
|
psp_master = sp->psp_data;
|
|
|
|
|
|
+ if (sev_get_api_version())
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ if (SEV_VERSION_GREATER_OR_EQUAL(0, 15) &&
|
|
|
+ sev_update_firmware(psp_master->dev) == 0)
|
|
|
+ sev_get_api_version();
|
|
|
+
|
|
|
/* Initialize the platform */
|
|
|
rc = sev_platform_init(&error);
|
|
|
if (rc) {
|
|
@@ -767,16 +853,9 @@ void psp_pci_init(void)
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
- /* Display SEV firmware version */
|
|
|
- status = &psp_master->status_cmd_buf;
|
|
|
- rc = sev_platform_status(status, &error);
|
|
|
- if (rc) {
|
|
|
- dev_err(sp->dev, "SEV: failed to get status error %#x\n", error);
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ dev_info(sp->dev, "SEV API:%d.%d build:%d\n", psp_master->api_major,
|
|
|
+ psp_master->api_minor, psp_master->build);
|
|
|
|
|
|
- dev_info(sp->dev, "SEV API:%d.%d build:%d\n", status->api_major,
|
|
|
- status->api_minor, status->build);
|
|
|
return;
|
|
|
|
|
|
err:
|