GNU Linux-libre 6.8.9-gnu
[releases.git] / arch / powerpc / platforms / powernv / opal-secvar.c
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);