18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AMD Secure Encrypted Virtualization (SEV) interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016,2019 Advanced Micro Devices, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Brijesh Singh <brijesh.singh@amd.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/kthread.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/spinlock_types.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 218c2ecf20Sopenharmony_ci#include <linux/ccp.h> 228c2ecf20Sopenharmony_ci#include <linux/firmware.h> 238c2ecf20Sopenharmony_ci#include <linux/gfp.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/smp.h> 268c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "psp-dev.h" 298c2ecf20Sopenharmony_ci#include "sev-dev.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define DEVICE_NAME "sev" 328c2ecf20Sopenharmony_ci#define SEV_FW_FILE "amd/sev.fw" 338c2ecf20Sopenharmony_ci#define SEV_FW_NAME_SIZE 64 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sev_cmd_mutex); 368c2ecf20Sopenharmony_cistatic struct sev_misc_dev *misc_dev; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int psp_cmd_timeout = 100; 398c2ecf20Sopenharmony_cimodule_param(psp_cmd_timeout, int, 0644); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(psp_cmd_timeout, " default timeout value, in seconds, for PSP commands"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int psp_probe_timeout = 5; 438c2ecf20Sopenharmony_cimodule_param(psp_probe_timeout, int, 0644); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(psp_probe_timeout, " default timeout value, in seconds, during PSP device probe"); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciMODULE_FIRMWARE("amd/amd_sev_fam17h_model0xh.sbin"); /* 1st gen EPYC */ 478c2ecf20Sopenharmony_ciMODULE_FIRMWARE("amd/amd_sev_fam17h_model3xh.sbin"); /* 2nd gen EPYC */ 488c2ecf20Sopenharmony_ciMODULE_FIRMWARE("amd/amd_sev_fam19h_model0xh.sbin"); /* 3rd gen EPYC */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic bool psp_dead; 518c2ecf20Sopenharmony_cistatic int psp_timeout; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Trusted Memory Region (TMR): 548c2ecf20Sopenharmony_ci * The TMR is a 1MB area that must be 1MB aligned. Use the page allocator 558c2ecf20Sopenharmony_ci * to allocate the memory, which will return aligned memory for the specified 568c2ecf20Sopenharmony_ci * allocation order. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci#define SEV_ES_TMR_SIZE (1024 * 1024) 598c2ecf20Sopenharmony_cistatic void *sev_es_tmr; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline bool sev_version_greater_or_equal(u8 maj, u8 min) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (sev->api_major > maj) 668c2ecf20Sopenharmony_ci return true; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (sev->api_major == maj && sev->api_minor >= min) 698c2ecf20Sopenharmony_ci return true; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return false; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void sev_irq_handler(int irq, void *data, unsigned int status) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct sev_device *sev = data; 778c2ecf20Sopenharmony_ci int reg; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Check if it is command completion: */ 808c2ecf20Sopenharmony_ci if (!(status & SEV_CMD_COMPLETE)) 818c2ecf20Sopenharmony_ci return; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Check if it is SEV command completion: */ 848c2ecf20Sopenharmony_ci reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); 858c2ecf20Sopenharmony_ci if (reg & PSP_CMDRESP_RESP) { 868c2ecf20Sopenharmony_ci sev->int_rcvd = 1; 878c2ecf20Sopenharmony_ci wake_up(&sev->int_queue); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int sev_wait_cmd_ioc(struct sev_device *sev, 928c2ecf20Sopenharmony_ci unsigned int *reg, unsigned int timeout) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci int ret; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret = wait_event_timeout(sev->int_queue, 978c2ecf20Sopenharmony_ci sev->int_rcvd, timeout * HZ); 988c2ecf20Sopenharmony_ci if (!ret) 998c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci *reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int sev_cmd_buffer_len(int cmd) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci switch (cmd) { 1098c2ecf20Sopenharmony_ci case SEV_CMD_INIT: return sizeof(struct sev_data_init); 1108c2ecf20Sopenharmony_ci case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_user_data_status); 1118c2ecf20Sopenharmony_ci case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr); 1128c2ecf20Sopenharmony_ci case SEV_CMD_PEK_CERT_IMPORT: return sizeof(struct sev_data_pek_cert_import); 1138c2ecf20Sopenharmony_ci case SEV_CMD_PDH_CERT_EXPORT: return sizeof(struct sev_data_pdh_cert_export); 1148c2ecf20Sopenharmony_ci case SEV_CMD_LAUNCH_START: return sizeof(struct sev_data_launch_start); 1158c2ecf20Sopenharmony_ci case SEV_CMD_LAUNCH_UPDATE_DATA: return sizeof(struct sev_data_launch_update_data); 1168c2ecf20Sopenharmony_ci case SEV_CMD_LAUNCH_UPDATE_VMSA: return sizeof(struct sev_data_launch_update_vmsa); 1178c2ecf20Sopenharmony_ci case SEV_CMD_LAUNCH_FINISH: return sizeof(struct sev_data_launch_finish); 1188c2ecf20Sopenharmony_ci case SEV_CMD_LAUNCH_MEASURE: return sizeof(struct sev_data_launch_measure); 1198c2ecf20Sopenharmony_ci case SEV_CMD_ACTIVATE: return sizeof(struct sev_data_activate); 1208c2ecf20Sopenharmony_ci case SEV_CMD_DEACTIVATE: return sizeof(struct sev_data_deactivate); 1218c2ecf20Sopenharmony_ci case SEV_CMD_DECOMMISSION: return sizeof(struct sev_data_decommission); 1228c2ecf20Sopenharmony_ci case SEV_CMD_GUEST_STATUS: return sizeof(struct sev_data_guest_status); 1238c2ecf20Sopenharmony_ci case SEV_CMD_DBG_DECRYPT: return sizeof(struct sev_data_dbg); 1248c2ecf20Sopenharmony_ci case SEV_CMD_DBG_ENCRYPT: return sizeof(struct sev_data_dbg); 1258c2ecf20Sopenharmony_ci case SEV_CMD_SEND_START: return sizeof(struct sev_data_send_start); 1268c2ecf20Sopenharmony_ci case SEV_CMD_SEND_UPDATE_DATA: return sizeof(struct sev_data_send_update_data); 1278c2ecf20Sopenharmony_ci case SEV_CMD_SEND_UPDATE_VMSA: return sizeof(struct sev_data_send_update_vmsa); 1288c2ecf20Sopenharmony_ci case SEV_CMD_SEND_FINISH: return sizeof(struct sev_data_send_finish); 1298c2ecf20Sopenharmony_ci case SEV_CMD_RECEIVE_START: return sizeof(struct sev_data_receive_start); 1308c2ecf20Sopenharmony_ci case SEV_CMD_RECEIVE_FINISH: return sizeof(struct sev_data_receive_finish); 1318c2ecf20Sopenharmony_ci case SEV_CMD_RECEIVE_UPDATE_DATA: return sizeof(struct sev_data_receive_update_data); 1328c2ecf20Sopenharmony_ci case SEV_CMD_RECEIVE_UPDATE_VMSA: return sizeof(struct sev_data_receive_update_vmsa); 1338c2ecf20Sopenharmony_ci case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret); 1348c2ecf20Sopenharmony_ci case SEV_CMD_DOWNLOAD_FIRMWARE: return sizeof(struct sev_data_download_firmware); 1358c2ecf20Sopenharmony_ci case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id); 1368c2ecf20Sopenharmony_ci default: return 0; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void *sev_fw_alloc(unsigned long len) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct page *page; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci page = alloc_pages(GFP_KERNEL, get_order(len)); 1478c2ecf20Sopenharmony_ci if (!page) 1488c2ecf20Sopenharmony_ci return NULL; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return page_address(page); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct psp_device *psp = psp_master; 1568c2ecf20Sopenharmony_ci struct sev_device *sev; 1578c2ecf20Sopenharmony_ci unsigned int phys_lsb, phys_msb; 1588c2ecf20Sopenharmony_ci unsigned int reg, ret = 0; 1598c2ecf20Sopenharmony_ci int buf_len; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!psp || !psp->sev_data) 1628c2ecf20Sopenharmony_ci return -ENODEV; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (psp_dead) 1658c2ecf20Sopenharmony_ci return -EBUSY; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci sev = psp->sev_data; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci buf_len = sev_cmd_buffer_len(cmd); 1708c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!data != !buf_len)) 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Copy the incoming data to driver's scratch buffer as __pa() will not 1758c2ecf20Sopenharmony_ci * work for some memory, e.g. vmalloc'd addresses, and @data may not be 1768c2ecf20Sopenharmony_ci * physically contiguous. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci if (data) 1798c2ecf20Sopenharmony_ci memcpy(sev->cmd_buf, data, buf_len); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Get the physical address of the command buffer */ 1828c2ecf20Sopenharmony_ci phys_lsb = data ? lower_32_bits(__psp_pa(sev->cmd_buf)) : 0; 1838c2ecf20Sopenharmony_ci phys_msb = data ? upper_32_bits(__psp_pa(sev->cmd_buf)) : 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n", 1868c2ecf20Sopenharmony_ci cmd, phys_msb, phys_lsb, psp_timeout); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data, 1898c2ecf20Sopenharmony_ci buf_len, false); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg); 1928c2ecf20Sopenharmony_ci iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci sev->int_rcvd = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci reg = cmd; 1978c2ecf20Sopenharmony_ci reg <<= SEV_CMDRESP_CMD_SHIFT; 1988c2ecf20Sopenharmony_ci reg |= SEV_CMDRESP_IOC; 1998c2ecf20Sopenharmony_ci iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* wait for command completion */ 2028c2ecf20Sopenharmony_ci ret = sev_wait_cmd_ioc(sev, ®, psp_timeout); 2038c2ecf20Sopenharmony_ci if (ret) { 2048c2ecf20Sopenharmony_ci if (psp_ret) 2058c2ecf20Sopenharmony_ci *psp_ret = 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd); 2088c2ecf20Sopenharmony_ci psp_dead = true; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci psp_timeout = psp_cmd_timeout; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (psp_ret) 2168c2ecf20Sopenharmony_ci *psp_ret = reg & PSP_CMDRESP_ERR_MASK; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (reg & PSP_CMDRESP_ERR_MASK) { 2198c2ecf20Sopenharmony_ci dev_dbg(sev->dev, "sev command %#x failed (%#010x)\n", 2208c2ecf20Sopenharmony_ci cmd, reg & PSP_CMDRESP_ERR_MASK); 2218c2ecf20Sopenharmony_ci ret = -EIO; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data, 2258c2ecf20Sopenharmony_ci buf_len, false); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * Copy potential output from the PSP back to data. Do this even on 2298c2ecf20Sopenharmony_ci * failure in case the caller wants to glean something from the error. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci if (data) 2328c2ecf20Sopenharmony_ci memcpy(data, sev->cmd_buf, buf_len); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int sev_do_cmd(int cmd, void *data, int *psp_ret) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int rc; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci mutex_lock(&sev_cmd_mutex); 2428c2ecf20Sopenharmony_ci rc = __sev_do_cmd_locked(cmd, data, psp_ret); 2438c2ecf20Sopenharmony_ci mutex_unlock(&sev_cmd_mutex); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return rc; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int __sev_platform_init_locked(int *error) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct psp_device *psp = psp_master; 2518c2ecf20Sopenharmony_ci struct sev_device *sev; 2528c2ecf20Sopenharmony_ci int rc = 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!psp || !psp->sev_data) 2558c2ecf20Sopenharmony_ci return -ENODEV; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci sev = psp->sev_data; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (sev->state == SEV_STATE_INIT) 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (sev_es_tmr) { 2638c2ecf20Sopenharmony_ci u64 tmr_pa; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * Do not include the encryption mask on the physical 2678c2ecf20Sopenharmony_ci * address of the TMR (firmware should clear it anyway). 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci tmr_pa = __pa(sev_es_tmr); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci sev->init_cmd_buf.flags |= SEV_INIT_FLAGS_SEV_ES; 2728c2ecf20Sopenharmony_ci sev->init_cmd_buf.tmr_address = tmr_pa; 2738c2ecf20Sopenharmony_ci sev->init_cmd_buf.tmr_len = SEV_ES_TMR_SIZE; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci rc = __sev_do_cmd_locked(SEV_CMD_INIT, &sev->init_cmd_buf, error); 2778c2ecf20Sopenharmony_ci if (rc) 2788c2ecf20Sopenharmony_ci return rc; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci sev->state = SEV_STATE_INIT; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Prepare for first SEV guest launch after INIT */ 2838c2ecf20Sopenharmony_ci wbinvd_on_all_cpus(); 2848c2ecf20Sopenharmony_ci rc = __sev_do_cmd_locked(SEV_CMD_DF_FLUSH, NULL, error); 2858c2ecf20Sopenharmony_ci if (rc) 2868c2ecf20Sopenharmony_ci return rc; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci dev_dbg(sev->dev, "SEV firmware initialized\n"); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return rc; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ciint sev_platform_init(int *error) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci int rc; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci mutex_lock(&sev_cmd_mutex); 2988c2ecf20Sopenharmony_ci rc = __sev_platform_init_locked(error); 2998c2ecf20Sopenharmony_ci mutex_unlock(&sev_cmd_mutex); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return rc; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sev_platform_init); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int __sev_platform_shutdown_locked(int *error) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct psp_device *psp = psp_master; 3088c2ecf20Sopenharmony_ci struct sev_device *sev; 3098c2ecf20Sopenharmony_ci int ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!psp || !psp->sev_data) 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci sev = psp->sev_data; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (sev->state == SEV_STATE_UNINIT) 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = __sev_do_cmd_locked(SEV_CMD_SHUTDOWN, NULL, error); 3208c2ecf20Sopenharmony_ci if (ret) 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci sev->state = SEV_STATE_UNINIT; 3248c2ecf20Sopenharmony_ci dev_dbg(sev->dev, "SEV firmware shutdown\n"); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int sev_platform_shutdown(int *error) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int rc; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci mutex_lock(&sev_cmd_mutex); 3348c2ecf20Sopenharmony_ci rc = __sev_platform_shutdown_locked(NULL); 3358c2ecf20Sopenharmony_ci mutex_unlock(&sev_cmd_mutex); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return rc; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int sev_get_platform_state(int *state, int *error) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct sev_user_data_status data; 3438c2ecf20Sopenharmony_ci int rc; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci rc = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, &data, error); 3468c2ecf20Sopenharmony_ci if (rc) 3478c2ecf20Sopenharmony_ci return rc; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci *state = data.state; 3508c2ecf20Sopenharmony_ci return rc; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic int sev_ioctl_do_reset(struct sev_issue_cmd *argp, bool writable) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci int state, rc; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!writable) 3588c2ecf20Sopenharmony_ci return -EPERM; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * The SEV spec requires that FACTORY_RESET must be issued in 3628c2ecf20Sopenharmony_ci * UNINIT state. Before we go further lets check if any guest is 3638c2ecf20Sopenharmony_ci * active. 3648c2ecf20Sopenharmony_ci * 3658c2ecf20Sopenharmony_ci * If FW is in WORKING state then deny the request otherwise issue 3668c2ecf20Sopenharmony_ci * SHUTDOWN command do INIT -> UNINIT before issuing the FACTORY_RESET. 3678c2ecf20Sopenharmony_ci * 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci rc = sev_get_platform_state(&state, &argp->error); 3708c2ecf20Sopenharmony_ci if (rc) 3718c2ecf20Sopenharmony_ci return rc; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (state == SEV_STATE_WORKING) 3748c2ecf20Sopenharmony_ci return -EBUSY; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (state == SEV_STATE_INIT) { 3778c2ecf20Sopenharmony_ci rc = __sev_platform_shutdown_locked(&argp->error); 3788c2ecf20Sopenharmony_ci if (rc) 3798c2ecf20Sopenharmony_ci return rc; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return __sev_do_cmd_locked(SEV_CMD_FACTORY_RESET, NULL, &argp->error); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct sev_user_data_status data; 3888c2ecf20Sopenharmony_ci int ret; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci memset(&data, 0, sizeof(data)); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, &data, &argp->error); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)argp->data, &data, sizeof(data))) 3978c2ecf20Sopenharmony_ci ret = -EFAULT; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return ret; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int sev_ioctl_do_pek_pdh_gen(int cmd, struct sev_issue_cmd *argp, bool writable) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 4058c2ecf20Sopenharmony_ci int rc; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (!writable) 4088c2ecf20Sopenharmony_ci return -EPERM; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (sev->state == SEV_STATE_UNINIT) { 4118c2ecf20Sopenharmony_ci rc = __sev_platform_init_locked(&argp->error); 4128c2ecf20Sopenharmony_ci if (rc) 4138c2ecf20Sopenharmony_ci return rc; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return __sev_do_cmd_locked(cmd, NULL, &argp->error); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 4228c2ecf20Sopenharmony_ci struct sev_user_data_pek_csr input; 4238c2ecf20Sopenharmony_ci struct sev_data_pek_csr data; 4248c2ecf20Sopenharmony_ci void __user *input_address; 4258c2ecf20Sopenharmony_ci void *blob = NULL; 4268c2ecf20Sopenharmony_ci int ret; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!writable) 4298c2ecf20Sopenharmony_ci return -EPERM; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) 4328c2ecf20Sopenharmony_ci return -EFAULT; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci memset(&data, 0, sizeof(data)); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* userspace wants to query CSR length */ 4378c2ecf20Sopenharmony_ci if (!input.address || !input.length) 4388c2ecf20Sopenharmony_ci goto cmd; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* allocate a physically contiguous buffer to store the CSR blob */ 4418c2ecf20Sopenharmony_ci input_address = (void __user *)input.address; 4428c2ecf20Sopenharmony_ci if (input.length > SEV_FW_BLOB_MAX_SIZE) 4438c2ecf20Sopenharmony_ci return -EFAULT; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci blob = kzalloc(input.length, GFP_KERNEL); 4468c2ecf20Sopenharmony_ci if (!blob) 4478c2ecf20Sopenharmony_ci return -ENOMEM; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci data.address = __psp_pa(blob); 4508c2ecf20Sopenharmony_ci data.len = input.length; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cicmd: 4538c2ecf20Sopenharmony_ci if (sev->state == SEV_STATE_UNINIT) { 4548c2ecf20Sopenharmony_ci ret = __sev_platform_init_locked(&argp->error); 4558c2ecf20Sopenharmony_ci if (ret) 4568c2ecf20Sopenharmony_ci goto e_free_blob; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci ret = __sev_do_cmd_locked(SEV_CMD_PEK_CSR, &data, &argp->error); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* If we query the CSR length, FW responded with expected data. */ 4628c2ecf20Sopenharmony_ci input.length = data.len; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) { 4658c2ecf20Sopenharmony_ci ret = -EFAULT; 4668c2ecf20Sopenharmony_ci goto e_free_blob; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (blob) { 4708c2ecf20Sopenharmony_ci if (copy_to_user(input_address, blob, input.length)) 4718c2ecf20Sopenharmony_ci ret = -EFAULT; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cie_free_blob: 4758c2ecf20Sopenharmony_ci kfree(blob); 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_civoid *psp_copy_user_blob(u64 uaddr, u32 len) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci if (!uaddr || !len) 4828c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* verify that blob length does not exceed our limit */ 4858c2ecf20Sopenharmony_ci if (len > SEV_FW_BLOB_MAX_SIZE) 4868c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return memdup_user((void __user *)uaddr, len); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(psp_copy_user_blob); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int sev_get_api_version(void) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 4958c2ecf20Sopenharmony_ci struct sev_user_data_status status; 4968c2ecf20Sopenharmony_ci int error = 0, ret; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ret = sev_platform_status(&status, &error); 4998c2ecf20Sopenharmony_ci if (ret) { 5008c2ecf20Sopenharmony_ci dev_err(sev->dev, 5018c2ecf20Sopenharmony_ci "SEV: failed to get status. Error: %#x\n", error); 5028c2ecf20Sopenharmony_ci return 1; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci sev->api_major = status.api_major; 5068c2ecf20Sopenharmony_ci sev->api_minor = status.api_minor; 5078c2ecf20Sopenharmony_ci sev->build = status.build; 5088c2ecf20Sopenharmony_ci sev->state = status.state; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int sev_get_firmware(struct device *dev, 5148c2ecf20Sopenharmony_ci const struct firmware **firmware) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci char fw_name_specific[SEV_FW_NAME_SIZE]; 5178c2ecf20Sopenharmony_ci char fw_name_subset[SEV_FW_NAME_SIZE]; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci snprintf(fw_name_specific, sizeof(fw_name_specific), 5208c2ecf20Sopenharmony_ci "amd/amd_sev_fam%.2xh_model%.2xh.sbin", 5218c2ecf20Sopenharmony_ci boot_cpu_data.x86, boot_cpu_data.x86_model); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci snprintf(fw_name_subset, sizeof(fw_name_subset), 5248c2ecf20Sopenharmony_ci "amd/amd_sev_fam%.2xh_model%.1xxh.sbin", 5258c2ecf20Sopenharmony_ci boot_cpu_data.x86, (boot_cpu_data.x86_model & 0xf0) >> 4); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Check for SEV FW for a particular model. 5288c2ecf20Sopenharmony_ci * Ex. amd_sev_fam17h_model00h.sbin for Family 17h Model 00h 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * or 5318c2ecf20Sopenharmony_ci * 5328c2ecf20Sopenharmony_ci * Check for SEV FW common to a subset of models. 5338c2ecf20Sopenharmony_ci * Ex. amd_sev_fam17h_model0xh.sbin for 5348c2ecf20Sopenharmony_ci * Family 17h Model 00h -- Family 17h Model 0Fh 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * or 5378c2ecf20Sopenharmony_ci * 5388c2ecf20Sopenharmony_ci * Fall-back to using generic name: sev.fw 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci if ((firmware_request_nowarn(firmware, fw_name_specific, dev) >= 0) || 5418c2ecf20Sopenharmony_ci (firmware_request_nowarn(firmware, fw_name_subset, dev) >= 0) || 5428c2ecf20Sopenharmony_ci (firmware_request_nowarn(firmware, SEV_FW_FILE, dev) >= 0)) 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return -ENOENT; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/* Don't fail if SEV FW couldn't be updated. Continue with existing SEV FW */ 5498c2ecf20Sopenharmony_cistatic int sev_update_firmware(struct device *dev) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct sev_data_download_firmware *data; 5528c2ecf20Sopenharmony_ci const struct firmware *firmware; 5538c2ecf20Sopenharmony_ci int ret, error, order; 5548c2ecf20Sopenharmony_ci struct page *p; 5558c2ecf20Sopenharmony_ci u64 data_size; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (sev_get_firmware(dev, &firmware) == -ENOENT) { 5588c2ecf20Sopenharmony_ci dev_dbg(dev, "No SEV firmware file present\n"); 5598c2ecf20Sopenharmony_ci return -1; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* 5638c2ecf20Sopenharmony_ci * SEV FW expects the physical address given to it to be 32 5648c2ecf20Sopenharmony_ci * byte aligned. Memory allocated has structure placed at the 5658c2ecf20Sopenharmony_ci * beginning followed by the firmware being passed to the SEV 5668c2ecf20Sopenharmony_ci * FW. Allocate enough memory for data structure + alignment 5678c2ecf20Sopenharmony_ci * padding + SEV FW. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_ci data_size = ALIGN(sizeof(struct sev_data_download_firmware), 32); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci order = get_order(firmware->size + data_size); 5728c2ecf20Sopenharmony_ci p = alloc_pages(GFP_KERNEL, order); 5738c2ecf20Sopenharmony_ci if (!p) { 5748c2ecf20Sopenharmony_ci ret = -1; 5758c2ecf20Sopenharmony_ci goto fw_err; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* 5798c2ecf20Sopenharmony_ci * Copy firmware data to a kernel allocated contiguous 5808c2ecf20Sopenharmony_ci * memory region. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci data = page_address(p); 5838c2ecf20Sopenharmony_ci memcpy(page_address(p) + data_size, firmware->data, firmware->size); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci data->address = __psp_pa(page_address(p) + data_size); 5868c2ecf20Sopenharmony_ci data->len = firmware->size; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci ret = sev_do_cmd(SEV_CMD_DOWNLOAD_FIRMWARE, data, &error); 5898c2ecf20Sopenharmony_ci if (ret) 5908c2ecf20Sopenharmony_ci dev_dbg(dev, "Failed to update SEV firmware: %#x\n", error); 5918c2ecf20Sopenharmony_ci else 5928c2ecf20Sopenharmony_ci dev_info(dev, "SEV firmware update successful\n"); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci __free_pages(p, order); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cifw_err: 5978c2ecf20Sopenharmony_ci release_firmware(firmware); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 6058c2ecf20Sopenharmony_ci struct sev_user_data_pek_cert_import input; 6068c2ecf20Sopenharmony_ci struct sev_data_pek_cert_import data; 6078c2ecf20Sopenharmony_ci void *pek_blob, *oca_blob; 6088c2ecf20Sopenharmony_ci int ret; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (!writable) 6118c2ecf20Sopenharmony_ci return -EPERM; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) 6148c2ecf20Sopenharmony_ci return -EFAULT; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* copy PEK certificate blobs from userspace */ 6178c2ecf20Sopenharmony_ci pek_blob = psp_copy_user_blob(input.pek_cert_address, input.pek_cert_len); 6188c2ecf20Sopenharmony_ci if (IS_ERR(pek_blob)) 6198c2ecf20Sopenharmony_ci return PTR_ERR(pek_blob); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci data.reserved = 0; 6228c2ecf20Sopenharmony_ci data.pek_cert_address = __psp_pa(pek_blob); 6238c2ecf20Sopenharmony_ci data.pek_cert_len = input.pek_cert_len; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* copy PEK certificate blobs from userspace */ 6268c2ecf20Sopenharmony_ci oca_blob = psp_copy_user_blob(input.oca_cert_address, input.oca_cert_len); 6278c2ecf20Sopenharmony_ci if (IS_ERR(oca_blob)) { 6288c2ecf20Sopenharmony_ci ret = PTR_ERR(oca_blob); 6298c2ecf20Sopenharmony_ci goto e_free_pek; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci data.oca_cert_address = __psp_pa(oca_blob); 6338c2ecf20Sopenharmony_ci data.oca_cert_len = input.oca_cert_len; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* If platform is not in INIT state then transition it to INIT */ 6368c2ecf20Sopenharmony_ci if (sev->state != SEV_STATE_INIT) { 6378c2ecf20Sopenharmony_ci ret = __sev_platform_init_locked(&argp->error); 6388c2ecf20Sopenharmony_ci if (ret) 6398c2ecf20Sopenharmony_ci goto e_free_oca; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci ret = __sev_do_cmd_locked(SEV_CMD_PEK_CERT_IMPORT, &data, &argp->error); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cie_free_oca: 6458c2ecf20Sopenharmony_ci kfree(oca_blob); 6468c2ecf20Sopenharmony_cie_free_pek: 6478c2ecf20Sopenharmony_ci kfree(pek_blob); 6488c2ecf20Sopenharmony_ci return ret; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct sev_user_data_get_id2 input; 6548c2ecf20Sopenharmony_ci struct sev_data_get_id data; 6558c2ecf20Sopenharmony_ci void __user *input_address; 6568c2ecf20Sopenharmony_ci void *id_blob = NULL; 6578c2ecf20Sopenharmony_ci int ret; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* SEV GET_ID is available from SEV API v0.16 and up */ 6608c2ecf20Sopenharmony_ci if (!sev_version_greater_or_equal(0, 16)) 6618c2ecf20Sopenharmony_ci return -ENOTSUPP; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) 6648c2ecf20Sopenharmony_ci return -EFAULT; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci input_address = (void __user *)input.address; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (input.address && input.length) { 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * The length of the ID shouldn't be assumed by software since 6718c2ecf20Sopenharmony_ci * it may change in the future. The allocation size is limited 6728c2ecf20Sopenharmony_ci * to 1 << (PAGE_SHIFT + MAX_ORDER - 1) by the page allocator. 6738c2ecf20Sopenharmony_ci * If the allocation fails, simply return ENOMEM rather than 6748c2ecf20Sopenharmony_ci * warning in the kernel log. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci id_blob = kzalloc(input.length, GFP_KERNEL | __GFP_NOWARN); 6778c2ecf20Sopenharmony_ci if (!id_blob) 6788c2ecf20Sopenharmony_ci return -ENOMEM; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci data.address = __psp_pa(id_blob); 6818c2ecf20Sopenharmony_ci data.len = input.length; 6828c2ecf20Sopenharmony_ci } else { 6838c2ecf20Sopenharmony_ci data.address = 0; 6848c2ecf20Sopenharmony_ci data.len = 0; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, &data, &argp->error); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* 6908c2ecf20Sopenharmony_ci * Firmware will return the length of the ID value (either the minimum 6918c2ecf20Sopenharmony_ci * required length or the actual length written), return it to the user. 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ci input.length = data.len; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) { 6968c2ecf20Sopenharmony_ci ret = -EFAULT; 6978c2ecf20Sopenharmony_ci goto e_free; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (id_blob) { 7018c2ecf20Sopenharmony_ci if (copy_to_user(input_address, id_blob, data.len)) { 7028c2ecf20Sopenharmony_ci ret = -EFAULT; 7038c2ecf20Sopenharmony_ci goto e_free; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cie_free: 7088c2ecf20Sopenharmony_ci kfree(id_blob); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return ret; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int sev_ioctl_do_get_id(struct sev_issue_cmd *argp) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct sev_data_get_id *data; 7168c2ecf20Sopenharmony_ci u64 data_size, user_size; 7178c2ecf20Sopenharmony_ci void *id_blob, *mem; 7188c2ecf20Sopenharmony_ci int ret; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* SEV GET_ID available from SEV API v0.16 and up */ 7218c2ecf20Sopenharmony_ci if (!sev_version_greater_or_equal(0, 16)) 7228c2ecf20Sopenharmony_ci return -ENOTSUPP; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* SEV FW expects the buffer it fills with the ID to be 7258c2ecf20Sopenharmony_ci * 8-byte aligned. Memory allocated should be enough to 7268c2ecf20Sopenharmony_ci * hold data structure + alignment padding + memory 7278c2ecf20Sopenharmony_ci * where SEV FW writes the ID. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci data_size = ALIGN(sizeof(struct sev_data_get_id), 8); 7308c2ecf20Sopenharmony_ci user_size = sizeof(struct sev_user_data_get_id); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci mem = kzalloc(data_size + user_size, GFP_KERNEL); 7338c2ecf20Sopenharmony_ci if (!mem) 7348c2ecf20Sopenharmony_ci return -ENOMEM; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci data = mem; 7378c2ecf20Sopenharmony_ci id_blob = mem + data_size; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci data->address = __psp_pa(id_blob); 7408c2ecf20Sopenharmony_ci data->len = user_size; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, data, &argp->error); 7438c2ecf20Sopenharmony_ci if (!ret) { 7448c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)argp->data, id_blob, data->len)) 7458c2ecf20Sopenharmony_ci ret = -EFAULT; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci kfree(mem); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return ret; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 7568c2ecf20Sopenharmony_ci struct sev_user_data_pdh_cert_export input; 7578c2ecf20Sopenharmony_ci void *pdh_blob = NULL, *cert_blob = NULL; 7588c2ecf20Sopenharmony_ci struct sev_data_pdh_cert_export data; 7598c2ecf20Sopenharmony_ci void __user *input_cert_chain_address; 7608c2ecf20Sopenharmony_ci void __user *input_pdh_cert_address; 7618c2ecf20Sopenharmony_ci int ret; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* If platform is not in INIT state then transition it to INIT. */ 7648c2ecf20Sopenharmony_ci if (sev->state != SEV_STATE_INIT) { 7658c2ecf20Sopenharmony_ci if (!writable) 7668c2ecf20Sopenharmony_ci return -EPERM; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci ret = __sev_platform_init_locked(&argp->error); 7698c2ecf20Sopenharmony_ci if (ret) 7708c2ecf20Sopenharmony_ci return ret; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) 7748c2ecf20Sopenharmony_ci return -EFAULT; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci memset(&data, 0, sizeof(data)); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Userspace wants to query the certificate length. */ 7798c2ecf20Sopenharmony_ci if (!input.pdh_cert_address || 7808c2ecf20Sopenharmony_ci !input.pdh_cert_len || 7818c2ecf20Sopenharmony_ci !input.cert_chain_address) 7828c2ecf20Sopenharmony_ci goto cmd; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci input_pdh_cert_address = (void __user *)input.pdh_cert_address; 7858c2ecf20Sopenharmony_ci input_cert_chain_address = (void __user *)input.cert_chain_address; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* Allocate a physically contiguous buffer to store the PDH blob. */ 7888c2ecf20Sopenharmony_ci if (input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) 7898c2ecf20Sopenharmony_ci return -EFAULT; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Allocate a physically contiguous buffer to store the cert chain blob. */ 7928c2ecf20Sopenharmony_ci if (input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) 7938c2ecf20Sopenharmony_ci return -EFAULT; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci pdh_blob = kzalloc(input.pdh_cert_len, GFP_KERNEL); 7968c2ecf20Sopenharmony_ci if (!pdh_blob) 7978c2ecf20Sopenharmony_ci return -ENOMEM; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci data.pdh_cert_address = __psp_pa(pdh_blob); 8008c2ecf20Sopenharmony_ci data.pdh_cert_len = input.pdh_cert_len; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci cert_blob = kzalloc(input.cert_chain_len, GFP_KERNEL); 8038c2ecf20Sopenharmony_ci if (!cert_blob) { 8048c2ecf20Sopenharmony_ci ret = -ENOMEM; 8058c2ecf20Sopenharmony_ci goto e_free_pdh; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci data.cert_chain_address = __psp_pa(cert_blob); 8098c2ecf20Sopenharmony_ci data.cert_chain_len = input.cert_chain_len; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cicmd: 8128c2ecf20Sopenharmony_ci ret = __sev_do_cmd_locked(SEV_CMD_PDH_CERT_EXPORT, &data, &argp->error); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* If we query the length, FW responded with expected data. */ 8158c2ecf20Sopenharmony_ci input.cert_chain_len = data.cert_chain_len; 8168c2ecf20Sopenharmony_ci input.pdh_cert_len = data.pdh_cert_len; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) { 8198c2ecf20Sopenharmony_ci ret = -EFAULT; 8208c2ecf20Sopenharmony_ci goto e_free_cert; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (pdh_blob) { 8248c2ecf20Sopenharmony_ci if (copy_to_user(input_pdh_cert_address, 8258c2ecf20Sopenharmony_ci pdh_blob, input.pdh_cert_len)) { 8268c2ecf20Sopenharmony_ci ret = -EFAULT; 8278c2ecf20Sopenharmony_ci goto e_free_cert; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (cert_blob) { 8328c2ecf20Sopenharmony_ci if (copy_to_user(input_cert_chain_address, 8338c2ecf20Sopenharmony_ci cert_blob, input.cert_chain_len)) 8348c2ecf20Sopenharmony_ci ret = -EFAULT; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cie_free_cert: 8388c2ecf20Sopenharmony_ci kfree(cert_blob); 8398c2ecf20Sopenharmony_cie_free_pdh: 8408c2ecf20Sopenharmony_ci kfree(pdh_blob); 8418c2ecf20Sopenharmony_ci return ret; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 8478c2ecf20Sopenharmony_ci struct sev_issue_cmd input; 8488c2ecf20Sopenharmony_ci int ret = -EFAULT; 8498c2ecf20Sopenharmony_ci bool writable = file->f_mode & FMODE_WRITE; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (!psp_master || !psp_master->sev_data) 8528c2ecf20Sopenharmony_ci return -ENODEV; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (ioctl != SEV_ISSUE_CMD) 8558c2ecf20Sopenharmony_ci return -EINVAL; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (copy_from_user(&input, argp, sizeof(struct sev_issue_cmd))) 8588c2ecf20Sopenharmony_ci return -EFAULT; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (input.cmd > SEV_MAX) 8618c2ecf20Sopenharmony_ci return -EINVAL; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci mutex_lock(&sev_cmd_mutex); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci switch (input.cmd) { 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci case SEV_FACTORY_RESET: 8688c2ecf20Sopenharmony_ci ret = sev_ioctl_do_reset(&input, writable); 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci case SEV_PLATFORM_STATUS: 8718c2ecf20Sopenharmony_ci ret = sev_ioctl_do_platform_status(&input); 8728c2ecf20Sopenharmony_ci break; 8738c2ecf20Sopenharmony_ci case SEV_PEK_GEN: 8748c2ecf20Sopenharmony_ci ret = sev_ioctl_do_pek_pdh_gen(SEV_CMD_PEK_GEN, &input, writable); 8758c2ecf20Sopenharmony_ci break; 8768c2ecf20Sopenharmony_ci case SEV_PDH_GEN: 8778c2ecf20Sopenharmony_ci ret = sev_ioctl_do_pek_pdh_gen(SEV_CMD_PDH_GEN, &input, writable); 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci case SEV_PEK_CSR: 8808c2ecf20Sopenharmony_ci ret = sev_ioctl_do_pek_csr(&input, writable); 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci case SEV_PEK_CERT_IMPORT: 8838c2ecf20Sopenharmony_ci ret = sev_ioctl_do_pek_import(&input, writable); 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci case SEV_PDH_CERT_EXPORT: 8868c2ecf20Sopenharmony_ci ret = sev_ioctl_do_pdh_export(&input, writable); 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci case SEV_GET_ID: 8898c2ecf20Sopenharmony_ci pr_warn_once("SEV_GET_ID command is deprecated, use SEV_GET_ID2\n"); 8908c2ecf20Sopenharmony_ci ret = sev_ioctl_do_get_id(&input); 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci case SEV_GET_ID2: 8938c2ecf20Sopenharmony_ci ret = sev_ioctl_do_get_id2(&input); 8948c2ecf20Sopenharmony_ci break; 8958c2ecf20Sopenharmony_ci default: 8968c2ecf20Sopenharmony_ci ret = -EINVAL; 8978c2ecf20Sopenharmony_ci goto out; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd))) 9018c2ecf20Sopenharmony_ci ret = -EFAULT; 9028c2ecf20Sopenharmony_ciout: 9038c2ecf20Sopenharmony_ci mutex_unlock(&sev_cmd_mutex); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci return ret; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic const struct file_operations sev_fops = { 9098c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9108c2ecf20Sopenharmony_ci .unlocked_ioctl = sev_ioctl, 9118c2ecf20Sopenharmony_ci}; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ciint sev_platform_status(struct sev_user_data_status *data, int *error) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci return sev_do_cmd(SEV_CMD_PLATFORM_STATUS, data, error); 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sev_platform_status); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ciint sev_guest_deactivate(struct sev_data_deactivate *data, int *error) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci return sev_do_cmd(SEV_CMD_DEACTIVATE, data, error); 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sev_guest_deactivate); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ciint sev_guest_activate(struct sev_data_activate *data, int *error) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci return sev_do_cmd(SEV_CMD_ACTIVATE, data, error); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sev_guest_activate); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ciint sev_guest_decommission(struct sev_data_decommission *data, int *error) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci return sev_do_cmd(SEV_CMD_DECOMMISSION, data, error); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sev_guest_decommission); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ciint sev_guest_df_flush(int *error) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci return sev_do_cmd(SEV_CMD_DF_FLUSH, NULL, error); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sev_guest_df_flush); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic void sev_exit(struct kref *ref) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci misc_deregister(&misc_dev->misc); 9468c2ecf20Sopenharmony_ci kfree(misc_dev); 9478c2ecf20Sopenharmony_ci misc_dev = NULL; 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic int sev_misc_init(struct sev_device *sev) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci struct device *dev = sev->dev; 9538c2ecf20Sopenharmony_ci int ret; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * SEV feature support can be detected on multiple devices but the SEV 9578c2ecf20Sopenharmony_ci * FW commands must be issued on the master. During probe, we do not 9588c2ecf20Sopenharmony_ci * know the master hence we create /dev/sev on the first device probe. 9598c2ecf20Sopenharmony_ci * sev_do_cmd() finds the right master device to which to issue the 9608c2ecf20Sopenharmony_ci * command to the firmware. 9618c2ecf20Sopenharmony_ci */ 9628c2ecf20Sopenharmony_ci if (!misc_dev) { 9638c2ecf20Sopenharmony_ci struct miscdevice *misc; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci misc_dev = kzalloc(sizeof(*misc_dev), GFP_KERNEL); 9668c2ecf20Sopenharmony_ci if (!misc_dev) 9678c2ecf20Sopenharmony_ci return -ENOMEM; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci misc = &misc_dev->misc; 9708c2ecf20Sopenharmony_ci misc->minor = MISC_DYNAMIC_MINOR; 9718c2ecf20Sopenharmony_ci misc->name = DEVICE_NAME; 9728c2ecf20Sopenharmony_ci misc->fops = &sev_fops; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci ret = misc_register(misc); 9758c2ecf20Sopenharmony_ci if (ret) 9768c2ecf20Sopenharmony_ci return ret; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci kref_init(&misc_dev->refcount); 9798c2ecf20Sopenharmony_ci } else { 9808c2ecf20Sopenharmony_ci kref_get(&misc_dev->refcount); 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci init_waitqueue_head(&sev->int_queue); 9848c2ecf20Sopenharmony_ci sev->misc = misc_dev; 9858c2ecf20Sopenharmony_ci dev_dbg(dev, "registered SEV device\n"); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return 0; 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ciint sev_dev_init(struct psp_device *psp) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct device *dev = psp->dev; 9938c2ecf20Sopenharmony_ci struct sev_device *sev; 9948c2ecf20Sopenharmony_ci int ret = -ENOMEM; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci sev = devm_kzalloc(dev, sizeof(*sev), GFP_KERNEL); 9978c2ecf20Sopenharmony_ci if (!sev) 9988c2ecf20Sopenharmony_ci goto e_err; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci sev->cmd_buf = (void *)devm_get_free_pages(dev, GFP_KERNEL, 0); 10018c2ecf20Sopenharmony_ci if (!sev->cmd_buf) 10028c2ecf20Sopenharmony_ci goto e_sev; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci psp->sev_data = sev; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci sev->dev = dev; 10078c2ecf20Sopenharmony_ci sev->psp = psp; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci sev->io_regs = psp->io_regs; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci sev->vdata = (struct sev_vdata *)psp->vdata->sev; 10128c2ecf20Sopenharmony_ci if (!sev->vdata) { 10138c2ecf20Sopenharmony_ci ret = -ENODEV; 10148c2ecf20Sopenharmony_ci dev_err(dev, "sev: missing driver data\n"); 10158c2ecf20Sopenharmony_ci goto e_buf; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci psp_set_sev_irq_handler(psp, sev_irq_handler, sev); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci ret = sev_misc_init(sev); 10218c2ecf20Sopenharmony_ci if (ret) 10228c2ecf20Sopenharmony_ci goto e_irq; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci dev_notice(dev, "sev enabled\n"); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci return 0; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cie_irq: 10298c2ecf20Sopenharmony_ci psp_clear_sev_irq_handler(psp); 10308c2ecf20Sopenharmony_cie_buf: 10318c2ecf20Sopenharmony_ci devm_free_pages(dev, (unsigned long)sev->cmd_buf); 10328c2ecf20Sopenharmony_cie_sev: 10338c2ecf20Sopenharmony_ci devm_kfree(dev, sev); 10348c2ecf20Sopenharmony_cie_err: 10358c2ecf20Sopenharmony_ci psp->sev_data = NULL; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci dev_notice(dev, "sev initialization failed\n"); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci return ret; 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cistatic void sev_firmware_shutdown(struct sev_device *sev) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci sev_platform_shutdown(NULL); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (sev_es_tmr) { 10478c2ecf20Sopenharmony_ci /* The TMR area was encrypted, flush it from the cache */ 10488c2ecf20Sopenharmony_ci wbinvd_on_all_cpus(); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci free_pages((unsigned long)sev_es_tmr, 10518c2ecf20Sopenharmony_ci get_order(SEV_ES_TMR_SIZE)); 10528c2ecf20Sopenharmony_ci sev_es_tmr = NULL; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_civoid sev_dev_destroy(struct psp_device *psp) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct sev_device *sev = psp->sev_data; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (!sev) 10618c2ecf20Sopenharmony_ci return; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci sev_firmware_shutdown(sev); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if (sev->misc) 10668c2ecf20Sopenharmony_ci kref_put(&misc_dev->refcount, sev_exit); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci psp_clear_sev_irq_handler(psp); 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ciint sev_issue_cmd_external_user(struct file *filep, unsigned int cmd, 10728c2ecf20Sopenharmony_ci void *data, int *error) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci if (!filep || filep->f_op != &sev_fops) 10758c2ecf20Sopenharmony_ci return -EBADF; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci return sev_do_cmd(cmd, data, error); 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sev_issue_cmd_external_user); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_civoid sev_pci_init(void) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 10848c2ecf20Sopenharmony_ci int error, rc; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (!sev) 10878c2ecf20Sopenharmony_ci return; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci psp_timeout = psp_probe_timeout; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (sev_get_api_version()) 10928c2ecf20Sopenharmony_ci goto err; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (sev_version_greater_or_equal(0, 15) && 10958c2ecf20Sopenharmony_ci sev_update_firmware(sev->dev) == 0) 10968c2ecf20Sopenharmony_ci sev_get_api_version(); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* Obtain the TMR memory area for SEV-ES use */ 10998c2ecf20Sopenharmony_ci sev_es_tmr = sev_fw_alloc(SEV_ES_TMR_SIZE); 11008c2ecf20Sopenharmony_ci if (sev_es_tmr) 11018c2ecf20Sopenharmony_ci /* Must flush the cache before giving it to the firmware */ 11028c2ecf20Sopenharmony_ci clflush_cache_range(sev_es_tmr, SEV_ES_TMR_SIZE); 11038c2ecf20Sopenharmony_ci else 11048c2ecf20Sopenharmony_ci dev_warn(sev->dev, 11058c2ecf20Sopenharmony_ci "SEV: TMR allocation failed, SEV-ES support unavailable\n"); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* Initialize the platform */ 11088c2ecf20Sopenharmony_ci rc = sev_platform_init(&error); 11098c2ecf20Sopenharmony_ci if (rc && (error == SEV_RET_SECURE_DATA_INVALID)) { 11108c2ecf20Sopenharmony_ci /* 11118c2ecf20Sopenharmony_ci * INIT command returned an integrity check failure 11128c2ecf20Sopenharmony_ci * status code, meaning that firmware load and 11138c2ecf20Sopenharmony_ci * validation of SEV related persistent data has 11148c2ecf20Sopenharmony_ci * failed and persistent state has been erased. 11158c2ecf20Sopenharmony_ci * Retrying INIT command here should succeed. 11168c2ecf20Sopenharmony_ci */ 11178c2ecf20Sopenharmony_ci dev_dbg(sev->dev, "SEV: retrying INIT command"); 11188c2ecf20Sopenharmony_ci rc = sev_platform_init(&error); 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (rc) { 11228c2ecf20Sopenharmony_ci dev_err(sev->dev, "SEV: failed to INIT error %#x\n", error); 11238c2ecf20Sopenharmony_ci return; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci dev_info(sev->dev, "SEV API:%d.%d build:%d\n", sev->api_major, 11278c2ecf20Sopenharmony_ci sev->api_minor, sev->build); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cierr: 11328c2ecf20Sopenharmony_ci psp_master->sev_data = NULL; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_civoid sev_pci_exit(void) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci struct sev_device *sev = psp_master->sev_data; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (!sev) 11408c2ecf20Sopenharmony_ci return; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci sev_firmware_shutdown(sev); 11438c2ecf20Sopenharmony_ci} 1144