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