1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * POWER Platform specific code for non-volatile SED key access
4 * Copyright (C) 2022 IBM Corporation
5 *
6 * Define operations for SED Opal to read/write keys
7 * from POWER LPAR Platform KeyStore(PLPKS).
8 *
9 * Self Encrypting Drives(SED) key storage using PLPKS
10 */
11
12#include <linux/kernel.h>
13#include <linux/slab.h>
14#include <linux/string.h>
15#include <linux/ioctl.h>
16#include <linux/sed-opal-key.h>
17#include <asm/plpks.h>
18
19static bool plpks_sed_initialized = false;
20static bool plpks_sed_available = false;
21
22/*
23 * structure that contains all SED data
24 */
25struct plpks_sed_object_data {
26 u_char version;
27 u_char pad1[7];
28 u_long authority;
29 u_long range;
30 u_int key_len;
31 u_char key[32];
32};
33
34#define PLPKS_SED_OBJECT_DATA_V0 0
35#define PLPKS_SED_MANGLED_LABEL "/default/pri"
36#define PLPKS_SED_COMPONENT "sed-opal"
37#define PLPKS_SED_KEY "opal-boot-pin"
38
39/*
40 * authority is admin1 and range is global
41 */
42#define PLPKS_SED_AUTHORITY 0x0000000900010001
43#define PLPKS_SED_RANGE 0x0000080200000001
44
45static void plpks_init_var(struct plpks_var *var, char *keyname)
46{
47 if (!plpks_sed_initialized) {
48 plpks_sed_initialized = true;
49 plpks_sed_available = plpks_is_available();
50 if (!plpks_sed_available)
51 pr_err("SED: plpks not available\n");
52 }
53
54 var->name = keyname;
55 var->namelen = strlen(keyname);
56 if (strcmp(PLPKS_SED_KEY, keyname) == 0) {
57 var->name = PLPKS_SED_MANGLED_LABEL;
58 var->namelen = strlen(keyname);
59 }
60 var->policy = PLPKS_WORLDREADABLE;
61 var->os = PLPKS_VAR_COMMON;
62 var->data = NULL;
63 var->datalen = 0;
64 var->component = PLPKS_SED_COMPONENT;
65}
66
67/*
68 * Read the SED Opal key from PLPKS given the label
69 */
70int sed_read_key(char *keyname, char *key, u_int *keylen)
71{
72 struct plpks_var var;
73 struct plpks_sed_object_data data;
74 int ret;
75 u_int len;
76
77 plpks_init_var(var: &var, keyname);
78
79 if (!plpks_sed_available)
80 return -EOPNOTSUPP;
81
82 var.data = (u8 *)&data;
83 var.datalen = sizeof(data);
84
85 ret = plpks_read_os_var(&var);
86 if (ret != 0)
87 return ret;
88
89 len = min_t(u16, be32_to_cpu(data.key_len), var.datalen);
90 memcpy(key, data.key, len);
91 key[len] = '\0';
92 *keylen = len;
93
94 return 0;
95}
96
97/*
98 * Write the SED Opal key to PLPKS given the label
99 */
100int sed_write_key(char *keyname, char *key, u_int keylen)
101{
102 struct plpks_var var;
103 struct plpks_sed_object_data data;
104 struct plpks_var_name vname;
105
106 plpks_init_var(var: &var, keyname);
107
108 if (!plpks_sed_available)
109 return -EOPNOTSUPP;
110
111 var.datalen = sizeof(struct plpks_sed_object_data);
112 var.data = (u8 *)&data;
113
114 /* initialize SED object */
115 data.version = PLPKS_SED_OBJECT_DATA_V0;
116 data.authority = cpu_to_be64(PLPKS_SED_AUTHORITY);
117 data.range = cpu_to_be64(PLPKS_SED_RANGE);
118 memset(&data.pad1, '\0', sizeof(data.pad1));
119 data.key_len = cpu_to_be32(keylen);
120 memcpy(data.key, (char *)key, keylen);
121
122 /*
123 * Key update requires remove first. The return value
124 * is ignored since it's okay if the key doesn't exist.
125 */
126 vname.namelen = var.namelen;
127 vname.name = var.name;
128 plpks_remove_var(var.component, var.os, vname);
129
130 return plpks_write_var(var);
131}
132

source code of linux/arch/powerpc/platforms/pseries/plpks_sed_ops.c