xref: /aosp_15_r20/bootable/libbootloader/gbl/efi/src/android_boot.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::{efi_blocks::find_block_devices, fastboot::fastboot, ops::Ops, ops::RambootOps};
16 use efi::{exit_boot_services, EfiEntry};
17 use libgbl::{android_boot::load_android_simple, gbl_print, gbl_println, GblOps, Os, Result};
18 
19 // The following implements a demo for booting Android from disk. It can be run from
20 // Cuttlefish by adding `--android_efi_loader=<path of this EFI binary>` to the command line.
21 //
22 // A number of simplifications are made (see `android_load::load_android_simple()`):
23 //
24 //   * No A/B slot switching is performed. It always boot from *_a slot.
25 //   * No AVB is performed.
26 //   * No dynamic partitions.
27 //   * Only support V3/V4 image and Android 13+ (generic ramdisk from the "init_boot" partition)
28 //
29 // The missing pieces above are currently under development as part of the full end-to-end boot
30 // flow in libgbl, which will eventually replace this demo. The demo is currently used as an
31 // end-to-end test for libraries developed so far.
android_boot_demo(entry: EfiEntry) -> Result<()>32 pub fn android_boot_demo(entry: EfiEntry) -> Result<()> {
33     let blks = find_block_devices(&entry)?;
34     let mut ops = Ops::new(&entry, &blks[..], Some(Os::Android));
35     let mut bootimg_buffer = &mut vec![0u8; 128 * 1024 * 1024][..]; // 128 MB
36 
37     match ops.should_stop_in_fastboot() {
38         Ok(true) => fastboot(&mut ops, &mut bootimg_buffer)?,
39         Err(e) => {
40             gbl_println!(ops, "Warning: error while checking fastboot trigger ({:?})", e);
41             gbl_println!(ops, "Ignoring error and continuing with normal boot");
42         }
43         _ => {}
44     }
45 
46     gbl_println!(ops, "Try booting as Android");
47 
48     // Allocate buffer for load.
49     let mut load_buffer = vec![0u8; 128 * 1024 * 1024]; // 128MB
50 
51     let (ramdisk, fdt, kernel, remains) = if bootimg_buffer.starts_with(b"ANDROID!") {
52         let mut ramboot_ops = RambootOps { ops: &mut ops, bootimg_buffer };
53         load_android_simple(&mut ramboot_ops, &mut load_buffer[..])?
54     } else {
55         load_android_simple(&mut ops, &mut load_buffer[..])?
56     };
57 
58     gbl_println!(ops, "");
59     gbl_println!(
60         ops,
61         "Booting kernel @ {:#x}, ramdisk @ {:#x}, fdt @ {:#x}",
62         kernel.as_ptr() as usize,
63         ramdisk.as_ptr() as usize,
64         fdt.as_ptr() as usize
65     );
66     gbl_println!(ops, "");
67 
68     #[cfg(target_arch = "aarch64")]
69     {
70         drop(blks); // Drop `blks` to release the borrow on `entry`.
71         let _ = exit_boot_services(entry, remains)?;
72         // SAFETY: We currently targets at Cuttlefish emulator where images are provided valid.
73         unsafe { boot::aarch64::jump_linux_el2_or_lower(kernel, ramdisk, fdt) };
74     }
75 
76     #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
77     {
78         use fdt::Fdt;
79         use liberror::Error;
80         use libgbl::android_boot::BOOTARGS_PROP;
81 
82         let fdt = Fdt::new(&fdt[..])?;
83         drop(blks); // Drop `blks` to release the borrow on `entry`.
84         let efi_mmap = exit_boot_services(entry, remains)?;
85         // SAFETY: We currently target at Cuttlefish emulator where images are provided valid.
86         unsafe {
87             boot::x86::boot_linux_bzimage(
88                 kernel,
89                 ramdisk,
90                 fdt.get_property("chosen", BOOTARGS_PROP).unwrap(),
91                 |e820_entries| {
92                     // Convert EFI memory type to e820 memory type.
93                     if efi_mmap.len() > e820_entries.len() {
94                         return Err(Error::MemoryMapCallbackError(-1));
95                     }
96                     for (idx, mem) in efi_mmap.into_iter().enumerate() {
97                         e820_entries[idx] = boot::x86::e820entry {
98                             addr: mem.physical_start,
99                             size: mem.number_of_pages * 4096,
100                             type_: crate::utils::efi_to_e820_mem_type(mem.memory_type),
101                         };
102                     }
103                     Ok(efi_mmap.len().try_into().unwrap())
104                 },
105                 0x9_0000,
106             )?;
107         }
108         unreachable!();
109     }
110 
111     #[cfg(target_arch = "riscv64")]
112     {
113         let boot_hart_id = entry
114             .system_table()
115             .boot_services()
116             .find_first_and_open::<efi::protocol::riscv::RiscvBootProtocol>()?
117             .get_boot_hartid()?;
118         gbl_println!(ops, "riscv boot_hart_id: {}", boot_hart_id);
119         drop(blks); // Drop `blks` to release the borrow on `entry`.
120         let _ = exit_boot_services(entry, remains)?;
121         // SAFETY: We currently target at Cuttlefish emulator where images are provided valid.
122         unsafe { boot::riscv64::jump_linux(kernel, boot_hart_id, fdt) };
123     }
124 }
125