]> Git Repo - J-u-boot.git/blob - cmd/bcb.c
cmd: bcb: rework the command to U_BOOT_LONGHELP approach
[J-u-boot.git] / cmd / bcb.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 Eugeniu Rosca <[email protected]>
4  *
5  * Command to read/modify/write Android BCB fields
6  */
7
8 #include <android_bootloader_message.h>
9 #include <bcb.h>
10 #include <command.h>
11 #include <display_options.h>
12 #include <log.h>
13 #include <part.h>
14 #include <malloc.h>
15 #include <memalign.h>
16 #include <vsprintf.h>
17 #include <linux/err.h>
18
19 static const char * const fields[] = {
20         "command",
21         "status",
22         "recovery",
23         "stage"
24 };
25
26 static struct bootloader_message bcb __aligned(ARCH_DMA_MINALIGN) = { { 0 } };
27 static struct disk_partition partition_data;
28
29 static struct blk_desc *block;
30 static struct disk_partition *partition = &partition_data;
31
32 static int bcb_not_loaded(void)
33 {
34         printf("Error: Please, load BCB first!\n");
35         return -1;
36 }
37
38 static int bcb_field_get(const char *name, char **fieldp, int *sizep)
39 {
40         if (!strcmp(name, "command")) {
41                 *fieldp = bcb.command;
42                 *sizep = sizeof(bcb.command);
43         } else if (!strcmp(name, "status")) {
44                 *fieldp = bcb.status;
45                 *sizep = sizeof(bcb.status);
46         } else if (!strcmp(name, "recovery")) {
47                 *fieldp = bcb.recovery;
48                 *sizep = sizeof(bcb.recovery);
49         } else if (!strcmp(name, "stage")) {
50                 *fieldp = bcb.stage;
51                 *sizep = sizeof(bcb.stage);
52         } else if (!strcmp(name, "reserved")) {
53                 *fieldp = bcb.reserved;
54                 *sizep = sizeof(bcb.reserved);
55         } else {
56                 printf("Error: Unknown bcb field '%s'\n", name);
57                 return -1;
58         }
59
60         return 0;
61 }
62
63 static void __bcb_reset(void)
64 {
65         block = NULL;
66         partition = &partition_data;
67         memset(&partition_data, 0, sizeof(struct disk_partition));
68         memset(&bcb, 0, sizeof(struct bootloader_message));
69 }
70
71 static int __bcb_initialize(const char *iface, int devnum, const char *partp)
72 {
73         char *endp;
74         int part, ret;
75
76         block = blk_get_dev(iface, devnum);
77         if (!block) {
78                 ret = -ENODEV;
79                 goto err_read_fail;
80         }
81
82         /*
83          * always select the first hwpart in case another
84          * blk operation selected a different hwpart
85          */
86         ret = blk_dselect_hwpart(block, 0);
87         if (IS_ERR_VALUE(ret)) {
88                 ret = -ENODEV;
89                 goto err_read_fail;
90         }
91
92         part = simple_strtoul(partp, &endp, 0);
93         if (*endp == '\0') {
94                 ret = part_get_info(block, part, partition);
95                 if (ret)
96                         goto err_read_fail;
97         } else {
98                 part = part_get_info_by_name(block, partp, partition);
99                 if (part < 0) {
100                         ret = part;
101                         goto err_read_fail;
102                 }
103         }
104
105         return CMD_RET_SUCCESS;
106
107 err_read_fail:
108         printf("Error: %s %d:%s read failed (%d)\n", iface, devnum,
109                partition->name, ret);
110         __bcb_reset();
111         return CMD_RET_FAILURE;
112 }
113
114 static int __bcb_load(void)
115 {
116         u64 cnt;
117         int ret;
118
119         cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), partition->blksz);
120         if (cnt > partition->size)
121                 goto err_too_small;
122
123         if (blk_dread(block, partition->start, cnt, &bcb) != cnt) {
124                 ret = -EIO;
125                 goto err_read_fail;
126         }
127
128         debug("%s: Loaded from %d %d:%s\n", __func__, block->uclass_id,
129               block->devnum, partition->name);
130
131         return CMD_RET_SUCCESS;
132 err_read_fail:
133         printf("Error: %d %d:%s read failed (%d)\n", block->uclass_id,
134                block->devnum, partition->name, ret);
135         goto err;
136 err_too_small:
137         printf("Error: %d %d:%s too small!", block->uclass_id,
138                block->devnum, partition->name);
139 err:
140         __bcb_reset();
141         return CMD_RET_FAILURE;
142 }
143
144 static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
145                        char * const argv[])
146 {
147         int ret;
148         int devnum;
149         char *endp;
150         char *iface = "mmc";
151
152         if (argc < 3)
153                 return CMD_RET_USAGE;
154
155         if (argc == 4) {
156                 iface = argv[1];
157                 argc--;
158                 argv++;
159         }
160
161         devnum = simple_strtoul(argv[1], &endp, 0);
162         if (*endp != '\0') {
163                 printf("Error: Device id '%s' not a number\n", argv[1]);
164                 return CMD_RET_FAILURE;
165         }
166
167         ret = __bcb_initialize(iface, devnum, argv[2]);
168         if (ret != CMD_RET_SUCCESS)
169                 return ret;
170
171         return __bcb_load();
172 }
173
174 static int __bcb_set(const char *fieldp, const char *valp)
175 {
176         int size, len;
177         char *field, *str, *found, *tmp;
178
179         if (bcb_field_get(fieldp, &field, &size))
180                 return CMD_RET_FAILURE;
181
182         len = strlen(valp);
183         if (len >= size) {
184                 printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
185                        valp, len, size, fieldp);
186                 return CMD_RET_FAILURE;
187         }
188         str = strdup(valp);
189         if (!str) {
190                 printf("Error: Out of memory while strdup\n");
191                 return CMD_RET_FAILURE;
192         }
193
194         tmp = str;
195         field[0] = '\0';
196         while ((found = strsep(&tmp, ":"))) {
197                 if (field[0] != '\0')
198                         strcat(field, "\n");
199                 strcat(field, found);
200         }
201         free(str);
202
203         return CMD_RET_SUCCESS;
204 }
205
206 static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc,
207                       char * const argv[])
208 {
209         if (argc < 3)
210                 return CMD_RET_USAGE;
211
212         if (!block)
213                 return bcb_not_loaded();
214
215         return __bcb_set(argv[1], argv[2]);
216 }
217
218 static int do_bcb_clear(struct cmd_tbl *cmdtp, int flag, int argc,
219                         char *const argv[])
220 {
221         int size;
222         char *field;
223
224         if (!block)
225                 return bcb_not_loaded();
226
227         if (argc == 1) {
228                 memset(&bcb, 0, sizeof(bcb));
229                 return CMD_RET_SUCCESS;
230         }
231
232         if (bcb_field_get(argv[1], &field, &size))
233                 return CMD_RET_FAILURE;
234
235         memset(field, 0, size);
236
237         return CMD_RET_SUCCESS;
238 }
239
240 static int do_bcb_test(struct cmd_tbl *cmdtp, int flag, int argc,
241                        char *const argv[])
242 {
243         int size;
244         char *field;
245         char *op;
246
247         if (argc < 4)
248                 return CMD_RET_USAGE;
249
250         if (!block)
251                 return bcb_not_loaded();
252
253         op = argv[2];
254
255         if (bcb_field_get(argv[1], &field, &size))
256                 return CMD_RET_FAILURE;
257
258         if (*op == '=' && *(op + 1) == '\0') {
259                 if (!strncmp(argv[3], field, size))
260                         return CMD_RET_SUCCESS;
261                 else
262                         return CMD_RET_FAILURE;
263         } else if (*op == '~' && *(op + 1) == '\0') {
264                 if (!strstr(field, argv[3]))
265                         return CMD_RET_FAILURE;
266                 else
267                         return CMD_RET_SUCCESS;
268         } else {
269                 printf("Error: Unknown operator '%s'\n", op);
270         }
271
272         return CMD_RET_FAILURE;
273 }
274
275 static int do_bcb_dump(struct cmd_tbl *cmdtp, int flag, int argc,
276                        char *const argv[])
277 {
278         int size;
279         char *field;
280
281         if (argc < 2)
282                 return CMD_RET_USAGE;
283
284         if (!block)
285                 return bcb_not_loaded();
286
287         if (bcb_field_get(argv[1], &field, &size))
288                 return CMD_RET_FAILURE;
289
290         print_buffer((ulong)field - (ulong)&bcb, (void *)field, 1, size, 16);
291
292         return CMD_RET_SUCCESS;
293 }
294
295 static int __bcb_store(void)
296 {
297         u64 cnt;
298         int ret;
299
300         cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), partition->blksz);
301
302         if (blk_dwrite(block, partition->start, cnt, &bcb) != cnt) {
303                 ret = -EIO;
304                 goto err;
305         }
306
307         return CMD_RET_SUCCESS;
308 err:
309         printf("Error: %d %d:%s write failed (%d)\n", block->uclass_id,
310                block->devnum, partition->name, ret);
311
312         return CMD_RET_FAILURE;
313 }
314
315 static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc,
316                         char * const argv[])
317 {
318         if (!block)
319                 return bcb_not_loaded();
320
321         return __bcb_store();
322 }
323
324 int bcb_find_partition_and_load(const char *iface, int devnum, char *partp)
325 {
326         int ret;
327
328         __bcb_reset();
329
330         ret = __bcb_initialize(iface, devnum, partp);
331         if (ret != CMD_RET_SUCCESS)
332                 return ret;
333
334         return __bcb_load();
335 }
336
337 int bcb_load(struct blk_desc *block_description, struct disk_partition *disk_partition)
338 {
339         __bcb_reset();
340
341         block = block_description;
342         partition = disk_partition;
343
344         return __bcb_load();
345 }
346
347 int bcb_set(enum bcb_field field, const char *value)
348 {
349         if (field > BCB_FIELD_STAGE)
350                 return CMD_RET_FAILURE;
351         return __bcb_set(fields[field], value);
352 }
353
354 int bcb_get(enum bcb_field field, char *value_out, size_t value_size)
355 {
356         int size;
357         char *field_value;
358
359         if (field > BCB_FIELD_STAGE)
360                 return CMD_RET_FAILURE;
361         if (bcb_field_get(fields[field], &field_value, &size))
362                 return CMD_RET_FAILURE;
363
364         strlcpy(value_out, field_value, value_size);
365
366         return CMD_RET_SUCCESS;
367 }
368
369 int bcb_store(void)
370 {
371         return __bcb_store();
372 }
373
374 void bcb_reset(void)
375 {
376         __bcb_reset();
377 }
378
379 U_BOOT_LONGHELP(bcb,
380         "load <interface> <dev> <part>  - load  BCB from <interface> <dev>:<part>\n"
381         "load <dev> <part>              - load  BCB from mmc <dev>:<part>\n"
382         "bcb set   <field> <val>        - set   BCB <field> to <val>\n"
383         "bcb clear [<field>]            - clear BCB <field> or all fields\n"
384         "bcb test  <field> <op> <val>   - test  BCB <field> against <val>\n"
385         "bcb dump  <field>              - dump  BCB <field>\n"
386         "bcb store                      - store BCB back to <interface>\n"
387         "\n"
388         "Legend:\n"
389         "<interface> - storage device interface (virtio, mmc, etc)\n"
390         "<dev>       - storage device index containing the BCB partition\n"
391         "<part>      - partition index or name containing the BCB\n"
392         "<field>     - one of {command,status,recovery,stage,reserved}\n"
393         "<op>        - the binary operator used in 'bcb test':\n"
394         "              '=' returns true if <val> matches the string stored in <field>\n"
395         "              '~' returns true if <val> matches a subset of <field>'s string\n"
396         "<val>       - string/text provided as input to bcb {set,test}\n"
397         "              NOTE: any ':' character in <val> will be replaced by line feed\n"
398         "              during 'bcb set' and used as separator by upper layers\n"
399 );
400
401 U_BOOT_CMD_WITH_SUBCMDS(bcb,
402         "Load/set/clear/test/dump/store Android BCB fields", bcb_help_text,
403         U_BOOT_SUBCMD_MKENT(load, 4, 1, do_bcb_load),
404         U_BOOT_SUBCMD_MKENT(set, 3, 1, do_bcb_set),
405         U_BOOT_SUBCMD_MKENT(clear, 2, 1, do_bcb_clear),
406         U_BOOT_SUBCMD_MKENT(test, 4, 1, do_bcb_test),
407         U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_bcb_dump),
408         U_BOOT_SUBCMD_MKENT(store, 1, 1, do_bcb_store),
409 );
This page took 0.04705 seconds and 4 git commands to generate.