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/of.h>
16#include <linux/platform_device.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, u64 ksize, u8 *data, u64 *dsize)
58{
59	int rc;
60
61	if (!key || !dsize)
62		return -EINVAL;
63
64	*dsize = cpu_to_be64(*dsize);
65
66	rc = opal_secvar_get(key, ksize, data, dsize);
67
68	*dsize = be64_to_cpu(*dsize);
69
70	return opal_status_to_err(rc);
71}
72
73static int opal_get_next_variable(const char *key, u64 *keylen, u64 keybufsize)
74{
75	int rc;
76
77	if (!key || !keylen)
78		return -EINVAL;
79
80	*keylen = cpu_to_be64(*keylen);
81
82	rc = opal_secvar_get_next(key, keylen, keybufsize);
83
84	*keylen = be64_to_cpu(*keylen);
85
86	return opal_status_to_err(rc);
87}
88
89static int opal_set_variable(const char *key, u64 ksize, u8 *data, u64 dsize)
90{
91	int rc;
92
93	if (!key || !data)
94		return -EINVAL;
95
96	rc = opal_secvar_enqueue_update(key, ksize, data, dsize);
97
98	return opal_status_to_err(rc);
99}
100
101static ssize_t opal_secvar_format(char *buf, size_t bufsize)
102{
103	ssize_t rc = 0;
104	struct device_node *node;
105	const char *format;
106
107	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
108	if (!of_device_is_available(node)) {
109		rc = -ENODEV;
110		goto out;
111	}
112
113	rc = of_property_read_string(node, "format", &format);
114	if (rc)
115		goto out;
116
117	rc = snprintf(buf, bufsize, "%s", format);
118
119out:
120	of_node_put(node);
121
122	return rc;
123}
124
125static int opal_secvar_max_size(u64 *max_size)
126{
127	int rc;
128	struct device_node *node;
129
130	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
131	if (!node)
132		return -ENODEV;
133
134	if (!of_device_is_available(node)) {
135		rc = -ENODEV;
136		goto out;
137	}
138
139	rc = of_property_read_u64(node, "max-var-size", max_size);
140
141out:
142	of_node_put(node);
143	return rc;
144}
145
146static const struct secvar_operations opal_secvar_ops = {
147	.get = opal_get_variable,
148	.get_next = opal_get_next_variable,
149	.set = opal_set_variable,
150	.format = opal_secvar_format,
151	.max_size = opal_secvar_max_size,
152};
153
154static int opal_secvar_probe(struct platform_device *pdev)
155{
156	if (!opal_check_token(OPAL_SECVAR_GET)
157			|| !opal_check_token(OPAL_SECVAR_GET_NEXT)
158			|| !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) {
159		pr_err("OPAL doesn't support secure variables\n");
160		return -ENODEV;
161	}
162
163	return set_secvar_ops(&opal_secvar_ops);
164}
165
166static const struct of_device_id opal_secvar_match[] = {
167	{ .compatible = "ibm,secvar-backend",},
168	{},
169};
170
171static struct platform_driver opal_secvar_driver = {
172	.driver = {
173		.name = "secvar",
174		.of_match_table = opal_secvar_match,
175	},
176};
177
178static int __init opal_secvar_init(void)
179{
180	return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe);
181}
182device_initcall(opal_secvar_init);
183