1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2016 Beniamino Galvani <b.galvani@gmail.com>
4  *
5  * Secure monitor calls.
6  */
7 
8 #include <common.h>
9 #include <command.h>
10 #include <env.h>
11 #include <log.h>
12 #include <asm/arch/sm.h>
13 #include <asm/cache.h>
14 #include <asm/global_data.h>
15 #include <asm/ptrace.h>
16 #include <linux/bitops.h>
17 #include <linux/err.h>
18 #include <linux/kernel.h>
19 #include <dm.h>
20 #include <linux/bitfield.h>
21 #include <regmap.h>
22 #include <syscon.h>
23 
24 #define FN_GET_SHARE_MEM_INPUT_BASE	0x82000020
25 #define FN_GET_SHARE_MEM_OUTPUT_BASE	0x82000021
26 #define FN_EFUSE_READ			0x82000030
27 #define FN_EFUSE_WRITE			0x82000031
28 #define FN_CHIP_ID			0x82000044
29 
30 static void *shmem_input;
31 static void *shmem_output;
32 
meson_init_shmem(void)33 static void meson_init_shmem(void)
34 {
35 	struct pt_regs regs;
36 
37 	if (shmem_input && shmem_output)
38 		return;
39 
40 	regs.regs[0] = FN_GET_SHARE_MEM_INPUT_BASE;
41 	smc_call(&regs);
42 	shmem_input = (void *)regs.regs[0];
43 
44 	regs.regs[0] = FN_GET_SHARE_MEM_OUTPUT_BASE;
45 	smc_call(&regs);
46 	shmem_output = (void *)regs.regs[0];
47 
48 	debug("Secure Monitor shmem: 0x%p 0x%p\n", shmem_input, shmem_output);
49 }
50 
meson_sm_read_efuse(uintptr_t offset,void * buffer,size_t size)51 ssize_t meson_sm_read_efuse(uintptr_t offset, void *buffer, size_t size)
52 {
53 	struct pt_regs regs;
54 
55 	meson_init_shmem();
56 
57 	regs.regs[0] = FN_EFUSE_READ;
58 	regs.regs[1] = offset;
59 	regs.regs[2] = size;
60 
61 	smc_call(&regs);
62 
63 	if (regs.regs[0] == 0)
64 		return -1;
65 
66 	memcpy(buffer, shmem_output, min(size, regs.regs[0]));
67 
68 	return regs.regs[0];
69 }
70 
71 #define SM_CHIP_ID_LENGTH	119
72 #define SM_CHIP_ID_OFFSET	4
73 #define SM_CHIP_ID_SIZE		12
74 
meson_sm_get_serial(void * buffer,size_t size)75 int meson_sm_get_serial(void *buffer, size_t size)
76 {
77 	struct pt_regs regs;
78 
79 	meson_init_shmem();
80 
81 	regs.regs[0] = FN_CHIP_ID;
82 	regs.regs[1] = 0;
83 	regs.regs[2] = 0;
84 
85 	smc_call(&regs);
86 
87 	memcpy(buffer, shmem_output + SM_CHIP_ID_OFFSET,
88 	       min_t(size_t, size, SM_CHIP_ID_SIZE));
89 
90 	return 0;
91 }
92 
93 #define AO_SEC_SD_CFG15		0xfc
94 #define REBOOT_REASON_MASK	GENMASK(15, 12)
95 
meson_sm_get_reboot_reason(void)96 int meson_sm_get_reboot_reason(void)
97 {
98 	struct regmap *regmap;
99 	int nodeoffset;
100 	ofnode node;
101 	unsigned int reason;
102 
103 	/* find the offset of compatible node */
104 	nodeoffset = fdt_node_offset_by_compatible(gd->fdt_blob, -1,
105 						   "amlogic,meson-gx-ao-secure");
106 	if (nodeoffset < 0) {
107 		printf("%s: failed to get amlogic,meson-gx-ao-secure\n",
108 		       __func__);
109 		return -ENODEV;
110 	}
111 
112 	/* get regmap from the syscon node */
113 	node = offset_to_ofnode(nodeoffset);
114 	regmap = syscon_node_to_regmap(node);
115 	if (IS_ERR(regmap)) {
116 		printf("%s: failed to get regmap\n", __func__);
117 		return -EINVAL;
118 	}
119 
120 	regmap_read(regmap, AO_SEC_SD_CFG15, &reason);
121 
122 	/* The SMC call is not used, we directly use AO_SEC_SD_CFG15 */
123 	return FIELD_GET(REBOOT_REASON_MASK, reason);
124 }
125 
do_sm_serial(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])126 static int do_sm_serial(struct cmd_tbl *cmdtp, int flag, int argc,
127 			char *const argv[])
128 {
129 	ulong address;
130 	int ret;
131 
132 	if (argc < 2)
133 		return CMD_RET_USAGE;
134 
135 	address = simple_strtoul(argv[1], NULL, 0);
136 
137 	ret = meson_sm_get_serial((void *)address, SM_CHIP_ID_SIZE);
138 	if (ret)
139 		return CMD_RET_FAILURE;
140 
141 	return CMD_RET_SUCCESS;
142 }
143 
144 #define MAX_REBOOT_REASONS 14
145 
146 static const char *reboot_reasons[MAX_REBOOT_REASONS] = {
147 	[REBOOT_REASON_COLD] = "cold_boot",
148 	[REBOOT_REASON_NORMAL] = "normal",
149 	[REBOOT_REASON_RECOVERY] = "recovery",
150 	[REBOOT_REASON_UPDATE] = "update",
151 	[REBOOT_REASON_FASTBOOT] = "fastboot",
152 	[REBOOT_REASON_SUSPEND_OFF] = "suspend_off",
153 	[REBOOT_REASON_HIBERNATE] = "hibernate",
154 	[REBOOT_REASON_BOOTLOADER] = "bootloader",
155 	[REBOOT_REASON_SHUTDOWN_REBOOT] = "shutdown_reboot",
156 	[REBOOT_REASON_RPMBP] = "rpmbp",
157 	[REBOOT_REASON_CRASH_DUMP] = "crash_dump",
158 	[REBOOT_REASON_KERNEL_PANIC] = "kernel_panic",
159 	[REBOOT_REASON_WATCHDOG_REBOOT] = "watchdog_reboot",
160 };
161 
do_sm_reboot_reason(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])162 static int do_sm_reboot_reason(struct cmd_tbl *cmdtp, int flag, int argc,
163 			       char *const argv[])
164 {
165 	const char *reason_str;
166 	char *destarg = NULL;
167 	int reason;
168 
169 	if (argc > 1)
170 		destarg = argv[1];
171 
172 	reason = meson_sm_get_reboot_reason();
173 	if (reason < 0)
174 		return CMD_RET_FAILURE;
175 
176 	if (reason >= MAX_REBOOT_REASONS ||
177 	    !reboot_reasons[reason])
178 		reason_str = "unknown";
179 	else
180 		reason_str = reboot_reasons[reason];
181 
182 	if (destarg)
183 		env_set(destarg, reason_str);
184 	else
185 		printf("reboot reason: %s (%x)\n", reason_str, reason);
186 
187 	return CMD_RET_SUCCESS;
188 }
189 
190 static struct cmd_tbl cmd_sm_sub[] = {
191 	U_BOOT_CMD_MKENT(serial, 2, 1, do_sm_serial, "", ""),
192 	U_BOOT_CMD_MKENT(reboot_reason, 1, 1, do_sm_reboot_reason, "", ""),
193 };
194 
do_sm(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])195 static int do_sm(struct cmd_tbl *cmdtp, int flag, int argc,
196 		 char *const argv[])
197 {
198 	struct cmd_tbl *c;
199 
200 	if (argc < 2)
201 		return CMD_RET_USAGE;
202 
203 	/* Strip off leading 'sm' command argument */
204 	argc--;
205 	argv++;
206 
207 	c = find_cmd_tbl(argv[0], &cmd_sm_sub[0], ARRAY_SIZE(cmd_sm_sub));
208 
209 	if (c)
210 		return c->cmd(cmdtp, flag, argc, argv);
211 	else
212 		return CMD_RET_USAGE;
213 }
214 
215 U_BOOT_CMD(
216 	sm, 5, 0, do_sm,
217 	"Secure Monitor Control",
218 	"serial <address> - read chip unique id to memory address\n"
219 	"sm reboot_reason [name] - get reboot reason and store to to environment"
220 );
221