diff options
author | Carlo Caione <carlo@endlessm.com> | 2016-08-27 09:43:43 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@baylibre.com> | 2016-09-01 17:23:39 -0400 |
commit | 2c4ddb215521d5dfb30f72123ef966ac6bdd16d7 (patch) | |
tree | 4ea8f9160358416781a64275ba47d9e3113b3db4 | |
parent | 6edf27ee25892571d275e2b3945d1b48c68d0476 (diff) |
firmware: Amlogic: Add secure monitor driver
Introduce a driver to provide calls into secure monitor mode.
In the Amlogic SoCs these calls are used for multiple reasons: access to
NVMEM, set USB boot, enable JTAG, etc...
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Carlo Caione <carlo@endlessm.com>
[khilman: add in SZ_4K cleanup]
Signed-off-by: Kevin Hilman <khilman@baylibre.com>
-rw-r--r-- | drivers/firmware/Kconfig | 1 | ||||
-rw-r--r-- | drivers/firmware/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/meson/Kconfig | 9 | ||||
-rw-r--r-- | drivers/firmware/meson/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/meson/meson_sm.c | 248 | ||||
-rw-r--r-- | include/linux/firmware/meson/meson_sm.h | 31 |
6 files changed, 291 insertions, 0 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 0e22f241403b..bca172d42c74 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig | |||
@@ -209,5 +209,6 @@ config HAVE_ARM_SMCCC | |||
209 | source "drivers/firmware/broadcom/Kconfig" | 209 | source "drivers/firmware/broadcom/Kconfig" |
210 | source "drivers/firmware/google/Kconfig" | 210 | source "drivers/firmware/google/Kconfig" |
211 | source "drivers/firmware/efi/Kconfig" | 211 | source "drivers/firmware/efi/Kconfig" |
212 | source "drivers/firmware/meson/Kconfig" | ||
212 | 213 | ||
213 | endmenu | 214 | endmenu |
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 44a59dcfc398..898ac41fa8b3 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile | |||
@@ -22,6 +22,7 @@ obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o | |||
22 | CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a | 22 | CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a |
23 | 23 | ||
24 | obj-y += broadcom/ | 24 | obj-y += broadcom/ |
25 | obj-y += meson/ | ||
25 | obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ | 26 | obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ |
26 | obj-$(CONFIG_EFI) += efi/ | 27 | obj-$(CONFIG_EFI) += efi/ |
27 | obj-$(CONFIG_UEFI_CPER) += efi/ | 28 | obj-$(CONFIG_UEFI_CPER) += efi/ |
diff --git a/drivers/firmware/meson/Kconfig b/drivers/firmware/meson/Kconfig new file mode 100644 index 000000000000..170d7e8bcdfb --- /dev/null +++ b/drivers/firmware/meson/Kconfig | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Amlogic Secure Monitor driver | ||
3 | # | ||
4 | config MESON_SM | ||
5 | bool | ||
6 | default ARCH_MESON | ||
7 | depends on ARM64_4K_PAGES | ||
8 | help | ||
9 | Say y here to enable the Amlogic secure monitor driver | ||
diff --git a/drivers/firmware/meson/Makefile b/drivers/firmware/meson/Makefile new file mode 100644 index 000000000000..9ab3884f96bc --- /dev/null +++ b/drivers/firmware/meson/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_MESON_SM) += meson_sm.o | |||
diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c new file mode 100644 index 000000000000..b0d254930ed3 --- /dev/null +++ b/drivers/firmware/meson/meson_sm.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * Amlogic Secure Monitor driver | ||
3 | * | ||
4 | * Copyright (C) 2016 Endless Mobile, Inc. | ||
5 | * Author: Carlo Caione <carlo@endlessm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * You should have received a copy of the GNU General Public License | ||
12 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
13 | */ | ||
14 | |||
15 | #define pr_fmt(fmt) "meson-sm: " fmt | ||
16 | |||
17 | #include <linux/arm-smccc.h> | ||
18 | #include <linux/bug.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/of_device.h> | ||
22 | #include <linux/printk.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/sizes.h> | ||
25 | |||
26 | #include <linux/firmware/meson/meson_sm.h> | ||
27 | |||
28 | struct meson_sm_cmd { | ||
29 | unsigned int index; | ||
30 | u32 smc_id; | ||
31 | }; | ||
32 | #define CMD(d, s) { .index = (d), .smc_id = (s), } | ||
33 | |||
34 | struct meson_sm_chip { | ||
35 | unsigned int shmem_size; | ||
36 | u32 cmd_shmem_in_base; | ||
37 | u32 cmd_shmem_out_base; | ||
38 | struct meson_sm_cmd cmd[]; | ||
39 | }; | ||
40 | |||
41 | struct meson_sm_chip gxbb_chip = { | ||
42 | .shmem_size = SZ_4K, | ||
43 | .cmd_shmem_in_base = 0x82000020, | ||
44 | .cmd_shmem_out_base = 0x82000021, | ||
45 | .cmd = { | ||
46 | CMD(SM_EFUSE_READ, 0x82000030), | ||
47 | CMD(SM_EFUSE_WRITE, 0x82000031), | ||
48 | CMD(SM_EFUSE_USER_MAX, 0x82000033), | ||
49 | { /* sentinel */ }, | ||
50 | }, | ||
51 | }; | ||
52 | |||
53 | struct meson_sm_firmware { | ||
54 | const struct meson_sm_chip *chip; | ||
55 | void __iomem *sm_shmem_in_base; | ||
56 | void __iomem *sm_shmem_out_base; | ||
57 | }; | ||
58 | |||
59 | static struct meson_sm_firmware fw; | ||
60 | |||
61 | static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip, | ||
62 | unsigned int cmd_index) | ||
63 | { | ||
64 | const struct meson_sm_cmd *cmd = chip->cmd; | ||
65 | |||
66 | while (cmd->smc_id && cmd->index != cmd_index) | ||
67 | cmd++; | ||
68 | |||
69 | return cmd->smc_id; | ||
70 | } | ||
71 | |||
72 | static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2, | ||
73 | u32 arg3, u32 arg4) | ||
74 | { | ||
75 | struct arm_smccc_res res; | ||
76 | |||
77 | arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res); | ||
78 | return res.a0; | ||
79 | } | ||
80 | |||
81 | static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) | ||
82 | { | ||
83 | u32 sm_phy_base; | ||
84 | |||
85 | sm_phy_base = __meson_sm_call(cmd_shmem, 0, 0, 0, 0, 0); | ||
86 | if (!sm_phy_base) | ||
87 | return 0; | ||
88 | |||
89 | return ioremap_cache(sm_phy_base, size); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * meson_sm_call - generic SMC32 call to the secure-monitor | ||
94 | * | ||
95 | * @cmd_index: Index of the SMC32 function ID | ||
96 | * @ret: Returned value | ||
97 | * @arg0: SMC32 Argument 0 | ||
98 | * @arg1: SMC32 Argument 1 | ||
99 | * @arg2: SMC32 Argument 2 | ||
100 | * @arg3: SMC32 Argument 3 | ||
101 | * @arg4: SMC32 Argument 4 | ||
102 | * | ||
103 | * Return: 0 on success, a negative value on error | ||
104 | */ | ||
105 | int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, | ||
106 | u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||
107 | { | ||
108 | u32 cmd, lret; | ||
109 | |||
110 | if (!fw.chip) | ||
111 | return -ENOENT; | ||
112 | |||
113 | cmd = meson_sm_get_cmd(fw.chip, cmd_index); | ||
114 | if (!cmd) | ||
115 | return -EINVAL; | ||
116 | |||
117 | lret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4); | ||
118 | |||
119 | if (ret) | ||
120 | *ret = lret; | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | EXPORT_SYMBOL(meson_sm_call); | ||
125 | |||
126 | /** | ||
127 | * meson_sm_call_read - retrieve data from secure-monitor | ||
128 | * | ||
129 | * @buffer: Buffer to store the retrieved data | ||
130 | * @cmd_index: Index of the SMC32 function ID | ||
131 | * @arg0: SMC32 Argument 0 | ||
132 | * @arg1: SMC32 Argument 1 | ||
133 | * @arg2: SMC32 Argument 2 | ||
134 | * @arg3: SMC32 Argument 3 | ||
135 | * @arg4: SMC32 Argument 4 | ||
136 | * | ||
137 | * Return: size of read data on success, a negative value on error | ||
138 | */ | ||
139 | int meson_sm_call_read(void *buffer, unsigned int cmd_index, u32 arg0, | ||
140 | u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||
141 | { | ||
142 | u32 size; | ||
143 | |||
144 | if (!fw.chip) | ||
145 | return -ENOENT; | ||
146 | |||
147 | if (!fw.chip->cmd_shmem_out_base) | ||
148 | return -EINVAL; | ||
149 | |||
150 | if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) | ||
151 | return -EINVAL; | ||
152 | |||
153 | if (!size || size > fw.chip->shmem_size) | ||
154 | return -EINVAL; | ||
155 | |||
156 | if (buffer) | ||
157 | memcpy(buffer, fw.sm_shmem_out_base, size); | ||
158 | |||
159 | return size; | ||
160 | } | ||
161 | EXPORT_SYMBOL(meson_sm_call_read); | ||
162 | |||
163 | /** | ||
164 | * meson_sm_call_write - send data to secure-monitor | ||
165 | * | ||
166 | * @buffer: Buffer containing data to send | ||
167 | * @size: Size of the data to send | ||
168 | * @cmd_index: Index of the SMC32 function ID | ||
169 | * @arg0: SMC32 Argument 0 | ||
170 | * @arg1: SMC32 Argument 1 | ||
171 | * @arg2: SMC32 Argument 2 | ||
172 | * @arg3: SMC32 Argument 3 | ||
173 | * @arg4: SMC32 Argument 4 | ||
174 | * | ||
175 | * Return: size of sent data on success, a negative value on error | ||
176 | */ | ||
177 | int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, | ||
178 | u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) | ||
179 | { | ||
180 | u32 written; | ||
181 | |||
182 | if (!fw.chip) | ||
183 | return -ENOENT; | ||
184 | |||
185 | if (size > fw.chip->shmem_size) | ||
186 | return -EINVAL; | ||
187 | |||
188 | if (!fw.chip->cmd_shmem_in_base) | ||
189 | return -EINVAL; | ||
190 | |||
191 | memcpy(fw.sm_shmem_in_base, buffer, size); | ||
192 | |||
193 | if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) | ||
194 | return -EINVAL; | ||
195 | |||
196 | if (!written) | ||
197 | return -EINVAL; | ||
198 | |||
199 | return written; | ||
200 | } | ||
201 | EXPORT_SYMBOL(meson_sm_call_write); | ||
202 | |||
203 | static const struct of_device_id meson_sm_ids[] = { | ||
204 | { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip }, | ||
205 | { /* sentinel */ }, | ||
206 | }; | ||
207 | |||
208 | int __init meson_sm_init(void) | ||
209 | { | ||
210 | const struct meson_sm_chip *chip; | ||
211 | const struct of_device_id *matched_np; | ||
212 | struct device_node *np; | ||
213 | |||
214 | np = of_find_matching_node_and_match(NULL, meson_sm_ids, &matched_np); | ||
215 | if (!np) | ||
216 | return -ENODEV; | ||
217 | |||
218 | chip = matched_np->data; | ||
219 | if (!chip) { | ||
220 | pr_err("unable to setup secure-monitor data\n"); | ||
221 | goto out; | ||
222 | } | ||
223 | |||
224 | if (chip->cmd_shmem_in_base) { | ||
225 | fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, | ||
226 | chip->shmem_size); | ||
227 | if (WARN_ON(!fw.sm_shmem_in_base)) | ||
228 | goto out; | ||
229 | } | ||
230 | |||
231 | if (chip->cmd_shmem_out_base) { | ||
232 | fw.sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base, | ||
233 | chip->shmem_size); | ||
234 | if (WARN_ON(!fw.sm_shmem_out_base)) | ||
235 | goto out_in_base; | ||
236 | } | ||
237 | |||
238 | fw.chip = chip; | ||
239 | pr_info("secure-monitor enabled\n"); | ||
240 | |||
241 | return 0; | ||
242 | |||
243 | out_in_base: | ||
244 | iounmap(fw.sm_shmem_in_base); | ||
245 | out: | ||
246 | return -EINVAL; | ||
247 | } | ||
248 | device_initcall(meson_sm_init); | ||
diff --git a/include/linux/firmware/meson/meson_sm.h b/include/linux/firmware/meson/meson_sm.h new file mode 100644 index 000000000000..8e953c6f394a --- /dev/null +++ b/include/linux/firmware/meson/meson_sm.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 Endless Mobile, Inc. | ||
3 | * Author: Carlo Caione <carlo@endlessm.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * version 2 as published by the Free Software Foundation. | ||
8 | * | ||
9 | * You should have received a copy of the GNU General Public License | ||
10 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
11 | */ | ||
12 | |||
13 | #ifndef _MESON_SM_FW_H_ | ||
14 | #define _MESON_SM_FW_H_ | ||
15 | |||
16 | enum { | ||
17 | SM_EFUSE_READ, | ||
18 | SM_EFUSE_WRITE, | ||
19 | SM_EFUSE_USER_MAX, | ||
20 | }; | ||
21 | |||
22 | struct meson_sm_firmware; | ||
23 | |||
24 | int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, u32 arg1, | ||
25 | u32 arg2, u32 arg3, u32 arg4); | ||
26 | int meson_sm_call_write(void *buffer, unsigned int b_size, unsigned int cmd_index, | ||
27 | u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); | ||
28 | int meson_sm_call_read(void *buffer, unsigned int cmd_index, u32 arg0, u32 arg1, | ||
29 | u32 arg2, u32 arg3, u32 arg4); | ||
30 | |||
31 | #endif /* _MESON_SM_FW_H_ */ | ||