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