blob: 9c2ce65a4e5bce0da6b18aa1b2818f7db556c528 [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)
Caleb Connolly11518ab2024-11-13 06:05:59 +0100233 /* If we don't know then just default to raw */
234 fastboot_okay("raw", response);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000235 else
236 fastboot_okay(fs_get_type_name(), response);
237 }
238}
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000239
Patrick Delaunayf82f9e42022-12-15 10:15:50 +0100240static void __maybe_unused getvar_partition_size(char *part_name, char *response)
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000241{
242 int r;
243 size_t size;
244
Sam Protsenko6646bbd2019-06-13 21:11:08 +0300245 r = getvar_get_part_info(part_name, response, &size);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000246 if (r >= 0)
247 fastboot_response("OKAY", response, "0x%016zx", size);
248}
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000249
Sam Protsenko0d748252019-07-03 19:00:22 +0300250static void getvar_is_userspace(char *var_parameter, char *response)
251{
252 fastboot_okay("no", response);
253}
254
Ion Agorria99fc6452024-01-05 09:22:07 +0200255static int current_all_dispatch;
256void fastboot_getvar_all(char *response)
257{
258 /*
259 * Find a dispatch getvar that can be listed and send
260 * it as INFO until we reach the end.
261 */
262 while (current_all_dispatch < ARRAY_SIZE(getvar_dispatch)) {
263 if (!getvar_dispatch[current_all_dispatch].list) {
264 current_all_dispatch++;
265 continue;
266 }
267
268 char envstr[FASTBOOT_RESPONSE_LEN] = { 0 };
269
270 getvar_dispatch[current_all_dispatch].dispatch(NULL, envstr);
271
272 char *envstr_start = envstr;
273
274 if (!strncmp("OKAY", envstr, 4) || !strncmp("FAIL", envstr, 4))
275 envstr_start += 4;
276
277 fastboot_response("INFO", response, "%s: %s",
278 getvar_dispatch[current_all_dispatch].variable,
279 envstr_start);
280
281 current_all_dispatch++;
282 return;
283 }
284
285 fastboot_response("OKAY", response, NULL);
286 current_all_dispatch = 0;
287}
288
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000289/**
290 * fastboot_getvar() - Writes variable indicated by cmd_parameter to response.
291 *
292 * @cmd_parameter: Pointer to command parameter
293 * @response: Pointer to fastboot response buffer
294 *
295 * Look up cmd_parameter first as an environment variable of the form
296 * fastboot.<cmd_parameter>, if that exists return use its value to set
297 * response.
298 *
299 * Otherwise lookup the name of variable and execute the appropriate
300 * function to return the requested value.
301 */
302void fastboot_getvar(char *cmd_parameter, char *response)
303{
304 if (!cmd_parameter) {
305 fastboot_fail("missing var", response);
Ion Agorria99fc6452024-01-05 09:22:07 +0200306 } else if (!strncmp("all", cmd_parameter, 3) && strlen(cmd_parameter) == 3) {
307 current_all_dispatch = 0;
308 fastboot_response(FASTBOOT_MULTIRESPONSE_START, response, NULL);
Alex Kiernand5aa57c2018-05-29 15:30:53 +0000309 } else {
310#define FASTBOOT_ENV_PREFIX "fastboot."
311 int i;
312 char *var_parameter = cmd_parameter;
313 char envstr[FASTBOOT_RESPONSE_LEN];
314 const char *s;
315
316 snprintf(envstr, sizeof(envstr) - 1,
317 FASTBOOT_ENV_PREFIX "%s", cmd_parameter);
318 s = env_get(envstr);
319 if (s) {
320 fastboot_response("OKAY", response, "%s", s);
321 return;
322 }
323
324 strsep(&var_parameter, ":");
325 for (i = 0; i < ARRAY_SIZE(getvar_dispatch); ++i) {
326 if (!strcmp(getvar_dispatch[i].variable,
327 cmd_parameter)) {
328 getvar_dispatch[i].dispatch(var_parameter,
329 response);
330 return;
331 }
332 }
333 pr_warn("WARNING: unknown variable: %s\n", cmd_parameter);
334 fastboot_fail("Variable not implemented", response);
335 }
336}