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::{
16 fastboot::{with_fastboot_channels, VecPinFut},
17 ops::Ops,
18 utils::{get_platform_buffer_info, BufferInfo},
19 };
20 use alloc::{boxed::Box, vec::Vec};
21 use core::{fmt::Write, str::from_utf8};
22 use efi::{efi_print, efi_println, exit_boot_services, EfiEntry};
23 use efi_types::{GBL_IMAGE_TYPE_FASTBOOT, GBL_IMAGE_TYPE_OS_LOAD};
24 use gbl_async::poll;
25 use libgbl::{android_boot::android_main, gbl_print, gbl_println, GblOps, Result};
26
27 const SZ_MB: usize = 1024 * 1024;
28
29 /// Android bootloader main entry (before booting).
30 ///
31 /// On success, returns a tuple of slices (ramdisk, fdt, kernel, remains).
efi_android_load( ops: &mut Ops, ) -> Result<(&'static mut [u8], &'static mut [u8], &'static mut [u8], &'static mut [u8])>32 pub fn efi_android_load(
33 ops: &mut Ops,
34 ) -> Result<(&'static mut [u8], &'static mut [u8], &'static mut [u8], &'static mut [u8])> {
35 let entry = ops.efi_entry;
36 // Prepares the OS load buffer.
37 let img_type_os_load = from_utf8(GBL_IMAGE_TYPE_OS_LOAD).unwrap();
38 let load_buffer = match get_platform_buffer_info(&entry, img_type_os_load, 256 * SZ_MB) {
39 BufferInfo::Static(v) => v,
40 BufferInfo::Alloc(sz) => {
41 let alloc = vec![0u8; sz];
42 gbl_println!(ops, "Allocated {:#x} bytes for OS load buffer.", alloc.len());
43 alloc.leak()
44 }
45 };
46
47 // Checks if we have a reserved buffer for fastboot
48 let img_type_fastboot = from_utf8(GBL_IMAGE_TYPE_FASTBOOT).unwrap();
49 let mut fastboot_buffer_info = None;
50
51 gbl_println!(ops, "Try booting as Android");
52
53 Ok(android_main(ops, load_buffer.as_mut(), |fb| {
54 // Note: `get_or_insert_with` lazily evaluates closure (only when insert is necessary).
55 let buffer = fastboot_buffer_info.get_or_insert_with(|| {
56 get_platform_buffer_info(&entry, img_type_fastboot, 512 * SZ_MB)
57 });
58 let mut alloc;
59 let buffer = match buffer {
60 BufferInfo::Static(v) => &mut v[..],
61 BufferInfo::Alloc(sz) => {
62 alloc = vec![0u8; *sz];
63 efi_println!(entry, "Allocated {:#x} bytes for fastboot buffer.", alloc.len());
64 &mut alloc
65 }
66 };
67 // TODO(b/383620444): Investigate letting GblOps return fastboot channels.
68 with_fastboot_channels(&entry, |local, usb, tcp| {
69 // We currently only consider 1 parallell flash + 1 parallel download.
70 // This can be made configurable if necessary.
71 const GBL_FB_N: usize = 2;
72 let mut bufs = Vec::from_iter(buffer.chunks_exact_mut(buffer.len() / GBL_FB_N));
73 let bufs = &(&mut bufs[..]).into();
74 let mut fut = Box::pin(fb.run(bufs, VecPinFut::default(), local, usb, tcp));
75 while poll(&mut fut).is_none() {}
76 })
77 })?)
78 }
79
80 /// Exits boot services and boots loaded android images.
efi_android_boot( entry: EfiEntry, kernel: &[u8], ramdisk: &[u8], fdt: &[u8], remains: &mut [u8], ) -> Result<()>81 pub fn efi_android_boot(
82 entry: EfiEntry,
83 kernel: &[u8],
84 ramdisk: &[u8],
85 fdt: &[u8],
86 remains: &mut [u8],
87 ) -> Result<()> {
88 efi_println!(entry, "");
89 efi_println!(
90 entry,
91 "Booting kernel @ {:#x}, ramdisk @ {:#x}, fdt @ {:#x}",
92 kernel.as_ptr() as usize,
93 ramdisk.as_ptr() as usize,
94 fdt.as_ptr() as usize
95 );
96 efi_println!(entry, "");
97
98 #[cfg(target_arch = "aarch64")]
99 {
100 let _ = exit_boot_services(entry, remains)?;
101 // SAFETY: We currently targets at Cuttlefish emulator where images are provided valid.
102 unsafe { boot::aarch64::jump_linux_el2_or_lower(kernel, ramdisk, fdt) };
103 }
104
105 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
106 {
107 use fdt::Fdt;
108 use liberror::Error;
109 use libgbl::android_boot::BOOTARGS_PROP;
110
111 let fdt = Fdt::new(&fdt[..])?;
112 let efi_mmap = exit_boot_services(entry, remains)?;
113 // SAFETY: We currently target at Cuttlefish emulator where images are provided valid.
114 unsafe {
115 boot::x86::boot_linux_bzimage(
116 kernel,
117 ramdisk,
118 fdt.get_property("chosen", BOOTARGS_PROP).unwrap(),
119 |e820_entries| {
120 // Convert EFI memory type to e820 memory type.
121 if efi_mmap.len() > e820_entries.len() {
122 return Err(Error::MemoryMapCallbackError(-1));
123 }
124 for (idx, mem) in efi_mmap.into_iter().enumerate() {
125 e820_entries[idx] = boot::x86::e820entry {
126 addr: mem.physical_start,
127 size: mem.number_of_pages * 4096,
128 type_: crate::utils::efi_to_e820_mem_type(mem.memory_type),
129 };
130 }
131 Ok(efi_mmap.len().try_into().unwrap())
132 },
133 0x9_0000,
134 )?;
135 }
136 unreachable!();
137 }
138
139 #[cfg(target_arch = "riscv64")]
140 {
141 let boot_hart_id = entry
142 .system_table()
143 .boot_services()
144 .find_first_and_open::<efi::protocol::riscv::RiscvBootProtocol>()?
145 .get_boot_hartid()?;
146 efi_println!(entry, "riscv boot_hart_id: {}", boot_hart_id);
147 let _ = exit_boot_services(entry, remains)?;
148 // SAFETY: We currently target at Cuttlefish emulator where images are provided valid.
149 unsafe { boot::riscv64::jump_linux(kernel, boot_hart_id, fdt) };
150 }
151 }
152