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 
21 static 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 
57 static 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 
73 static 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 
89 static 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 
101 static 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 
119 out:
120 	of_node_put(node);
121 
122 	return rc;
123 }
124 
125 static 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 
141 out:
142 	of_node_put(node);
143 	return rc;
144 }
145 
146 static 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 
154 static 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 
166 static const struct of_device_id opal_secvar_match[] = {
167 	{ .compatible = "ibm,secvar-backend",},
168 	{},
169 };
170 
171 static struct platform_driver opal_secvar_driver = {
172 	.driver = {
173 		.name = "secvar",
174 		.of_match_table = opal_secvar_match,
175 	},
176 };
177 
178 static int __init opal_secvar_init(void)
179 {
180 	return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe);
181 }
182 device_initcall(opal_secvar_init);
183