xref: /linux/drivers/crypto/ccp/sfs.c (revision 22bdd6e68bbe270a916233ec5f34a13ae5e80ed9)
1*648dbcccSAshish Kalra // SPDX-License-Identifier: GPL-2.0-only
2*648dbcccSAshish Kalra /*
3*648dbcccSAshish Kalra  * AMD Secure Processor Seamless Firmware Servicing support.
4*648dbcccSAshish Kalra  *
5*648dbcccSAshish Kalra  * Copyright (C) 2025 Advanced Micro Devices, Inc.
6*648dbcccSAshish Kalra  *
7*648dbcccSAshish Kalra  * Author: Ashish Kalra <ashish.kalra@amd.com>
8*648dbcccSAshish Kalra  */
9*648dbcccSAshish Kalra 
10*648dbcccSAshish Kalra #include <linux/firmware.h>
11*648dbcccSAshish Kalra 
12*648dbcccSAshish Kalra #include "sfs.h"
13*648dbcccSAshish Kalra #include "sev-dev.h"
14*648dbcccSAshish Kalra 
15*648dbcccSAshish Kalra #define SFS_DEFAULT_TIMEOUT		(10 * MSEC_PER_SEC)
16*648dbcccSAshish Kalra #define SFS_MAX_PAYLOAD_SIZE		(2 * 1024 * 1024)
17*648dbcccSAshish Kalra #define SFS_NUM_2MB_PAGES_CMDBUF	(SFS_MAX_PAYLOAD_SIZE / PMD_SIZE)
18*648dbcccSAshish Kalra #define SFS_NUM_PAGES_CMDBUF		(SFS_MAX_PAYLOAD_SIZE / PAGE_SIZE)
19*648dbcccSAshish Kalra 
20*648dbcccSAshish Kalra static DEFINE_MUTEX(sfs_ioctl_mutex);
21*648dbcccSAshish Kalra 
22*648dbcccSAshish Kalra static struct sfs_misc_dev *misc_dev;
23*648dbcccSAshish Kalra 
send_sfs_cmd(struct sfs_device * sfs_dev,int msg)24*648dbcccSAshish Kalra static int send_sfs_cmd(struct sfs_device *sfs_dev, int msg)
25*648dbcccSAshish Kalra {
26*648dbcccSAshish Kalra 	int ret;
27*648dbcccSAshish Kalra 
28*648dbcccSAshish Kalra 	sfs_dev->command_buf->hdr.status = 0;
29*648dbcccSAshish Kalra 	sfs_dev->command_buf->hdr.sub_cmd_id = msg;
30*648dbcccSAshish Kalra 
31*648dbcccSAshish Kalra 	ret = psp_extended_mailbox_cmd(sfs_dev->psp,
32*648dbcccSAshish Kalra 				       SFS_DEFAULT_TIMEOUT,
33*648dbcccSAshish Kalra 				       (struct psp_ext_request *)sfs_dev->command_buf);
34*648dbcccSAshish Kalra 	if (ret == -EIO) {
35*648dbcccSAshish Kalra 		dev_dbg(sfs_dev->dev,
36*648dbcccSAshish Kalra 			 "msg 0x%x failed with PSP error: 0x%x, extended status: 0x%x\n",
37*648dbcccSAshish Kalra 			 msg, sfs_dev->command_buf->hdr.status,
38*648dbcccSAshish Kalra 			 *(u32 *)sfs_dev->command_buf->buf);
39*648dbcccSAshish Kalra 	}
40*648dbcccSAshish Kalra 
41*648dbcccSAshish Kalra 	return ret;
42*648dbcccSAshish Kalra }
43*648dbcccSAshish Kalra 
send_sfs_get_fw_versions(struct sfs_device * sfs_dev)44*648dbcccSAshish Kalra static int send_sfs_get_fw_versions(struct sfs_device *sfs_dev)
45*648dbcccSAshish Kalra {
46*648dbcccSAshish Kalra 	/*
47*648dbcccSAshish Kalra 	 * SFS_GET_FW_VERSIONS command needs the output buffer to be
48*648dbcccSAshish Kalra 	 * initialized to 0xC7 in every byte.
49*648dbcccSAshish Kalra 	 */
50*648dbcccSAshish Kalra 	memset(sfs_dev->command_buf->sfs_buffer, 0xc7, PAGE_SIZE);
51*648dbcccSAshish Kalra 	sfs_dev->command_buf->hdr.payload_size = 2 * PAGE_SIZE;
52*648dbcccSAshish Kalra 
53*648dbcccSAshish Kalra 	return send_sfs_cmd(sfs_dev, PSP_SFS_GET_FW_VERSIONS);
54*648dbcccSAshish Kalra }
55*648dbcccSAshish Kalra 
send_sfs_update_package(struct sfs_device * sfs_dev,const char * payload_name)56*648dbcccSAshish Kalra static int send_sfs_update_package(struct sfs_device *sfs_dev, const char *payload_name)
57*648dbcccSAshish Kalra {
58*648dbcccSAshish Kalra 	char payload_path[PAYLOAD_NAME_SIZE + sizeof("amd/")];
59*648dbcccSAshish Kalra 	const struct firmware *firmware;
60*648dbcccSAshish Kalra 	unsigned long package_size;
61*648dbcccSAshish Kalra 	int ret;
62*648dbcccSAshish Kalra 
63*648dbcccSAshish Kalra 	/* Sanitize userspace provided payload name */
64*648dbcccSAshish Kalra 	if (!strnchr(payload_name, PAYLOAD_NAME_SIZE, '\0'))
65*648dbcccSAshish Kalra 		return -EINVAL;
66*648dbcccSAshish Kalra 
67*648dbcccSAshish Kalra 	snprintf(payload_path, sizeof(payload_path), "amd/%s", payload_name);
68*648dbcccSAshish Kalra 
69*648dbcccSAshish Kalra 	ret = firmware_request_nowarn(&firmware, payload_path, sfs_dev->dev);
70*648dbcccSAshish Kalra 	if (ret < 0) {
71*648dbcccSAshish Kalra 		dev_warn_ratelimited(sfs_dev->dev, "firmware request failed for %s (%d)\n",
72*648dbcccSAshish Kalra 				     payload_path, ret);
73*648dbcccSAshish Kalra 		return -ENOENT;
74*648dbcccSAshish Kalra 	}
75*648dbcccSAshish Kalra 
76*648dbcccSAshish Kalra 	/*
77*648dbcccSAshish Kalra 	 * SFS Update Package command's input buffer contains TEE_EXT_CMD_BUFFER
78*648dbcccSAshish Kalra 	 * followed by the Update Package and it should be 64KB aligned.
79*648dbcccSAshish Kalra 	 */
80*648dbcccSAshish Kalra 	package_size = ALIGN(firmware->size + PAGE_SIZE, 0x10000U);
81*648dbcccSAshish Kalra 
82*648dbcccSAshish Kalra 	/*
83*648dbcccSAshish Kalra 	 * SFS command buffer is a pre-allocated 2MB buffer, fail update package
84*648dbcccSAshish Kalra 	 * if SFS payload is larger than the pre-allocated command buffer.
85*648dbcccSAshish Kalra 	 */
86*648dbcccSAshish Kalra 	if (package_size > SFS_MAX_PAYLOAD_SIZE) {
87*648dbcccSAshish Kalra 		dev_warn_ratelimited(sfs_dev->dev,
88*648dbcccSAshish Kalra 			 "SFS payload size %ld larger than maximum supported payload size of %u\n",
89*648dbcccSAshish Kalra 			 package_size, SFS_MAX_PAYLOAD_SIZE);
90*648dbcccSAshish Kalra 		release_firmware(firmware);
91*648dbcccSAshish Kalra 		return -E2BIG;
92*648dbcccSAshish Kalra 	}
93*648dbcccSAshish Kalra 
94*648dbcccSAshish Kalra 	/*
95*648dbcccSAshish Kalra 	 * Copy firmware data to a HV_Fixed memory region.
96*648dbcccSAshish Kalra 	 */
97*648dbcccSAshish Kalra 	memcpy(sfs_dev->command_buf->sfs_buffer, firmware->data, firmware->size);
98*648dbcccSAshish Kalra 	sfs_dev->command_buf->hdr.payload_size = package_size;
99*648dbcccSAshish Kalra 
100*648dbcccSAshish Kalra 	release_firmware(firmware);
101*648dbcccSAshish Kalra 
102*648dbcccSAshish Kalra 	return send_sfs_cmd(sfs_dev, PSP_SFS_UPDATE);
103*648dbcccSAshish Kalra }
104*648dbcccSAshish Kalra 
sfs_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)105*648dbcccSAshish Kalra static long sfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
106*648dbcccSAshish Kalra {
107*648dbcccSAshish Kalra 	struct sfs_user_get_fw_versions __user *sfs_get_fw_versions;
108*648dbcccSAshish Kalra 	struct sfs_user_update_package __user *sfs_update_package;
109*648dbcccSAshish Kalra 	struct psp_device *psp_master = psp_get_master_device();
110*648dbcccSAshish Kalra 	char payload_name[PAYLOAD_NAME_SIZE];
111*648dbcccSAshish Kalra 	struct sfs_device *sfs_dev;
112*648dbcccSAshish Kalra 	int ret = 0;
113*648dbcccSAshish Kalra 
114*648dbcccSAshish Kalra 	if (!psp_master || !psp_master->sfs_data)
115*648dbcccSAshish Kalra 		return -ENODEV;
116*648dbcccSAshish Kalra 
117*648dbcccSAshish Kalra 	sfs_dev = psp_master->sfs_data;
118*648dbcccSAshish Kalra 
119*648dbcccSAshish Kalra 	guard(mutex)(&sfs_ioctl_mutex);
120*648dbcccSAshish Kalra 
121*648dbcccSAshish Kalra 	switch (cmd) {
122*648dbcccSAshish Kalra 	case SFSIOCFWVERS:
123*648dbcccSAshish Kalra 		dev_dbg(sfs_dev->dev, "in SFSIOCFWVERS\n");
124*648dbcccSAshish Kalra 
125*648dbcccSAshish Kalra 		sfs_get_fw_versions = (struct sfs_user_get_fw_versions __user *)arg;
126*648dbcccSAshish Kalra 
127*648dbcccSAshish Kalra 		ret = send_sfs_get_fw_versions(sfs_dev);
128*648dbcccSAshish Kalra 		if (ret && ret != -EIO)
129*648dbcccSAshish Kalra 			return ret;
130*648dbcccSAshish Kalra 
131*648dbcccSAshish Kalra 		/*
132*648dbcccSAshish Kalra 		 * Return SFS status and extended status back to userspace
133*648dbcccSAshish Kalra 		 * if PSP status indicated success or command error.
134*648dbcccSAshish Kalra 		 */
135*648dbcccSAshish Kalra 		if (copy_to_user(&sfs_get_fw_versions->blob, sfs_dev->command_buf->sfs_buffer,
136*648dbcccSAshish Kalra 				 PAGE_SIZE))
137*648dbcccSAshish Kalra 			return -EFAULT;
138*648dbcccSAshish Kalra 		if (copy_to_user(&sfs_get_fw_versions->sfs_status,
139*648dbcccSAshish Kalra 				 &sfs_dev->command_buf->hdr.status,
140*648dbcccSAshish Kalra 				 sizeof(sfs_get_fw_versions->sfs_status)))
141*648dbcccSAshish Kalra 			return -EFAULT;
142*648dbcccSAshish Kalra 		if (copy_to_user(&sfs_get_fw_versions->sfs_extended_status,
143*648dbcccSAshish Kalra 				 &sfs_dev->command_buf->buf,
144*648dbcccSAshish Kalra 				 sizeof(sfs_get_fw_versions->sfs_extended_status)))
145*648dbcccSAshish Kalra 			return -EFAULT;
146*648dbcccSAshish Kalra 		break;
147*648dbcccSAshish Kalra 	case SFSIOCUPDATEPKG:
148*648dbcccSAshish Kalra 		dev_dbg(sfs_dev->dev, "in SFSIOCUPDATEPKG\n");
149*648dbcccSAshish Kalra 
150*648dbcccSAshish Kalra 		sfs_update_package = (struct sfs_user_update_package __user *)arg;
151*648dbcccSAshish Kalra 
152*648dbcccSAshish Kalra 		if (copy_from_user(payload_name, sfs_update_package->payload_name,
153*648dbcccSAshish Kalra 				   PAYLOAD_NAME_SIZE))
154*648dbcccSAshish Kalra 			return -EFAULT;
155*648dbcccSAshish Kalra 
156*648dbcccSAshish Kalra 		ret = send_sfs_update_package(sfs_dev, payload_name);
157*648dbcccSAshish Kalra 		if (ret && ret != -EIO)
158*648dbcccSAshish Kalra 			return ret;
159*648dbcccSAshish Kalra 
160*648dbcccSAshish Kalra 		/*
161*648dbcccSAshish Kalra 		 * Return SFS status and extended status back to userspace
162*648dbcccSAshish Kalra 		 * if PSP status indicated success or command error.
163*648dbcccSAshish Kalra 		 */
164*648dbcccSAshish Kalra 		if (copy_to_user(&sfs_update_package->sfs_status,
165*648dbcccSAshish Kalra 				 &sfs_dev->command_buf->hdr.status,
166*648dbcccSAshish Kalra 				 sizeof(sfs_update_package->sfs_status)))
167*648dbcccSAshish Kalra 			return -EFAULT;
168*648dbcccSAshish Kalra 		if (copy_to_user(&sfs_update_package->sfs_extended_status,
169*648dbcccSAshish Kalra 				 &sfs_dev->command_buf->buf,
170*648dbcccSAshish Kalra 				 sizeof(sfs_update_package->sfs_extended_status)))
171*648dbcccSAshish Kalra 			return -EFAULT;
172*648dbcccSAshish Kalra 		break;
173*648dbcccSAshish Kalra 	default:
174*648dbcccSAshish Kalra 		ret = -EINVAL;
175*648dbcccSAshish Kalra 	}
176*648dbcccSAshish Kalra 
177*648dbcccSAshish Kalra 	return ret;
178*648dbcccSAshish Kalra }
179*648dbcccSAshish Kalra 
180*648dbcccSAshish Kalra static const struct file_operations sfs_fops = {
181*648dbcccSAshish Kalra 	.owner	= THIS_MODULE,
182*648dbcccSAshish Kalra 	.unlocked_ioctl = sfs_ioctl,
183*648dbcccSAshish Kalra };
184*648dbcccSAshish Kalra 
sfs_exit(struct kref * ref)185*648dbcccSAshish Kalra static void sfs_exit(struct kref *ref)
186*648dbcccSAshish Kalra {
187*648dbcccSAshish Kalra 	misc_deregister(&misc_dev->misc);
188*648dbcccSAshish Kalra 	kfree(misc_dev);
189*648dbcccSAshish Kalra 	misc_dev = NULL;
190*648dbcccSAshish Kalra }
191*648dbcccSAshish Kalra 
sfs_dev_destroy(struct psp_device * psp)192*648dbcccSAshish Kalra void sfs_dev_destroy(struct psp_device *psp)
193*648dbcccSAshish Kalra {
194*648dbcccSAshish Kalra 	struct sfs_device *sfs_dev = psp->sfs_data;
195*648dbcccSAshish Kalra 
196*648dbcccSAshish Kalra 	if (!sfs_dev)
197*648dbcccSAshish Kalra 		return;
198*648dbcccSAshish Kalra 
199*648dbcccSAshish Kalra 	/*
200*648dbcccSAshish Kalra 	 * Change SFS command buffer back to the default "Write-Back" type.
201*648dbcccSAshish Kalra 	 */
202*648dbcccSAshish Kalra 	set_memory_wb((unsigned long)sfs_dev->command_buf, SFS_NUM_PAGES_CMDBUF);
203*648dbcccSAshish Kalra 
204*648dbcccSAshish Kalra 	snp_free_hv_fixed_pages(sfs_dev->page);
205*648dbcccSAshish Kalra 
206*648dbcccSAshish Kalra 	if (sfs_dev->misc)
207*648dbcccSAshish Kalra 		kref_put(&misc_dev->refcount, sfs_exit);
208*648dbcccSAshish Kalra 
209*648dbcccSAshish Kalra 	psp->sfs_data = NULL;
210*648dbcccSAshish Kalra }
211*648dbcccSAshish Kalra 
212*648dbcccSAshish Kalra /* Based on sev_misc_init() */
sfs_misc_init(struct sfs_device * sfs)213*648dbcccSAshish Kalra static int sfs_misc_init(struct sfs_device *sfs)
214*648dbcccSAshish Kalra {
215*648dbcccSAshish Kalra 	struct device *dev = sfs->dev;
216*648dbcccSAshish Kalra 	int ret;
217*648dbcccSAshish Kalra 
218*648dbcccSAshish Kalra 	/*
219*648dbcccSAshish Kalra 	 * SFS feature support can be detected on multiple devices but the SFS
220*648dbcccSAshish Kalra 	 * FW commands must be issued on the master. During probe, we do not
221*648dbcccSAshish Kalra 	 * know the master hence we create /dev/sfs on the first device probe.
222*648dbcccSAshish Kalra 	 */
223*648dbcccSAshish Kalra 	if (!misc_dev) {
224*648dbcccSAshish Kalra 		struct miscdevice *misc;
225*648dbcccSAshish Kalra 
226*648dbcccSAshish Kalra 		misc_dev = kzalloc(sizeof(*misc_dev), GFP_KERNEL);
227*648dbcccSAshish Kalra 		if (!misc_dev)
228*648dbcccSAshish Kalra 			return -ENOMEM;
229*648dbcccSAshish Kalra 
230*648dbcccSAshish Kalra 		misc = &misc_dev->misc;
231*648dbcccSAshish Kalra 		misc->minor = MISC_DYNAMIC_MINOR;
232*648dbcccSAshish Kalra 		misc->name = "sfs";
233*648dbcccSAshish Kalra 		misc->fops = &sfs_fops;
234*648dbcccSAshish Kalra 		misc->mode = 0600;
235*648dbcccSAshish Kalra 
236*648dbcccSAshish Kalra 		ret = misc_register(misc);
237*648dbcccSAshish Kalra 		if (ret)
238*648dbcccSAshish Kalra 			return ret;
239*648dbcccSAshish Kalra 
240*648dbcccSAshish Kalra 		kref_init(&misc_dev->refcount);
241*648dbcccSAshish Kalra 	} else {
242*648dbcccSAshish Kalra 		kref_get(&misc_dev->refcount);
243*648dbcccSAshish Kalra 	}
244*648dbcccSAshish Kalra 
245*648dbcccSAshish Kalra 	sfs->misc = misc_dev;
246*648dbcccSAshish Kalra 	dev_dbg(dev, "registered SFS device\n");
247*648dbcccSAshish Kalra 
248*648dbcccSAshish Kalra 	return 0;
249*648dbcccSAshish Kalra }
250*648dbcccSAshish Kalra 
sfs_dev_init(struct psp_device * psp)251*648dbcccSAshish Kalra int sfs_dev_init(struct psp_device *psp)
252*648dbcccSAshish Kalra {
253*648dbcccSAshish Kalra 	struct device *dev = psp->dev;
254*648dbcccSAshish Kalra 	struct sfs_device *sfs_dev;
255*648dbcccSAshish Kalra 	struct page *page;
256*648dbcccSAshish Kalra 	int ret = -ENOMEM;
257*648dbcccSAshish Kalra 
258*648dbcccSAshish Kalra 	sfs_dev = devm_kzalloc(dev, sizeof(*sfs_dev), GFP_KERNEL);
259*648dbcccSAshish Kalra 	if (!sfs_dev)
260*648dbcccSAshish Kalra 		return -ENOMEM;
261*648dbcccSAshish Kalra 
262*648dbcccSAshish Kalra 	/*
263*648dbcccSAshish Kalra 	 * Pre-allocate 2MB command buffer for all SFS commands using
264*648dbcccSAshish Kalra 	 * SNP HV_Fixed page allocator which also transitions the
265*648dbcccSAshish Kalra 	 * SFS command buffer to HV_Fixed page state if SNP is enabled.
266*648dbcccSAshish Kalra 	 */
267*648dbcccSAshish Kalra 	page = snp_alloc_hv_fixed_pages(SFS_NUM_2MB_PAGES_CMDBUF);
268*648dbcccSAshish Kalra 	if (!page) {
269*648dbcccSAshish Kalra 		dev_dbg(dev, "Command Buffer HV-Fixed page allocation failed\n");
270*648dbcccSAshish Kalra 		goto cleanup_dev;
271*648dbcccSAshish Kalra 	}
272*648dbcccSAshish Kalra 	sfs_dev->page = page;
273*648dbcccSAshish Kalra 	sfs_dev->command_buf = page_address(page);
274*648dbcccSAshish Kalra 
275*648dbcccSAshish Kalra 	dev_dbg(dev, "Command buffer 0x%px to be marked as HV_Fixed\n", sfs_dev->command_buf);
276*648dbcccSAshish Kalra 
277*648dbcccSAshish Kalra 	/*
278*648dbcccSAshish Kalra 	 * SFS command buffer must be mapped as non-cacheable.
279*648dbcccSAshish Kalra 	 */
280*648dbcccSAshish Kalra 	ret = set_memory_uc((unsigned long)sfs_dev->command_buf, SFS_NUM_PAGES_CMDBUF);
281*648dbcccSAshish Kalra 	if (ret) {
282*648dbcccSAshish Kalra 		dev_dbg(dev, "Set memory uc failed\n");
283*648dbcccSAshish Kalra 		goto cleanup_cmd_buf;
284*648dbcccSAshish Kalra 	}
285*648dbcccSAshish Kalra 
286*648dbcccSAshish Kalra 	dev_dbg(dev, "Command buffer 0x%px marked uncacheable\n", sfs_dev->command_buf);
287*648dbcccSAshish Kalra 
288*648dbcccSAshish Kalra 	psp->sfs_data = sfs_dev;
289*648dbcccSAshish Kalra 	sfs_dev->dev = dev;
290*648dbcccSAshish Kalra 	sfs_dev->psp = psp;
291*648dbcccSAshish Kalra 
292*648dbcccSAshish Kalra 	ret = sfs_misc_init(sfs_dev);
293*648dbcccSAshish Kalra 	if (ret)
294*648dbcccSAshish Kalra 		goto cleanup_mem_attr;
295*648dbcccSAshish Kalra 
296*648dbcccSAshish Kalra 	dev_notice(sfs_dev->dev, "SFS support is available\n");
297*648dbcccSAshish Kalra 
298*648dbcccSAshish Kalra 	return 0;
299*648dbcccSAshish Kalra 
300*648dbcccSAshish Kalra cleanup_mem_attr:
301*648dbcccSAshish Kalra 	set_memory_wb((unsigned long)sfs_dev->command_buf, SFS_NUM_PAGES_CMDBUF);
302*648dbcccSAshish Kalra 
303*648dbcccSAshish Kalra cleanup_cmd_buf:
304*648dbcccSAshish Kalra 	snp_free_hv_fixed_pages(page);
305*648dbcccSAshish Kalra 
306*648dbcccSAshish Kalra cleanup_dev:
307*648dbcccSAshish Kalra 	psp->sfs_data = NULL;
308*648dbcccSAshish Kalra 	devm_kfree(dev, sfs_dev);
309*648dbcccSAshish Kalra 
310*648dbcccSAshish Kalra 	return ret;
311*648dbcccSAshish Kalra }
312