#define pr_fmt(fmt) "secvar: "fmt
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/nls.h>
#include <asm/machdep.h>
#include <asm/secvar.h>
#include <asm/plpks.h>
static u32 get_policy(const char *name)
{
if ((strcmp(name, "db") == 0) ||
(strcmp(name, "dbx") == 0) ||
(strcmp(name, "grubdb") == 0) ||
(strcmp(name, "grubdbx") == 0) ||
(strcmp(name, "sbat") == 0))
return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);
else
return PLPKS_SIGNEDUPDATE;
}
static const char * const plpks_var_names_static[] = {
"PK",
"moduledb",
"trustedcadb",
NULL,
};
static const char * const plpks_var_names_dynamic[] = {
"PK",
"KEK",
"db",
"dbx",
"grubdb",
"grubdbx",
"sbat",
"moduledb",
"trustedcadb",
NULL,
};
static int plpks_get_variable(const char *key, u64 key_len, u8 *data,
u64 *data_size)
{
struct plpks_var var = {0};
int rc = 0;
var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
if (!var.name)
return -ENOMEM;
rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
key_len - 1);
if (rc < 0)
goto err;
var.namelen = rc * 2;
var.os = PLPKS_VAR_LINUX;
if (data) {
var.data = data;
var.datalen = *data_size;
}
rc = plpks_read_os_var(&var);
if (rc)
goto err;
*data_size = var.datalen;
err:
kfree(var.name);
if (rc && rc != -ENOENT) {
pr_err("Failed to read variable '%s': %d\n", key, rc);
rc = -EIO;
}
return rc;
}
static int plpks_set_variable(const char *key, u64 key_len, u8 *data,
u64 data_size)
{
struct plpks_var var = {0};
int rc = 0;
u64 flags;
if (data_size <= sizeof(flags))
return -EINVAL;
var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
if (!var.name)
return -ENOMEM;
rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
key_len - 1);
if (rc < 0)
goto err;
var.namelen = rc * 2;
flags = be64_to_cpup((__be64 *)data);
var.datalen = data_size - sizeof(flags);
var.data = data + sizeof(flags);
var.os = PLPKS_VAR_LINUX;
var.policy = get_policy(key);
rc = plpks_signed_update_var(&var, flags);
err:
kfree(var.name);
return rc;
}
static u8 plpks_get_sb_keymgmt_mode(void)
{
u8 mode;
ssize_t rc;
struct plpks_var var = {
.component = NULL,
.name = "SB_VERSION",
.namelen = 10,
.datalen = 1,
.data = &mode,
};
rc = plpks_read_fw_var(&var);
if (rc) {
if (rc != -ENOENT && rc != -EPERM)
pr_info("Error %ld reading SB_VERSION from firmware\n", rc);
mode = 0;
}
return mode;
}
static ssize_t plpks_secvar_format(char *buf, size_t bufsize)
{
u8 mode;
mode = plpks_get_sb_keymgmt_mode();
return snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", mode);
}
static int plpks_max_size(u64 *max_size)
{
*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);
return 0;
}
static const struct secvar_operations plpks_secvar_ops_static = {
.get = plpks_get_variable,
.set = plpks_set_variable,
.format = plpks_secvar_format,
.max_size = plpks_max_size,
.var_names = plpks_var_names_static,
};
static const struct secvar_operations plpks_secvar_ops_dynamic = {
.get = plpks_get_variable,
.set = plpks_set_variable,
.format = plpks_secvar_format,
.max_size = plpks_max_size,
.var_names = plpks_var_names_dynamic,
};
static int plpks_secvar_init(void)
{
u8 mode;
if (!plpks_is_available())
return -ENODEV;
mode = plpks_get_sb_keymgmt_mode();
if (mode)
return set_secvar_ops(&plpks_secvar_ops_dynamic);
return set_secvar_ops(&plpks_secvar_ops_static);
}
machine_device_initcall(pseries, plpks_secvar_init);