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> |
19 | #include <linux/of_fdt.h> |
20 | #include <linux/libfdt.h> |
21 | #include <linux/memblock.h> |
22 | #include <asm/hvcall.h> |
23 | #include <asm/machdep.h> |
24 | #include <asm/plpks.h> |
25 | #include <asm/firmware.h> |
26 | |
27 | static u8 *ospassword; |
28 | static u16 ospasswordlength; |
29 | |
30 | // Retrieved with H_PKS_GET_CONFIG |
31 | static u8 version; |
32 | static u16 objoverhead; |
33 | static u16 maxpwsize; |
34 | static u16 maxobjsize; |
35 | static s16 maxobjlabelsize; |
36 | static u32 totalsize; |
37 | static u32 usedspace; |
38 | static u32 supportedpolicies; |
39 | static u32 maxlargeobjectsize; |
40 | static u64 signedupdatealgorithms; |
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; |
61 | u8 name[PLPKS_MAX_NAME_SIZE]; |
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; |
76 | case H_PARAMETER: |
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: |
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: |
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: |
118 | err = -EIO; |
119 | break; |
120 | default: |
121 | err = -EINVAL; |
122 | } |
123 | |
124 | pr_debug("Converted hypervisor code %d to Linux %d\n" , rc, err); |
125 | |
126 | return err; |
127 | } |
128 | |
129 | static int plpks_gen_password(void) |
130 | { |
131 | unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 }; |
132 | u8 *password, consumer = PLPKS_OS_OWNER; |
133 | int rc; |
134 | |
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 | |
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); |
143 | if (!password) |
144 | return -ENOMEM; |
145 | |
146 | rc = plpar_hcall(H_PKS_GEN_PASSWORD, retbuf, consumer, 0, |
147 | virt_to_phys(address: password), maxpwsize); |
148 | |
149 | if (!rc) { |
150 | ospasswordlength = maxpwsize; |
151 | ospassword = kzalloc(size: maxpwsize, GFP_KERNEL); |
152 | if (!ospassword) { |
153 | kfree_sensitive(objp: password); |
154 | return -ENOMEM; |
155 | } |
156 | memcpy(ospassword, password, ospasswordlength); |
157 | } else { |
158 | if (rc == H_IN_USE) { |
159 | pr_warn("Password already set - authenticated operations will fail\n" ); |
160 | rc = 0; |
161 | } else { |
162 | goto out; |
163 | } |
164 | } |
165 | out: |
166 | kfree_sensitive(objp: password); |
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 | |
175 | if (consumer > PLPKS_OS_OWNER) |
176 | return ERR_PTR(error: -EINVAL); |
177 | |
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); |
181 | if (!auth) |
182 | return ERR_PTR(error: -ENOMEM); |
183 | |
184 | auth->version = 1; |
185 | auth->consumer = consumer; |
186 | |
187 | if (consumer == PLPKS_FW_OWNER || consumer == PLPKS_BOOTLOADER_OWNER) |
188 | return auth; |
189 | |
190 | memcpy(auth->password, ospassword, ospasswordlength); |
191 | |
192 | auth->passwordlength = cpu_to_be16(ospasswordlength); |
193 | |
194 | return auth; |
195 | } |
196 | |
197 | /* |
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; |
205 | size_t slen = 0; |
206 | |
207 | if (!name || namelen > PLPKS_MAX_NAME_SIZE) |
208 | return ERR_PTR(error: -EINVAL); |
209 | |
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(error: -EINVAL); |
215 | } |
216 | |
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); |
219 | if (!label) |
220 | return ERR_PTR(error: -ENOMEM); |
221 | |
222 | if (component) |
223 | memcpy(&label->attr.prefix, component, slen); |
224 | |
225 | label->attr.version = PLPKS_LABEL_VERSION; |
226 | label->attr.os = varos; |
227 | label->attr.length = PLPKS_MAX_LABEL_ATTR_SIZE; |
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 }; |
238 | struct config { |
239 | u8 version; |
240 | u8 flags; |
241 | __be16 rsvd0; |
242 | __be16 objoverhead; |
243 | __be16 maxpwsize; |
244 | __be16 maxobjlabelsize; |
245 | __be16 maxobjsize; |
246 | __be32 totalsize; |
247 | __be32 usedspace; |
248 | __be32 supportedpolicies; |
249 | __be32 maxlargeobjectsize; |
250 | __be64 signedupdatealgorithms; |
251 | u8 rsvd1[476]; |
252 | } __packed * config; |
253 | size_t size; |
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(objp: 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 | } |
361 | |
362 | u32 plpks_get_maxlargeobjectsize(void) |
363 | { |
364 | return maxlargeobjectsize; |
365 | } |
366 | |
367 | u64 plpks_get_signedupdatealgorithms(void) |
368 | { |
369 | return signedupdatealgorithms; |
370 | } |
371 | |
372 | u16 plpks_get_passwordlen(void) |
373 | { |
374 | return ospasswordlength; |
375 | } |
376 | |
377 | bool plpks_is_available(void) |
378 | { |
379 | int rc; |
380 | |
381 | if (!firmware_has_feature(FW_FEATURE_PLPKS)) |
382 | return false; |
383 | |
384 | rc = _plpks_get_config(); |
385 | if (rc) |
386 | return false; |
387 | |
388 | return true; |
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 }; |
395 | bool timed_out = true; |
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) { |
407 | timed_out = false; |
408 | if (rc == H_NOT_FOUND && status == 1) |
409 | rc = 0; |
410 | break; |
411 | } |
412 | |
413 | if (!rc && status == 1) { |
414 | timed_out = false; |
415 | break; |
416 | } |
417 | |
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); |
422 | |
423 | if (timed_out) |
424 | return -ETIMEDOUT; |
425 | |
426 | return pseries_status_to_err(rc); |
427 | } |
428 | |
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(ptr: auth)) |
450 | return PTR_ERR(ptr: auth); |
451 | |
452 | label = construct_label(component: var->component, varos: var->os, name: var->name, namelen: var->namelen); |
453 | if (IS_ERR(ptr: label)) { |
454 | rc = PTR_ERR(ptr: 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(objp: label); |
478 | out: |
479 | kfree(objp: auth); |
480 | |
481 | return rc; |
482 | } |
483 | |
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 || |
492 | var.namelen > PLPKS_MAX_NAME_SIZE || var.datalen > PLPKS_MAX_DATA_SIZE) |
493 | return -EINVAL; |
494 | |
495 | if (var.policy & PLPKS_SIGNEDUPDATE) |
496 | return -EINVAL; |
497 | |
498 | auth = construct_auth(PLPKS_OS_OWNER); |
499 | if (IS_ERR(ptr: auth)) |
500 | return PTR_ERR(ptr: auth); |
501 | |
502 | label = construct_label(component: var.component, varos: var.os, name: var.name, namelen: var.namelen); |
503 | if (IS_ERR(ptr: label)) { |
504 | rc = PTR_ERR(ptr: 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 | |
515 | rc = pseries_status_to_err(rc); |
516 | kfree(objp: label); |
517 | out: |
518 | kfree(objp: 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 | |
530 | if (vname.namelen > PLPKS_MAX_NAME_SIZE) |
531 | return -EINVAL; |
532 | |
533 | auth = construct_auth(PLPKS_OS_OWNER); |
534 | if (IS_ERR(ptr: auth)) |
535 | return PTR_ERR(ptr: auth); |
536 | |
537 | label = construct_label(component, varos, name: vname.name, namelen: vname.namelen); |
538 | if (IS_ERR(ptr: label)) { |
539 | rc = PTR_ERR(ptr: 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 | |
549 | rc = pseries_status_to_err(rc); |
550 | kfree(objp: label); |
551 | out: |
552 | kfree(objp: 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; |
561 | struct label *label = NULL; |
562 | u8 *output; |
563 | int rc; |
564 | |
565 | if (var->namelen > PLPKS_MAX_NAME_SIZE) |
566 | return -EINVAL; |
567 | |
568 | auth = construct_auth(consumer); |
569 | if (IS_ERR(ptr: auth)) |
570 | return PTR_ERR(ptr: auth); |
571 | |
572 | if (consumer == PLPKS_OS_OWNER) { |
573 | label = construct_label(component: var->component, varos: var->os, name: var->name, |
574 | namelen: var->namelen); |
575 | if (IS_ERR(ptr: label)) { |
576 | rc = PTR_ERR(ptr: label); |
577 | goto out_free_auth; |
578 | } |
579 | } |
580 | |
581 | output = kzalloc(size: maxobjsize, GFP_KERNEL); |
582 | if (!output) { |
583 | rc = -ENOMEM; |
584 | goto out_free_label; |
585 | } |
586 | |
587 | if (consumer == PLPKS_OS_OWNER) |
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 | |
596 | |
597 | if (rc != H_SUCCESS) { |
598 | rc = pseries_status_to_err(rc); |
599 | goto out_free_output; |
600 | } |
601 | |
602 | if (!var->data || var->datalen > retbuf[0]) |
603 | var->datalen = retbuf[0]; |
604 | |
605 | var->policy = retbuf[1]; |
606 | |
607 | if (var->data) |
608 | memcpy(var->data, output, var->datalen); |
609 | |
610 | rc = 0; |
611 | |
612 | out_free_output: |
613 | kfree(objp: output); |
614 | out_free_label: |
615 | kfree(objp: label); |
616 | out_free_auth: |
617 | kfree(objp: auth); |
618 | |
619 | return rc; |
620 | } |
621 | |
622 | int plpks_read_os_var(struct plpks_var *var) |
623 | { |
624 | return plpks_read_var(PLPKS_OS_OWNER, var); |
625 | } |
626 | |
627 | int plpks_read_fw_var(struct plpks_var *var) |
628 | { |
629 | return plpks_read_var(PLPKS_FW_OWNER, var); |
630 | } |
631 | |
632 | int plpks_read_bootloader_var(struct plpks_var *var) |
633 | { |
634 | return plpks_read_var(PLPKS_BOOTLOADER_OWNER, var); |
635 | } |
636 | |
637 | int plpks_populate_fdt(void *fdt) |
638 | { |
639 | int chosen_offset = fdt_path_offset(fdt, path: "/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, nodeoffset: chosen_offset, name: "ibm,plpks-pw" , val: ospassword, len: 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, path: "/chosen" ); |
662 | const u8 *password; |
663 | int len; |
664 | |
665 | if (chosen_node < 0) |
666 | return; |
667 | |
668 | password = fdt_getprop(fdt, nodeoffset: chosen_node, name: "ibm,plpks-pw" , lenp: &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(size: 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, nodeoffset: chosen_node, name: "ibm,plpks-pw" ); |
685 | // Since we've cleared the password, we must update the FDT checksum |
686 | early_init_dt_verify(params: fdt); |
687 | } |
688 | |
689 | static __init int pseries_plpks_init(void) |
690 | { |
691 | int rc; |
692 | |
693 | if (!firmware_has_feature(FW_FEATURE_PLPKS)) |
694 | return -ENODEV; |
695 | |
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 | } |
711 | machine_arch_initcall(pseries, pseries_plpks_init); |
712 | |