1// SPDX-License-Identifier: GPL-2.0
2/*
3 * PowerNV code for secure variables
4 *
5 * Copyright (C) 2019 IBM Corporation
6 * Author: Claudio Carvalho
7 *         Nayna Jain
8 *
9 * APIs to access secure variables managed by OPAL.
10 */
11
12#define pr_fmt(fmt) "secvar: "fmt
13
14#include <linux/types.h>
15#include <linux/platform_device.h>
16#include <linux/of_platform.h>
17#include <asm/opal.h>
18#include <asm/secvar.h>
19#include <asm/secure_boot.h>
20
21static int opal_status_to_err(int rc)
22{
23	int err;
24
25	switch (rc) {
26	case OPAL_SUCCESS:
27		err = 0;
28		break;
29	case OPAL_UNSUPPORTED:
30		err = -ENXIO;
31		break;
32	case OPAL_PARAMETER:
33		err = -EINVAL;
34		break;
35	case OPAL_RESOURCE:
36		err = -ENOSPC;
37		break;
38	case OPAL_HARDWARE:
39		err = -EIO;
40		break;
41	case OPAL_NO_MEM:
42		err = -ENOMEM;
43		break;
44	case OPAL_EMPTY:
45		err = -ENOENT;
46		break;
47	case OPAL_PARTIAL:
48		err = -EFBIG;
49		break;
50	default:
51		err = -EINVAL;
52	}
53
54	return err;
55}
56
57static int opal_get_variable(const char *key, uint64_t ksize,
58			     u8 *data, uint64_t *dsize)
59{
60	int rc;
61
62	if (!key || !dsize)
63		return -EINVAL;
64
65	*dsize = cpu_to_be64(*dsize);
66
67	rc = opal_secvar_get(key, ksize, data, dsize);
68
69	*dsize = be64_to_cpu(*dsize);
70
71	return opal_status_to_err(rc);
72}
73
74static int opal_get_next_variable(const char *key, uint64_t *keylen,
75				  uint64_t keybufsize)
76{
77	int rc;
78
79	if (!key || !keylen)
80		return -EINVAL;
81
82	*keylen = cpu_to_be64(*keylen);
83
84	rc = opal_secvar_get_next(key, keylen, keybufsize);
85
86	*keylen = be64_to_cpu(*keylen);
87
88	return opal_status_to_err(rc);
89}
90
91static int opal_set_variable(const char *key, uint64_t ksize, u8 *data,
92			     uint64_t dsize)
93{
94	int rc;
95
96	if (!key || !data)
97		return -EINVAL;
98
99	rc = opal_secvar_enqueue_update(key, ksize, data, dsize);
100
101	return opal_status_to_err(rc);
102}
103
104static const struct secvar_operations opal_secvar_ops = {
105	.get = opal_get_variable,
106	.get_next = opal_get_next_variable,
107	.set = opal_set_variable,
108};
109
110static int opal_secvar_probe(struct platform_device *pdev)
111{
112	if (!opal_check_token(OPAL_SECVAR_GET)
113			|| !opal_check_token(OPAL_SECVAR_GET_NEXT)
114			|| !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) {
115		pr_err("OPAL doesn't support secure variables\n");
116		return -ENODEV;
117	}
118
119	set_secvar_ops(&opal_secvar_ops);
120
121	return 0;
122}
123
124static const struct of_device_id opal_secvar_match[] = {
125	{ .compatible = "ibm,secvar-backend",},
126	{},
127};
128
129static struct platform_driver opal_secvar_driver = {
130	.driver = {
131		.name = "secvar",
132		.of_match_table = opal_secvar_match,
133	},
134};
135
136static int __init opal_secvar_init(void)
137{
138	return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe);
139}
140device_initcall(opal_secvar_init);
141