root/arch/powerpc/platforms/powernv/opal-secvar.c
// 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);