]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
2a6170df | 2 | /* |
9b67d08d | 3 | * AMD Secure Encrypted Virtualization (SEV) interface |
2a6170df | 4 | * |
9b67d08d | 5 | * Copyright (C) 2016,2019 Advanced Micro Devices, Inc. |
2a6170df BS |
6 | * |
7 | * Author: Brijesh Singh <brijesh.singh@amd.com> | |
2a6170df BS |
8 | */ |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/kthread.h> | |
13 | #include <linux/sched.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/spinlock.h> | |
16 | #include <linux/spinlock_types.h> | |
17 | #include <linux/types.h> | |
18 | #include <linux/mutex.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/hw_random.h> | |
21 | #include <linux/ccp.h> | |
edd303ff | 22 | #include <linux/firmware.h> |
97f9ac3d | 23 | #include <linux/gfp.h> |
2a6170df | 24 | |
0fc5deae TL |
25 | #include <asm/smp.h> |
26 | ||
b93566f1 | 27 | #include "psp-dev.h" |
9b67d08d | 28 | #include "sev-dev.h" |
2a6170df | 29 | |
e9372060 JN |
30 | #define DEVICE_NAME "sev" |
31 | #define SEV_FW_FILE "amd/sev.fw" | |
32 | #define SEV_FW_NAME_SIZE 64 | |
200664d5 BS |
33 | |
34 | static DEFINE_MUTEX(sev_cmd_mutex); | |
35 | static struct sev_misc_dev *misc_dev; | |
200664d5 | 36 | |
e82867fd BS |
37 | static int psp_cmd_timeout = 100; |
38 | module_param(psp_cmd_timeout, int, 0644); | |
39 | MODULE_PARM_DESC(psp_cmd_timeout, " default timeout value, in seconds, for PSP commands"); | |
40 | ||
41 | static int psp_probe_timeout = 5; | |
42 | module_param(psp_probe_timeout, int, 0644); | |
43 | MODULE_PARM_DESC(psp_probe_timeout, " default timeout value, in seconds, during PSP device probe"); | |
44 | ||
45 | static bool psp_dead; | |
46 | static int psp_timeout; | |
47 | ||
97f9ac3d TL |
48 | /* Trusted Memory Region (TMR): |
49 | * The TMR is a 1MB area that must be 1MB aligned. Use the page allocator | |
50 | * to allocate the memory, which will return aligned memory for the specified | |
51 | * allocation order. | |
52 | */ | |
53 | #define SEV_ES_TMR_SIZE (1024 * 1024) | |
54 | static void *sev_es_tmr; | |
55 | ||
83bf4251 DR |
56 | static inline bool sev_version_greater_or_equal(u8 maj, u8 min) |
57 | { | |
b93566f1 | 58 | struct sev_device *sev = psp_master->sev_data; |
2a6170df | 59 | |
b93566f1 RT |
60 | if (sev->api_major > maj) |
61 | return true; | |
2a6170df | 62 | |
b93566f1 RT |
63 | if (sev->api_major == maj && sev->api_minor >= min) |
64 | return true; | |
2a6170df | 65 | |
b93566f1 | 66 | return false; |
2a6170df BS |
67 | } |
68 | ||
b93566f1 | 69 | static void sev_irq_handler(int irq, void *data, unsigned int status) |
2a6170df | 70 | { |
b93566f1 | 71 | struct sev_device *sev = data; |
200664d5 BS |
72 | int reg; |
73 | ||
200664d5 | 74 | /* Check if it is command completion: */ |
b93566f1 RT |
75 | if (!(status & SEV_CMD_COMPLETE)) |
76 | return; | |
200664d5 BS |
77 | |
78 | /* Check if it is SEV command completion: */ | |
6eb0cc72 | 79 | reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); |
200664d5 | 80 | if (reg & PSP_CMDRESP_RESP) { |
b93566f1 RT |
81 | sev->int_rcvd = 1; |
82 | wake_up(&sev->int_queue); | |
200664d5 | 83 | } |
2a6170df BS |
84 | } |
85 | ||
b93566f1 | 86 | static int sev_wait_cmd_ioc(struct sev_device *sev, |
e82867fd | 87 | unsigned int *reg, unsigned int timeout) |
200664d5 | 88 | { |
e82867fd BS |
89 | int ret; |
90 | ||
b93566f1 RT |
91 | ret = wait_event_timeout(sev->int_queue, |
92 | sev->int_rcvd, timeout * HZ); | |
e82867fd BS |
93 | if (!ret) |
94 | return -ETIMEDOUT; | |
95 | ||
6eb0cc72 | 96 | *reg = ioread32(sev->io_regs + sev->vdata->cmdresp_reg); |
e82867fd BS |
97 | |
98 | return 0; | |
200664d5 BS |
99 | } |
100 | ||
101 | static int sev_cmd_buffer_len(int cmd) | |
102 | { | |
103 | switch (cmd) { | |
104 | case SEV_CMD_INIT: return sizeof(struct sev_data_init); | |
105 | case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_user_data_status); | |
106 | case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr); | |
107 | case SEV_CMD_PEK_CERT_IMPORT: return sizeof(struct sev_data_pek_cert_import); | |
108 | case SEV_CMD_PDH_CERT_EXPORT: return sizeof(struct sev_data_pdh_cert_export); | |
109 | case SEV_CMD_LAUNCH_START: return sizeof(struct sev_data_launch_start); | |
110 | case SEV_CMD_LAUNCH_UPDATE_DATA: return sizeof(struct sev_data_launch_update_data); | |
111 | case SEV_CMD_LAUNCH_UPDATE_VMSA: return sizeof(struct sev_data_launch_update_vmsa); | |
112 | case SEV_CMD_LAUNCH_FINISH: return sizeof(struct sev_data_launch_finish); | |
113 | case SEV_CMD_LAUNCH_MEASURE: return sizeof(struct sev_data_launch_measure); | |
114 | case SEV_CMD_ACTIVATE: return sizeof(struct sev_data_activate); | |
115 | case SEV_CMD_DEACTIVATE: return sizeof(struct sev_data_deactivate); | |
116 | case SEV_CMD_DECOMMISSION: return sizeof(struct sev_data_decommission); | |
117 | case SEV_CMD_GUEST_STATUS: return sizeof(struct sev_data_guest_status); | |
118 | case SEV_CMD_DBG_DECRYPT: return sizeof(struct sev_data_dbg); | |
119 | case SEV_CMD_DBG_ENCRYPT: return sizeof(struct sev_data_dbg); | |
120 | case SEV_CMD_SEND_START: return sizeof(struct sev_data_send_start); | |
121 | case SEV_CMD_SEND_UPDATE_DATA: return sizeof(struct sev_data_send_update_data); | |
122 | case SEV_CMD_SEND_UPDATE_VMSA: return sizeof(struct sev_data_send_update_vmsa); | |
123 | case SEV_CMD_SEND_FINISH: return sizeof(struct sev_data_send_finish); | |
124 | case SEV_CMD_RECEIVE_START: return sizeof(struct sev_data_receive_start); | |
125 | case SEV_CMD_RECEIVE_FINISH: return sizeof(struct sev_data_receive_finish); | |
126 | case SEV_CMD_RECEIVE_UPDATE_DATA: return sizeof(struct sev_data_receive_update_data); | |
127 | case SEV_CMD_RECEIVE_UPDATE_VMSA: return sizeof(struct sev_data_receive_update_vmsa); | |
128 | case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret); | |
edd303ff | 129 | case SEV_CMD_DOWNLOAD_FIRMWARE: return sizeof(struct sev_data_download_firmware); |
0b3a830b | 130 | case SEV_CMD_GET_ID: return sizeof(struct sev_data_get_id); |
200664d5 BS |
131 | default: return 0; |
132 | } | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret) | |
138 | { | |
139 | struct psp_device *psp = psp_master; | |
b93566f1 | 140 | struct sev_device *sev; |
200664d5 BS |
141 | unsigned int phys_lsb, phys_msb; |
142 | unsigned int reg, ret = 0; | |
143 | ||
b93566f1 | 144 | if (!psp || !psp->sev_data) |
200664d5 BS |
145 | return -ENODEV; |
146 | ||
e82867fd BS |
147 | if (psp_dead) |
148 | return -EBUSY; | |
149 | ||
b93566f1 RT |
150 | sev = psp->sev_data; |
151 | ||
200664d5 BS |
152 | /* Get the physical address of the command buffer */ |
153 | phys_lsb = data ? lower_32_bits(__psp_pa(data)) : 0; | |
154 | phys_msb = data ? upper_32_bits(__psp_pa(data)) : 0; | |
155 | ||
b93566f1 | 156 | dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n", |
e82867fd | 157 | cmd, phys_msb, phys_lsb, psp_timeout); |
200664d5 BS |
158 | |
159 | print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data, | |
160 | sev_cmd_buffer_len(cmd), false); | |
161 | ||
6eb0cc72 RT |
162 | iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg); |
163 | iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg); | |
200664d5 | 164 | |
b93566f1 | 165 | sev->int_rcvd = 0; |
f426d2b2 | 166 | |
200664d5 | 167 | reg = cmd; |
b93566f1 RT |
168 | reg <<= SEV_CMDRESP_CMD_SHIFT; |
169 | reg |= SEV_CMDRESP_IOC; | |
6eb0cc72 | 170 | iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg); |
200664d5 BS |
171 | |
172 | /* wait for command completion */ | |
b93566f1 | 173 | ret = sev_wait_cmd_ioc(sev, ®, psp_timeout); |
e82867fd BS |
174 | if (ret) { |
175 | if (psp_ret) | |
176 | *psp_ret = 0; | |
177 | ||
b93566f1 | 178 | dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd); |
e82867fd BS |
179 | psp_dead = true; |
180 | ||
181 | return ret; | |
182 | } | |
183 | ||
184 | psp_timeout = psp_cmd_timeout; | |
200664d5 BS |
185 | |
186 | if (psp_ret) | |
187 | *psp_ret = reg & PSP_CMDRESP_ERR_MASK; | |
188 | ||
189 | if (reg & PSP_CMDRESP_ERR_MASK) { | |
b93566f1 | 190 | dev_dbg(sev->dev, "sev command %#x failed (%#010x)\n", |
200664d5 BS |
191 | cmd, reg & PSP_CMDRESP_ERR_MASK); |
192 | ret = -EIO; | |
193 | } | |
194 | ||
195 | print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data, | |
196 | sev_cmd_buffer_len(cmd), false); | |
197 | ||
198 | return ret; | |
199 | } | |
200 | ||
201 | static int sev_do_cmd(int cmd, void *data, int *psp_ret) | |
202 | { | |
203 | int rc; | |
204 | ||
205 | mutex_lock(&sev_cmd_mutex); | |
206 | rc = __sev_do_cmd_locked(cmd, data, psp_ret); | |
207 | mutex_unlock(&sev_cmd_mutex); | |
208 | ||
209 | return rc; | |
210 | } | |
211 | ||
212 | static int __sev_platform_init_locked(int *error) | |
213 | { | |
214 | struct psp_device *psp = psp_master; | |
b93566f1 | 215 | struct sev_device *sev; |
200664d5 BS |
216 | int rc = 0; |
217 | ||
b93566f1 | 218 | if (!psp || !psp->sev_data) |
200664d5 BS |
219 | return -ENODEV; |
220 | ||
b93566f1 RT |
221 | sev = psp->sev_data; |
222 | ||
223 | if (sev->state == SEV_STATE_INIT) | |
200664d5 BS |
224 | return 0; |
225 | ||
97f9ac3d TL |
226 | if (sev_es_tmr) { |
227 | u64 tmr_pa; | |
228 | ||
229 | /* | |
230 | * Do not include the encryption mask on the physical | |
231 | * address of the TMR (firmware should clear it anyway). | |
232 | */ | |
233 | tmr_pa = __pa(sev_es_tmr); | |
234 | ||
235 | sev->init_cmd_buf.flags |= SEV_INIT_FLAGS_SEV_ES; | |
236 | sev->init_cmd_buf.tmr_address = tmr_pa; | |
237 | sev->init_cmd_buf.tmr_len = SEV_ES_TMR_SIZE; | |
238 | } | |
239 | ||
b93566f1 | 240 | rc = __sev_do_cmd_locked(SEV_CMD_INIT, &sev->init_cmd_buf, error); |
200664d5 BS |
241 | if (rc) |
242 | return rc; | |
243 | ||
b93566f1 | 244 | sev->state = SEV_STATE_INIT; |
0fc5deae TL |
245 | |
246 | /* Prepare for first SEV guest launch after INIT */ | |
247 | wbinvd_on_all_cpus(); | |
248 | rc = __sev_do_cmd_locked(SEV_CMD_DF_FLUSH, NULL, error); | |
249 | if (rc) | |
250 | return rc; | |
251 | ||
b93566f1 | 252 | dev_dbg(sev->dev, "SEV firmware initialized\n"); |
200664d5 BS |
253 | |
254 | return rc; | |
255 | } | |
256 | ||
257 | int sev_platform_init(int *error) | |
258 | { | |
259 | int rc; | |
260 | ||
261 | mutex_lock(&sev_cmd_mutex); | |
262 | rc = __sev_platform_init_locked(error); | |
263 | mutex_unlock(&sev_cmd_mutex); | |
264 | ||
265 | return rc; | |
266 | } | |
267 | EXPORT_SYMBOL_GPL(sev_platform_init); | |
268 | ||
269 | static int __sev_platform_shutdown_locked(int *error) | |
270 | { | |
b93566f1 | 271 | struct sev_device *sev = psp_master->sev_data; |
200664d5 BS |
272 | int ret; |
273 | ||
e385b5b7 | 274 | ret = __sev_do_cmd_locked(SEV_CMD_SHUTDOWN, NULL, error); |
200664d5 BS |
275 | if (ret) |
276 | return ret; | |
277 | ||
b93566f1 RT |
278 | sev->state = SEV_STATE_UNINIT; |
279 | dev_dbg(sev->dev, "SEV firmware shutdown\n"); | |
200664d5 BS |
280 | |
281 | return ret; | |
282 | } | |
283 | ||
284 | static int sev_platform_shutdown(int *error) | |
285 | { | |
286 | int rc; | |
287 | ||
288 | mutex_lock(&sev_cmd_mutex); | |
289 | rc = __sev_platform_shutdown_locked(NULL); | |
290 | mutex_unlock(&sev_cmd_mutex); | |
291 | ||
292 | return rc; | |
293 | } | |
294 | ||
2960f9a5 BS |
295 | static int sev_get_platform_state(int *state, int *error) |
296 | { | |
b93566f1 | 297 | struct sev_device *sev = psp_master->sev_data; |
2960f9a5 BS |
298 | int rc; |
299 | ||
300 | rc = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, | |
b93566f1 | 301 | &sev->status_cmd_buf, error); |
2960f9a5 BS |
302 | if (rc) |
303 | return rc; | |
304 | ||
b93566f1 | 305 | *state = sev->status_cmd_buf.state; |
2960f9a5 BS |
306 | return rc; |
307 | } | |
308 | ||
b6102813 | 309 | static int sev_ioctl_do_reset(struct sev_issue_cmd *argp, bool writable) |
2960f9a5 BS |
310 | { |
311 | int state, rc; | |
312 | ||
b6102813 | 313 | if (!writable) |
ec310caf BS |
314 | return -EPERM; |
315 | ||
2960f9a5 BS |
316 | /* |
317 | * The SEV spec requires that FACTORY_RESET must be issued in | |
318 | * UNINIT state. Before we go further lets check if any guest is | |
319 | * active. | |
320 | * | |
321 | * If FW is in WORKING state then deny the request otherwise issue | |
322 | * SHUTDOWN command do INIT -> UNINIT before issuing the FACTORY_RESET. | |
323 | * | |
324 | */ | |
325 | rc = sev_get_platform_state(&state, &argp->error); | |
326 | if (rc) | |
327 | return rc; | |
328 | ||
329 | if (state == SEV_STATE_WORKING) | |
330 | return -EBUSY; | |
331 | ||
332 | if (state == SEV_STATE_INIT) { | |
333 | rc = __sev_platform_shutdown_locked(&argp->error); | |
334 | if (rc) | |
335 | return rc; | |
336 | } | |
337 | ||
e385b5b7 | 338 | return __sev_do_cmd_locked(SEV_CMD_FACTORY_RESET, NULL, &argp->error); |
2960f9a5 BS |
339 | } |
340 | ||
efe1829b BS |
341 | static int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp) |
342 | { | |
b93566f1 RT |
343 | struct sev_device *sev = psp_master->sev_data; |
344 | struct sev_user_data_status *data = &sev->status_cmd_buf; | |
efe1829b BS |
345 | int ret; |
346 | ||
347 | ret = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, data, &argp->error); | |
348 | if (ret) | |
349 | return ret; | |
350 | ||
351 | if (copy_to_user((void __user *)argp->data, data, sizeof(*data))) | |
352 | ret = -EFAULT; | |
353 | ||
354 | return ret; | |
355 | } | |
356 | ||
b6102813 | 357 | static int sev_ioctl_do_pek_pdh_gen(int cmd, struct sev_issue_cmd *argp, bool writable) |
4d84b726 | 358 | { |
b93566f1 | 359 | struct sev_device *sev = psp_master->sev_data; |
4d84b726 BS |
360 | int rc; |
361 | ||
b6102813 | 362 | if (!writable) |
ec310caf BS |
363 | return -EPERM; |
364 | ||
b93566f1 | 365 | if (sev->state == SEV_STATE_UNINIT) { |
4d84b726 BS |
366 | rc = __sev_platform_init_locked(&argp->error); |
367 | if (rc) | |
368 | return rc; | |
369 | } | |
370 | ||
e385b5b7 | 371 | return __sev_do_cmd_locked(cmd, NULL, &argp->error); |
4d84b726 BS |
372 | } |
373 | ||
b6102813 | 374 | static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable) |
e7990356 | 375 | { |
b93566f1 | 376 | struct sev_device *sev = psp_master->sev_data; |
e7990356 BS |
377 | struct sev_user_data_pek_csr input; |
378 | struct sev_data_pek_csr *data; | |
379 | void *blob = NULL; | |
380 | int ret; | |
381 | ||
b6102813 | 382 | if (!writable) |
ec310caf BS |
383 | return -EPERM; |
384 | ||
e7990356 BS |
385 | if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) |
386 | return -EFAULT; | |
387 | ||
388 | data = kzalloc(sizeof(*data), GFP_KERNEL); | |
389 | if (!data) | |
390 | return -ENOMEM; | |
391 | ||
392 | /* userspace wants to query CSR length */ | |
393 | if (!input.address || !input.length) | |
394 | goto cmd; | |
395 | ||
396 | /* allocate a physically contiguous buffer to store the CSR blob */ | |
96d4f267 | 397 | if (!access_ok(input.address, input.length) || |
e7990356 BS |
398 | input.length > SEV_FW_BLOB_MAX_SIZE) { |
399 | ret = -EFAULT; | |
400 | goto e_free; | |
401 | } | |
402 | ||
403 | blob = kmalloc(input.length, GFP_KERNEL); | |
404 | if (!blob) { | |
405 | ret = -ENOMEM; | |
406 | goto e_free; | |
407 | } | |
408 | ||
409 | data->address = __psp_pa(blob); | |
410 | data->len = input.length; | |
411 | ||
412 | cmd: | |
b93566f1 | 413 | if (sev->state == SEV_STATE_UNINIT) { |
e7990356 BS |
414 | ret = __sev_platform_init_locked(&argp->error); |
415 | if (ret) | |
416 | goto e_free_blob; | |
417 | } | |
418 | ||
419 | ret = __sev_do_cmd_locked(SEV_CMD_PEK_CSR, data, &argp->error); | |
420 | ||
421 | /* If we query the CSR length, FW responded with expected data. */ | |
422 | input.length = data->len; | |
423 | ||
424 | if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) { | |
425 | ret = -EFAULT; | |
426 | goto e_free_blob; | |
427 | } | |
428 | ||
429 | if (blob) { | |
430 | if (copy_to_user((void __user *)input.address, blob, input.length)) | |
431 | ret = -EFAULT; | |
432 | } | |
433 | ||
434 | e_free_blob: | |
435 | kfree(blob); | |
436 | e_free: | |
437 | kfree(data); | |
438 | return ret; | |
439 | } | |
440 | ||
7360e4b1 BS |
441 | void *psp_copy_user_blob(u64 __user uaddr, u32 len) |
442 | { | |
7360e4b1 BS |
443 | if (!uaddr || !len) |
444 | return ERR_PTR(-EINVAL); | |
445 | ||
446 | /* verify that blob length does not exceed our limit */ | |
447 | if (len > SEV_FW_BLOB_MAX_SIZE) | |
448 | return ERR_PTR(-EINVAL); | |
449 | ||
6c51dddd | 450 | return memdup_user((void __user *)(uintptr_t)uaddr, len); |
7360e4b1 BS |
451 | } |
452 | EXPORT_SYMBOL_GPL(psp_copy_user_blob); | |
453 | ||
edd303ff JN |
454 | static int sev_get_api_version(void) |
455 | { | |
b93566f1 | 456 | struct sev_device *sev = psp_master->sev_data; |
edd303ff | 457 | struct sev_user_data_status *status; |
b78d3795 | 458 | int error = 0, ret; |
edd303ff | 459 | |
b93566f1 | 460 | status = &sev->status_cmd_buf; |
edd303ff JN |
461 | ret = sev_platform_status(status, &error); |
462 | if (ret) { | |
b93566f1 | 463 | dev_err(sev->dev, |
edd303ff JN |
464 | "SEV: failed to get status. Error: %#x\n", error); |
465 | return 1; | |
466 | } | |
467 | ||
b93566f1 RT |
468 | sev->api_major = status->api_major; |
469 | sev->api_minor = status->api_minor; | |
470 | sev->build = status->build; | |
471 | sev->state = status->state; | |
edd303ff JN |
472 | |
473 | return 0; | |
474 | } | |
475 | ||
5182f26f WY |
476 | static int sev_get_firmware(struct device *dev, |
477 | const struct firmware **firmware) | |
e9372060 JN |
478 | { |
479 | char fw_name_specific[SEV_FW_NAME_SIZE]; | |
480 | char fw_name_subset[SEV_FW_NAME_SIZE]; | |
481 | ||
482 | snprintf(fw_name_specific, sizeof(fw_name_specific), | |
483 | "amd/amd_sev_fam%.2xh_model%.2xh.sbin", | |
484 | boot_cpu_data.x86, boot_cpu_data.x86_model); | |
485 | ||
486 | snprintf(fw_name_subset, sizeof(fw_name_subset), | |
487 | "amd/amd_sev_fam%.2xh_model%.1xxh.sbin", | |
488 | boot_cpu_data.x86, (boot_cpu_data.x86_model & 0xf0) >> 4); | |
489 | ||
490 | /* Check for SEV FW for a particular model. | |
491 | * Ex. amd_sev_fam17h_model00h.sbin for Family 17h Model 00h | |
492 | * | |
493 | * or | |
494 | * | |
495 | * Check for SEV FW common to a subset of models. | |
496 | * Ex. amd_sev_fam17h_model0xh.sbin for | |
497 | * Family 17h Model 00h -- Family 17h Model 0Fh | |
498 | * | |
499 | * or | |
500 | * | |
501 | * Fall-back to using generic name: sev.fw | |
502 | */ | |
503 | if ((firmware_request_nowarn(firmware, fw_name_specific, dev) >= 0) || | |
504 | (firmware_request_nowarn(firmware, fw_name_subset, dev) >= 0) || | |
505 | (firmware_request_nowarn(firmware, SEV_FW_FILE, dev) >= 0)) | |
506 | return 0; | |
507 | ||
508 | return -ENOENT; | |
509 | } | |
510 | ||
edd303ff JN |
511 | /* Don't fail if SEV FW couldn't be updated. Continue with existing SEV FW */ |
512 | static int sev_update_firmware(struct device *dev) | |
513 | { | |
514 | struct sev_data_download_firmware *data; | |
515 | const struct firmware *firmware; | |
516 | int ret, error, order; | |
517 | struct page *p; | |
518 | u64 data_size; | |
519 | ||
e9372060 JN |
520 | if (sev_get_firmware(dev, &firmware) == -ENOENT) { |
521 | dev_dbg(dev, "No SEV firmware file present\n"); | |
edd303ff | 522 | return -1; |
e9372060 | 523 | } |
edd303ff JN |
524 | |
525 | /* | |
526 | * SEV FW expects the physical address given to it to be 32 | |
527 | * byte aligned. Memory allocated has structure placed at the | |
528 | * beginning followed by the firmware being passed to the SEV | |
529 | * FW. Allocate enough memory for data structure + alignment | |
530 | * padding + SEV FW. | |
531 | */ | |
532 | data_size = ALIGN(sizeof(struct sev_data_download_firmware), 32); | |
533 | ||
534 | order = get_order(firmware->size + data_size); | |
535 | p = alloc_pages(GFP_KERNEL, order); | |
536 | if (!p) { | |
537 | ret = -1; | |
538 | goto fw_err; | |
539 | } | |
540 | ||
541 | /* | |
542 | * Copy firmware data to a kernel allocated contiguous | |
543 | * memory region. | |
544 | */ | |
545 | data = page_address(p); | |
546 | memcpy(page_address(p) + data_size, firmware->data, firmware->size); | |
547 | ||
548 | data->address = __psp_pa(page_address(p) + data_size); | |
549 | data->len = firmware->size; | |
550 | ||
551 | ret = sev_do_cmd(SEV_CMD_DOWNLOAD_FIRMWARE, data, &error); | |
552 | if (ret) | |
553 | dev_dbg(dev, "Failed to update SEV firmware: %#x\n", error); | |
554 | else | |
555 | dev_info(dev, "SEV firmware update successful\n"); | |
556 | ||
557 | __free_pages(p, order); | |
558 | ||
559 | fw_err: | |
560 | release_firmware(firmware); | |
561 | ||
562 | return ret; | |
563 | } | |
564 | ||
b6102813 | 565 | static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable) |
7360e4b1 | 566 | { |
b93566f1 | 567 | struct sev_device *sev = psp_master->sev_data; |
7360e4b1 BS |
568 | struct sev_user_data_pek_cert_import input; |
569 | struct sev_data_pek_cert_import *data; | |
570 | void *pek_blob, *oca_blob; | |
571 | int ret; | |
572 | ||
b6102813 | 573 | if (!writable) |
ec310caf BS |
574 | return -EPERM; |
575 | ||
7360e4b1 BS |
576 | if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) |
577 | return -EFAULT; | |
578 | ||
579 | data = kzalloc(sizeof(*data), GFP_KERNEL); | |
580 | if (!data) | |
581 | return -ENOMEM; | |
582 | ||
583 | /* copy PEK certificate blobs from userspace */ | |
584 | pek_blob = psp_copy_user_blob(input.pek_cert_address, input.pek_cert_len); | |
585 | if (IS_ERR(pek_blob)) { | |
586 | ret = PTR_ERR(pek_blob); | |
587 | goto e_free; | |
588 | } | |
589 | ||
590 | data->pek_cert_address = __psp_pa(pek_blob); | |
591 | data->pek_cert_len = input.pek_cert_len; | |
592 | ||
593 | /* copy PEK certificate blobs from userspace */ | |
594 | oca_blob = psp_copy_user_blob(input.oca_cert_address, input.oca_cert_len); | |
595 | if (IS_ERR(oca_blob)) { | |
596 | ret = PTR_ERR(oca_blob); | |
597 | goto e_free_pek; | |
598 | } | |
599 | ||
600 | data->oca_cert_address = __psp_pa(oca_blob); | |
601 | data->oca_cert_len = input.oca_cert_len; | |
602 | ||
603 | /* If platform is not in INIT state then transition it to INIT */ | |
b93566f1 | 604 | if (sev->state != SEV_STATE_INIT) { |
7360e4b1 BS |
605 | ret = __sev_platform_init_locked(&argp->error); |
606 | if (ret) | |
607 | goto e_free_oca; | |
608 | } | |
609 | ||
610 | ret = __sev_do_cmd_locked(SEV_CMD_PEK_CERT_IMPORT, data, &argp->error); | |
611 | ||
612 | e_free_oca: | |
613 | kfree(oca_blob); | |
614 | e_free_pek: | |
615 | kfree(pek_blob); | |
616 | e_free: | |
617 | kfree(data); | |
618 | return ret; | |
619 | } | |
620 | ||
d6112ea0 SB |
621 | static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp) |
622 | { | |
623 | struct sev_user_data_get_id2 input; | |
624 | struct sev_data_get_id *data; | |
625 | void *id_blob = NULL; | |
626 | int ret; | |
627 | ||
628 | /* SEV GET_ID is available from SEV API v0.16 and up */ | |
83bf4251 | 629 | if (!sev_version_greater_or_equal(0, 16)) |
d6112ea0 SB |
630 | return -ENOTSUPP; |
631 | ||
632 | if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) | |
633 | return -EFAULT; | |
634 | ||
635 | /* Check if we have write access to the userspace buffer */ | |
636 | if (input.address && | |
637 | input.length && | |
638 | !access_ok(input.address, input.length)) | |
639 | return -EFAULT; | |
640 | ||
641 | data = kzalloc(sizeof(*data), GFP_KERNEL); | |
642 | if (!data) | |
643 | return -ENOMEM; | |
644 | ||
645 | if (input.address && input.length) { | |
646 | id_blob = kmalloc(input.length, GFP_KERNEL); | |
647 | if (!id_blob) { | |
648 | kfree(data); | |
649 | return -ENOMEM; | |
650 | } | |
651 | ||
652 | data->address = __psp_pa(id_blob); | |
653 | data->len = input.length; | |
654 | } | |
655 | ||
656 | ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, data, &argp->error); | |
657 | ||
658 | /* | |
659 | * Firmware will return the length of the ID value (either the minimum | |
660 | * required length or the actual length written), return it to the user. | |
661 | */ | |
662 | input.length = data->len; | |
663 | ||
664 | if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) { | |
665 | ret = -EFAULT; | |
666 | goto e_free; | |
667 | } | |
668 | ||
669 | if (id_blob) { | |
670 | if (copy_to_user((void __user *)input.address, | |
671 | id_blob, data->len)) { | |
672 | ret = -EFAULT; | |
673 | goto e_free; | |
674 | } | |
675 | } | |
676 | ||
677 | e_free: | |
678 | kfree(id_blob); | |
679 | kfree(data); | |
680 | ||
681 | return ret; | |
682 | } | |
683 | ||
0b3a830b JN |
684 | static int sev_ioctl_do_get_id(struct sev_issue_cmd *argp) |
685 | { | |
686 | struct sev_data_get_id *data; | |
687 | u64 data_size, user_size; | |
688 | void *id_blob, *mem; | |
689 | int ret; | |
690 | ||
691 | /* SEV GET_ID available from SEV API v0.16 and up */ | |
83bf4251 | 692 | if (!sev_version_greater_or_equal(0, 16)) |
0b3a830b JN |
693 | return -ENOTSUPP; |
694 | ||
695 | /* SEV FW expects the buffer it fills with the ID to be | |
696 | * 8-byte aligned. Memory allocated should be enough to | |
697 | * hold data structure + alignment padding + memory | |
698 | * where SEV FW writes the ID. | |
699 | */ | |
700 | data_size = ALIGN(sizeof(struct sev_data_get_id), 8); | |
701 | user_size = sizeof(struct sev_user_data_get_id); | |
702 | ||
703 | mem = kzalloc(data_size + user_size, GFP_KERNEL); | |
704 | if (!mem) | |
705 | return -ENOMEM; | |
706 | ||
707 | data = mem; | |
708 | id_blob = mem + data_size; | |
709 | ||
710 | data->address = __psp_pa(id_blob); | |
711 | data->len = user_size; | |
712 | ||
713 | ret = __sev_do_cmd_locked(SEV_CMD_GET_ID, data, &argp->error); | |
714 | if (!ret) { | |
715 | if (copy_to_user((void __user *)argp->data, id_blob, data->len)) | |
716 | ret = -EFAULT; | |
717 | } | |
718 | ||
719 | kfree(mem); | |
720 | ||
721 | return ret; | |
722 | } | |
723 | ||
b6102813 | 724 | static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) |
76a2b524 | 725 | { |
b93566f1 | 726 | struct sev_device *sev = psp_master->sev_data; |
76a2b524 BS |
727 | struct sev_user_data_pdh_cert_export input; |
728 | void *pdh_blob = NULL, *cert_blob = NULL; | |
729 | struct sev_data_pdh_cert_export *data; | |
730 | int ret; | |
731 | ||
ec310caf | 732 | /* If platform is not in INIT state then transition it to INIT. */ |
b93566f1 | 733 | if (sev->state != SEV_STATE_INIT) { |
b6102813 | 734 | if (!writable) |
ec310caf BS |
735 | return -EPERM; |
736 | ||
737 | ret = __sev_platform_init_locked(&argp->error); | |
738 | if (ret) | |
739 | return ret; | |
740 | } | |
741 | ||
76a2b524 BS |
742 | if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) |
743 | return -EFAULT; | |
744 | ||
745 | data = kzalloc(sizeof(*data), GFP_KERNEL); | |
746 | if (!data) | |
747 | return -ENOMEM; | |
748 | ||
749 | /* Userspace wants to query the certificate length. */ | |
750 | if (!input.pdh_cert_address || | |
751 | !input.pdh_cert_len || | |
752 | !input.cert_chain_address) | |
753 | goto cmd; | |
754 | ||
755 | /* Allocate a physically contiguous buffer to store the PDH blob. */ | |
756 | if ((input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) || | |
96d4f267 | 757 | !access_ok(input.pdh_cert_address, input.pdh_cert_len)) { |
76a2b524 BS |
758 | ret = -EFAULT; |
759 | goto e_free; | |
760 | } | |
761 | ||
762 | /* Allocate a physically contiguous buffer to store the cert chain blob. */ | |
763 | if ((input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) || | |
96d4f267 | 764 | !access_ok(input.cert_chain_address, input.cert_chain_len)) { |
76a2b524 BS |
765 | ret = -EFAULT; |
766 | goto e_free; | |
767 | } | |
768 | ||
769 | pdh_blob = kmalloc(input.pdh_cert_len, GFP_KERNEL); | |
770 | if (!pdh_blob) { | |
771 | ret = -ENOMEM; | |
772 | goto e_free; | |
773 | } | |
774 | ||
775 | data->pdh_cert_address = __psp_pa(pdh_blob); | |
776 | data->pdh_cert_len = input.pdh_cert_len; | |
777 | ||
778 | cert_blob = kmalloc(input.cert_chain_len, GFP_KERNEL); | |
779 | if (!cert_blob) { | |
780 | ret = -ENOMEM; | |
781 | goto e_free_pdh; | |
782 | } | |
783 | ||
784 | data->cert_chain_address = __psp_pa(cert_blob); | |
785 | data->cert_chain_len = input.cert_chain_len; | |
786 | ||
787 | cmd: | |
76a2b524 BS |
788 | ret = __sev_do_cmd_locked(SEV_CMD_PDH_CERT_EXPORT, data, &argp->error); |
789 | ||
790 | /* If we query the length, FW responded with expected data. */ | |
791 | input.cert_chain_len = data->cert_chain_len; | |
792 | input.pdh_cert_len = data->pdh_cert_len; | |
793 | ||
794 | if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) { | |
795 | ret = -EFAULT; | |
796 | goto e_free_cert; | |
797 | } | |
798 | ||
799 | if (pdh_blob) { | |
800 | if (copy_to_user((void __user *)input.pdh_cert_address, | |
801 | pdh_blob, input.pdh_cert_len)) { | |
802 | ret = -EFAULT; | |
803 | goto e_free_cert; | |
804 | } | |
805 | } | |
806 | ||
807 | if (cert_blob) { | |
808 | if (copy_to_user((void __user *)input.cert_chain_address, | |
809 | cert_blob, input.cert_chain_len)) | |
810 | ret = -EFAULT; | |
811 | } | |
812 | ||
813 | e_free_cert: | |
814 | kfree(cert_blob); | |
815 | e_free_pdh: | |
816 | kfree(pdh_blob); | |
817 | e_free: | |
818 | kfree(data); | |
819 | return ret; | |
820 | } | |
821 | ||
200664d5 BS |
822 | static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) |
823 | { | |
2960f9a5 BS |
824 | void __user *argp = (void __user *)arg; |
825 | struct sev_issue_cmd input; | |
826 | int ret = -EFAULT; | |
b6102813 | 827 | bool writable = file->f_mode & FMODE_WRITE; |
2960f9a5 | 828 | |
b93566f1 | 829 | if (!psp_master || !psp_master->sev_data) |
2960f9a5 BS |
830 | return -ENODEV; |
831 | ||
832 | if (ioctl != SEV_ISSUE_CMD) | |
833 | return -EINVAL; | |
834 | ||
835 | if (copy_from_user(&input, argp, sizeof(struct sev_issue_cmd))) | |
836 | return -EFAULT; | |
837 | ||
838 | if (input.cmd > SEV_MAX) | |
839 | return -EINVAL; | |
840 | ||
841 | mutex_lock(&sev_cmd_mutex); | |
842 | ||
843 | switch (input.cmd) { | |
844 | ||
845 | case SEV_FACTORY_RESET: | |
b6102813 | 846 | ret = sev_ioctl_do_reset(&input, writable); |
2960f9a5 | 847 | break; |
efe1829b BS |
848 | case SEV_PLATFORM_STATUS: |
849 | ret = sev_ioctl_do_platform_status(&input); | |
850 | break; | |
4d84b726 | 851 | case SEV_PEK_GEN: |
b6102813 | 852 | ret = sev_ioctl_do_pek_pdh_gen(SEV_CMD_PEK_GEN, &input, writable); |
4d84b726 | 853 | break; |
77f65327 | 854 | case SEV_PDH_GEN: |
b6102813 | 855 | ret = sev_ioctl_do_pek_pdh_gen(SEV_CMD_PDH_GEN, &input, writable); |
77f65327 | 856 | break; |
e7990356 | 857 | case SEV_PEK_CSR: |
b6102813 | 858 | ret = sev_ioctl_do_pek_csr(&input, writable); |
e7990356 | 859 | break; |
7360e4b1 | 860 | case SEV_PEK_CERT_IMPORT: |
b6102813 | 861 | ret = sev_ioctl_do_pek_import(&input, writable); |
7360e4b1 | 862 | break; |
76a2b524 | 863 | case SEV_PDH_CERT_EXPORT: |
b6102813 | 864 | ret = sev_ioctl_do_pdh_export(&input, writable); |
76a2b524 | 865 | break; |
0b3a830b | 866 | case SEV_GET_ID: |
d6112ea0 | 867 | pr_warn_once("SEV_GET_ID command is deprecated, use SEV_GET_ID2\n"); |
0b3a830b JN |
868 | ret = sev_ioctl_do_get_id(&input); |
869 | break; | |
d6112ea0 SB |
870 | case SEV_GET_ID2: |
871 | ret = sev_ioctl_do_get_id2(&input); | |
872 | break; | |
2960f9a5 BS |
873 | default: |
874 | ret = -EINVAL; | |
875 | goto out; | |
876 | } | |
877 | ||
878 | if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd))) | |
879 | ret = -EFAULT; | |
880 | out: | |
881 | mutex_unlock(&sev_cmd_mutex); | |
882 | ||
883 | return ret; | |
200664d5 BS |
884 | } |
885 | ||
886 | static const struct file_operations sev_fops = { | |
887 | .owner = THIS_MODULE, | |
888 | .unlocked_ioctl = sev_ioctl, | |
889 | }; | |
890 | ||
891 | int sev_platform_status(struct sev_user_data_status *data, int *error) | |
892 | { | |
893 | return sev_do_cmd(SEV_CMD_PLATFORM_STATUS, data, error); | |
894 | } | |
895 | EXPORT_SYMBOL_GPL(sev_platform_status); | |
896 | ||
897 | int sev_guest_deactivate(struct sev_data_deactivate *data, int *error) | |
898 | { | |
899 | return sev_do_cmd(SEV_CMD_DEACTIVATE, data, error); | |
900 | } | |
901 | EXPORT_SYMBOL_GPL(sev_guest_deactivate); | |
902 | ||
903 | int sev_guest_activate(struct sev_data_activate *data, int *error) | |
904 | { | |
905 | return sev_do_cmd(SEV_CMD_ACTIVATE, data, error); | |
906 | } | |
907 | EXPORT_SYMBOL_GPL(sev_guest_activate); | |
908 | ||
909 | int sev_guest_decommission(struct sev_data_decommission *data, int *error) | |
910 | { | |
911 | return sev_do_cmd(SEV_CMD_DECOMMISSION, data, error); | |
912 | } | |
913 | EXPORT_SYMBOL_GPL(sev_guest_decommission); | |
914 | ||
915 | int sev_guest_df_flush(int *error) | |
916 | { | |
e385b5b7 | 917 | return sev_do_cmd(SEV_CMD_DF_FLUSH, NULL, error); |
200664d5 BS |
918 | } |
919 | EXPORT_SYMBOL_GPL(sev_guest_df_flush); | |
920 | ||
921 | static void sev_exit(struct kref *ref) | |
922 | { | |
200664d5 | 923 | misc_deregister(&misc_dev->misc); |
1f14b57f JA |
924 | kfree(misc_dev); |
925 | misc_dev = NULL; | |
200664d5 BS |
926 | } |
927 | ||
b93566f1 | 928 | static int sev_misc_init(struct sev_device *sev) |
200664d5 | 929 | { |
b93566f1 | 930 | struct device *dev = sev->dev; |
200664d5 BS |
931 | int ret; |
932 | ||
933 | /* | |
934 | * SEV feature support can be detected on multiple devices but the SEV | |
935 | * FW commands must be issued on the master. During probe, we do not | |
936 | * know the master hence we create /dev/sev on the first device probe. | |
937 | * sev_do_cmd() finds the right master device to which to issue the | |
938 | * command to the firmware. | |
939 | */ | |
940 | if (!misc_dev) { | |
941 | struct miscdevice *misc; | |
942 | ||
1f14b57f | 943 | misc_dev = kzalloc(sizeof(*misc_dev), GFP_KERNEL); |
200664d5 BS |
944 | if (!misc_dev) |
945 | return -ENOMEM; | |
946 | ||
947 | misc = &misc_dev->misc; | |
948 | misc->minor = MISC_DYNAMIC_MINOR; | |
949 | misc->name = DEVICE_NAME; | |
950 | misc->fops = &sev_fops; | |
951 | ||
952 | ret = misc_register(misc); | |
953 | if (ret) | |
954 | return ret; | |
955 | ||
956 | kref_init(&misc_dev->refcount); | |
957 | } else { | |
958 | kref_get(&misc_dev->refcount); | |
959 | } | |
960 | ||
b93566f1 RT |
961 | init_waitqueue_head(&sev->int_queue); |
962 | sev->misc = misc_dev; | |
200664d5 BS |
963 | dev_dbg(dev, "registered SEV device\n"); |
964 | ||
965 | return 0; | |
966 | } | |
967 | ||
b93566f1 | 968 | int sev_dev_init(struct psp_device *psp) |
2a6170df | 969 | { |
b93566f1 RT |
970 | struct device *dev = psp->dev; |
971 | struct sev_device *sev; | |
972 | int ret = -ENOMEM; | |
2a6170df | 973 | |
b93566f1 RT |
974 | sev = devm_kzalloc(dev, sizeof(*sev), GFP_KERNEL); |
975 | if (!sev) | |
2a6170df BS |
976 | goto e_err; |
977 | ||
b93566f1 | 978 | psp->sev_data = sev; |
2a6170df | 979 | |
b93566f1 RT |
980 | sev->dev = dev; |
981 | sev->psp = psp; | |
2a6170df | 982 | |
b93566f1 | 983 | sev->io_regs = psp->io_regs; |
2a6170df | 984 | |
6eb0cc72 RT |
985 | sev->vdata = (struct sev_vdata *)psp->vdata->sev; |
986 | if (!sev->vdata) { | |
987 | ret = -ENODEV; | |
988 | dev_err(dev, "sev: missing driver data\n"); | |
989 | goto e_err; | |
990 | } | |
991 | ||
b93566f1 | 992 | psp_set_sev_irq_handler(psp, sev_irq_handler, sev); |
7df5218d | 993 | |
b93566f1 | 994 | ret = sev_misc_init(sev); |
200664d5 BS |
995 | if (ret) |
996 | goto e_irq; | |
997 | ||
b93566f1 | 998 | dev_notice(dev, "sev enabled\n"); |
015c8c85 | 999 | |
2a6170df BS |
1000 | return 0; |
1001 | ||
200664d5 | 1002 | e_irq: |
b93566f1 | 1003 | psp_clear_sev_irq_handler(psp); |
2a6170df | 1004 | e_err: |
b93566f1 | 1005 | psp->sev_data = NULL; |
2a6170df | 1006 | |
b93566f1 | 1007 | dev_notice(dev, "sev initialization failed\n"); |
7df5218d | 1008 | |
2a6170df BS |
1009 | return ret; |
1010 | } | |
1011 | ||
b93566f1 | 1012 | void sev_dev_destroy(struct psp_device *psp) |
2a6170df | 1013 | { |
b93566f1 | 1014 | struct sev_device *sev = psp->sev_data; |
2a6170df | 1015 | |
b93566f1 | 1016 | if (!sev) |
afb31cd2 TL |
1017 | return; |
1018 | ||
b93566f1 | 1019 | if (sev->misc) |
200664d5 BS |
1020 | kref_put(&misc_dev->refcount, sev_exit); |
1021 | ||
b93566f1 | 1022 | psp_clear_sev_irq_handler(psp); |
2a6170df | 1023 | } |
200664d5 BS |
1024 | |
1025 | int sev_issue_cmd_external_user(struct file *filep, unsigned int cmd, | |
1026 | void *data, int *error) | |
1027 | { | |
1028 | if (!filep || filep->f_op != &sev_fops) | |
1029 | return -EBADF; | |
1030 | ||
b93566f1 | 1031 | return sev_do_cmd(cmd, data, error); |
200664d5 BS |
1032 | } |
1033 | EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user); | |
1034 | ||
b93566f1 | 1035 | void sev_pci_init(void) |
200664d5 | 1036 | { |
b93566f1 | 1037 | struct sev_device *sev = psp_master->sev_data; |
97f9ac3d | 1038 | struct page *tmr_page; |
200664d5 BS |
1039 | int error, rc; |
1040 | ||
b93566f1 | 1041 | if (!sev) |
200664d5 BS |
1042 | return; |
1043 | ||
e82867fd BS |
1044 | psp_timeout = psp_probe_timeout; |
1045 | ||
edd303ff JN |
1046 | if (sev_get_api_version()) |
1047 | goto err; | |
1048 | ||
f8903b3e SB |
1049 | /* |
1050 | * If platform is not in UNINIT state then firmware upgrade and/or | |
1051 | * platform INIT command will fail. These command require UNINIT state. | |
1052 | * | |
1053 | * In a normal boot we should never run into case where the firmware | |
1054 | * is not in UNINIT state on boot. But in case of kexec boot, a reboot | |
1055 | * may not go through a typical shutdown sequence and may leave the | |
1056 | * firmware in INIT or WORKING state. | |
1057 | */ | |
1058 | ||
b93566f1 | 1059 | if (sev->state != SEV_STATE_UNINIT) { |
f8903b3e | 1060 | sev_platform_shutdown(NULL); |
b93566f1 | 1061 | sev->state = SEV_STATE_UNINIT; |
f8903b3e SB |
1062 | } |
1063 | ||
83bf4251 | 1064 | if (sev_version_greater_or_equal(0, 15) && |
b93566f1 | 1065 | sev_update_firmware(sev->dev) == 0) |
edd303ff JN |
1066 | sev_get_api_version(); |
1067 | ||
97f9ac3d TL |
1068 | /* Obtain the TMR memory area for SEV-ES use */ |
1069 | tmr_page = alloc_pages(GFP_KERNEL, get_order(SEV_ES_TMR_SIZE)); | |
1070 | if (tmr_page) { | |
1071 | sev_es_tmr = page_address(tmr_page); | |
1072 | } else { | |
1073 | sev_es_tmr = NULL; | |
1074 | dev_warn(sev->dev, | |
1075 | "SEV: TMR allocation failed, SEV-ES support unavailable\n"); | |
1076 | } | |
1077 | ||
200664d5 BS |
1078 | /* Initialize the platform */ |
1079 | rc = sev_platform_init(&error); | |
1d55fdc8 AK |
1080 | if (rc && (error == SEV_RET_SECURE_DATA_INVALID)) { |
1081 | /* | |
1082 | * INIT command returned an integrity check failure | |
1083 | * status code, meaning that firmware load and | |
1084 | * validation of SEV related persistent data has | |
1085 | * failed and persistent state has been erased. | |
1086 | * Retrying INIT command here should succeed. | |
1087 | */ | |
b93566f1 | 1088 | dev_dbg(sev->dev, "SEV: retrying INIT command"); |
1d55fdc8 AK |
1089 | rc = sev_platform_init(&error); |
1090 | } | |
1091 | ||
200664d5 | 1092 | if (rc) { |
b93566f1 | 1093 | dev_err(sev->dev, "SEV: failed to INIT error %#x\n", error); |
f5a2aeb8 | 1094 | return; |
200664d5 BS |
1095 | } |
1096 | ||
b93566f1 RT |
1097 | dev_info(sev->dev, "SEV API:%d.%d build:%d\n", sev->api_major, |
1098 | sev->api_minor, sev->build); | |
200664d5 | 1099 | |
200664d5 BS |
1100 | return; |
1101 | ||
1102 | err: | |
b93566f1 | 1103 | psp_master->sev_data = NULL; |
200664d5 BS |
1104 | } |
1105 | ||
b93566f1 | 1106 | void sev_pci_exit(void) |
200664d5 | 1107 | { |
b93566f1 | 1108 | if (!psp_master->sev_data) |
200664d5 BS |
1109 | return; |
1110 | ||
1111 | sev_platform_shutdown(NULL); | |
97f9ac3d TL |
1112 | |
1113 | if (sev_es_tmr) { | |
1114 | /* The TMR area was encrypted, flush it from the cache */ | |
1115 | wbinvd_on_all_cpus(); | |
1116 | ||
1117 | free_pages((unsigned long)sev_es_tmr, | |
1118 | get_order(SEV_ES_TMR_SIZE)); | |
1119 | sev_es_tmr = NULL; | |
1120 | } | |
200664d5 | 1121 | } |