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