• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 Eugeniu Rosca <rosca.eugeniu@gmail.com>
4  *
5  * Command to read/modify/write Android BCB fields
6  */
7 
8 #include <android_bootloader_message.h>
9 #include <command.h>
10 #include <common.h>
11 
12 enum bcb_cmd {
13 	BCB_CMD_LOAD,
14 	BCB_CMD_FIELD_SET,
15 	BCB_CMD_FIELD_CLEAR,
16 	BCB_CMD_FIELD_TEST,
17 	BCB_CMD_FIELD_DUMP,
18 	BCB_CMD_STORE,
19 };
20 
21 static int bcb_dev = -1;
22 static int bcb_part = -1;
23 static struct bootloader_message bcb = { { 0 } };
24 
bcb_cmd_get(char * cmd)25 static int bcb_cmd_get(char *cmd)
26 {
27 	if (!strcmp(cmd, "load"))
28 		return BCB_CMD_LOAD;
29 	if (!strcmp(cmd, "set"))
30 		return BCB_CMD_FIELD_SET;
31 	if (!strcmp(cmd, "clear"))
32 		return BCB_CMD_FIELD_CLEAR;
33 	if (!strcmp(cmd, "test"))
34 		return BCB_CMD_FIELD_TEST;
35 	if (!strcmp(cmd, "store"))
36 		return BCB_CMD_STORE;
37 	if (!strcmp(cmd, "dump"))
38 		return BCB_CMD_FIELD_DUMP;
39 	else
40 		return -1;
41 }
42 
bcb_is_misused(int argc,char * const argv[])43 static int bcb_is_misused(int argc, char *const argv[])
44 {
45 	int cmd = bcb_cmd_get(argv[0]);
46 
47 	switch (cmd) {
48 	case BCB_CMD_LOAD:
49 	case BCB_CMD_FIELD_SET:
50 		if (argc != 3)
51 			goto err;
52 		break;
53 	case BCB_CMD_FIELD_TEST:
54 		if (argc != 4)
55 			goto err;
56 		break;
57 	case BCB_CMD_FIELD_CLEAR:
58 		if (argc != 1 && argc != 2)
59 			goto err;
60 		break;
61 	case BCB_CMD_STORE:
62 		if (argc != 1)
63 			goto err;
64 		break;
65 	case BCB_CMD_FIELD_DUMP:
66 		if (argc != 2)
67 			goto err;
68 		break;
69 	default:
70 		printf("Error: 'bcb %s' not supported\n", argv[0]);
71 		return -1;
72 	}
73 
74 	if (cmd != BCB_CMD_LOAD && (bcb_dev < 0 || bcb_part < 0)) {
75 		printf("Error: Please, load BCB first!\n");
76 		return -1;
77 	}
78 
79 	return 0;
80 err:
81 	printf("Error: Bad usage of 'bcb %s'\n", argv[0]);
82 
83 	return -1;
84 }
85 
bcb_field_get(char * name,char ** fieldp,int * sizep)86 static int bcb_field_get(char *name, char **fieldp, int *sizep)
87 {
88 	if (!strcmp(name, "command")) {
89 		*fieldp = bcb.command;
90 		*sizep = sizeof(bcb.command);
91 	} else if (!strcmp(name, "status")) {
92 		*fieldp = bcb.status;
93 		*sizep = sizeof(bcb.status);
94 	} else if (!strcmp(name, "recovery")) {
95 		*fieldp = bcb.recovery;
96 		*sizep = sizeof(bcb.recovery);
97 	} else if (!strcmp(name, "stage")) {
98 		*fieldp = bcb.stage;
99 		*sizep = sizeof(bcb.stage);
100 	} else if (!strcmp(name, "reserved")) {
101 		*fieldp = bcb.reserved;
102 		*sizep = sizeof(bcb.reserved);
103 	} else {
104 		printf("Error: Unknown bcb field '%s'\n", name);
105 		return -1;
106 	}
107 
108 	return 0;
109 }
110 
do_bcb_load(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])111 static int do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc,
112 		       char * const argv[])
113 {
114 	struct blk_desc *desc;
115 	disk_partition_t info;
116 	u64 cnt;
117 	char *endp;
118 	int part, ret;
119 
120 	ret = blk_get_device_by_str("mmc", argv[1], &desc);
121 	if (ret < 0)
122 		goto err_read_fail;
123 
124 	part = simple_strtoul(argv[2], &endp, 0);
125 	if (*endp == '\0') {
126 		ret = part_get_info(desc, part, &info);
127 		if (ret)
128 			goto err_read_fail;
129 	} else {
130 		part = part_get_info_by_name(desc, argv[2], &info);
131 		if (part < 0) {
132 			ret = part;
133 			goto err_read_fail;
134 		}
135 	}
136 
137 	cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz);
138 	if (cnt > info.size)
139 		goto err_too_small;
140 
141 	if (blk_dread(desc, info.start, cnt, &bcb) != cnt) {
142 		ret = -EIO;
143 		goto err_read_fail;
144 	}
145 
146 	bcb_dev = desc->devnum;
147 	bcb_part = part;
148 	debug("%s: Loaded from mmc %d:%d\n", __func__, bcb_dev, bcb_part);
149 
150 	return CMD_RET_SUCCESS;
151 err_read_fail:
152 	printf("Error: mmc %s:%s read failed (%d)\n", argv[1], argv[2], ret);
153 	goto err;
154 err_too_small:
155 	printf("Error: mmc %s:%s too small!", argv[1], argv[2]);
156 	goto err;
157 err:
158 	bcb_dev = -1;
159 	bcb_part = -1;
160 
161 	return CMD_RET_FAILURE;
162 }
163 
do_bcb_set(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])164 static int do_bcb_set(cmd_tbl_t *cmdtp, int flag, int argc,
165 		      char * const argv[])
166 {
167 	int size, len;
168 	char *field, *str, *found;
169 
170 	if (bcb_field_get(argv[1], &field, &size))
171 		return CMD_RET_FAILURE;
172 
173 	len = strlen(argv[2]);
174 	if (len >= size) {
175 		printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
176 		       argv[2], len, size, argv[1]);
177 		return CMD_RET_FAILURE;
178 	}
179 	str = argv[2];
180 
181 	field[0] = '\0';
182 	while ((found = strsep(&str, ":"))) {
183 		if (field[0] != '\0')
184 			strcat(field, "\n");
185 		strcat(field, found);
186 	}
187 
188 	return CMD_RET_SUCCESS;
189 }
190 
do_bcb_clear(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])191 static int do_bcb_clear(cmd_tbl_t *cmdtp, int flag, int argc,
192 			char * const argv[])
193 {
194 	int size;
195 	char *field;
196 
197 	if (argc == 1) {
198 		memset(&bcb, 0, sizeof(bcb));
199 		return CMD_RET_SUCCESS;
200 	}
201 
202 	if (bcb_field_get(argv[1], &field, &size))
203 		return CMD_RET_FAILURE;
204 
205 	memset(field, 0, size);
206 
207 	return CMD_RET_SUCCESS;
208 }
209 
do_bcb_test(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])210 static int do_bcb_test(cmd_tbl_t *cmdtp, int flag, int argc,
211 		       char * const argv[])
212 {
213 	int size;
214 	char *field;
215 	char *op = argv[2];
216 
217 	if (bcb_field_get(argv[1], &field, &size))
218 		return CMD_RET_FAILURE;
219 
220 	if (*op == '=' && *(op + 1) == '\0') {
221 		if (!strncmp(argv[3], field, size))
222 			return CMD_RET_SUCCESS;
223 		else
224 			return CMD_RET_FAILURE;
225 	} else if (*op == '~' && *(op + 1) == '\0') {
226 		if (!strstr(field, argv[3]))
227 			return CMD_RET_FAILURE;
228 		else
229 			return CMD_RET_SUCCESS;
230 	} else {
231 		printf("Error: Unknown operator '%s'\n", op);
232 	}
233 
234 	return CMD_RET_FAILURE;
235 }
236 
do_bcb_dump(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])237 static int do_bcb_dump(cmd_tbl_t *cmdtp, int flag, int argc,
238 		       char * const argv[])
239 {
240 	int size;
241 	char *field;
242 
243 	if (bcb_field_get(argv[1], &field, &size))
244 		return CMD_RET_FAILURE;
245 
246 	print_buffer((ulong)field - (ulong)&bcb, (void *)field, 1, size, 16);
247 
248 	return CMD_RET_SUCCESS;
249 }
250 
do_bcb_store(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])251 static int do_bcb_store(cmd_tbl_t *cmdtp, int flag, int argc,
252 			char * const argv[])
253 {
254 	struct blk_desc *desc;
255 	disk_partition_t info;
256 	u64 cnt;
257 	int ret;
258 
259 	desc = blk_get_devnum_by_type(IF_TYPE_MMC, bcb_dev);
260 	if (!desc) {
261 		ret = -ENODEV;
262 		goto err;
263 	}
264 
265 	ret = part_get_info(desc, bcb_part, &info);
266 	if (ret)
267 		goto err;
268 
269 	cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz);
270 
271 	if (blk_dwrite(desc, info.start, cnt, &bcb) != cnt) {
272 		ret = -EIO;
273 		goto err;
274 	}
275 
276 	return CMD_RET_SUCCESS;
277 err:
278 	printf("Error: mmc %d:%d write failed (%d)\n", bcb_dev, bcb_part, ret);
279 
280 	return CMD_RET_FAILURE;
281 }
282 
283 static cmd_tbl_t cmd_bcb_sub[] = {
284 	U_BOOT_CMD_MKENT(load, CONFIG_SYS_MAXARGS, 1, do_bcb_load, "", ""),
285 	U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bcb_set, "", ""),
286 	U_BOOT_CMD_MKENT(clear, CONFIG_SYS_MAXARGS, 1, do_bcb_clear, "", ""),
287 	U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_bcb_test, "", ""),
288 	U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_bcb_dump, "", ""),
289 	U_BOOT_CMD_MKENT(store, CONFIG_SYS_MAXARGS, 1, do_bcb_store, "", ""),
290 };
291 
do_bcb(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])292 static int do_bcb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
293 {
294 	cmd_tbl_t *c;
295 
296 	if (argc < 2)
297 		return CMD_RET_USAGE;
298 
299 	argc--;
300 	argv++;
301 
302 	c = find_cmd_tbl(argv[0], cmd_bcb_sub, ARRAY_SIZE(cmd_bcb_sub));
303 	if (!c)
304 		return CMD_RET_USAGE;
305 
306 	if (bcb_is_misused(argc, argv)) {
307 		/*
308 		 * We try to improve the user experience by reporting the
309 		 * root-cause of misusage, so don't return CMD_RET_USAGE,
310 		 * since the latter prints out the full-blown help text
311 		 */
312 		return CMD_RET_FAILURE;
313 	}
314 
315 	return c->cmd(cmdtp, flag, argc, argv);
316 }
317 
318 U_BOOT_CMD(
319 	bcb, CONFIG_SYS_MAXARGS, 1, do_bcb,
320 	"Load/set/clear/test/dump/store Android BCB fields",
321 	"load  <dev> <part>       - load  BCB from mmc <dev>:<part>\n"
322 	"bcb set   <field> <val>      - set   BCB <field> to <val>\n"
323 	"bcb clear [<field>]          - clear BCB <field> or all fields\n"
324 	"bcb test  <field> <op> <val> - test  BCB <field> against <val>\n"
325 	"bcb dump  <field>            - dump  BCB <field>\n"
326 	"bcb store                    - store BCB back to mmc\n"
327 	"\n"
328 	"Legend:\n"
329 	"<dev>   - MMC device index containing the BCB partition\n"
330 	"<part>  - MMC partition index or name containing the BCB\n"
331 	"<field> - one of {command,status,recovery,stage,reserved}\n"
332 	"<op>    - the binary operator used in 'bcb test':\n"
333 	"          '=' returns true if <val> matches the string stored in <field>\n"
334 	"          '~' returns true if <val> matches a subset of <field>'s string\n"
335 	"<val>   - string/text provided as input to bcb {set,test}\n"
336 	"          NOTE: any ':' character in <val> will be replaced by line feed\n"
337 	"          during 'bcb set' and used as separator by upper layers\n"
338 );
339