// SPDX-License-Identifier: GPL-2.0 /* * PowerNV code for secure variables * * Copyright (C) 2019 IBM Corporation * Author: Claudio Carvalho * Nayna Jain * * APIs to access secure variables managed by OPAL. */ #define pr_fmt(fmt) "secvar: "fmt #include <linux/types.h> #include <linux/of.h> #include <linux/platform_device.h> #include <asm/opal.h> #include <asm/secvar.h> #include <asm/secure_boot.h> static int opal_status_to_err(int rc) { int err; switch (rc) { case OPAL_SUCCESS: err = 0; break; case OPAL_UNSUPPORTED: err = -ENXIO; break; case OPAL_PARAMETER: err = -EINVAL; break; case OPAL_RESOURCE: err = -ENOSPC; break; case OPAL_HARDWARE: err = -EIO; break; case OPAL_NO_MEM: err = -ENOMEM; break; case OPAL_EMPTY: err = -ENOENT; break; case OPAL_PARTIAL: err = -EFBIG; break; default: err = -EINVAL; } return err; } static int opal_get_variable(const char *key, u64 ksize, u8 *data, u64 *dsize) { int rc; if (!key || !dsize) return -EINVAL; *dsize = cpu_to_be64(*dsize); rc = opal_secvar_get(key, ksize, data, dsize); *dsize = be64_to_cpu(*dsize); return opal_status_to_err(rc); } static int opal_get_next_variable(const char *key, u64 *keylen, u64 keybufsize) { int rc; if (!key || !keylen) return -EINVAL; *keylen = cpu_to_be64(*keylen); rc = opal_secvar_get_next(key, keylen, keybufsize); *keylen = be64_to_cpu(*keylen); return opal_status_to_err(rc); } static int opal_set_variable(const char *key, u64 ksize, u8 *data, u64 dsize) { int rc; if (!key || !data) return -EINVAL; rc = opal_secvar_enqueue_update(key, ksize, data, dsize); return opal_status_to_err(rc); } static ssize_t opal_secvar_format(char *buf, size_t bufsize) { ssize_t rc = 0; struct device_node *node; const char *format; node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend"); if (!of_device_is_available(node)) { rc = -ENODEV; goto out; } rc = of_property_read_string(node, "format", &format); if (rc) goto out; rc = snprintf(buf, bufsize, "%s", format); out: of_node_put(node); return rc; } static int opal_secvar_max_size(u64 *max_size) { int rc; struct device_node *node; node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend"); if (!node) return -ENODEV; if (!of_device_is_available(node)) { rc = -ENODEV; goto out; } rc = of_property_read_u64(node, "max-var-size", max_size); out: of_node_put(node); return rc; } static const struct secvar_operations opal_secvar_ops = { .get = opal_get_variable, .get_next = opal_get_next_variable, .set = opal_set_variable, .format = opal_secvar_format, .max_size = opal_secvar_max_size, }; static int opal_secvar_probe(struct platform_device *pdev) { if (!opal_check_token(OPAL_SECVAR_GET) || !opal_check_token(OPAL_SECVAR_GET_NEXT) || !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) { pr_err("OPAL doesn't support secure variables\n"); return -ENODEV; } return set_secvar_ops(&opal_secvar_ops); } static const struct of_device_id opal_secvar_match[] = { { .compatible = "ibm,secvar-backend",}, {}, }; static struct platform_driver opal_secvar_driver = { .driver = { .name = "secvar", .of_match_table = opal_secvar_match, }, }; static int __init opal_secvar_init(void) { return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe); } device_initcall(opal_secvar_init);