Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/crypto/ccp/sfs.c
29278 views
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 <[email protected]>
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
}
312
313