blob: 8c2bde10e171f2141cb544586bd6e1d34efb9959 [file] [log] [blame]
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Bootmeth for Android
4 *
5 * Copyright (C) 2024 BayLibre, SAS
6 * Written by Mattijs Korpershoek <mkorpershoek@baylibre.com>
7 */
8#define LOG_CATEGORY UCLASS_BOOTSTD
9
10#include <android_ab.h>
11#include <android_image.h>
12#if CONFIG_IS_ENABLED(AVB_VERIFY)
13#include <avb_verify.h>
14#endif
15#include <bcb.h>
16#include <blk.h>
17#include <bootflow.h>
18#include <bootm.h>
19#include <bootmeth.h>
20#include <dm.h>
Tom Rini22856382025-05-14 16:46:03 -060021#include <env.h>
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +020022#include <image.h>
23#include <malloc.h>
24#include <mapmem.h>
25#include <part.h>
Mattijs Korpershoekc818a712024-09-12 16:00:01 +020026#include <version.h>
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +020027#include "bootmeth_android.h"
28
29#define BCB_FIELD_COMMAND_SZ 32
30#define BCB_PART_NAME "misc"
31#define BOOT_PART_NAME "boot"
32#define VENDOR_BOOT_PART_NAME "vendor_boot"
Guillaume La Roquedf9e6412024-11-26 09:06:10 +010033#define SLOT_LEN 2
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +020034
35/**
36 * struct android_priv - Private data
37 *
38 * This is read from the disk and recorded for use when the full Android
39 * kernel must be loaded and booted
40 *
41 * @boot_mode: Requested boot mode (normal, recovery, bootloader)
42 * @slot: Nul-terminated partition slot suffix read from BCB ("a\0" or "b\0")
43 * @header_version: Android boot image header version
44 */
45struct android_priv {
46 enum android_boot_mode boot_mode;
Guillaume La Roquedf9e6412024-11-26 09:06:10 +010047 char *slot;
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +020048 u32 header_version;
Julien Massonb238dee2024-11-21 11:59:55 +010049 u32 boot_img_size;
50 u32 vendor_boot_img_size;
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +020051};
52
53static int android_check(struct udevice *dev, struct bootflow_iter *iter)
54{
55 /* This only works on mmc devices */
56 if (bootflow_iter_check_mmc(iter))
57 return log_msg_ret("mmc", -ENOTSUPP);
58
59 /*
60 * This only works on whole devices, as multiple
61 * partitions are needed to boot Android
62 */
63 if (iter->part != 0)
64 return log_msg_ret("mmc part", -ENOTSUPP);
65
66 return 0;
67}
68
69static int scan_boot_part(struct udevice *blk, struct android_priv *priv)
70{
71 struct blk_desc *desc = dev_get_uclass_plat(blk);
72 struct disk_partition partition;
73 char partname[PART_NAME_LEN];
74 ulong num_blks, bufsz;
75 char *buf;
76 int ret;
77
Guillaume La Roquedf9e6412024-11-26 09:06:10 +010078 if (priv->slot)
79 sprintf(partname, BOOT_PART_NAME "_%s", priv->slot);
80 else
81 sprintf(partname, BOOT_PART_NAME);
82
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +020083 ret = part_get_info_by_name(desc, partname, &partition);
84 if (ret < 0)
85 return log_msg_ret("part info", ret);
86
87 num_blks = DIV_ROUND_UP(sizeof(struct andr_boot_img_hdr_v0), desc->blksz);
88 bufsz = num_blks * desc->blksz;
89 buf = malloc(bufsz);
90 if (!buf)
91 return log_msg_ret("buf", -ENOMEM);
92
93 ret = blk_read(blk, partition.start, num_blks, buf);
94 if (ret != num_blks) {
95 free(buf);
96 return log_msg_ret("part read", -EIO);
97 }
98
99 if (!is_android_boot_image_header(buf)) {
100 free(buf);
101 return log_msg_ret("header", -ENOENT);
102 }
103
Julien Massonb238dee2024-11-21 11:59:55 +0100104 if (!android_image_get_bootimg_size(buf, &priv->boot_img_size)) {
105 free(buf);
106 return log_msg_ret("get bootimg size", -EINVAL);
107 }
108
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200109 priv->header_version = ((struct andr_boot_img_hdr_v0 *)buf)->header_version;
Julien Massonb238dee2024-11-21 11:59:55 +0100110
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200111 free(buf);
112
113 return 0;
114}
115
116static int scan_vendor_boot_part(struct udevice *blk, struct android_priv *priv)
117{
118 struct blk_desc *desc = dev_get_uclass_plat(blk);
119 struct disk_partition partition;
120 char partname[PART_NAME_LEN];
121 ulong num_blks, bufsz;
122 char *buf;
123 int ret;
124
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100125 if (priv->slot)
126 sprintf(partname, VENDOR_BOOT_PART_NAME "_%s", priv->slot);
127 else
128 sprintf(partname, VENDOR_BOOT_PART_NAME);
129
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200130 ret = part_get_info_by_name(desc, partname, &partition);
131 if (ret < 0)
132 return log_msg_ret("part info", ret);
133
134 num_blks = DIV_ROUND_UP(sizeof(struct andr_vnd_boot_img_hdr), desc->blksz);
135 bufsz = num_blks * desc->blksz;
136 buf = malloc(bufsz);
137 if (!buf)
138 return log_msg_ret("buf", -ENOMEM);
139
140 ret = blk_read(blk, partition.start, num_blks, buf);
141 if (ret != num_blks) {
142 free(buf);
143 return log_msg_ret("part read", -EIO);
144 }
145
146 if (!is_android_vendor_boot_image_header(buf)) {
147 free(buf);
148 return log_msg_ret("header", -ENOENT);
149 }
Julien Massonb238dee2024-11-21 11:59:55 +0100150
151 if (!android_image_get_vendor_bootimg_size(buf, &priv->vendor_boot_img_size)) {
152 free(buf);
153 return log_msg_ret("get vendor bootimg size", -EINVAL);
154 }
155
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200156 free(buf);
157
158 return 0;
159}
160
161static int android_read_slot_from_bcb(struct bootflow *bflow, bool decrement)
162{
163 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
164 struct android_priv *priv = bflow->bootmeth_priv;
165 struct disk_partition misc;
166 char slot_suffix[3];
167 int ret;
168
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100169 if (!CONFIG_IS_ENABLED(ANDROID_AB)) {
170 priv->slot = NULL;
171 return 0;
172 }
173
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200174 ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc);
175 if (ret < 0)
176 return log_msg_ret("part", ret);
177
178 ret = ab_select_slot(desc, &misc, decrement);
179 if (ret < 0)
180 return log_msg_ret("slot", ret);
181
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100182 priv->slot = malloc(SLOT_LEN);
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200183 priv->slot[0] = BOOT_SLOT_NAME(ret);
184 priv->slot[1] = '\0';
185
186 sprintf(slot_suffix, "_%s", priv->slot);
187 ret = bootflow_cmdline_set_arg(bflow, "androidboot.slot_suffix",
188 slot_suffix, false);
189 if (ret < 0)
190 return log_msg_ret("cmdl", ret);
191
192 return 0;
193}
194
195static int configure_serialno(struct bootflow *bflow)
196{
197 char *serialno = env_get("serial#");
198
199 if (!serialno)
200 return log_msg_ret("serial", -ENOENT);
201
202 return bootflow_cmdline_set_arg(bflow, "androidboot.serialno", serialno, false);
203}
204
Mattijs Korpershoekc818a712024-09-12 16:00:01 +0200205static int configure_bootloader_version(struct bootflow *bflow)
206{
207 return bootflow_cmdline_set_arg(bflow, "androidboot.bootloader",
208 PLAIN_VERSION, false);
209}
210
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200211static int android_read_bootflow(struct udevice *dev, struct bootflow *bflow)
212{
213 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
214 struct disk_partition misc;
215 struct android_priv *priv;
216 char command[BCB_FIELD_COMMAND_SZ];
217 int ret;
218
219 bflow->state = BOOTFLOWST_MEDIA;
220
221 /*
222 * bcb_find_partition_and_load() will print errors to stdout
223 * if BCB_PART_NAME is not found. To avoid that, check if the
224 * partition exists first.
225 */
226 ret = part_get_info_by_name(desc, BCB_PART_NAME, &misc);
227 if (ret < 0)
228 return log_msg_ret("part", ret);
229
230 ret = bcb_find_partition_and_load("mmc", desc->devnum, BCB_PART_NAME);
231 if (ret < 0)
232 return log_msg_ret("bcb load", ret);
233
234 ret = bcb_get(BCB_FIELD_COMMAND, command, sizeof(command));
235 if (ret < 0)
236 return log_msg_ret("bcb read", ret);
237
238 priv = malloc(sizeof(struct android_priv));
239 if (!priv)
240 return log_msg_ret("buf", -ENOMEM);
241
242 if (!strcmp("bootonce-bootloader", command)) {
243 priv->boot_mode = ANDROID_BOOT_MODE_BOOTLOADER;
244 bflow->os_name = strdup("Android (bootloader)");
245 } else if (!strcmp("boot-fastboot", command)) {
246 priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY;
247 bflow->os_name = strdup("Android (fastbootd)");
248 } else if (!strcmp("boot-recovery", command)) {
249 priv->boot_mode = ANDROID_BOOT_MODE_RECOVERY;
250 bflow->os_name = strdup("Android (recovery)");
251 } else {
252 priv->boot_mode = ANDROID_BOOT_MODE_NORMAL;
253 bflow->os_name = strdup("Android");
254 }
255 if (!bflow->os_name)
256 return log_msg_ret("os", -ENOMEM);
257
258 if (priv->boot_mode == ANDROID_BOOT_MODE_BOOTLOADER) {
259 /* Clear BCB */
260 memset(command, 0, sizeof(command));
261 ret = bcb_set(BCB_FIELD_COMMAND, command);
262 if (ret < 0) {
263 free(priv);
264 return log_msg_ret("bcb set", ret);
265 }
266 ret = bcb_store();
267 if (ret < 0) {
268 free(priv);
269 return log_msg_ret("bcb store", ret);
270 }
271
272 bflow->bootmeth_priv = priv;
273 bflow->state = BOOTFLOWST_READY;
274 return 0;
275 }
276
277 bflow->bootmeth_priv = priv;
278
279 /* For recovery and normal boot, we need to scan the partitions */
280 ret = android_read_slot_from_bcb(bflow, false);
281 if (ret < 0) {
282 log_err("read slot: %d", ret);
283 goto free_priv;
284 }
285
286 ret = scan_boot_part(bflow->blk, priv);
287 if (ret < 0) {
288 log_debug("scan boot failed: err=%d\n", ret);
289 goto free_priv;
290 }
291
Guillaume La Roque4b501112024-11-26 09:06:09 +0100292 if (priv->header_version >= 3) {
293 ret = scan_vendor_boot_part(bflow->blk, priv);
294 if (ret < 0) {
295 log_debug("scan vendor_boot failed: err=%d\n", ret);
296 goto free_priv;
297 }
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200298 }
299
Mattijs Korpershoekc818a712024-09-12 16:00:01 +0200300 /*
301 * Ignoring return code for the following configurations:
302 * these are not mandatory for booting.
303 */
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200304 configure_serialno(bflow);
Mattijs Korpershoekc818a712024-09-12 16:00:01 +0200305 configure_bootloader_version(bflow);
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200306
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100307 if (priv->boot_mode == ANDROID_BOOT_MODE_NORMAL && priv->slot) {
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200308 ret = bootflow_cmdline_set_arg(bflow, "androidboot.force_normal_boot",
309 "1", false);
310 if (ret < 0) {
311 log_debug("normal_boot %d", ret);
312 goto free_priv;
313 }
314 }
315
316 bflow->state = BOOTFLOWST_READY;
317
318 return 0;
319
320 free_priv:
321 free(priv);
322 bflow->bootmeth_priv = NULL;
323 return ret;
324}
325
326static int android_read_file(struct udevice *dev, struct bootflow *bflow,
Simon Glassf39b5592024-11-15 16:19:17 -0700327 const char *file_path, ulong addr,
328 enum bootflow_img_t type, ulong *sizep)
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200329{
330 /*
331 * Reading individual files is not supported since we only
332 * operate on whole mmc devices (because we require multiple partitions)
333 */
334 return log_msg_ret("Unsupported", -ENOSYS);
335}
336
337/**
338 * read_slotted_partition() - Read a partition by appending a slot suffix
339 *
340 * Most modern Android devices use Seamless Updates, where each partition
341 * is duplicated. For example, the boot partition has boot_a and boot_b.
342 * For more information, see:
343 * https://source.android.com/docs/core/ota/ab
344 * https://source.android.com/docs/core/ota/ab/ab_implement
345 *
346 * @blk: Block device to read
347 * @name: Partition name to read
348 * @slot: Nul-terminated slot suffixed to partition name ("a\0" or "b\0")
Julien Massonb238dee2024-11-21 11:59:55 +0100349 * @image_size: Image size in bytes used when reading the partition
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200350 * @addr: Address where the partition content is loaded into
351 * Return: 0 if OK, negative errno on failure.
352 */
353static int read_slotted_partition(struct blk_desc *desc, const char *const name,
Julien Massonb238dee2024-11-21 11:59:55 +0100354 const char slot[2], ulong image_size, ulong addr)
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200355{
356 struct disk_partition partition;
357 char partname[PART_NAME_LEN];
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100358 size_t partname_len;
Julien Massonb238dee2024-11-21 11:59:55 +0100359 ulong num_blks = DIV_ROUND_UP(image_size, desc->blksz);
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200360 int ret;
361 u32 n;
362
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100363 /*
364 * Ensure name fits in partname.
365 * For A/B, it should be <name>_<slot>\0
366 * For non A/B, it should be <name>\0
367 */
368 if (CONFIG_IS_ENABLED(ANDROID_AB))
369 partname_len = PART_NAME_LEN - 2 - 1;
370 else
371 partname_len = PART_NAME_LEN - 1;
372
373 if (strlen(name) > partname_len)
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200374 return log_msg_ret("name too long", -EINVAL);
375
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100376 if (slot)
377 sprintf(partname, "%s_%s", name, slot);
378 else
379 sprintf(partname, "%s", name);
380
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200381 ret = part_get_info_by_name(desc, partname, &partition);
382 if (ret < 0)
383 return log_msg_ret("part", ret);
384
Julien Massonb238dee2024-11-21 11:59:55 +0100385 n = blk_dread(desc, partition.start, num_blks, map_sysmem(addr, 0));
386 if (n < num_blks)
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200387 return log_msg_ret("part read", -EIO);
388
389 return 0;
390}
391
392#if CONFIG_IS_ENABLED(AVB_VERIFY)
393static int avb_append_commandline_arg(struct bootflow *bflow, char *arg)
394{
395 char *key = strsep(&arg, "=");
396 char *value = arg;
397 int ret;
398
399 ret = bootflow_cmdline_set_arg(bflow, key, value, false);
400 if (ret < 0)
401 return log_msg_ret("avb cmdline", ret);
402
403 return 0;
404}
405
406static int avb_append_commandline(struct bootflow *bflow, char *cmdline)
407{
408 char *arg = strsep(&cmdline, " ");
409 int ret;
410
411 while (arg) {
412 ret = avb_append_commandline_arg(bflow, arg);
413 if (ret < 0)
414 return ret;
415
416 arg = strsep(&cmdline, " ");
417 }
418
419 return 0;
420}
421
422static int run_avb_verification(struct bootflow *bflow)
423{
424 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
425 struct android_priv *priv = bflow->bootmeth_priv;
Mattijs Korpershoek2c22ec22025-01-08 15:38:41 +0100426 const char * const requested_partitions[] = {"boot", "vendor_boot", NULL};
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200427 struct AvbOps *avb_ops;
428 AvbSlotVerifyResult result;
429 AvbSlotVerifyData *out_data;
430 enum avb_boot_state boot_state;
431 char *extra_args;
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100432 char slot_suffix[3] = "";
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200433 bool unlocked = false;
434 int ret;
435
436 avb_ops = avb_ops_alloc(desc->devnum);
437 if (!avb_ops)
438 return log_msg_ret("avb ops", -ENOMEM);
439
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100440 if (priv->slot)
441 sprintf(slot_suffix, "_%s", priv->slot);
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200442
443 ret = avb_ops->read_is_device_unlocked(avb_ops, &unlocked);
444 if (ret != AVB_IO_RESULT_OK)
445 return log_msg_ret("avb lock", -EIO);
446
447 result = avb_slot_verify(avb_ops,
448 requested_partitions,
449 slot_suffix,
450 unlocked,
451 AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
452 &out_data);
453
Mattijs Korpershoek1bb662d2025-01-08 15:38:42 +0100454 if (!unlocked) {
455 /* When device is locked, we only accept AVB_SLOT_VERIFY_RESULT_OK */
456 if (result != AVB_SLOT_VERIFY_RESULT_OK) {
457 printf("Verification failed, reason: %s\n",
458 str_avb_slot_error(result));
Gary Bisson7d08a852025-04-02 16:42:19 +0200459 if (out_data)
460 avb_slot_verify_data_free(out_data);
Mattijs Korpershoek1bb662d2025-01-08 15:38:42 +0100461 return log_msg_ret("avb verify", -EIO);
462 }
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200463 boot_state = AVB_GREEN;
Mattijs Korpershoek1bb662d2025-01-08 15:38:42 +0100464 } else {
465 /* When device is unlocked, we also accept verification errors */
466 if (result != AVB_SLOT_VERIFY_RESULT_OK &&
467 result != AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION) {
468 printf("Unlocked verification failed, reason: %s\n",
469 str_avb_slot_error(result));
Gary Bisson7d08a852025-04-02 16:42:19 +0200470 if (out_data)
471 avb_slot_verify_data_free(out_data);
Mattijs Korpershoek1bb662d2025-01-08 15:38:42 +0100472 return log_msg_ret("avb verify unlocked", -EIO);
473 }
474 boot_state = AVB_ORANGE;
475 }
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200476
477 extra_args = avb_set_state(avb_ops, boot_state);
478 if (extra_args) {
479 /* extra_args will be modified after this. This is fine */
480 ret = avb_append_commandline_arg(bflow, extra_args);
481 if (ret < 0)
482 goto free_out_data;
483 }
484
Mattijs Korpershoek1bb662d2025-01-08 15:38:42 +0100485 if (result == AVB_SLOT_VERIFY_RESULT_OK) {
486 ret = avb_append_commandline(bflow, out_data->cmdline);
487 if (ret < 0)
488 goto free_out_data;
489 }
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200490
491 return 0;
492
493 free_out_data:
494 if (out_data)
495 avb_slot_verify_data_free(out_data);
496
497 return log_msg_ret("avb cmdline", ret);
498}
499#else
500static int run_avb_verification(struct bootflow *bflow)
501{
502 int ret;
503
504 /* When AVB is unsupported, pass ORANGE state */
505 ret = bootflow_cmdline_set_arg(bflow,
506 "androidboot.verifiedbootstate",
507 "orange", false);
508 if (ret < 0)
509 return log_msg_ret("avb cmdline", ret);
510
511 return 0;
512}
513#endif /* AVB_VERIFY */
514
515static int boot_android_normal(struct bootflow *bflow)
516{
517 struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
518 struct android_priv *priv = bflow->bootmeth_priv;
519 int ret;
520 ulong loadaddr = env_get_hex("loadaddr", 0);
521 ulong vloadaddr = env_get_hex("vendor_boot_comp_addr_r", 0);
522
523 ret = run_avb_verification(bflow);
524 if (ret < 0)
525 return log_msg_ret("avb", ret);
526
527 /* Read slot once more to decrement counter from BCB */
528 ret = android_read_slot_from_bcb(bflow, true);
529 if (ret < 0)
530 return log_msg_ret("read slot", ret);
531
Julien Massonb238dee2024-11-21 11:59:55 +0100532 ret = read_slotted_partition(desc, "boot", priv->slot, priv->boot_img_size,
533 loadaddr);
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200534 if (ret < 0)
535 return log_msg_ret("read boot", ret);
536
Guillaume La Roque4b501112024-11-26 09:06:09 +0100537 if (priv->header_version >= 3) {
Julien Massonb238dee2024-11-21 11:59:55 +0100538 ret = read_slotted_partition(desc, "vendor_boot", priv->slot,
539 priv->vendor_boot_img_size, vloadaddr);
Guillaume La Roque4b501112024-11-26 09:06:09 +0100540 if (ret < 0)
541 return log_msg_ret("read vendor_boot", ret);
542 set_avendor_bootimg_addr(vloadaddr);
543 }
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200544 set_abootimg_addr(loadaddr);
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200545
Guillaume La Roquedf9e6412024-11-26 09:06:10 +0100546 if (priv->slot)
547 free(priv->slot);
548
Mattijs Korpershoekb30baa92024-07-10 10:40:05 +0200549 ret = bootm_boot_start(loadaddr, bflow->cmdline);
550
551 return log_msg_ret("boot", ret);
552}
553
554static int boot_android_recovery(struct bootflow *bflow)
555{
556 int ret;
557
558 ret = boot_android_normal(bflow);
559
560 return log_msg_ret("boot", ret);
561}
562
563static int boot_android_bootloader(struct bootflow *bflow)
564{
565 int ret;
566
567 ret = run_command("fastboot usb 0", 0);
568 do_reset(NULL, 0, 0, NULL);
569
570 return log_msg_ret("boot", ret);
571}
572
573static int android_boot(struct udevice *dev, struct bootflow *bflow)
574{
575 struct android_priv *priv = bflow->bootmeth_priv;
576 int ret;
577
578 switch (priv->boot_mode) {
579 case ANDROID_BOOT_MODE_NORMAL:
580 ret = boot_android_normal(bflow);
581 break;
582 case ANDROID_BOOT_MODE_RECOVERY:
583 ret = boot_android_recovery(bflow);
584 break;
585 case ANDROID_BOOT_MODE_BOOTLOADER:
586 ret = boot_android_bootloader(bflow);
587 break;
588 default:
589 printf("ANDROID: Unknown boot mode %d. Running fastboot...\n",
590 priv->boot_mode);
591 boot_android_bootloader(bflow);
592 /* Tell we failed to boot since boot mode is unknown */
593 ret = -EFAULT;
594 }
595
596 return ret;
597}
598
599static int android_bootmeth_bind(struct udevice *dev)
600{
601 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
602
603 plat->desc = "Android boot";
604 plat->flags = BOOTMETHF_ANY_PART;
605
606 return 0;
607}
608
609static struct bootmeth_ops android_bootmeth_ops = {
610 .check = android_check,
611 .read_bootflow = android_read_bootflow,
612 .read_file = android_read_file,
613 .boot = android_boot,
614};
615
616static const struct udevice_id android_bootmeth_ids[] = {
617 { .compatible = "u-boot,android" },
618 { }
619};
620
621U_BOOT_DRIVER(bootmeth_android) = {
622 .name = "bootmeth_android",
623 .id = UCLASS_BOOTMETH,
624 .of_match = android_bootmeth_ids,
625 .ops = &android_bootmeth_ops,
626 .bind = android_bootmeth_bind,
627};