]>
Commit | Line | Data |
---|---|---|
f73a7df9 AK |
1 | // SPDX-License-Identifier: BSD-2-Clause |
2 | /* | |
3 | * Copyright (C) 2016 The Android Open Source Project | |
4 | */ | |
5 | ||
d678a59d | 6 | #include <common.h> |
7b51b576 | 7 | #include <env.h> |
f73a7df9 AK |
8 | #include <fastboot.h> |
9 | #include <fastboot-internal.h> | |
10 | #include <fb_mmc.h> | |
11 | #include <fb_nand.h> | |
12 | #include <fs.h> | |
e6f6f9e6 | 13 | #include <part.h> |
f73a7df9 | 14 | #include <version.h> |
1e94b46f | 15 | #include <linux/printk.h> |
f73a7df9 AK |
16 | |
17 | static void getvar_version(char *var_parameter, char *response); | |
29a81142 | 18 | static void getvar_version_bootloader(char *var_parameter, char *response); |
f73a7df9 AK |
19 | static void getvar_downloadsize(char *var_parameter, char *response); |
20 | static void getvar_serialno(char *var_parameter, char *response); | |
21 | static void getvar_version_baseband(char *var_parameter, char *response); | |
22 | static void getvar_product(char *var_parameter, char *response); | |
d73d9fb7 | 23 | static void getvar_platform(char *var_parameter, char *response); |
f73a7df9 | 24 | static void getvar_current_slot(char *var_parameter, char *response); |
f73a7df9 | 25 | static void getvar_has_slot(char *var_parameter, char *response); |
f73a7df9 | 26 | static void getvar_partition_type(char *part_name, char *response); |
f73a7df9 | 27 | static void getvar_partition_size(char *part_name, char *response); |
139db354 | 28 | static void getvar_is_userspace(char *var_parameter, char *response); |
f73a7df9 AK |
29 | |
30 | static const struct { | |
31 | const char *variable; | |
475aa9aa | 32 | bool list; |
f73a7df9 AK |
33 | void (*dispatch)(char *var_parameter, char *response); |
34 | } getvar_dispatch[] = { | |
35 | { | |
36 | .variable = "version", | |
475aa9aa IA |
37 | .dispatch = getvar_version, |
38 | .list = true, | |
f73a7df9 AK |
39 | }, { |
40 | .variable = "version-bootloader", | |
475aa9aa IA |
41 | .dispatch = getvar_version_bootloader, |
42 | .list = true | |
f73a7df9 AK |
43 | }, { |
44 | .variable = "downloadsize", | |
475aa9aa IA |
45 | .dispatch = getvar_downloadsize, |
46 | .list = true | |
f73a7df9 AK |
47 | }, { |
48 | .variable = "max-download-size", | |
475aa9aa IA |
49 | .dispatch = getvar_downloadsize, |
50 | .list = true | |
f73a7df9 AK |
51 | }, { |
52 | .variable = "serialno", | |
475aa9aa IA |
53 | .dispatch = getvar_serialno, |
54 | .list = true | |
f73a7df9 AK |
55 | }, { |
56 | .variable = "version-baseband", | |
475aa9aa IA |
57 | .dispatch = getvar_version_baseband, |
58 | .list = true | |
f73a7df9 AK |
59 | }, { |
60 | .variable = "product", | |
475aa9aa IA |
61 | .dispatch = getvar_product, |
62 | .list = true | |
d73d9fb7 ER |
63 | }, { |
64 | .variable = "platform", | |
475aa9aa IA |
65 | .dispatch = getvar_platform, |
66 | .list = true | |
f73a7df9 AK |
67 | }, { |
68 | .variable = "current-slot", | |
475aa9aa IA |
69 | .dispatch = getvar_current_slot, |
70 | .list = true | |
64d6bfb6 | 71 | #if IS_ENABLED(CONFIG_FASTBOOT_FLASH) |
f73a7df9 | 72 | }, { |
4c829466 | 73 | .variable = "has-slot", |
475aa9aa IA |
74 | .dispatch = getvar_has_slot, |
75 | .list = false | |
220f6551 | 76 | #endif |
7cb10e51 | 77 | #if IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC) |
f73a7df9 AK |
78 | }, { |
79 | .variable = "partition-type", | |
475aa9aa IA |
80 | .dispatch = getvar_partition_type, |
81 | .list = false | |
f73a7df9 | 82 | #endif |
64d6bfb6 | 83 | #if IS_ENABLED(CONFIG_FASTBOOT_FLASH) |
f73a7df9 AK |
84 | }, { |
85 | .variable = "partition-size", | |
475aa9aa IA |
86 | .dispatch = getvar_partition_size, |
87 | .list = false | |
f73a7df9 | 88 | #endif |
139db354 SP |
89 | }, { |
90 | .variable = "is-userspace", | |
475aa9aa IA |
91 | .dispatch = getvar_is_userspace, |
92 | .list = true | |
f73a7df9 AK |
93 | } |
94 | }; | |
95 | ||
f23a87d5 SP |
96 | /** |
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 | |
293a6dfe | 106 | * @param[out] size If not NULL, will contain partition size |
185f812c | 107 | * Return: Partition number or negative value on error |
f23a87d5 SP |
108 | */ |
109 | static int getvar_get_part_info(const char *part_name, char *response, | |
110 | size_t *size) | |
111 | { | |
112 | int r; | |
f23a87d5 | 113 | struct blk_desc *dev_desc; |
d0379900 | 114 | struct disk_partition disk_part; |
f23a87d5 SP |
115 | struct part_info *part_info; |
116 | ||
7cb10e51 | 117 | if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)) { |
d0379900 PD |
118 | 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; | |
c6228edf | 122 | } else if (IS_ENABLED(CONFIG_FASTBOOT_FLASH_NAND)) { |
d0379900 PD |
123 | 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 | } | |
f23a87d5 SP |
130 | |
131 | return r; | |
132 | } | |
f23a87d5 | 133 | |
f73a7df9 AK |
134 | static void getvar_version(char *var_parameter, char *response) |
135 | { | |
136 | fastboot_okay(FASTBOOT_VERSION, response); | |
137 | } | |
138 | ||
29a81142 | 139 | static void getvar_version_bootloader(char *var_parameter, char *response) |
f73a7df9 AK |
140 | { |
141 | fastboot_okay(U_BOOT_VERSION, response); | |
142 | } | |
143 | ||
144 | static void getvar_downloadsize(char *var_parameter, char *response) | |
145 | { | |
146 | fastboot_response("OKAY", response, "0x%08x", fastboot_buf_size); | |
147 | } | |
148 | ||
149 | static 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 | ||
159 | static void getvar_version_baseband(char *var_parameter, char *response) | |
160 | { | |
161 | fastboot_okay("N/A", response); | |
162 | } | |
163 | ||
164 | static 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 | ||
d73d9fb7 ER |
174 | static 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 | ||
f73a7df9 AK |
184 | static void getvar_current_slot(char *var_parameter, char *response) |
185 | { | |
97a0c6ff SP |
186 | /* A/B not implemented, for now always return "a" */ |
187 | fastboot_okay("a", response); | |
f73a7df9 AK |
188 | } |
189 | ||
d0379900 | 190 | static void __maybe_unused getvar_has_slot(char *part_name, char *response) |
f73a7df9 | 191 | { |
220f6551 IO |
192 | 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); | |
61582872 | 201 | if (len >= PART_NAME_LEN - 3) |
220f6551 IO |
202 | 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 | ||
218 | fail: | |
219 | fastboot_fail("invalid partition name", response); | |
f73a7df9 AK |
220 | } |
221 | ||
d0379900 | 222 | static void __maybe_unused getvar_partition_type(char *part_name, char *response) |
f73a7df9 AK |
223 | { |
224 | int r; | |
225 | struct blk_desc *dev_desc; | |
0528979f | 226 | struct disk_partition part_info; |
f73a7df9 AK |
227 | |
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 | } | |
f73a7df9 | 238 | |
d0379900 | 239 | static void __maybe_unused getvar_partition_size(char *part_name, char *response) |
f73a7df9 AK |
240 | { |
241 | int r; | |
242 | size_t size; | |
243 | ||
f23a87d5 | 244 | r = getvar_get_part_info(part_name, response, &size); |
f73a7df9 AK |
245 | if (r >= 0) |
246 | fastboot_response("OKAY", response, "0x%016zx", size); | |
247 | } | |
f73a7df9 | 248 | |
139db354 SP |
249 | static void getvar_is_userspace(char *var_parameter, char *response) |
250 | { | |
251 | fastboot_okay("no", response); | |
252 | } | |
253 | ||
475aa9aa IA |
254 | static int current_all_dispatch; |
255 | void 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 | ||
f73a7df9 AK |
288 | /** |
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 | */ | |
301 | void fastboot_getvar(char *cmd_parameter, char *response) | |
302 | { | |
303 | if (!cmd_parameter) { | |
304 | fastboot_fail("missing var", response); | |
475aa9aa IA |
305 | } else if (!strncmp("all", cmd_parameter, 3) && strlen(cmd_parameter) == 3) { |
306 | current_all_dispatch = 0; | |
307 | fastboot_response(FASTBOOT_MULTIRESPONSE_START, response, NULL); | |
f73a7df9 AK |
308 | } 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 | } |