blob: 93cbd598e024fe9a7c4fcbef302534d35da1ad57 [file] [log] [blame]
Alex Kiernand5aa57c2018-05-29 15:30:53 +00001// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright (C) 2016 The Android Open Source Project
4 */
5
Simon Glass0af6e2d2019-08-01 09:46:52 -06006#include <env.h>
Alex Kiernand5aa57c2018-05-29 15:30:53 +00007#include <fastboot.h>
8#include <fastboot-internal.h>
9#include <fb_mmc.h>
10#include <fb_nand.h>
11#include <fs.h>
Simon Glass655306c2020-05-10 11:39:58 -060012#include <part.h>
Alex Kiernand5aa57c2018-05-29 15:30:53 +000013#include <version.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060014#include <vsprintf.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060015#include <linux/printk.h>
Alex Kiernand5aa57c2018-05-29 15:30:53 +000016
17static void getvar_version(char *var_parameter, char *response);
Sam Protsenkoac21e742019-07-03 19:34:07 +030018static void getvar_version_bootloader(char *var_parameter, char *response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +000019static void getvar_downloadsize(char *var_parameter, char *response);
20static void getvar_serialno(char *var_parameter, char *response);
21static void getvar_version_baseband(char *var_parameter, char *response);
22static void getvar_product(char *var_parameter, char *response);
Eugeniu Roscafb614d42019-04-09 21:11:40 +020023static void getvar_platform(char *var_parameter, char *response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +000024static void getvar_current_slot(char *var_parameter, char *response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +000025static void getvar_has_slot(char *var_parameter, char *response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +000026static void getvar_partition_type(char *part_name, char *response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +000027static void getvar_partition_size(char *part_name, char *response);
Sam Protsenko0d748252019-07-03 19:00:22 +030028static void getvar_is_userspace(char *var_parameter, char *response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +000029
30static const struct {
31 const char *variable;
Ion Agorria99fc6452024-01-05 09:22:07 +020032 bool list;
Alex Kiernand5aa57c2018-05-29 15:30:53 +000033 void (*dispatch)(char *var_parameter, char *response);
34} getvar_dispatch[] = {
35 {
36 .variable = "version",
Ion Agorria99fc6452024-01-05 09:22:07 +020037 .dispatch = getvar_version,
38 .list = true,
Alex Kiernand5aa57c2018-05-29 15:30:53 +000039 }, {
Alex Kiernand5aa57c2018-05-29 15:30:53 +000040 .variable = "version-bootloader",
Ion Agorria99fc6452024-01-05 09:22:07 +020041 .dispatch = getvar_version_bootloader,
42 .list = true
Alex Kiernand5aa57c2018-05-29 15:30:53 +000043 }, {
44 .variable = "downloadsize",
Ion Agorria99fc6452024-01-05 09:22:07 +020045 .dispatch = getvar_downloadsize,
46 .list = true
Alex Kiernand5aa57c2018-05-29 15:30:53 +000047 }, {
48 .variable = "max-download-size",
Ion Agorria99fc6452024-01-05 09:22:07 +020049 .dispatch = getvar_downloadsize,
50 .list = true
Alex Kiernand5aa57c2018-05-29 15:30:53 +000051 }, {
52 .variable = "serialno",
Ion Agorria99fc6452024-01-05 09:22:07 +020053 .dispatch = getvar_serialno,
54 .list = true
Alex Kiernand5aa57c2018-05-29 15:30:53 +000055 }, {
56 .variable = "version-baseband",
Ion Agorria99fc6452024-01-05 09:22:07 +020057 .dispatch = getvar_version_baseband,
58 .list = true
Alex Kiernand5aa57c2018-05-29 15:30:53 +000059 }, {
60 .variable = "product",
Ion Agorria99fc6452024-01-05 09:22:07 +020061 .dispatch = getvar_product,
62 .list = true
Alex Kiernand5aa57c2018-05-29 15:30:53 +000063 }, {
Eugeniu Roscafb614d42019-04-09 21:11:40 +020064 .variable = "platform",
Ion Agorria99fc6452024-01-05 09:22:07 +020065 .dispatch = getvar_platform,
66 .list = true
Eugeniu Roscafb614d42019-04-09 21:11:40 +020067 }, {
Alex Kiernand5aa57c2018-05-29 15:30:53 +000068 .variable = "current-slot",
Ion Agorria99fc6452024-01-05 09:22:07 +020069 .dispatch = getvar_current_slot,
70 .list = true
Simon Glass3eaba0792023-02-05 15:39:53 -070071#if IS_ENABLED(CONFIG_FASTBOOT_FLASH)
Alex Kiernand5aa57c2018-05-29 15:30:53 +000072 }, {
Eugeniu Rosca2ef97cf2019-03-26 17:46:14 +010073 .variable = "has-slot",
Ion Agorria99fc6452024-01-05 09:22:07 +020074 .dispatch = getvar_has_slot,
75 .list = false
Igor Opaniuk73dac9b2019-06-13 21:11:09 +030076#endif
Simon Glass091a0712023-02-05 17:54:12 -070077#if IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)
Alex Kiernand5aa57c2018-05-29 15:30:53 +000078 }, {
79 .variable = "partition-type",
Ion Agorria99fc6452024-01-05 09:22:07 +020080 .dispatch = getvar_partition_type,
81 .list = false
Alex Kiernand5aa57c2018-05-29 15:30:53 +000082#endif
Simon Glass3eaba0792023-02-05 15:39:53 -070083#if IS_ENABLED(CONFIG_FASTBOOT_FLASH)
Alex Kiernand5aa57c2018-05-29 15:30:53 +000084 }, {
85 .variable = "partition-size",
Ion Agorria99fc6452024-01-05 09:22:07 +020086 .dispatch = getvar_partition_size,
87 .list = false
Alex Kiernand5aa57c2018-05-29 15:30:53 +000088#endif
Sam Protsenko0d748252019-07-03 19:00:22 +030089 }, {
90 .variable = "is-userspace",
Ion Agorria99fc6452024-01-05 09:22:07 +020091 .dispatch = getvar_is_userspace,
92 .list = true
Alex Kiernand5aa57c2018-05-29 15:30:53 +000093 }
94};
95
Sam Protsenko6646bbd2019-06-13 21:11:08 +030096/**
97 * Get partition number and size for any storage type.
98 *
99 * Can be used to check if partition with specified name exists.
100 *
101 * If error occurs, this function guarantees to fill @p response with fail
102 * string. @p response can be rewritten in caller, if needed.
103 *
104 * @param[in] part_name Info for which partition name to look for
105 * @param[in,out] response Pointer to fastboot response buffer
Gary Bissonf8674282020-08-27 10:51:14 +0200106 * @param[out] size If not NULL, will contain partition size
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100107 * Return: Partition number or negative value on error
Sam Protsenko6646bbd2019-06-13 21:11:08 +0300108 */
109static int getvar_get_part_info(const char *part_name, char *response,
110 size_t *size)
111{
112 int r;
Sam Protsenko6646bbd2019-06-13 21:11:08 +0300113 struct blk_desc *dev_desc;
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100114 struct disk_partition disk_part;
Sam Protsenko6646bbd2019-06-13 21:11:08 +0300115 struct part_info *part_info;
116
Simon Glass091a0712023-02-05 17:54:12 -0700117 if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)) {
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100118 r = fastboot_mmc_get_part_info(part_name, &dev_desc, &disk_part,
119 response);
120 if (r >= 0 && size)
121 *size = disk_part.size * disk_part.blksz;
Simon Glass067995932023-02-05 17:54:13 -0700122 } else if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND)) {
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100123 r = fastboot_nand_get_part_info(part_name, &part_info, response);
124 if (r >= 0 && size)
125 *size = part_info->size;
126 } else {
127 fastboot_fail("this storage is not supported in bootloader", response);
128 r = -ENODEV;
129 }
Sam Protsenko6646bbd2019-06-13 21:11:08 +0300130
131 return r;
132}
Sam Protsenko6646bbd2019-06-13 21:11:08 +0300133
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000134static void getvar_version(char *var_parameter, char *response)
135{
136 fastboot_okay(FASTBOOT_VERSION, response);
137}
138
Sam Protsenkoac21e742019-07-03 19:34:07 +0300139static void getvar_version_bootloader(char *var_parameter, char *response)
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000140{
141 fastboot_okay(U_BOOT_VERSION, response);
142}
143
144static void getvar_downloadsize(char *var_parameter, char *response)
145{
146 fastboot_response("OKAY", response, "0x%08x", fastboot_buf_size);
147}
148
149static void getvar_serialno(char *var_parameter, char *response)
150{
151 const char *tmp = env_get("serial#");
152
153 if (tmp)
154 fastboot_okay(tmp, response);
155 else
156 fastboot_fail("Value not set", response);
157}
158
159static void getvar_version_baseband(char *var_parameter, char *response)
160{
161 fastboot_okay("N/A", response);
162}
163
164static void getvar_product(char *var_parameter, char *response)
165{
166 const char *board = env_get("board");
167
168 if (board)
169 fastboot_okay(board, response);
170 else
171 fastboot_fail("Board not set", response);
172}
173
Eugeniu Roscafb614d42019-04-09 21:11:40 +0200174static void getvar_platform(char *var_parameter, char *response)
175{
176 const char *p = env_get("platform");
177
178 if (p)
179 fastboot_okay(p, response);
180 else
181 fastboot_fail("platform not set", response);
182}
183
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000184static void getvar_current_slot(char *var_parameter, char *response)
185{
Sam Protsenkofd7467e2019-06-13 00:49:45 +0300186 /* A/B not implemented, for now always return "a" */
187 fastboot_okay("a", response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000188}
189
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100190static void __maybe_unused getvar_has_slot(char *part_name, char *response)
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000191{
Igor Opaniuk73dac9b2019-06-13 21:11:09 +0300192 char part_name_wslot[PART_NAME_LEN];
193 size_t len;
194 int r;
195
196 if (!part_name || part_name[0] == '\0')
197 goto fail;
198
199 /* part_name_wslot = part_name + "_a" */
200 len = strlcpy(part_name_wslot, part_name, PART_NAME_LEN - 3);
Matthias Schiffer02482ee2023-07-14 13:24:50 +0200201 if (len >= PART_NAME_LEN - 3)
Igor Opaniuk73dac9b2019-06-13 21:11:09 +0300202 goto fail;
203 strcat(part_name_wslot, "_a");
204
205 r = getvar_get_part_info(part_name_wslot, response, NULL);
206 if (r >= 0) {
207 fastboot_okay("yes", response); /* part exists and slotted */
208 return;
209 }
210
211 r = getvar_get_part_info(part_name, response, NULL);
212 if (r >= 0)
213 fastboot_okay("no", response); /* part exists but not slotted */
214
215 /* At this point response is filled with okay or fail string */
216 return;
217
218fail:
219 fastboot_fail("invalid partition name", response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000220}
221
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100222static void __maybe_unused getvar_partition_type(char *part_name, char *response)
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000223{
224 int r;
225 struct blk_desc *dev_desc;
Simon Glassc1c4a8f2020-05-10 11:39:57 -0600226 struct disk_partition part_info;
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000227
228 r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info,
229 response);
230 if (r >= 0) {
231 r = fs_set_blk_dev_with_part(dev_desc, r);
232 if (r < 0)
233 fastboot_fail("failed to set partition", response);
234 else
235 fastboot_okay(fs_get_type_name(), response);
236 }
237}
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000238
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100239static void __maybe_unused getvar_partition_size(char *part_name, char *response)
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000240{
241 int r;
242 size_t size;
243
Sam Protsenko6646bbd2019-06-13 21:11:08 +0300244 r = getvar_get_part_info(part_name, response, &size);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000245 if (r >= 0)
246 fastboot_response("OKAY", response, "0x%016zx", size);
247}
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000248
Sam Protsenko0d748252019-07-03 19:00:22 +0300249static void getvar_is_userspace(char *var_parameter, char *response)
250{
251 fastboot_okay("no", response);
252}
253
Ion Agorria99fc6452024-01-05 09:22:07 +0200254static int current_all_dispatch;
255void fastboot_getvar_all(char *response)
256{
257 /*
258 * Find a dispatch getvar that can be listed and send
259 * it as INFO until we reach the end.
260 */
261 while (current_all_dispatch < ARRAY_SIZE(getvar_dispatch)) {
262 if (!getvar_dispatch[current_all_dispatch].list) {
263 current_all_dispatch++;
264 continue;
265 }
266
267 char envstr[FASTBOOT_RESPONSE_LEN] = { 0 };
268
269 getvar_dispatch[current_all_dispatch].dispatch(NULL, envstr);
270
271 char *envstr_start = envstr;
272
273 if (!strncmp("OKAY", envstr, 4) || !strncmp("FAIL", envstr, 4))
274 envstr_start += 4;
275
276 fastboot_response("INFO", response, "%s: %s",
277 getvar_dispatch[current_all_dispatch].variable,
278 envstr_start);
279
280 current_all_dispatch++;
281 return;
282 }
283
284 fastboot_response("OKAY", response, NULL);
285 current_all_dispatch = 0;
286}
287
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000288/**
289 * fastboot_getvar() - Writes variable indicated by cmd_parameter to response.
290 *
291 * @cmd_parameter: Pointer to command parameter
292 * @response: Pointer to fastboot response buffer
293 *
294 * Look up cmd_parameter first as an environment variable of the form
295 * fastboot.<cmd_parameter>, if that exists return use its value to set
296 * response.
297 *
298 * Otherwise lookup the name of variable and execute the appropriate
299 * function to return the requested value.
300 */
301void fastboot_getvar(char *cmd_parameter, char *response)
302{
303 if (!cmd_parameter) {
304 fastboot_fail("missing var", response);
Ion Agorria99fc6452024-01-05 09:22:07 +0200305 } else if (!strncmp("all", cmd_parameter, 3) && strlen(cmd_parameter) == 3) {
306 current_all_dispatch = 0;
307 fastboot_response(FASTBOOT_MULTIRESPONSE_START, response, NULL);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000308 } else {
309#define FASTBOOT_ENV_PREFIX "fastboot."
310 int i;
311 char *var_parameter = cmd_parameter;
312 char envstr[FASTBOOT_RESPONSE_LEN];
313 const char *s;
314
315 snprintf(envstr, sizeof(envstr) - 1,
316 FASTBOOT_ENV_PREFIX "%s", cmd_parameter);
317 s = env_get(envstr);
318 if (s) {
319 fastboot_response("OKAY", response, "%s", s);
320 return;
321 }
322
323 strsep(&var_parameter, ":");
324 for (i = 0; i < ARRAY_SIZE(getvar_dispatch); ++i) {
325 if (!strcmp(getvar_dispatch[i].variable,
326 cmd_parameter)) {
327 getvar_dispatch[i].dispatch(var_parameter,
328 response);
329 return;
330 }
331 }
332 pr_warn("WARNING: unknown variable: %s\n", cmd_parameter);
333 fastboot_fail("Variable not implemented", response);
334 }
335}