162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci// Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS) 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2022, 2023 IBM Corporation 662306a36Sopenharmony_ci// Authors: Russell Currey 762306a36Sopenharmony_ci// Andrew Donnellan 862306a36Sopenharmony_ci// Nayna Jain 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "secvar: "fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/printk.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/kobject.h> 1862306a36Sopenharmony_ci#include <linux/nls.h> 1962306a36Sopenharmony_ci#include <asm/machdep.h> 2062306a36Sopenharmony_ci#include <asm/secvar.h> 2162306a36Sopenharmony_ci#include <asm/plpks.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci// Config attributes for sysfs 2462306a36Sopenharmony_ci#define PLPKS_CONFIG_ATTR(name, fmt, func) \ 2562306a36Sopenharmony_ci static ssize_t name##_show(struct kobject *kobj, \ 2662306a36Sopenharmony_ci struct kobj_attribute *attr, \ 2762306a36Sopenharmony_ci char *buf) \ 2862306a36Sopenharmony_ci { \ 2962306a36Sopenharmony_ci return sysfs_emit(buf, fmt, func()); \ 3062306a36Sopenharmony_ci } \ 3162306a36Sopenharmony_ci static struct kobj_attribute attr_##name = __ATTR_RO(name) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version); 3462306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize); 3562306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize); 3662306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace); 3762306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies); 3862306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const struct attribute *config_attrs[] = { 4162306a36Sopenharmony_ci &attr_version.attr, 4262306a36Sopenharmony_ci &attr_max_object_size.attr, 4362306a36Sopenharmony_ci &attr_total_size.attr, 4462306a36Sopenharmony_ci &attr_used_space.attr, 4562306a36Sopenharmony_ci &attr_supported_policies.attr, 4662306a36Sopenharmony_ci &attr_signed_update_algorithms.attr, 4762306a36Sopenharmony_ci NULL, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic u32 get_policy(const char *name) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci if ((strcmp(name, "db") == 0) || 5362306a36Sopenharmony_ci (strcmp(name, "dbx") == 0) || 5462306a36Sopenharmony_ci (strcmp(name, "grubdb") == 0) || 5562306a36Sopenharmony_ci (strcmp(name, "grubdbx") == 0) || 5662306a36Sopenharmony_ci (strcmp(name, "sbat") == 0)) 5762306a36Sopenharmony_ci return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE); 5862306a36Sopenharmony_ci else 5962306a36Sopenharmony_ci return PLPKS_SIGNEDUPDATE; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic const char * const plpks_var_names[] = { 6362306a36Sopenharmony_ci "PK", 6462306a36Sopenharmony_ci "KEK", 6562306a36Sopenharmony_ci "db", 6662306a36Sopenharmony_ci "dbx", 6762306a36Sopenharmony_ci "grubdb", 6862306a36Sopenharmony_ci "grubdbx", 6962306a36Sopenharmony_ci "sbat", 7062306a36Sopenharmony_ci "moduledb", 7162306a36Sopenharmony_ci "trustedcadb", 7262306a36Sopenharmony_ci NULL, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int plpks_get_variable(const char *key, u64 key_len, u8 *data, 7662306a36Sopenharmony_ci u64 *data_size) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct plpks_var var = {0}; 7962306a36Sopenharmony_ci int rc = 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci // We subtract 1 from key_len because we don't need to include the 8262306a36Sopenharmony_ci // null terminator at the end of the string 8362306a36Sopenharmony_ci var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL); 8462306a36Sopenharmony_ci if (!var.name) 8562306a36Sopenharmony_ci return -ENOMEM; 8662306a36Sopenharmony_ci rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name, 8762306a36Sopenharmony_ci key_len - 1); 8862306a36Sopenharmony_ci if (rc < 0) 8962306a36Sopenharmony_ci goto err; 9062306a36Sopenharmony_ci var.namelen = rc * 2; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci var.os = PLPKS_VAR_LINUX; 9362306a36Sopenharmony_ci if (data) { 9462306a36Sopenharmony_ci var.data = data; 9562306a36Sopenharmony_ci var.datalen = *data_size; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci rc = plpks_read_os_var(&var); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (rc) 10062306a36Sopenharmony_ci goto err; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci *data_size = var.datalen; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cierr: 10562306a36Sopenharmony_ci kfree(var.name); 10662306a36Sopenharmony_ci if (rc && rc != -ENOENT) { 10762306a36Sopenharmony_ci pr_err("Failed to read variable '%s': %d\n", key, rc); 10862306a36Sopenharmony_ci // Return -EIO since userspace probably doesn't care about the 10962306a36Sopenharmony_ci // specific error 11062306a36Sopenharmony_ci rc = -EIO; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci return rc; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int plpks_set_variable(const char *key, u64 key_len, u8 *data, 11662306a36Sopenharmony_ci u64 data_size) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct plpks_var var = {0}; 11962306a36Sopenharmony_ci int rc = 0; 12062306a36Sopenharmony_ci u64 flags; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci // Secure variables need to be prefixed with 8 bytes of flags. 12362306a36Sopenharmony_ci // We only want to perform the write if we have at least one byte of data. 12462306a36Sopenharmony_ci if (data_size <= sizeof(flags)) 12562306a36Sopenharmony_ci return -EINVAL; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci // We subtract 1 from key_len because we don't need to include the 12862306a36Sopenharmony_ci // null terminator at the end of the string 12962306a36Sopenharmony_ci var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL); 13062306a36Sopenharmony_ci if (!var.name) 13162306a36Sopenharmony_ci return -ENOMEM; 13262306a36Sopenharmony_ci rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name, 13362306a36Sopenharmony_ci key_len - 1); 13462306a36Sopenharmony_ci if (rc < 0) 13562306a36Sopenharmony_ci goto err; 13662306a36Sopenharmony_ci var.namelen = rc * 2; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci // Flags are contained in the first 8 bytes of the buffer, and are always big-endian 13962306a36Sopenharmony_ci flags = be64_to_cpup((__be64 *)data); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci var.datalen = data_size - sizeof(flags); 14262306a36Sopenharmony_ci var.data = data + sizeof(flags); 14362306a36Sopenharmony_ci var.os = PLPKS_VAR_LINUX; 14462306a36Sopenharmony_ci var.policy = get_policy(key); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci // Unlike in the read case, the plpks error code can be useful to 14762306a36Sopenharmony_ci // userspace on write, so we return it rather than just -EIO 14862306a36Sopenharmony_ci rc = plpks_signed_update_var(&var, flags); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cierr: 15162306a36Sopenharmony_ci kfree(var.name); 15262306a36Sopenharmony_ci return rc; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does. 15662306a36Sopenharmony_ci// Instead, report the format using the SB_VERSION variable in the keystore. 15762306a36Sopenharmony_ci// The string is made up by us, and takes the form "ibm,plpks-sb-v<n>" (or "ibm,plpks-sb-unknown" 15862306a36Sopenharmony_ci// if the SB_VERSION variable doesn't exist). Hypervisor defines the SB_VERSION variable as a 15962306a36Sopenharmony_ci// "1 byte unsigned integer value". 16062306a36Sopenharmony_cistatic ssize_t plpks_secvar_format(char *buf, size_t bufsize) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct plpks_var var = {0}; 16362306a36Sopenharmony_ci ssize_t ret; 16462306a36Sopenharmony_ci u8 version; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci var.component = NULL; 16762306a36Sopenharmony_ci // Only the signed variables have null bytes in their names, this one doesn't 16862306a36Sopenharmony_ci var.name = "SB_VERSION"; 16962306a36Sopenharmony_ci var.namelen = strlen(var.name); 17062306a36Sopenharmony_ci var.datalen = 1; 17162306a36Sopenharmony_ci var.data = &version; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci // Unlike the other vars, SB_VERSION is owned by firmware instead of the OS 17462306a36Sopenharmony_ci ret = plpks_read_fw_var(&var); 17562306a36Sopenharmony_ci if (ret) { 17662306a36Sopenharmony_ci if (ret == -ENOENT) { 17762306a36Sopenharmony_ci ret = snprintf(buf, bufsize, "ibm,plpks-sb-unknown"); 17862306a36Sopenharmony_ci } else { 17962306a36Sopenharmony_ci pr_err("Error %ld reading SB_VERSION from firmware\n", ret); 18062306a36Sopenharmony_ci ret = -EIO; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci goto err; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", version); 18662306a36Sopenharmony_cierr: 18762306a36Sopenharmony_ci return ret; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int plpks_max_size(u64 *max_size) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci // The max object size reported by the hypervisor is accurate for the 19362306a36Sopenharmony_ci // object itself, but we use the first 8 bytes of data on write as the 19462306a36Sopenharmony_ci // signed update flags, so the max size a user can write is larger. 19562306a36Sopenharmony_ci *max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic const struct secvar_operations plpks_secvar_ops = { 20262306a36Sopenharmony_ci .get = plpks_get_variable, 20362306a36Sopenharmony_ci .set = plpks_set_variable, 20462306a36Sopenharmony_ci .format = plpks_secvar_format, 20562306a36Sopenharmony_ci .max_size = plpks_max_size, 20662306a36Sopenharmony_ci .config_attrs = config_attrs, 20762306a36Sopenharmony_ci .var_names = plpks_var_names, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int plpks_secvar_init(void) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci if (!plpks_is_available()) 21362306a36Sopenharmony_ci return -ENODEV; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return set_secvar_ops(&plpks_secvar_ops); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_cimachine_device_initcall(pseries, plpks_secvar_init); 218