|
| 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 | + |
| 19 | +static bool plpks_sed_initialized = false; |
| 20 | +static bool plpks_sed_available = false; |
| 21 | + |
| 22 | +/* |
| 23 | + * structure that contains all SED data |
| 24 | + */ |
| 25 | +struct 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 | + |
| 45 | +static 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 | + */ |
| 70 | +int 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, 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 | + */ |
| 100 | +int 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, 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 | +} |
0 commit comments