xref: /linux/drivers/firmware/meson/meson_sm.c (revision 9a6b55ac)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Amlogic Secure Monitor driver
4  *
5  * Copyright (C) 2016 Endless Mobile, Inc.
6  * Author: Carlo Caione <carlo@endlessm.com>
7  */
8 
9 #define pr_fmt(fmt) "meson-sm: " fmt
10 
11 #include <linux/arm-smccc.h>
12 #include <linux/bug.h>
13 #include <linux/io.h>
14 #include <linux/module.h>
15 #include <linux/of.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/printk.h>
19 #include <linux/types.h>
20 #include <linux/sizes.h>
21  #include <linux/slab.h>
22 
23 #include <linux/firmware/meson/meson_sm.h>
24 
25 struct meson_sm_cmd {
26 	unsigned int index;
27 	u32 smc_id;
28 };
29 #define CMD(d, s) { .index = (d), .smc_id = (s), }
30 
31 struct meson_sm_chip {
32 	unsigned int shmem_size;
33 	u32 cmd_shmem_in_base;
34 	u32 cmd_shmem_out_base;
35 	struct meson_sm_cmd cmd[];
36 };
37 
38 static const struct meson_sm_chip gxbb_chip = {
39 	.shmem_size		= SZ_4K,
40 	.cmd_shmem_in_base	= 0x82000020,
41 	.cmd_shmem_out_base	= 0x82000021,
42 	.cmd = {
43 		CMD(SM_EFUSE_READ,	0x82000030),
44 		CMD(SM_EFUSE_WRITE,	0x82000031),
45 		CMD(SM_EFUSE_USER_MAX,	0x82000033),
46 		CMD(SM_GET_CHIP_ID,	0x82000044),
47 		{ /* sentinel */ },
48 	},
49 };
50 
51 struct meson_sm_firmware {
52 	const struct meson_sm_chip *chip;
53 	void __iomem *sm_shmem_in_base;
54 	void __iomem *sm_shmem_out_base;
55 };
56 
57 static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip,
58 			    unsigned int cmd_index)
59 {
60 	const struct meson_sm_cmd *cmd = chip->cmd;
61 
62 	while (cmd->smc_id && cmd->index != cmd_index)
63 		cmd++;
64 
65 	return cmd->smc_id;
66 }
67 
68 static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2,
69 			   u32 arg3, u32 arg4)
70 {
71 	struct arm_smccc_res res;
72 
73 	arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res);
74 	return res.a0;
75 }
76 
77 static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size)
78 {
79 	u32 sm_phy_base;
80 
81 	sm_phy_base = __meson_sm_call(cmd_shmem, 0, 0, 0, 0, 0);
82 	if (!sm_phy_base)
83 		return 0;
84 
85 	return ioremap_cache(sm_phy_base, size);
86 }
87 
88 /**
89  * meson_sm_call - generic SMC32 call to the secure-monitor
90  *
91  * @fw:		Pointer to secure-monitor firmware
92  * @cmd_index:	Index of the SMC32 function ID
93  * @ret:	Returned value
94  * @arg0:	SMC32 Argument 0
95  * @arg1:	SMC32 Argument 1
96  * @arg2:	SMC32 Argument 2
97  * @arg3:	SMC32 Argument 3
98  * @arg4:	SMC32 Argument 4
99  *
100  * Return:	0 on success, a negative value on error
101  */
102 int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index,
103 		  u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
104 {
105 	u32 cmd, lret;
106 
107 	if (!fw->chip)
108 		return -ENOENT;
109 
110 	cmd = meson_sm_get_cmd(fw->chip, cmd_index);
111 	if (!cmd)
112 		return -EINVAL;
113 
114 	lret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4);
115 
116 	if (ret)
117 		*ret = lret;
118 
119 	return 0;
120 }
121 EXPORT_SYMBOL(meson_sm_call);
122 
123 /**
124  * meson_sm_call_read - retrieve data from secure-monitor
125  *
126  * @fw:		Pointer to secure-monitor firmware
127  * @buffer:	Buffer to store the retrieved data
128  * @bsize:	Size of the buffer
129  * @cmd_index:	Index of the SMC32 function ID
130  * @arg0:	SMC32 Argument 0
131  * @arg1:	SMC32 Argument 1
132  * @arg2:	SMC32 Argument 2
133  * @arg3:	SMC32 Argument 3
134  * @arg4:	SMC32 Argument 4
135  *
136  * Return:	size of read data on success, a negative value on error
137  *		When 0 is returned there is no guarantee about the amount of
138  *		data read and bsize bytes are copied in buffer.
139  */
140 int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer,
141 		       unsigned int bsize, unsigned int cmd_index, u32 arg0,
142 		       u32 arg1, u32 arg2, u32 arg3, u32 arg4)
143 {
144 	u32 size;
145 	int ret;
146 
147 	if (!fw->chip)
148 		return -ENOENT;
149 
150 	if (!fw->chip->cmd_shmem_out_base)
151 		return -EINVAL;
152 
153 	if (bsize > fw->chip->shmem_size)
154 		return -EINVAL;
155 
156 	if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
157 		return -EINVAL;
158 
159 	if (size > bsize)
160 		return -EINVAL;
161 
162 	ret = size;
163 
164 	if (!size)
165 		size = bsize;
166 
167 	if (buffer)
168 		memcpy(buffer, fw->sm_shmem_out_base, size);
169 
170 	return ret;
171 }
172 EXPORT_SYMBOL(meson_sm_call_read);
173 
174 /**
175  * meson_sm_call_write - send data to secure-monitor
176  *
177  * @fw:		Pointer to secure-monitor firmware
178  * @buffer:	Buffer containing data to send
179  * @size:	Size of the data to send
180  * @cmd_index:	Index of the SMC32 function ID
181  * @arg0:	SMC32 Argument 0
182  * @arg1:	SMC32 Argument 1
183  * @arg2:	SMC32 Argument 2
184  * @arg3:	SMC32 Argument 3
185  * @arg4:	SMC32 Argument 4
186  *
187  * Return:	size of sent data on success, a negative value on error
188  */
189 int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer,
190 			unsigned int size, unsigned int cmd_index, u32 arg0,
191 			u32 arg1, u32 arg2, u32 arg3, u32 arg4)
192 {
193 	u32 written;
194 
195 	if (!fw->chip)
196 		return -ENOENT;
197 
198 	if (size > fw->chip->shmem_size)
199 		return -EINVAL;
200 
201 	if (!fw->chip->cmd_shmem_in_base)
202 		return -EINVAL;
203 
204 	memcpy(fw->sm_shmem_in_base, buffer, size);
205 
206 	if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
207 		return -EINVAL;
208 
209 	if (!written)
210 		return -EINVAL;
211 
212 	return written;
213 }
214 EXPORT_SYMBOL(meson_sm_call_write);
215 
216 /**
217  * meson_sm_get - get pointer to meson_sm_firmware structure.
218  *
219  * @sm_node:		Pointer to the secure-monitor Device Tree node.
220  *
221  * Return:		NULL is the secure-monitor device is not ready.
222  */
223 struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node)
224 {
225 	struct platform_device *pdev = of_find_device_by_node(sm_node);
226 
227 	if (!pdev)
228 		return NULL;
229 
230 	return platform_get_drvdata(pdev);
231 }
232 EXPORT_SYMBOL_GPL(meson_sm_get);
233 
234 #define SM_CHIP_ID_LENGTH	119
235 #define SM_CHIP_ID_OFFSET	4
236 #define SM_CHIP_ID_SIZE		12
237 
238 static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
239 			 char *buf)
240 {
241 	struct platform_device *pdev = to_platform_device(dev);
242 	struct meson_sm_firmware *fw;
243 	uint8_t *id_buf;
244 	int ret;
245 
246 	fw = platform_get_drvdata(pdev);
247 
248 	id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
249 	if (!id_buf)
250 		return -ENOMEM;
251 
252 	ret = meson_sm_call_read(fw, id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
253 				 0, 0, 0, 0, 0);
254 	if (ret < 0) {
255 		kfree(id_buf);
256 		return ret;
257 	}
258 
259 	ret = sprintf(buf, "%12phN\n", &id_buf[SM_CHIP_ID_OFFSET]);
260 
261 	kfree(id_buf);
262 
263 	return ret;
264 }
265 
266 static DEVICE_ATTR_RO(serial);
267 
268 static struct attribute *meson_sm_sysfs_attributes[] = {
269 	&dev_attr_serial.attr,
270 	NULL,
271 };
272 
273 static const struct attribute_group meson_sm_sysfs_attr_group = {
274 	.attrs = meson_sm_sysfs_attributes,
275 };
276 
277 static const struct of_device_id meson_sm_ids[] = {
278 	{ .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
279 	{ /* sentinel */ },
280 };
281 
282 static int __init meson_sm_probe(struct platform_device *pdev)
283 {
284 	struct device *dev = &pdev->dev;
285 	const struct meson_sm_chip *chip;
286 	struct meson_sm_firmware *fw;
287 
288 	fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL);
289 	if (!fw)
290 		return -ENOMEM;
291 
292 	chip = of_match_device(meson_sm_ids, dev)->data;
293 
294 	if (chip->cmd_shmem_in_base) {
295 		fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
296 							  chip->shmem_size);
297 		if (WARN_ON(!fw->sm_shmem_in_base))
298 			goto out;
299 	}
300 
301 	if (chip->cmd_shmem_out_base) {
302 		fw->sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
303 							   chip->shmem_size);
304 		if (WARN_ON(!fw->sm_shmem_out_base))
305 			goto out_in_base;
306 	}
307 
308 	fw->chip = chip;
309 
310 	platform_set_drvdata(pdev, fw);
311 
312 	pr_info("secure-monitor enabled\n");
313 
314 	if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
315 		goto out_in_base;
316 
317 	return 0;
318 
319 out_in_base:
320 	iounmap(fw->sm_shmem_in_base);
321 out:
322 	return -EINVAL;
323 }
324 
325 static struct platform_driver meson_sm_driver = {
326 	.driver = {
327 		.name = "meson-sm",
328 		.of_match_table = of_match_ptr(meson_sm_ids),
329 	},
330 };
331 module_platform_driver_probe(meson_sm_driver, meson_sm_probe);
332