Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: ccp - Add AMD Seamless Firmware Servicing (SFS) driver

AMD Seamless Firmware Servicing (SFS) is a secure method to allow
non-persistent updates to running firmware and settings without
requiring BIOS reflash and/or system reset.

SFS does not address anything that runs on the x86 processors and
it can be used to update ASP firmware, modules, register settings
and update firmware for other microprocessors like TMPM, etc.

SFS driver support adds ioctl support to communicate the SFS
commands to the ASP/PSP by using the TEE mailbox interface.

The Seamless Firmware Servicing (SFS) driver is added as a
PSP sub-device.

For detailed information, please look at the SFS specifications:
https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/58604.pdf

Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Link: https://lore.kernel.org/cover.1758057691.git.ashish.kalra@amd.com

authored by

Ashish Kalra and committed by
Borislav Petkov (AMD)
648dbccc e09701dc

+476 -2
+2 -1
drivers/crypto/ccp/Makefile
··· 13 13 tee-dev.o \ 14 14 platform-access.o \ 15 15 dbc.o \ 16 - hsti.o 16 + hsti.o \ 17 + sfs.o 17 18 18 19 obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o 19 20 ccp-crypto-objs := ccp-crypto-main.o \
+20
drivers/crypto/ccp/psp-dev.c
··· 17 17 #include "psp-dev.h" 18 18 #include "sev-dev.h" 19 19 #include "tee-dev.h" 20 + #include "sfs.h" 20 21 #include "platform-access.h" 21 22 #include "dbc.h" 22 23 #include "hsti.h" ··· 183 182 return 0; 184 183 } 185 184 185 + static int psp_check_sfs_support(struct psp_device *psp) 186 + { 187 + /* Check if device supports SFS feature */ 188 + if (!psp->capability.sfs) { 189 + dev_dbg(psp->dev, "psp does not support SFS\n"); 190 + return -ENODEV; 191 + } 192 + 193 + return 0; 194 + } 195 + 186 196 static int psp_init(struct psp_device *psp) 187 197 { 188 198 int ret; ··· 206 194 207 195 if (!psp_check_tee_support(psp)) { 208 196 ret = tee_dev_init(psp); 197 + if (ret) 198 + return ret; 199 + } 200 + 201 + if (!psp_check_sfs_support(psp)) { 202 + ret = sfs_dev_init(psp); 209 203 if (ret) 210 204 return ret; 211 205 } ··· 319 301 sev_dev_destroy(psp); 320 302 321 303 tee_dev_destroy(psp); 304 + 305 + sfs_dev_destroy(psp); 322 306 323 307 dbc_dev_destroy(psp); 324 308
+7 -1
drivers/crypto/ccp/psp-dev.h
··· 32 32 unsigned int sev :1, 33 33 tee :1, 34 34 dbc_thru_ext :1, 35 - rsvd1 :4, 35 + sfs :1, 36 + rsvd1 :3, 36 37 security_reporting :1, 37 38 fused_part :1, 38 39 rsvd2 :1, ··· 69 68 void *tee_data; 70 69 void *platform_access_data; 71 70 void *dbc_data; 71 + void *sfs_data; 72 72 73 73 union psp_cap_register capability; 74 74 }; ··· 120 118 * @PSP_SUB_CMD_DBC_SET_UID: Set UID for DBC 121 119 * @PSP_SUB_CMD_DBC_GET_PARAMETER: Get parameter from DBC 122 120 * @PSP_SUB_CMD_DBC_SET_PARAMETER: Set parameter for DBC 121 + * @PSP_SUB_CMD_SFS_GET_FW_VERS: Get firmware versions for ASP and other MP 122 + * @PSP_SUB_CMD_SFS_UPDATE: Command to load, verify and execute SFS package 123 123 */ 124 124 enum psp_sub_cmd { 125 125 PSP_SUB_CMD_DBC_GET_NONCE = PSP_DYNAMIC_BOOST_GET_NONCE, 126 126 PSP_SUB_CMD_DBC_SET_UID = PSP_DYNAMIC_BOOST_SET_UID, 127 127 PSP_SUB_CMD_DBC_GET_PARAMETER = PSP_DYNAMIC_BOOST_GET_PARAMETER, 128 128 PSP_SUB_CMD_DBC_SET_PARAMETER = PSP_DYNAMIC_BOOST_SET_PARAMETER, 129 + PSP_SUB_CMD_SFS_GET_FW_VERS = PSP_SFS_GET_FW_VERSIONS, 130 + PSP_SUB_CMD_SFS_UPDATE = PSP_SFS_UPDATE, 129 131 }; 130 132 131 133 int psp_extended_mailbox_cmd(struct psp_device *psp, unsigned int timeout_msecs,
+311
drivers/crypto/ccp/sfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * AMD Secure Processor Seamless Firmware Servicing support. 4 + * 5 + * Copyright (C) 2025 Advanced Micro Devices, Inc. 6 + * 7 + * Author: Ashish Kalra <ashish.kalra@amd.com> 8 + */ 9 + 10 + #include <linux/firmware.h> 11 + 12 + #include "sfs.h" 13 + #include "sev-dev.h" 14 + 15 + #define SFS_DEFAULT_TIMEOUT (10 * MSEC_PER_SEC) 16 + #define SFS_MAX_PAYLOAD_SIZE (2 * 1024 * 1024) 17 + #define SFS_NUM_2MB_PAGES_CMDBUF (SFS_MAX_PAYLOAD_SIZE / PMD_SIZE) 18 + #define SFS_NUM_PAGES_CMDBUF (SFS_MAX_PAYLOAD_SIZE / PAGE_SIZE) 19 + 20 + static DEFINE_MUTEX(sfs_ioctl_mutex); 21 + 22 + static struct sfs_misc_dev *misc_dev; 23 + 24 + static int send_sfs_cmd(struct sfs_device *sfs_dev, int msg) 25 + { 26 + int ret; 27 + 28 + sfs_dev->command_buf->hdr.status = 0; 29 + sfs_dev->command_buf->hdr.sub_cmd_id = msg; 30 + 31 + ret = psp_extended_mailbox_cmd(sfs_dev->psp, 32 + SFS_DEFAULT_TIMEOUT, 33 + (struct psp_ext_request *)sfs_dev->command_buf); 34 + if (ret == -EIO) { 35 + dev_dbg(sfs_dev->dev, 36 + "msg 0x%x failed with PSP error: 0x%x, extended status: 0x%x\n", 37 + msg, sfs_dev->command_buf->hdr.status, 38 + *(u32 *)sfs_dev->command_buf->buf); 39 + } 40 + 41 + return ret; 42 + } 43 + 44 + static int send_sfs_get_fw_versions(struct sfs_device *sfs_dev) 45 + { 46 + /* 47 + * SFS_GET_FW_VERSIONS command needs the output buffer to be 48 + * initialized to 0xC7 in every byte. 49 + */ 50 + memset(sfs_dev->command_buf->sfs_buffer, 0xc7, PAGE_SIZE); 51 + sfs_dev->command_buf->hdr.payload_size = 2 * PAGE_SIZE; 52 + 53 + return send_sfs_cmd(sfs_dev, PSP_SFS_GET_FW_VERSIONS); 54 + } 55 + 56 + static int send_sfs_update_package(struct sfs_device *sfs_dev, const char *payload_name) 57 + { 58 + char payload_path[PAYLOAD_NAME_SIZE + sizeof("amd/")]; 59 + const struct firmware *firmware; 60 + unsigned long package_size; 61 + int ret; 62 + 63 + /* Sanitize userspace provided payload name */ 64 + if (!strnchr(payload_name, PAYLOAD_NAME_SIZE, '\0')) 65 + return -EINVAL; 66 + 67 + snprintf(payload_path, sizeof(payload_path), "amd/%s", payload_name); 68 + 69 + ret = firmware_request_nowarn(&firmware, payload_path, sfs_dev->dev); 70 + if (ret < 0) { 71 + dev_warn_ratelimited(sfs_dev->dev, "firmware request failed for %s (%d)\n", 72 + payload_path, ret); 73 + return -ENOENT; 74 + } 75 + 76 + /* 77 + * SFS Update Package command's input buffer contains TEE_EXT_CMD_BUFFER 78 + * followed by the Update Package and it should be 64KB aligned. 79 + */ 80 + package_size = ALIGN(firmware->size + PAGE_SIZE, 0x10000U); 81 + 82 + /* 83 + * SFS command buffer is a pre-allocated 2MB buffer, fail update package 84 + * if SFS payload is larger than the pre-allocated command buffer. 85 + */ 86 + if (package_size > SFS_MAX_PAYLOAD_SIZE) { 87 + dev_warn_ratelimited(sfs_dev->dev, 88 + "SFS payload size %ld larger than maximum supported payload size of %u\n", 89 + package_size, SFS_MAX_PAYLOAD_SIZE); 90 + release_firmware(firmware); 91 + return -E2BIG; 92 + } 93 + 94 + /* 95 + * Copy firmware data to a HV_Fixed memory region. 96 + */ 97 + memcpy(sfs_dev->command_buf->sfs_buffer, firmware->data, firmware->size); 98 + sfs_dev->command_buf->hdr.payload_size = package_size; 99 + 100 + release_firmware(firmware); 101 + 102 + return send_sfs_cmd(sfs_dev, PSP_SFS_UPDATE); 103 + } 104 + 105 + static long sfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 106 + { 107 + struct sfs_user_get_fw_versions __user *sfs_get_fw_versions; 108 + struct sfs_user_update_package __user *sfs_update_package; 109 + struct psp_device *psp_master = psp_get_master_device(); 110 + char payload_name[PAYLOAD_NAME_SIZE]; 111 + struct sfs_device *sfs_dev; 112 + int ret = 0; 113 + 114 + if (!psp_master || !psp_master->sfs_data) 115 + return -ENODEV; 116 + 117 + sfs_dev = psp_master->sfs_data; 118 + 119 + guard(mutex)(&sfs_ioctl_mutex); 120 + 121 + switch (cmd) { 122 + case SFSIOCFWVERS: 123 + dev_dbg(sfs_dev->dev, "in SFSIOCFWVERS\n"); 124 + 125 + sfs_get_fw_versions = (struct sfs_user_get_fw_versions __user *)arg; 126 + 127 + ret = send_sfs_get_fw_versions(sfs_dev); 128 + if (ret && ret != -EIO) 129 + return ret; 130 + 131 + /* 132 + * Return SFS status and extended status back to userspace 133 + * if PSP status indicated success or command error. 134 + */ 135 + if (copy_to_user(&sfs_get_fw_versions->blob, sfs_dev->command_buf->sfs_buffer, 136 + PAGE_SIZE)) 137 + return -EFAULT; 138 + if (copy_to_user(&sfs_get_fw_versions->sfs_status, 139 + &sfs_dev->command_buf->hdr.status, 140 + sizeof(sfs_get_fw_versions->sfs_status))) 141 + return -EFAULT; 142 + if (copy_to_user(&sfs_get_fw_versions->sfs_extended_status, 143 + &sfs_dev->command_buf->buf, 144 + sizeof(sfs_get_fw_versions->sfs_extended_status))) 145 + return -EFAULT; 146 + break; 147 + case SFSIOCUPDATEPKG: 148 + dev_dbg(sfs_dev->dev, "in SFSIOCUPDATEPKG\n"); 149 + 150 + sfs_update_package = (struct sfs_user_update_package __user *)arg; 151 + 152 + if (copy_from_user(payload_name, sfs_update_package->payload_name, 153 + PAYLOAD_NAME_SIZE)) 154 + return -EFAULT; 155 + 156 + ret = send_sfs_update_package(sfs_dev, payload_name); 157 + if (ret && ret != -EIO) 158 + return ret; 159 + 160 + /* 161 + * Return SFS status and extended status back to userspace 162 + * if PSP status indicated success or command error. 163 + */ 164 + if (copy_to_user(&sfs_update_package->sfs_status, 165 + &sfs_dev->command_buf->hdr.status, 166 + sizeof(sfs_update_package->sfs_status))) 167 + return -EFAULT; 168 + if (copy_to_user(&sfs_update_package->sfs_extended_status, 169 + &sfs_dev->command_buf->buf, 170 + sizeof(sfs_update_package->sfs_extended_status))) 171 + return -EFAULT; 172 + break; 173 + default: 174 + ret = -EINVAL; 175 + } 176 + 177 + return ret; 178 + } 179 + 180 + static const struct file_operations sfs_fops = { 181 + .owner = THIS_MODULE, 182 + .unlocked_ioctl = sfs_ioctl, 183 + }; 184 + 185 + static void sfs_exit(struct kref *ref) 186 + { 187 + misc_deregister(&misc_dev->misc); 188 + kfree(misc_dev); 189 + misc_dev = NULL; 190 + } 191 + 192 + void sfs_dev_destroy(struct psp_device *psp) 193 + { 194 + struct sfs_device *sfs_dev = psp->sfs_data; 195 + 196 + if (!sfs_dev) 197 + return; 198 + 199 + /* 200 + * Change SFS command buffer back to the default "Write-Back" type. 201 + */ 202 + set_memory_wb((unsigned long)sfs_dev->command_buf, SFS_NUM_PAGES_CMDBUF); 203 + 204 + snp_free_hv_fixed_pages(sfs_dev->page); 205 + 206 + if (sfs_dev->misc) 207 + kref_put(&misc_dev->refcount, sfs_exit); 208 + 209 + psp->sfs_data = NULL; 210 + } 211 + 212 + /* Based on sev_misc_init() */ 213 + static int sfs_misc_init(struct sfs_device *sfs) 214 + { 215 + struct device *dev = sfs->dev; 216 + int ret; 217 + 218 + /* 219 + * SFS feature support can be detected on multiple devices but the SFS 220 + * FW commands must be issued on the master. During probe, we do not 221 + * know the master hence we create /dev/sfs on the first device probe. 222 + */ 223 + if (!misc_dev) { 224 + struct miscdevice *misc; 225 + 226 + misc_dev = kzalloc(sizeof(*misc_dev), GFP_KERNEL); 227 + if (!misc_dev) 228 + return -ENOMEM; 229 + 230 + misc = &misc_dev->misc; 231 + misc->minor = MISC_DYNAMIC_MINOR; 232 + misc->name = "sfs"; 233 + misc->fops = &sfs_fops; 234 + misc->mode = 0600; 235 + 236 + ret = misc_register(misc); 237 + if (ret) 238 + return ret; 239 + 240 + kref_init(&misc_dev->refcount); 241 + } else { 242 + kref_get(&misc_dev->refcount); 243 + } 244 + 245 + sfs->misc = misc_dev; 246 + dev_dbg(dev, "registered SFS device\n"); 247 + 248 + return 0; 249 + } 250 + 251 + int sfs_dev_init(struct psp_device *psp) 252 + { 253 + struct device *dev = psp->dev; 254 + struct sfs_device *sfs_dev; 255 + struct page *page; 256 + int ret = -ENOMEM; 257 + 258 + sfs_dev = devm_kzalloc(dev, sizeof(*sfs_dev), GFP_KERNEL); 259 + if (!sfs_dev) 260 + return -ENOMEM; 261 + 262 + /* 263 + * Pre-allocate 2MB command buffer for all SFS commands using 264 + * SNP HV_Fixed page allocator which also transitions the 265 + * SFS command buffer to HV_Fixed page state if SNP is enabled. 266 + */ 267 + page = snp_alloc_hv_fixed_pages(SFS_NUM_2MB_PAGES_CMDBUF); 268 + if (!page) { 269 + dev_dbg(dev, "Command Buffer HV-Fixed page allocation failed\n"); 270 + goto cleanup_dev; 271 + } 272 + sfs_dev->page = page; 273 + sfs_dev->command_buf = page_address(page); 274 + 275 + dev_dbg(dev, "Command buffer 0x%px to be marked as HV_Fixed\n", sfs_dev->command_buf); 276 + 277 + /* 278 + * SFS command buffer must be mapped as non-cacheable. 279 + */ 280 + ret = set_memory_uc((unsigned long)sfs_dev->command_buf, SFS_NUM_PAGES_CMDBUF); 281 + if (ret) { 282 + dev_dbg(dev, "Set memory uc failed\n"); 283 + goto cleanup_cmd_buf; 284 + } 285 + 286 + dev_dbg(dev, "Command buffer 0x%px marked uncacheable\n", sfs_dev->command_buf); 287 + 288 + psp->sfs_data = sfs_dev; 289 + sfs_dev->dev = dev; 290 + sfs_dev->psp = psp; 291 + 292 + ret = sfs_misc_init(sfs_dev); 293 + if (ret) 294 + goto cleanup_mem_attr; 295 + 296 + dev_notice(sfs_dev->dev, "SFS support is available\n"); 297 + 298 + return 0; 299 + 300 + cleanup_mem_attr: 301 + set_memory_wb((unsigned long)sfs_dev->command_buf, SFS_NUM_PAGES_CMDBUF); 302 + 303 + cleanup_cmd_buf: 304 + snp_free_hv_fixed_pages(page); 305 + 306 + cleanup_dev: 307 + psp->sfs_data = NULL; 308 + devm_kfree(dev, sfs_dev); 309 + 310 + return ret; 311 + }
+47
drivers/crypto/ccp/sfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * AMD Platform Security Processor (PSP) Seamless Firmware (SFS) Support. 4 + * 5 + * Copyright (C) 2025 Advanced Micro Devices, Inc. 6 + * 7 + * Author: Ashish Kalra <ashish.kalra@amd.com> 8 + */ 9 + 10 + #ifndef __SFS_H__ 11 + #define __SFS_H__ 12 + 13 + #include <uapi/linux/psp-sfs.h> 14 + 15 + #include <linux/device.h> 16 + #include <linux/miscdevice.h> 17 + #include <linux/psp-sev.h> 18 + #include <linux/psp-platform-access.h> 19 + #include <linux/set_memory.h> 20 + 21 + #include "psp-dev.h" 22 + 23 + struct sfs_misc_dev { 24 + struct kref refcount; 25 + struct miscdevice misc; 26 + }; 27 + 28 + struct sfs_command { 29 + struct psp_ext_req_buffer_hdr hdr; 30 + u8 buf[PAGE_SIZE - sizeof(struct psp_ext_req_buffer_hdr)]; 31 + u8 sfs_buffer[]; 32 + } __packed; 33 + 34 + struct sfs_device { 35 + struct device *dev; 36 + struct psp_device *psp; 37 + 38 + struct page *page; 39 + struct sfs_command *command_buf; 40 + 41 + struct sfs_misc_dev *misc; 42 + }; 43 + 44 + void sfs_dev_destroy(struct psp_device *psp); 45 + int sfs_dev_init(struct psp_device *psp); 46 + 47 + #endif /* __SFS_H__ */
+2
include/linux/psp-platform-access.h
··· 7 7 8 8 enum psp_platform_access_msg { 9 9 PSP_CMD_NONE = 0x0, 10 + PSP_SFS_GET_FW_VERSIONS, 11 + PSP_SFS_UPDATE, 10 12 PSP_CMD_HSTI_QUERY = 0x14, 11 13 PSP_I2C_REQ_BUS_CMD = 0x64, 12 14 PSP_DYNAMIC_BOOST_GET_NONCE,
+87
include/uapi/linux/psp-sfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ 2 + /* 3 + * Userspace interface for AMD Seamless Firmware Servicing (SFS) 4 + * 5 + * Copyright (C) 2025 Advanced Micro Devices, Inc. 6 + * 7 + * Author: Ashish Kalra <ashish.kalra@amd.com> 8 + */ 9 + 10 + #ifndef __PSP_SFS_USER_H__ 11 + #define __PSP_SFS_USER_H__ 12 + 13 + #include <linux/types.h> 14 + 15 + /** 16 + * SFS: AMD Seamless Firmware Support (SFS) interface 17 + */ 18 + 19 + #define PAYLOAD_NAME_SIZE 64 20 + #define TEE_EXT_CMD_BUFFER_SIZE 4096 21 + 22 + /** 23 + * struct sfs_user_get_fw_versions - get current level of base firmware (output). 24 + * @blob: current level of base firmware for ASP and patch levels (input/output). 25 + * @sfs_status: 32-bit SFS status value (output). 26 + * @sfs_extended_status: 32-bit SFS extended status value (output). 27 + */ 28 + struct sfs_user_get_fw_versions { 29 + __u8 blob[TEE_EXT_CMD_BUFFER_SIZE]; 30 + __u32 sfs_status; 31 + __u32 sfs_extended_status; 32 + } __packed; 33 + 34 + /** 35 + * struct sfs_user_update_package - update SFS package (input). 36 + * @payload_name: name of SFS package to load, verify and execute (input). 37 + * @sfs_status: 32-bit SFS status value (output). 38 + * @sfs_extended_status: 32-bit SFS extended status value (output). 39 + */ 40 + struct sfs_user_update_package { 41 + char payload_name[PAYLOAD_NAME_SIZE]; 42 + __u32 sfs_status; 43 + __u32 sfs_extended_status; 44 + } __packed; 45 + 46 + /** 47 + * Seamless Firmware Support (SFS) IOC 48 + * 49 + * possible return codes for all SFS IOCTLs: 50 + * 0: success 51 + * -EINVAL: invalid input 52 + * -E2BIG: excess data passed 53 + * -EFAULT: failed to copy to/from userspace 54 + * -EBUSY: mailbox in recovery or in use 55 + * -ENODEV: driver not bound with PSP device 56 + * -EACCES: request isn't authorized 57 + * -EINVAL: invalid parameter 58 + * -ETIMEDOUT: request timed out 59 + * -EAGAIN: invalid request for state machine 60 + * -ENOENT: not implemented 61 + * -ENFILE: overflow 62 + * -EPERM: invalid signature 63 + * -EIO: PSP I/O error 64 + */ 65 + #define SFS_IOC_TYPE 'S' 66 + 67 + /** 68 + * SFSIOCFWVERS - returns blob containing FW versions 69 + * ASP provides the current level of Base Firmware for the ASP 70 + * and the other microprocessors as well as current patch 71 + * level(s). 72 + */ 73 + #define SFSIOCFWVERS _IOWR(SFS_IOC_TYPE, 0x1, struct sfs_user_get_fw_versions) 74 + 75 + /** 76 + * SFSIOCUPDATEPKG - updates package/payload 77 + * ASP loads, verifies and executes the SFS package. 78 + * By default, the SFS package/payload is loaded from 79 + * /lib/firmware/amd, but alternative firmware loading 80 + * path can be specified using kernel parameter 81 + * firmware_class.path or the firmware loading path 82 + * can be customized using sysfs file: 83 + * /sys/module/firmware_class/parameters/path. 84 + */ 85 + #define SFSIOCUPDATEPKG _IOWR(SFS_IOC_TYPE, 0x2, struct sfs_user_update_package) 86 + 87 + #endif /* __PSP_SFS_USER_H__ */