// SPDX-License-Identifier: GPL-2.0-only/* * AMD Secure Encrypted Virtualization (SEV) interface * * Copyright (C) 2016,2019 Advanced Micro Devices, Inc. * * Author: Brijesh Singh <brijesh.singh@amd.com> */ #include <linux/bitfield.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/kthread.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/spinlock_types.h>#include <linux/types.h>#include <linux/mutex.h>#include <linux/delay.h>#include <linux/hw_random.h>#include <linux/ccp.h>#include <linux/firmware.h>#include <linux/panic_notifier.h>#include <linux/gfp.h>#include <linux/cpufeature.h>#include <linux/fs.h>#include <linux/fs_struct.h>#include <linux/psp.h>#include <linux/amd-iommu.h>#include <linux/crash_dump.h> #include <asm/smp.h>#include <asm/cacheflush.h>#include <asm/e820/types.h>#include <asm/sev.h>#include <asm/msr.h> #include "psp-dev.h"#include "sev-dev.h" #define DEVICE_NAME "sev"#define SEV_FW_FILE "amd/sev.fw"#define SEV_FW_NAME_SIZE 64 /* Minimum firmware version required for the SEV-SNP support */#define SNP_MIN_API_MAJOR 1#define SNP_MIN_API_MINOR 51 /* * Maximum number of firmware-writable buffers that might be specified * in the parameters of a legacy SEV command buffer. */#define CMD_BUF_FW_WRITABLE_MAX 2 /* Leave room in the descriptor array for an end-of-list indicator. */#define CMD_BUF_DESC_MAX (CMD_BUF_FW_WRITABLE_MAX + 1) static DEFINE_MUTEX(sev_cmd_mutex);static struct sev_misc_dev *misc_dev; static int psp_cmd_timeout = 100;module_param(psp_cmd_timeout, int, 0644);MODULE_PARM_DESC(psp_cmd_timeout, " default timeout value, in seconds, for PSP commands"); static int psp_probe_timeout = 5;module_param(psp_probe_timeout, int, 0644);MODULE_PARM_DESC(psp_probe_timeout, " default timeout value, in seconds, during PSP device probe"); static char *init_ex_path;module_param(init_ex_path, charp, 0444);MODULE_PARM_DESC(init_ex_path, " Path for INIT_EX data; if set try INIT_EX"); static bool psp_init_on_probe = true;module_param(psp_init_on_probe, bool, 0444);MODULE_PARM_DESC(psp_init_on_probe, " if true, the PSP will be initialized on module init. Else the PSP will be initialized on the first command requiring it"); #if IS_ENABLED(CONFIG_PCI_TSM)static bool sev_tio_enabled = true;module_param_named(tio, sev_tio_enabled, bool, 0444);MODULE_PARM_DESC(tio, "Enables TIO in SNP_INIT_EX");#elsestatic const bool sev_tio_enabled = false;#endif MODULE_FIRMWARE("amd/amd_sev_fam17h_model0xh.sbin"); /* 1st gen EPYC */MODULE_FIRMWARE("amd/amd_sev_fam17h_model3xh.sbin"); /* 2nd gen EPYC */MODULE_FIRMWARE("amd/amd_sev_fam19h_model0xh.sbin"); /* 3rd gen EPYC */MODULE_FIRMWARE("amd/amd_sev_fam19h_model1xh.sbin"); /* 4th gen EPYC */ static bool psp_dead;static int psp_timeout; enum snp_hv_fixed_pages_state { ALLOCATED, HV_FIXED,}; struct snp_hv_fixed_pages_entry { struct list_head list; struct page *page; unsigned int order; bool free; enum snp_hv_fixed_pages_state page_state;}; static LIST_HEAD(snp_hv_fixed_pages); /* Trusted Memory Region (TMR): * The TMR is a 1MB area that must be 1MB aligned. Use the page allocator * to allocate the memory, which will return aligned memory for the specified * allocation order. * * When SEV-SNP is enabled the TMR needs to be 2MB aligned and 2MB sized. */#define SEV_TMR_SIZE (1024 * 1024)#define SNP_TMR_SIZE (2 * 1024 * 1024) static void *sev_es_tmr;static size_t sev_es_tmr_size = SEV_TMR_SIZE; /* INIT_EX NV Storage: * The NV Storage is a 32Kb area and must be 4Kb page aligned. Use the page * allocator to allocate the memory, which will return aligned memory for the * specified allocation order. */#define NV_LENGTH (32 * 1024)static void *sev_init_ex_buffer; static void __sev_firmware_shutdown(struct sev_device *sev, bool panic); static int snp_shutdown_on_panic(struct notifier_block *nb, unsigned long reason, void *arg); static struct notifier_block snp_panic_notifier = { .notifier_call = snp_shutdown_on_panic,}; static inline bool sev_version_greater_or_equal(u8 maj, u8 min){ struct sev_device *sev = psp_master->sev_data; if (sev->api_major > maj) return true; if (sev->api_major == maj && sev->api_minor >= min) return true; return false;} static void sev_irq_handler(int irq, void *data, unsigned int status){ struct sev_device *sev = data; int reg; /* Check if it is command completion: */ if (!(status & SEV_CMD_COMPLETE)) return; /* Check if it is SEV command completion: */ reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); if (FIELD_GET(PSP_CMDRESP_RESP, reg)) { sev->int_rcvd = 1; wake_up(&sev->int_queue); }} static int sev_wait_cmd_ioc(struct sev_device *sev, unsigned int *reg, unsigned int timeout){ int ret; /* * If invoked during panic handling, local interrupts are disabled, * so the PSP command completion interrupt can't be used. Poll for * PSP command completion instead. */ if (irqs_disabled()) { unsigned long timeout_usecs = (timeout * USEC_PER_SEC) / 10; /* Poll for SEV command completion: */ while (timeout_usecs--) { *reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); if (*reg & PSP_CMDRESP_RESP) return 0; udelay(10); } return -ETIMEDOUT; } ret = wait_event_timeout(sev->int_queue, sev->int_rcvd, timeout * HZ); if (!ret) return -ETIMEDOUT; *reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); return 0;} static int sev_cmd_buffer_len(int cmd){ switch (cmd) { case SEV_CMD_INIT: return sizeof(struct sev_data_init); case SEV_CMD_INIT_EX: return sizeof(struct sev_data_init_ex); case SEV_CMD_SNP_SHUTDOWN_EX: return sizeof(struct sev_data_snp_shutdown_ex); case SEV_CMD_SNP_INIT_EX: return sizeof(struct sev_data_snp_init_ex); case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_user_data_status); case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr); case SEV_CMD_PEK_CERT_IMPORT: return sizeof(struct sev_data_pek_cert_import); case SEV_CMD_PDH_CERT_EXPORT: return sizeof(struct sev_data_pdh_cert_export); case SEV_CMD_LAUNCH_START: return sizeof(struct sev_data_launch_start); case SEV_CMD_LAUNCH_UPDATE_DATA: return sizeof(struct sev_data_launch_update_data); case SEV_CMD_LAUNCH_UPDATE_VMSA: return sizeof(struct sev_data_launch_update_vmsa); case SEV_CMD_LAUNCH_FINISH: return sizeof(struct sev_data_launch_finish); case SEV_CMD_LAUNCH_MEASURE: return sizeof(struct sev_data_launch_measure); case SEV_CMD_ACTIVATE: return sizeof(struct sev_data_activate); case SEV_CMD_DEACTIVATE: return sizeof(struct sev_data_deactivate); case SEV_CMD_DECOMMISSION: return sizeof(struct sev_data_decommission); case SEV_CMD_GUEST_STATUS: return sizeof(struct sev_data_guest_status); case SEV_CMD_DBG_DECRYPT: return sizeof(struct sev_data_dbg); case SEV_CMD_DBG_ENCRYPT: return sizeof(struct sev_data_dbg); case SEV_CMD_SEND_START: return sizeof(struct sev_data_send_start); case SEV_CMD_SEND_UPDATE_DATA: return sizeof(struct sev_data_send_update_data); case SEV_CMD_SEND_UPDATE_VMSA: return sizeof(struct sev_data_send_update_vmsa); case SEV_CMD_SEND_FINISH: return sizeof(struct sev_data_send_finish); case SEV_CMD_RECEIVE_START: return sizeof(struct sev_data_receive_start); case SEV_CMD_RECEIVE_FINISH: return sizeof(struct sev_data_receive_finish); 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); case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id); case SEV_CMD_ATTESTATION_REPORT: return sizeof(struct sev_data_attestation_report); case SEV_CMD_SEND_CANCEL: return sizeof(struct sev_data_send_cancel); case SEV_CMD_SNP_GCTX_CREATE: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_LAUNCH_START: return sizeof(struct sev_data_snp_launch_start); case SEV_CMD_SNP_LAUNCH_UPDATE: return sizeof(struct sev_data_snp_launch_update); case SEV_CMD_SNP_ACTIVATE: return sizeof(struct sev_data_snp_activate); case SEV_CMD_SNP_DECOMMISSION: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_PAGE_RECLAIM: return sizeof(struct sev_data_snp_page_reclaim); case SEV_CMD_SNP_GUEST_STATUS: return sizeof(struct sev_data_snp_guest_status); case SEV_CMD_SNP_LAUNCH_FINISH: return sizeof(struct sev_data_snp_launch_finish); case SEV_CMD_SNP_DBG_DECRYPT: return sizeof(struct sev_data_snp_dbg); case SEV_CMD_SNP_DBG_ENCRYPT: return sizeof(struct sev_data_snp_dbg); case SEV_CMD_SNP_VERIFY_MITIGATION: return sizeof(struct sev_data_snp_verify_mitigation); case SEV_CMD_SNP_PAGE_UNSMASH: return sizeof(struct sev_data_snp_page_unsmash); case SEV_CMD_SNP_PLATFORM_STATUS: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_GUEST_REQUEST: return sizeof(struct sev_data_snp_guest_request); case SEV_CMD_SNP_CONFIG: return sizeof(struct sev_user_data_snp_config); case SEV_CMD_SNP_COMMIT: return sizeof(struct sev_data_snp_commit); case SEV_CMD_SNP_FEATURE_INFO: return sizeof(struct sev_data_snp_feature_info); case SEV_CMD_SNP_VLEK_LOAD: return sizeof(struct sev_user_data_snp_vlek_load); default: return sev_tio_cmd_buffer_len(cmd); } return 0;} static struct file *open_file_as_root(const char *filename, int flags, umode_t mode){ CLASS(prepare_creds, cred)(); if (!cred) return ERR_PTR(-ENOMEM); cred->fsuid = GLOBAL_ROOT_UID; scoped_with_init_fs() { scoped_with_creds(cred) return filp_open(filename, flags, mode); }} static int sev_read_init_ex_file(void){ struct sev_device *sev = psp_master->sev_data; struct file *fp; ssize_t nread; lockdep_assert_held(&sev_cmd_mutex); if (!sev_init_ex_buffer) return -EOPNOTSUPP; fp = open_file_as_root(init_ex_path, O_RDONLY, 0); if (IS_ERR(fp)) { int ret = PTR_ERR(fp); if (ret == -ENOENT) { dev_info(sev->dev, "SEV: %s does not exist and will be created later.\n", init_ex_path); ret = 0; } else { dev_err(sev->dev, "SEV: could not open %s for read, error %d\n", init_ex_path, ret); } return ret; } nread = kernel_read(fp, sev_init_ex_buffer, NV_LENGTH, NULL); if (nread != NV_LENGTH) { dev_info(sev->dev, "SEV: could not read %u bytes to non volatile memory area, ret %ld\n", NV_LENGTH, nread); } dev_dbg(sev->dev, "SEV: read %ld bytes from NV file\n", nread); filp_close(fp, NULL); return 0;} static int sev_write_init_ex_file(void){ struct sev_device *sev = psp_master->sev_data; struct file *fp; loff_t offset = 0; ssize_t nwrite; lockdep_assert_held(&sev_cmd_mutex); if (!sev_init_ex_buffer) return 0; fp = open_file_as_root(init_ex_path, O_CREAT | O_WRONLY, 0600); if (IS_ERR(fp)) { int ret = PTR_ERR(fp); dev_err(sev->dev, "SEV: could not open file for write, error %d\n", ret); return ret; } nwrite = kernel_write(fp, sev_init_ex_buffer, NV_LENGTH, &offset); vfs_fsync(fp, 0); filp_close(fp, NULL); if (nwrite != NV_LENGTH) { dev_err(sev->dev, "SEV: failed to write %u bytes to non volatile memory area, ret %ld\n", NV_LENGTH, nwrite); return -EIO; } dev_dbg(sev->dev, "SEV: write successful to NV file\n"); return 0;} static int sev_write_init_ex_file_if_required(int cmd_id){ lockdep_assert_held(&sev_cmd_mutex); if (!sev_init_ex_buffer) return 0; /* * Only a few platform commands modify the SPI/NV area, but none of the * non-platform commands do. Only INIT(_EX), PLATFORM_RESET, PEK_GEN, * PEK_CERT_IMPORT, and PDH_GEN do. */ switch (cmd_id) { case SEV_CMD_FACTORY_RESET: case SEV_CMD_INIT_EX: case SEV_CMD_PDH_GEN: case SEV_CMD_PEK_CERT_IMPORT: case SEV_CMD_PEK_GEN: break; default: return 0; } return sev_write_init_ex_file();} int snp_reclaim_pages(unsigned long paddr, unsigned int npages, bool locked){ int ret, err, i; paddr = __sme_clr(ALIGN_DOWN(paddr, PAGE_SIZE)); for (i = 0; i < npages; i++, paddr += PAGE_SIZE) { struct sev_data_snp_page_reclaim data = {0}; data.paddr = paddr; if (locked) ret = __sev_do_cmd_locked(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err); else ret = sev_do_cmd(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err); if (ret) goto cleanup; ret = rmp_make_shared(__phys_to_pfn(paddr), PG_LEVEL_4K); if (ret) goto cleanup; } return 0; cleanup: /* * If there was a failure reclaiming the page then it is no longer safe * to release it back to the system; leak it instead. */ snp_leak_pages(__phys_to_pfn(paddr), npages - i); return ret;}EXPORT_SYMBOL_GPL(snp_reclaim_pages); static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, bool locked){ unsigned long pfn = __sme_clr(paddr) >> PAGE_SHIFT; int rc, i; for (i = 0; i < npages; i++, pfn++) { rc = rmp_make_private(pfn, 0, PG_LEVEL_4K, 0, true); if (rc) goto cleanup; } return 0; cleanup: /* * Try unrolling the firmware state changes by * reclaiming the pages which were already changed to the * firmware state. */ snp_reclaim_pages(paddr, i, locked); return rc;} static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order, bool locked){ unsigned long npages = 1ul << order, paddr; struct sev_device *sev; struct page *page; if (!psp_master || !psp_master->sev_data) return NULL; page = alloc_pages(gfp_mask, order); if (!page) return NULL; /* If SEV-SNP is initialized then add the page in RMP table. */ sev = psp_master->sev_data; if (!sev->snp_initialized) return page; paddr = __pa((unsigned long)page_address(page)); if (rmp_mark_pages_firmware(paddr, npages, locked)) return NULL; return page;} void *snp_alloc_firmware_page(gfp_t gfp_mask){ struct page *page; page = __snp_alloc_firmware_pages(gfp_mask, 0, false); return page ? page_address(page) : NULL;}EXPORT_SYMBOL_GPL(snp_alloc_firmware_page); static void __snp_free_firmware_pages(struct page *page, int order, bool locked){ struct sev_device *sev = psp_master->sev_data; unsigned long paddr, npages = 1ul << order; if (!page) return; paddr = __pa((unsigned long)page_address(page)); if (sev->snp_initialized && snp_reclaim_pages(paddr, npages, locked)) return; __free_pages(page, order);} void snp_free_firmware_page(void *addr){ if (!addr) return; __snp_free_firmware_pages(virt_to_page(addr), 0, false);}EXPORT_SYMBOL_GPL(snp_free_firmware_page); Lines 1–500 of 3168. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * AMD Secure Encrypted Virtualization (SEV) interface * * Copyright (C) 2016,2019 Advanced Micro Devices, Inc. * * Author: Brijesh Singh <brijesh.singh@amd.com> */ #include <linux/bitfield.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/spinlock_types.h> #include <linux/types.h> #include <linux/mutex.h> #include <linux/delay.h> #include <linux/hw_random.h> #include <linux/ccp.h> #include <linux/firmware.h> #include <linux/panic_notifier.h> #include <linux/gfp.h> #include <linux/cpufeature.h> #include <linux/fs.h> #include <linux/fs_struct.h> #include <linux/psp.h> #include <linux/amd-iommu.h> #include <linux/crash_dump.h> #include <asm/smp.h> #include <asm/cacheflush.h> #include <asm/e820/types.h> #include <asm/sev.h> #include <asm/msr.h> #include "psp-dev.h" #include "sev-dev.h" #define DEVICE_NAME "sev" #define SEV_FW_FILE "amd/sev.fw" #define SEV_FW_NAME_SIZE 64 /* Minimum firmware version required for the SEV-SNP support */ #define SNP_MIN_API_MAJOR 1 #define SNP_MIN_API_MINOR 51 /* * Maximum number of firmware-writable buffers that might be specified * in the parameters of a legacy SEV command buffer. */ #define CMD_BUF_FW_WRITABLE_MAX 2 /* Leave room in the descriptor array for an end-of-list indicator. */ #define CMD_BUF_DESC_MAX (CMD_BUF_FW_WRITABLE_MAX + 1) static DEFINE_MUTEX(sev_cmd_mutex); static struct sev_misc_dev *misc_dev; static int psp_cmd_timeout = 100; module_param(psp_cmd_timeout, int, 0644); MODULE_PARM_DESC(psp_cmd_timeout, " default timeout value, in seconds, for PSP commands"); static int psp_probe_timeout = 5; module_param(psp_probe_timeout, int, 0644); MODULE_PARM_DESC(psp_probe_timeout, " default timeout value, in seconds, during PSP device probe"); static char *init_ex_path; module_param(init_ex_path, charp, 0444); MODULE_PARM_DESC(init_ex_path, " Path for INIT_EX data; if set try INIT_EX"); static bool psp_init_on_probe = true; module_param(psp_init_on_probe, bool, 0444); MODULE_PARM_DESC(psp_init_on_probe, " if true, the PSP will be initialized on module init. Else the PSP will be initialized on the first command requiring it"); #if IS_ENABLED(CONFIG_PCI_TSM) static bool sev_tio_enabled = true; module_param_named(tio, sev_tio_enabled, bool, 0444); MODULE_PARM_DESC(tio, "Enables TIO in SNP_INIT_EX"); #else static const bool sev_tio_enabled = false; #endif MODULE_FIRMWARE("amd/amd_sev_fam17h_model0xh.sbin"); /* 1st gen EPYC */ MODULE_FIRMWARE("amd/amd_sev_fam17h_model3xh.sbin"); /* 2nd gen EPYC */ MODULE_FIRMWARE("amd/amd_sev_fam19h_model0xh.sbin"); /* 3rd gen EPYC */ MODULE_FIRMWARE("amd/amd_sev_fam19h_model1xh.sbin"); /* 4th gen EPYC */ static bool psp_dead; static int psp_timeout; enum snp_hv_fixed_pages_state { ALLOCATED, HV_FIXED, }; struct snp_hv_fixed_pages_entry { struct list_head list; struct page *page; unsigned int order; bool free; enum snp_hv_fixed_pages_state page_state; }; static LIST_HEAD(snp_hv_fixed_pages); /* Trusted Memory Region (TMR): * The TMR is a 1MB area that must be 1MB aligned. Use the page allocator * to allocate the memory, which will return aligned memory for the specified * allocation order. * * When SEV-SNP is enabled the TMR needs to be 2MB aligned and 2MB sized. */ #define SEV_TMR_SIZE (1024 * 1024) #define SNP_TMR_SIZE (2 * 1024 * 1024) static void *sev_es_tmr; static size_t sev_es_tmr_size = SEV_TMR_SIZE; /* INIT_EX NV Storage: * The NV Storage is a 32Kb area and must be 4Kb page aligned. Use the page * allocator to allocate the memory, which will return aligned memory for the * specified allocation order. */ #define NV_LENGTH (32 * 1024) static void *sev_init_ex_buffer; static void __sev_firmware_shutdown(struct sev_device *sev, bool panic); static int snp_shutdown_on_panic(struct notifier_block *nb, unsigned long reason, void *arg); static struct notifier_block snp_panic_notifier = { .notifier_call = snp_shutdown_on_panic, }; static inline bool sev_version_greater_or_equal(u8 maj, u8 min) { struct sev_device *sev = psp_master->sev_data; if (sev->api_major > maj) return true; if (sev->api_major == maj && sev->api_minor >= min) return true; return false; } static void sev_irq_handler(int irq, void *data, unsigned int status) { struct sev_device *sev = data; int reg; /* Check if it is command completion: */ if (!(status & SEV_CMD_COMPLETE)) return; /* Check if it is SEV command completion: */ reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); if (FIELD_GET(PSP_CMDRESP_RESP, reg)) { sev->int_rcvd = 1; wake_up(&sev->int_queue); } } static int sev_wait_cmd_ioc(struct sev_device *sev, unsigned int *reg, unsigned int timeout) { int ret; /* * If invoked during panic handling, local interrupts are disabled, * so the PSP command completion interrupt can't be used. Poll for * PSP command completion instead. */ if (irqs_disabled()) { unsigned long timeout_usecs = (timeout * USEC_PER_SEC) / 10; /* Poll for SEV command completion: */ while (timeout_usecs--) { *reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); if (*reg & PSP_CMDRESP_RESP) return 0; udelay(10); } return -ETIMEDOUT; } ret = wait_event_timeout(sev->int_queue, sev->int_rcvd, timeout * HZ); if (!ret) return -ETIMEDOUT; *reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); return 0; } static int sev_cmd_buffer_len(int cmd) { switch (cmd) { case SEV_CMD_INIT: return sizeof(struct sev_data_init); case SEV_CMD_INIT_EX: return sizeof(struct sev_data_init_ex); case SEV_CMD_SNP_SHUTDOWN_EX: return sizeof(struct sev_data_snp_shutdown_ex); case SEV_CMD_SNP_INIT_EX: return sizeof(struct sev_data_snp_init_ex); case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_user_data_status); case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr); case SEV_CMD_PEK_CERT_IMPORT: return sizeof(struct sev_data_pek_cert_import); case SEV_CMD_PDH_CERT_EXPORT: return sizeof(struct sev_data_pdh_cert_export); case SEV_CMD_LAUNCH_START: return sizeof(struct sev_data_launch_start); case SEV_CMD_LAUNCH_UPDATE_DATA: return sizeof(struct sev_data_launch_update_data); case SEV_CMD_LAUNCH_UPDATE_VMSA: return sizeof(struct sev_data_launch_update_vmsa); case SEV_CMD_LAUNCH_FINISH: return sizeof(struct sev_data_launch_finish); case SEV_CMD_LAUNCH_MEASURE: return sizeof(struct sev_data_launch_measure); case SEV_CMD_ACTIVATE: return sizeof(struct sev_data_activate); case SEV_CMD_DEACTIVATE: return sizeof(struct sev_data_deactivate); case SEV_CMD_DECOMMISSION: return sizeof(struct sev_data_decommission); case SEV_CMD_GUEST_STATUS: return sizeof(struct sev_data_guest_status); case SEV_CMD_DBG_DECRYPT: return sizeof(struct sev_data_dbg); case SEV_CMD_DBG_ENCRYPT: return sizeof(struct sev_data_dbg); case SEV_CMD_SEND_START: return sizeof(struct sev_data_send_start); case SEV_CMD_SEND_UPDATE_DATA: return sizeof(struct sev_data_send_update_data); case SEV_CMD_SEND_UPDATE_VMSA: return sizeof(struct sev_data_send_update_vmsa); case SEV_CMD_SEND_FINISH: return sizeof(struct sev_data_send_finish); case SEV_CMD_RECEIVE_START: return sizeof(struct sev_data_receive_start); case SEV_CMD_RECEIVE_FINISH: return sizeof(struct sev_data_receive_finish); 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); case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id); case SEV_CMD_ATTESTATION_REPORT: return sizeof(struct sev_data_attestation_report); case SEV_CMD_SEND_CANCEL: return sizeof(struct sev_data_send_cancel); case SEV_CMD_SNP_GCTX_CREATE: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_LAUNCH_START: return sizeof(struct sev_data_snp_launch_start); case SEV_CMD_SNP_LAUNCH_UPDATE: return sizeof(struct sev_data_snp_launch_update); case SEV_CMD_SNP_ACTIVATE: return sizeof(struct sev_data_snp_activate); case SEV_CMD_SNP_DECOMMISSION: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_PAGE_RECLAIM: return sizeof(struct sev_data_snp_page_reclaim); case SEV_CMD_SNP_GUEST_STATUS: return sizeof(struct sev_data_snp_guest_status); case SEV_CMD_SNP_LAUNCH_FINISH: return sizeof(struct sev_data_snp_launch_finish); case SEV_CMD_SNP_DBG_DECRYPT: return sizeof(struct sev_data_snp_dbg); case SEV_CMD_SNP_DBG_ENCRYPT: return sizeof(struct sev_data_snp_dbg); case SEV_CMD_SNP_VERIFY_MITIGATION: return sizeof(struct sev_data_snp_verify_mitigation); case SEV_CMD_SNP_PAGE_UNSMASH: return sizeof(struct sev_data_snp_page_unsmash); case SEV_CMD_SNP_PLATFORM_STATUS: return sizeof(struct sev_data_snp_addr); case SEV_CMD_SNP_GUEST_REQUEST: return sizeof(struct sev_data_snp_guest_request); case SEV_CMD_SNP_CONFIG: return sizeof(struct sev_user_data_snp_config); case SEV_CMD_SNP_COMMIT: return sizeof(struct sev_data_snp_commit); case SEV_CMD_SNP_FEATURE_INFO: return sizeof(struct sev_data_snp_feature_info); case SEV_CMD_SNP_VLEK_LOAD: return sizeof(struct sev_user_data_snp_vlek_load); default: return sev_tio_cmd_buffer_len(cmd); } return 0; } static struct file *open_file_as_root(const char *filename, int flags, umode_t mode) { CLASS(prepare_creds, cred)(); if (!cred) return ERR_PTR(-ENOMEM); cred->fsuid = GLOBAL_ROOT_UID; scoped_with_init_fs() { scoped_with_creds(cred) return filp_open(filename, flags, mode); } } static int sev_read_init_ex_file(void) { struct sev_device *sev = psp_master->sev_data; struct file *fp; ssize_t nread; lockdep_assert_held(&sev_cmd_mutex); if (!sev_init_ex_buffer) return -EOPNOTSUPP; fp = open_file_as_root(init_ex_path, O_RDONLY, 0); if (IS_ERR(fp)) { int ret = PTR_ERR(fp); if (ret == -ENOENT) { dev_info(sev->dev, "SEV: %s does not exist and will be created later.\n", init_ex_path); ret = 0; } else { dev_err(sev->dev, "SEV: could not open %s for read, error %d\n", init_ex_path, ret); } return ret; } nread = kernel_read(fp, sev_init_ex_buffer, NV_LENGTH, NULL); if (nread != NV_LENGTH) { dev_info(sev->dev, "SEV: could not read %u bytes to non volatile memory area, ret %ld\n", NV_LENGTH, nread); } dev_dbg(sev->dev, "SEV: read %ld bytes from NV file\n", nread); filp_close(fp, NULL); return 0; } static int sev_write_init_ex_file(void) { struct sev_device *sev = psp_master->sev_data; struct file *fp; loff_t offset = 0; ssize_t nwrite; lockdep_assert_held(&sev_cmd_mutex); if (!sev_init_ex_buffer) return 0; fp = open_file_as_root(init_ex_path, O_CREAT | O_WRONLY, 0600); if (IS_ERR(fp)) { int ret = PTR_ERR(fp); dev_err(sev->dev, "SEV: could not open file for write, error %d\n", ret); return ret; } nwrite = kernel_write(fp, sev_init_ex_buffer, NV_LENGTH, &offset); vfs_fsync(fp, 0); filp_close(fp, NULL); if (nwrite != NV_LENGTH) { dev_err(sev->dev, "SEV: failed to write %u bytes to non volatile memory area, ret %ld\n", NV_LENGTH, nwrite); return -EIO; } dev_dbg(sev->dev, "SEV: write successful to NV file\n"); return 0; } static int sev_write_init_ex_file_if_required(int cmd_id) { lockdep_assert_held(&sev_cmd_mutex); if (!sev_init_ex_buffer) return 0; /* * Only a few platform commands modify the SPI/NV area, but none of the * non-platform commands do. Only INIT(_EX), PLATFORM_RESET, PEK_GEN, * PEK_CERT_IMPORT, and PDH_GEN do. */ switch (cmd_id) { case SEV_CMD_FACTORY_RESET: case SEV_CMD_INIT_EX: case SEV_CMD_PDH_GEN: case SEV_CMD_PEK_CERT_IMPORT: case SEV_CMD_PEK_GEN: break; default: return 0; } return sev_write_init_ex_file(); } int snp_reclaim_pages(unsigned long paddr, unsigned int npages, bool locked) { int ret, err, i; paddr = __sme_clr(ALIGN_DOWN(paddr, PAGE_SIZE)); for (i = 0; i < npages; i++, paddr += PAGE_SIZE) { struct sev_data_snp_page_reclaim data = {0}; data.paddr = paddr; if (locked) ret = __sev_do_cmd_locked(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err); else ret = sev_do_cmd(SEV_CMD_SNP_PAGE_RECLAIM, &data, &err); if (ret) goto cleanup; ret = rmp_make_shared(__phys_to_pfn(paddr), PG_LEVEL_4K); if (ret) goto cleanup; } return 0; cleanup: /* * If there was a failure reclaiming the page then it is no longer safe * to release it back to the system; leak it instead. */ snp_leak_pages(__phys_to_pfn(paddr), npages - i); return ret; } EXPORT_SYMBOL_GPL(snp_reclaim_pages); static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, bool locked) { unsigned long pfn = __sme_clr(paddr) >> PAGE_SHIFT; int rc, i; for (i = 0; i < npages; i++, pfn++) { rc = rmp_make_private(pfn, 0, PG_LEVEL_4K, 0, true); if (rc) goto cleanup; } return 0; cleanup: /* * Try unrolling the firmware state changes by * reclaiming the pages which were already changed to the * firmware state. */ snp_reclaim_pages(paddr, i, locked); return rc; } static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order, bool locked) { unsigned long npages = 1ul << order, paddr; struct sev_device *sev; struct page *page; if (!psp_master || !psp_master->sev_data) return NULL; page = alloc_pages(gfp_mask, order); if (!page) return NULL; /* If SEV-SNP is initialized then add the page in RMP table. */ sev = psp_master->sev_data; if (!sev->snp_initialized) return page; paddr = __pa((unsigned long)page_address(page)); if (rmp_mark_pages_firmware(paddr, npages, locked)) return NULL; return page; } void *snp_alloc_firmware_page(gfp_t gfp_mask) { struct page *page; page = __snp_alloc_firmware_pages(gfp_mask, 0, false); return page ? page_address(page) : NULL; } EXPORT_SYMBOL_GPL(snp_alloc_firmware_page); static void __snp_free_firmware_pages(struct page *page, int order, bool locked) { struct sev_device *sev = psp_master->sev_data; unsigned long paddr, npages = 1ul << order; if (!page) return; paddr = __pa((unsigned long)page_address(page)); if (sev->snp_initialized && snp_reclaim_pages(paddr, npages, locked)) return; __free_pages(page, order); } void snp_free_firmware_page(void *addr) { if (!addr) return; __snp_free_firmware_pages(virt_to_page(addr), 0, false); } EXPORT_SYMBOL_GPL(snp_free_firmware_page);