Commit | Line | Data |
---|---|---|
2454a7af NJ |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * POWER LPAR Platform KeyStore(PLPKS) | |
4 | * Copyright (C) 2022 IBM Corporation | |
5 | * Author: Nayna Jain <nayna@linux.ibm.com> | |
6 | * | |
7 | * Provides access to variables stored in Power LPAR Platform KeyStore(PLPKS). | |
8 | */ | |
9 | ||
10 | #define pr_fmt(fmt) "plpks: " fmt | |
11 | ||
12 | #include <linux/delay.h> | |
13 | #include <linux/errno.h> | |
14 | #include <linux/io.h> | |
15 | #include <linux/printk.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/string.h> | |
18 | #include <linux/types.h> | |
91361b51 RC |
19 | #include <linux/of_fdt.h> |
20 | #include <linux/libfdt.h> | |
21 | #include <linux/memblock.h> | |
2454a7af | 22 | #include <asm/hvcall.h> |
a66de528 | 23 | #include <asm/machdep.h> |
90b74e30 | 24 | #include <asm/plpks.h> |
f82cdc37 | 25 | #include <asm/firmware.h> |
2454a7af | 26 | |
2454a7af NJ |
27 | static u8 *ospassword; |
28 | static u16 ospasswordlength; | |
29 | ||
30 | // Retrieved with H_PKS_GET_CONFIG | |
119da30d NJ |
31 | static u8 version; |
32 | static u16 objoverhead; | |
2454a7af NJ |
33 | static u16 maxpwsize; |
34 | static u16 maxobjsize; | |
119da30d NJ |
35 | static s16 maxobjlabelsize; |
36 | static u32 totalsize; | |
37 | static u32 usedspace; | |
38 | static u32 supportedpolicies; | |
39 | static u32 maxlargeobjectsize; | |
40 | static u64 signedupdatealgorithms; | |
2454a7af NJ |
41 | |
42 | struct plpks_auth { | |
43 | u8 version; | |
44 | u8 consumer; | |
45 | __be64 rsvd0; | |
46 | __be32 rsvd1; | |
47 | __be16 passwordlength; | |
48 | u8 password[]; | |
49 | } __packed __aligned(16); | |
50 | ||
51 | struct label_attr { | |
52 | u8 prefix[8]; | |
53 | u8 version; | |
54 | u8 os; | |
55 | u8 length; | |
56 | u8 reserved[5]; | |
57 | }; | |
58 | ||
59 | struct label { | |
60 | struct label_attr attr; | |
3def7a3e | 61 | u8 name[PLPKS_MAX_NAME_SIZE]; |
2454a7af NJ |
62 | size_t size; |
63 | }; | |
64 | ||
65 | static int pseries_status_to_err(int rc) | |
66 | { | |
67 | int err; | |
68 | ||
69 | switch (rc) { | |
70 | case H_SUCCESS: | |
71 | err = 0; | |
72 | break; | |
73 | case H_FUNCTION: | |
74 | err = -ENXIO; | |
75 | break; | |
af223e17 | 76 | case H_PARAMETER: |
2454a7af NJ |
77 | case H_P2: |
78 | case H_P3: | |
79 | case H_P4: | |
80 | case H_P5: | |
81 | case H_P6: | |
82 | err = -EINVAL; | |
83 | break; | |
84 | case H_NOT_FOUND: | |
85 | err = -ENOENT; | |
86 | break; | |
87 | case H_BUSY: | |
899d9b8f NJ |
88 | case H_LONG_BUSY_ORDER_1_MSEC: |
89 | case H_LONG_BUSY_ORDER_10_MSEC: | |
90 | case H_LONG_BUSY_ORDER_100_MSEC: | |
91 | case H_LONG_BUSY_ORDER_1_SEC: | |
92 | case H_LONG_BUSY_ORDER_10_SEC: | |
93 | case H_LONG_BUSY_ORDER_100_SEC: | |
2454a7af NJ |
94 | err = -EBUSY; |
95 | break; | |
96 | case H_AUTHORITY: | |
97 | err = -EPERM; | |
98 | break; | |
99 | case H_NO_MEM: | |
100 | err = -ENOMEM; | |
101 | break; | |
102 | case H_RESOURCE: | |
103 | err = -EEXIST; | |
104 | break; | |
105 | case H_TOO_BIG: | |
106 | err = -EFBIG; | |
107 | break; | |
108 | case H_STATE: | |
109 | err = -EIO; | |
110 | break; | |
111 | case H_R_STATE: | |
112 | err = -EIO; | |
113 | break; | |
114 | case H_IN_USE: | |
115 | err = -EEXIST; | |
116 | break; | |
117 | case H_ABORTED: | |
bb8e4c7c | 118 | err = -EIO; |
2454a7af NJ |
119 | break; |
120 | default: | |
121 | err = -EINVAL; | |
122 | } | |
123 | ||
ebdcd423 RC |
124 | pr_debug("Converted hypervisor code %d to Linux %d\n", rc, err); |
125 | ||
2454a7af NJ |
126 | return err; |
127 | } | |
128 | ||
129 | static int plpks_gen_password(void) | |
130 | { | |
131 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 }; | |
3def7a3e | 132 | u8 *password, consumer = PLPKS_OS_OWNER; |
2454a7af NJ |
133 | int rc; |
134 | ||
91361b51 RC |
135 | // If we booted from kexec, we could be reusing an existing password already |
136 | if (ospassword) { | |
137 | pr_debug("Password of length %u already in use\n", ospasswordlength); | |
138 | return 0; | |
139 | } | |
140 | ||
fcf63d6b AD |
141 | // The password must not cross a page boundary, so we align to the next power of 2 |
142 | password = kzalloc(roundup_pow_of_two(maxpwsize), GFP_KERNEL); | |
2454a7af NJ |
143 | if (!password) |
144 | return -ENOMEM; | |
145 | ||
146 | rc = plpar_hcall(H_PKS_GEN_PASSWORD, retbuf, consumer, 0, | |
147 | virt_to_phys(password), maxpwsize); | |
148 | ||
149 | if (!rc) { | |
150 | ospasswordlength = maxpwsize; | |
151 | ospassword = kzalloc(maxpwsize, GFP_KERNEL); | |
152 | if (!ospassword) { | |
ca2b746d | 153 | kfree_sensitive(password); |
2454a7af NJ |
154 | return -ENOMEM; |
155 | } | |
156 | memcpy(ospassword, password, ospasswordlength); | |
157 | } else { | |
158 | if (rc == H_IN_USE) { | |
ca4f1d22 | 159 | pr_warn("Password already set - authenticated operations will fail\n"); |
2454a7af NJ |
160 | rc = 0; |
161 | } else { | |
162 | goto out; | |
163 | } | |
164 | } | |
165 | out: | |
ca2b746d | 166 | kfree_sensitive(password); |
2454a7af NJ |
167 | |
168 | return pseries_status_to_err(rc); | |
169 | } | |
170 | ||
171 | static struct plpks_auth *construct_auth(u8 consumer) | |
172 | { | |
173 | struct plpks_auth *auth; | |
174 | ||
3def7a3e | 175 | if (consumer > PLPKS_OS_OWNER) |
2454a7af NJ |
176 | return ERR_PTR(-EINVAL); |
177 | ||
fcf63d6b AD |
178 | // The auth structure must not cross a page boundary and must be |
179 | // 16 byte aligned. We align to the next largest power of 2 | |
180 | auth = kzalloc(roundup_pow_of_two(struct_size(auth, password, maxpwsize)), GFP_KERNEL); | |
2454a7af NJ |
181 | if (!auth) |
182 | return ERR_PTR(-ENOMEM); | |
183 | ||
184 | auth->version = 1; | |
185 | auth->consumer = consumer; | |
2454a7af | 186 | |
3def7a3e | 187 | if (consumer == PLPKS_FW_OWNER || consumer == PLPKS_BOOTLOADER_OWNER) |
2454a7af | 188 | return auth; |
2454a7af NJ |
189 | |
190 | memcpy(auth->password, ospassword, ospasswordlength); | |
191 | ||
192 | auth->passwordlength = cpu_to_be16(ospasswordlength); | |
193 | ||
194 | return auth; | |
195 | } | |
196 | ||
506e550a | 197 | /* |
2454a7af NJ |
198 | * Label is combination of label attributes + name. |
199 | * Label attributes are used internally by kernel and not exposed to the user. | |
200 | */ | |
201 | static struct label *construct_label(char *component, u8 varos, u8 *name, | |
202 | u16 namelen) | |
203 | { | |
204 | struct label *label; | |
899d9b8f | 205 | size_t slen = 0; |
2454a7af | 206 | |
3def7a3e | 207 | if (!name || namelen > PLPKS_MAX_NAME_SIZE) |
2454a7af NJ |
208 | return ERR_PTR(-EINVAL); |
209 | ||
899d9b8f NJ |
210 | // Support NULL component for signed updates |
211 | if (component) { | |
212 | slen = strlen(component); | |
213 | if (slen > sizeof(label->attr.prefix)) | |
214 | return ERR_PTR(-EINVAL); | |
215 | } | |
2454a7af | 216 | |
fcf63d6b AD |
217 | // The label structure must not cross a page boundary, so we align to the next power of 2 |
218 | label = kzalloc(roundup_pow_of_two(sizeof(*label)), GFP_KERNEL); | |
2454a7af NJ |
219 | if (!label) |
220 | return ERR_PTR(-ENOMEM); | |
221 | ||
222 | if (component) | |
223 | memcpy(&label->attr.prefix, component, slen); | |
224 | ||
3def7a3e | 225 | label->attr.version = PLPKS_LABEL_VERSION; |
2454a7af | 226 | label->attr.os = varos; |
3def7a3e | 227 | label->attr.length = PLPKS_MAX_LABEL_ATTR_SIZE; |
2454a7af NJ |
228 | memcpy(&label->name, name, namelen); |
229 | ||
230 | label->size = sizeof(struct label_attr) + namelen; | |
231 | ||
232 | return label; | |
233 | } | |
234 | ||
235 | static int _plpks_get_config(void) | |
236 | { | |
237 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 }; | |
119da30d | 238 | struct config { |
2454a7af NJ |
239 | u8 version; |
240 | u8 flags; | |
119da30d NJ |
241 | __be16 rsvd0; |
242 | __be16 objoverhead; | |
2454a7af NJ |
243 | __be16 maxpwsize; |
244 | __be16 maxobjlabelsize; | |
245 | __be16 maxobjsize; | |
246 | __be32 totalsize; | |
247 | __be32 usedspace; | |
248 | __be32 supportedpolicies; | |
119da30d NJ |
249 | __be32 maxlargeobjectsize; |
250 | __be64 signedupdatealgorithms; | |
251 | u8 rsvd1[476]; | |
252 | } __packed * config; | |
2454a7af | 253 | size_t size; |
119da30d NJ |
254 | int rc = 0; |
255 | ||
256 | size = sizeof(*config); | |
257 | ||
258 | // Config struct must not cross a page boundary. So long as the struct | |
259 | // size is a power of 2, this should be fine as alignment is guaranteed | |
260 | config = kzalloc(size, GFP_KERNEL); | |
261 | if (!config) { | |
262 | rc = -ENOMEM; | |
263 | goto err; | |
264 | } | |
265 | ||
266 | rc = plpar_hcall(H_PKS_GET_CONFIG, retbuf, virt_to_phys(config), size); | |
267 | ||
268 | if (rc != H_SUCCESS) { | |
269 | rc = pseries_status_to_err(rc); | |
270 | goto err; | |
271 | } | |
272 | ||
273 | version = config->version; | |
274 | objoverhead = be16_to_cpu(config->objoverhead); | |
275 | maxpwsize = be16_to_cpu(config->maxpwsize); | |
276 | maxobjsize = be16_to_cpu(config->maxobjsize); | |
277 | maxobjlabelsize = be16_to_cpu(config->maxobjlabelsize); | |
278 | totalsize = be32_to_cpu(config->totalsize); | |
279 | usedspace = be32_to_cpu(config->usedspace); | |
280 | supportedpolicies = be32_to_cpu(config->supportedpolicies); | |
281 | maxlargeobjectsize = be32_to_cpu(config->maxlargeobjectsize); | |
282 | signedupdatealgorithms = be64_to_cpu(config->signedupdatealgorithms); | |
283 | ||
284 | // Validate that the numbers we get back match the requirements of the spec | |
285 | if (maxpwsize < 32) { | |
286 | pr_err("Invalid Max Password Size received from hypervisor (%d < 32)\n", maxpwsize); | |
287 | rc = -EIO; | |
288 | goto err; | |
289 | } | |
290 | ||
291 | if (maxobjlabelsize < 255) { | |
292 | pr_err("Invalid Max Object Label Size received from hypervisor (%d < 255)\n", | |
293 | maxobjlabelsize); | |
294 | rc = -EIO; | |
295 | goto err; | |
296 | } | |
297 | ||
298 | if (totalsize < 4096) { | |
299 | pr_err("Invalid Total Size received from hypervisor (%d < 4096)\n", totalsize); | |
300 | rc = -EIO; | |
301 | goto err; | |
302 | } | |
303 | ||
304 | if (version >= 3 && maxlargeobjectsize >= 65536 && maxobjsize != 0xFFFF) { | |
305 | pr_err("Invalid Max Object Size (0x%x != 0xFFFF)\n", maxobjsize); | |
306 | rc = -EIO; | |
307 | goto err; | |
308 | } | |
309 | ||
310 | err: | |
311 | kfree(config); | |
312 | return rc; | |
313 | } | |
314 | ||
315 | u8 plpks_get_version(void) | |
316 | { | |
317 | return version; | |
318 | } | |
319 | ||
320 | u16 plpks_get_objoverhead(void) | |
321 | { | |
322 | return objoverhead; | |
323 | } | |
324 | ||
325 | u16 plpks_get_maxpwsize(void) | |
326 | { | |
327 | return maxpwsize; | |
328 | } | |
329 | ||
330 | u16 plpks_get_maxobjectsize(void) | |
331 | { | |
332 | return maxobjsize; | |
333 | } | |
334 | ||
335 | u16 plpks_get_maxobjectlabelsize(void) | |
336 | { | |
337 | return maxobjlabelsize; | |
338 | } | |
339 | ||
340 | u32 plpks_get_totalsize(void) | |
341 | { | |
342 | return totalsize; | |
343 | } | |
344 | ||
345 | u32 plpks_get_usedspace(void) | |
346 | { | |
347 | // Unlike other config values, usedspace regularly changes as objects | |
348 | // are updated, so we need to refresh. | |
349 | int rc = _plpks_get_config(); | |
350 | if (rc) { | |
351 | pr_err("Couldn't get config, rc: %d\n", rc); | |
352 | return 0; | |
353 | } | |
354 | return usedspace; | |
355 | } | |
356 | ||
357 | u32 plpks_get_supportedpolicies(void) | |
358 | { | |
359 | return supportedpolicies; | |
360 | } | |
2454a7af | 361 | |
119da30d NJ |
362 | u32 plpks_get_maxlargeobjectsize(void) |
363 | { | |
364 | return maxlargeobjectsize; | |
365 | } | |
2454a7af | 366 | |
119da30d NJ |
367 | u64 plpks_get_signedupdatealgorithms(void) |
368 | { | |
369 | return signedupdatealgorithms; | |
370 | } | |
2454a7af | 371 | |
9ee76bd5 RC |
372 | u16 plpks_get_passwordlen(void) |
373 | { | |
374 | return ospasswordlength; | |
375 | } | |
376 | ||
119da30d NJ |
377 | bool plpks_is_available(void) |
378 | { | |
379 | int rc; | |
2454a7af | 380 | |
df9cad09 | 381 | if (!firmware_has_feature(FW_FEATURE_PLPKS)) |
f82cdc37 RC |
382 | return false; |
383 | ||
119da30d NJ |
384 | rc = _plpks_get_config(); |
385 | if (rc) | |
386 | return false; | |
2454a7af | 387 | |
119da30d | 388 | return true; |
2454a7af NJ |
389 | } |
390 | ||
391 | static int plpks_confirm_object_flushed(struct label *label, | |
392 | struct plpks_auth *auth) | |
393 | { | |
394 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 }; | |
f74dcbfd | 395 | bool timed_out = true; |
2454a7af NJ |
396 | u64 timeout = 0; |
397 | u8 status; | |
398 | int rc; | |
399 | ||
400 | do { | |
401 | rc = plpar_hcall(H_PKS_CONFIRM_OBJECT_FLUSHED, retbuf, | |
402 | virt_to_phys(auth), virt_to_phys(label), | |
403 | label->size); | |
404 | ||
405 | status = retbuf[0]; | |
406 | if (rc) { | |
f74dcbfd | 407 | timed_out = false; |
2454a7af NJ |
408 | if (rc == H_NOT_FOUND && status == 1) |
409 | rc = 0; | |
410 | break; | |
411 | } | |
412 | ||
f74dcbfd AD |
413 | if (!rc && status == 1) { |
414 | timed_out = false; | |
2454a7af | 415 | break; |
f74dcbfd | 416 | } |
2454a7af | 417 | |
3def7a3e RC |
418 | usleep_range(PLPKS_FLUSH_SLEEP, |
419 | PLPKS_FLUSH_SLEEP + PLPKS_FLUSH_SLEEP_RANGE); | |
420 | timeout = timeout + PLPKS_FLUSH_SLEEP; | |
421 | } while (timeout < PLPKS_MAX_TIMEOUT); | |
2454a7af | 422 | |
f74dcbfd AD |
423 | if (timed_out) |
424 | return -ETIMEDOUT; | |
2454a7af | 425 | |
f74dcbfd | 426 | return pseries_status_to_err(rc); |
2454a7af NJ |
427 | } |
428 | ||
899d9b8f NJ |
429 | int plpks_signed_update_var(struct plpks_var *var, u64 flags) |
430 | { | |
431 | unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = {0}; | |
432 | int rc; | |
433 | struct label *label; | |
434 | struct plpks_auth *auth; | |
435 | u64 continuetoken = 0; | |
436 | u64 timeout = 0; | |
437 | ||
438 | if (!var->data || var->datalen <= 0 || var->namelen > PLPKS_MAX_NAME_SIZE) | |
439 | return -EINVAL; | |
440 | ||
441 | if (!(var->policy & PLPKS_SIGNEDUPDATE)) | |
442 | return -EINVAL; | |
443 | ||
444 | // Signed updates need the component to be NULL. | |
445 | if (var->component) | |
446 | return -EINVAL; | |
447 | ||
448 | auth = construct_auth(PLPKS_OS_OWNER); | |
449 | if (IS_ERR(auth)) | |
450 | return PTR_ERR(auth); | |
451 | ||
452 | label = construct_label(var->component, var->os, var->name, var->namelen); | |
453 | if (IS_ERR(label)) { | |
454 | rc = PTR_ERR(label); | |
455 | goto out; | |
456 | } | |
457 | ||
458 | do { | |
459 | rc = plpar_hcall9(H_PKS_SIGNED_UPDATE, retbuf, | |
460 | virt_to_phys(auth), virt_to_phys(label), | |
461 | label->size, var->policy, flags, | |
462 | virt_to_phys(var->data), var->datalen, | |
463 | continuetoken); | |
464 | ||
465 | continuetoken = retbuf[0]; | |
466 | if (pseries_status_to_err(rc) == -EBUSY) { | |
467 | int delay_ms = get_longbusy_msecs(rc); | |
468 | mdelay(delay_ms); | |
469 | timeout += delay_ms; | |
470 | } | |
471 | rc = pseries_status_to_err(rc); | |
472 | } while (rc == -EBUSY && timeout < PLPKS_MAX_TIMEOUT); | |
473 | ||
474 | if (!rc) | |
475 | rc = plpks_confirm_object_flushed(label, auth); | |
476 | ||
477 | kfree(label); | |
478 | out: | |
479 | kfree(auth); | |
480 | ||
481 | return rc; | |
482 | } | |
483 | ||
2454a7af NJ |
484 | int plpks_write_var(struct plpks_var var) |
485 | { | |
486 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 }; | |
487 | struct plpks_auth *auth; | |
488 | struct label *label; | |
489 | int rc; | |
490 | ||
491 | if (!var.component || !var.data || var.datalen <= 0 || | |
3def7a3e | 492 | var.namelen > PLPKS_MAX_NAME_SIZE || var.datalen > PLPKS_MAX_DATA_SIZE) |
2454a7af NJ |
493 | return -EINVAL; |
494 | ||
3def7a3e | 495 | if (var.policy & PLPKS_SIGNEDUPDATE) |
2454a7af NJ |
496 | return -EINVAL; |
497 | ||
3def7a3e | 498 | auth = construct_auth(PLPKS_OS_OWNER); |
2454a7af NJ |
499 | if (IS_ERR(auth)) |
500 | return PTR_ERR(auth); | |
501 | ||
502 | label = construct_label(var.component, var.os, var.name, var.namelen); | |
503 | if (IS_ERR(label)) { | |
504 | rc = PTR_ERR(label); | |
505 | goto out; | |
506 | } | |
507 | ||
508 | rc = plpar_hcall(H_PKS_WRITE_OBJECT, retbuf, virt_to_phys(auth), | |
509 | virt_to_phys(label), label->size, var.policy, | |
510 | virt_to_phys(var.data), var.datalen); | |
511 | ||
512 | if (!rc) | |
513 | rc = plpks_confirm_object_flushed(label, auth); | |
514 | ||
2454a7af NJ |
515 | rc = pseries_status_to_err(rc); |
516 | kfree(label); | |
517 | out: | |
518 | kfree(auth); | |
519 | ||
520 | return rc; | |
521 | } | |
522 | ||
523 | int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname) | |
524 | { | |
525 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 }; | |
526 | struct plpks_auth *auth; | |
527 | struct label *label; | |
528 | int rc; | |
529 | ||
899d9b8f | 530 | if (vname.namelen > PLPKS_MAX_NAME_SIZE) |
2454a7af NJ |
531 | return -EINVAL; |
532 | ||
3def7a3e | 533 | auth = construct_auth(PLPKS_OS_OWNER); |
2454a7af NJ |
534 | if (IS_ERR(auth)) |
535 | return PTR_ERR(auth); | |
536 | ||
537 | label = construct_label(component, varos, vname.name, vname.namelen); | |
538 | if (IS_ERR(label)) { | |
539 | rc = PTR_ERR(label); | |
540 | goto out; | |
541 | } | |
542 | ||
543 | rc = plpar_hcall(H_PKS_REMOVE_OBJECT, retbuf, virt_to_phys(auth), | |
544 | virt_to_phys(label), label->size); | |
545 | ||
546 | if (!rc) | |
547 | rc = plpks_confirm_object_flushed(label, auth); | |
548 | ||
2454a7af NJ |
549 | rc = pseries_status_to_err(rc); |
550 | kfree(label); | |
551 | out: | |
552 | kfree(auth); | |
553 | ||
554 | return rc; | |
555 | } | |
556 | ||
557 | static int plpks_read_var(u8 consumer, struct plpks_var *var) | |
558 | { | |
559 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 }; | |
560 | struct plpks_auth *auth; | |
1f622f3f | 561 | struct label *label = NULL; |
2454a7af NJ |
562 | u8 *output; |
563 | int rc; | |
564 | ||
3def7a3e | 565 | if (var->namelen > PLPKS_MAX_NAME_SIZE) |
2454a7af NJ |
566 | return -EINVAL; |
567 | ||
1f622f3f | 568 | auth = construct_auth(consumer); |
2454a7af NJ |
569 | if (IS_ERR(auth)) |
570 | return PTR_ERR(auth); | |
571 | ||
3def7a3e | 572 | if (consumer == PLPKS_OS_OWNER) { |
1f622f3f NJ |
573 | label = construct_label(var->component, var->os, var->name, |
574 | var->namelen); | |
575 | if (IS_ERR(label)) { | |
576 | rc = PTR_ERR(label); | |
577 | goto out_free_auth; | |
578 | } | |
2454a7af NJ |
579 | } |
580 | ||
581 | output = kzalloc(maxobjsize, GFP_KERNEL); | |
582 | if (!output) { | |
583 | rc = -ENOMEM; | |
584 | goto out_free_label; | |
585 | } | |
586 | ||
3def7a3e | 587 | if (consumer == PLPKS_OS_OWNER) |
1f622f3f NJ |
588 | rc = plpar_hcall(H_PKS_READ_OBJECT, retbuf, virt_to_phys(auth), |
589 | virt_to_phys(label), label->size, virt_to_phys(output), | |
590 | maxobjsize); | |
591 | else | |
592 | rc = plpar_hcall(H_PKS_READ_OBJECT, retbuf, virt_to_phys(auth), | |
593 | virt_to_phys(var->name), var->namelen, virt_to_phys(output), | |
594 | maxobjsize); | |
595 | ||
2454a7af NJ |
596 | |
597 | if (rc != H_SUCCESS) { | |
2454a7af NJ |
598 | rc = pseries_status_to_err(rc); |
599 | goto out_free_output; | |
600 | } | |
601 | ||
0cf2cc1f | 602 | if (!var->data || var->datalen > retbuf[0]) |
2454a7af NJ |
603 | var->datalen = retbuf[0]; |
604 | ||
2454a7af NJ |
605 | var->policy = retbuf[1]; |
606 | ||
0cf2cc1f AD |
607 | if (var->data) |
608 | memcpy(var->data, output, var->datalen); | |
609 | ||
2454a7af NJ |
610 | rc = 0; |
611 | ||
612 | out_free_output: | |
613 | kfree(output); | |
614 | out_free_label: | |
615 | kfree(label); | |
616 | out_free_auth: | |
617 | kfree(auth); | |
618 | ||
619 | return rc; | |
620 | } | |
621 | ||
622 | int plpks_read_os_var(struct plpks_var *var) | |
623 | { | |
3def7a3e | 624 | return plpks_read_var(PLPKS_OS_OWNER, var); |
2454a7af NJ |
625 | } |
626 | ||
627 | int plpks_read_fw_var(struct plpks_var *var) | |
628 | { | |
3def7a3e | 629 | return plpks_read_var(PLPKS_FW_OWNER, var); |
2454a7af NJ |
630 | } |
631 | ||
632 | int plpks_read_bootloader_var(struct plpks_var *var) | |
633 | { | |
3def7a3e | 634 | return plpks_read_var(PLPKS_BOOTLOADER_OWNER, var); |
2454a7af NJ |
635 | } |
636 | ||
91361b51 RC |
637 | int plpks_populate_fdt(void *fdt) |
638 | { | |
639 | int chosen_offset = fdt_path_offset(fdt, "/chosen"); | |
640 | ||
641 | if (chosen_offset < 0) { | |
642 | pr_err("Can't find chosen node: %s\n", | |
643 | fdt_strerror(chosen_offset)); | |
644 | return chosen_offset; | |
645 | } | |
646 | ||
647 | return fdt_setprop(fdt, chosen_offset, "ibm,plpks-pw", ospassword, ospasswordlength); | |
648 | } | |
649 | ||
650 | // Once a password is registered with the hypervisor it cannot be cleared without | |
651 | // rebooting the LPAR, so to keep using the PLPKS across kexec boots we need to | |
652 | // recover the previous password from the FDT. | |
653 | // | |
654 | // There are a few challenges here. We don't want the password to be visible to | |
655 | // users, so we need to clear it from the FDT. This has to be done in early boot. | |
656 | // Clearing it from the FDT would make the FDT's checksum invalid, so we have to | |
657 | // manually cause the checksum to be recalculated. | |
658 | void __init plpks_early_init_devtree(void) | |
659 | { | |
660 | void *fdt = initial_boot_params; | |
661 | int chosen_node = fdt_path_offset(fdt, "/chosen"); | |
662 | const u8 *password; | |
663 | int len; | |
664 | ||
665 | if (chosen_node < 0) | |
666 | return; | |
667 | ||
668 | password = fdt_getprop(fdt, chosen_node, "ibm,plpks-pw", &len); | |
669 | if (len <= 0) { | |
670 | pr_debug("Couldn't find ibm,plpks-pw node.\n"); | |
671 | return; | |
672 | } | |
673 | ||
674 | ospassword = memblock_alloc_raw(len, SMP_CACHE_BYTES); | |
675 | if (!ospassword) { | |
676 | pr_err("Error allocating memory for password.\n"); | |
677 | goto out; | |
678 | } | |
679 | ||
680 | memcpy(ospassword, password, len); | |
681 | ospasswordlength = (u16)len; | |
682 | ||
683 | out: | |
684 | fdt_nop_property(fdt, chosen_node, "ibm,plpks-pw"); | |
685 | // Since we've cleared the password, we must update the FDT checksum | |
686 | early_init_dt_verify(fdt); | |
687 | } | |
688 | ||
2454a7af NJ |
689 | static __init int pseries_plpks_init(void) |
690 | { | |
691 | int rc; | |
692 | ||
df9cad09 AD |
693 | if (!firmware_has_feature(FW_FEATURE_PLPKS)) |
694 | return -ENODEV; | |
695 | ||
2454a7af NJ |
696 | rc = _plpks_get_config(); |
697 | ||
698 | if (rc) { | |
699 | pr_err("POWER LPAR Platform KeyStore is not supported or enabled\n"); | |
700 | return rc; | |
701 | } | |
702 | ||
703 | rc = plpks_gen_password(); | |
704 | if (rc) | |
705 | pr_err("Failed setting POWER LPAR Platform KeyStore Password\n"); | |
706 | else | |
707 | pr_info("POWER LPAR Platform KeyStore initialized successfully\n"); | |
708 | ||
709 | return rc; | |
710 | } | |
a66de528 | 711 | machine_arch_initcall(pseries, pseries_plpks_init); |