1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project
2*5225e6b1SAndroid Build Coastguard Worker //
3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*5225e6b1SAndroid Build Coastguard Worker //
7*5225e6b1SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*5225e6b1SAndroid Build Coastguard Worker //
9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License.
14*5225e6b1SAndroid Build Coastguard Worker
15*5225e6b1SAndroid Build Coastguard Worker // This EFI application implements a demo for booting Android/Fuchsia from disk. See
16*5225e6b1SAndroid Build Coastguard Worker // bootable/libbootloader/gbl/README.md for how to run the demo. See comments of
17*5225e6b1SAndroid Build Coastguard Worker // `android_boot:android_boot_demo()` and `fuchsia_boot:fuchsia_boot_demo()` for
18*5225e6b1SAndroid Build Coastguard Worker // supported/unsupported features at the moment.
19*5225e6b1SAndroid Build Coastguard Worker
20*5225e6b1SAndroid Build Coastguard Worker use crate::{
21*5225e6b1SAndroid Build Coastguard Worker net::{EfiGblNetwork, EfiTcpSocket},
22*5225e6b1SAndroid Build Coastguard Worker ops::Ops,
23*5225e6b1SAndroid Build Coastguard Worker };
24*5225e6b1SAndroid Build Coastguard Worker use alloc::{boxed::Box, vec::Vec};
25*5225e6b1SAndroid Build Coastguard Worker use core::{cmp::min, fmt::Write, future::Future, mem::take, pin::Pin, sync::atomic::AtomicU64};
26*5225e6b1SAndroid Build Coastguard Worker use efi::{
27*5225e6b1SAndroid Build Coastguard Worker efi_print, efi_println,
28*5225e6b1SAndroid Build Coastguard Worker protocol::{gbl_efi_fastboot_usb::GblFastbootUsbProtocol, Protocol},
29*5225e6b1SAndroid Build Coastguard Worker EfiEntry,
30*5225e6b1SAndroid Build Coastguard Worker };
31*5225e6b1SAndroid Build Coastguard Worker use fastboot::{TcpStream, Transport};
32*5225e6b1SAndroid Build Coastguard Worker use gbl_async::{block_on, YieldCounter};
33*5225e6b1SAndroid Build Coastguard Worker use liberror::{Error, Result};
34*5225e6b1SAndroid Build Coastguard Worker use libgbl::fastboot::{run_gbl_fastboot, GblTcpStream, GblUsbTransport, PinFutContainer};
35*5225e6b1SAndroid Build Coastguard Worker
36*5225e6b1SAndroid Build Coastguard Worker const DEFAULT_TIMEOUT_MS: u64 = 5_000;
37*5225e6b1SAndroid Build Coastguard Worker const FASTBOOT_TCP_PORT: u16 = 5554;
38*5225e6b1SAndroid Build Coastguard Worker
39*5225e6b1SAndroid Build Coastguard Worker struct EfiFastbootTcpTransport<'a, 'b, 'c> {
40*5225e6b1SAndroid Build Coastguard Worker socket: &'c mut EfiTcpSocket<'a, 'b>,
41*5225e6b1SAndroid Build Coastguard Worker }
42*5225e6b1SAndroid Build Coastguard Worker
43*5225e6b1SAndroid Build Coastguard Worker impl<'a, 'b, 'c> EfiFastbootTcpTransport<'a, 'b, 'c> {
new(socket: &'c mut EfiTcpSocket<'a, 'b>) -> Self44*5225e6b1SAndroid Build Coastguard Worker fn new(socket: &'c mut EfiTcpSocket<'a, 'b>) -> Self {
45*5225e6b1SAndroid Build Coastguard Worker Self { socket: socket }
46*5225e6b1SAndroid Build Coastguard Worker }
47*5225e6b1SAndroid Build Coastguard Worker }
48*5225e6b1SAndroid Build Coastguard Worker
49*5225e6b1SAndroid Build Coastguard Worker impl TcpStream for EfiFastbootTcpTransport<'_, '_, '_> {
50*5225e6b1SAndroid Build Coastguard Worker /// Reads to `out` for exactly `out.len()` number bytes from the TCP connection.
read_exact(&mut self, out: &mut [u8]) -> Result<()>51*5225e6b1SAndroid Build Coastguard Worker async fn read_exact(&mut self, out: &mut [u8]) -> Result<()> {
52*5225e6b1SAndroid Build Coastguard Worker self.socket.receive_exact(out, DEFAULT_TIMEOUT_MS).await
53*5225e6b1SAndroid Build Coastguard Worker }
54*5225e6b1SAndroid Build Coastguard Worker
55*5225e6b1SAndroid Build Coastguard Worker /// Sends exactly `data.len()` number bytes from `data` to the TCP connection.
write_exact(&mut self, data: &[u8]) -> Result<()>56*5225e6b1SAndroid Build Coastguard Worker async fn write_exact(&mut self, data: &[u8]) -> Result<()> {
57*5225e6b1SAndroid Build Coastguard Worker self.socket.send_exact(data, DEFAULT_TIMEOUT_MS).await
58*5225e6b1SAndroid Build Coastguard Worker }
59*5225e6b1SAndroid Build Coastguard Worker }
60*5225e6b1SAndroid Build Coastguard Worker
61*5225e6b1SAndroid Build Coastguard Worker impl GblTcpStream for EfiFastbootTcpTransport<'_, '_, '_> {
accept_new(&mut self) -> bool62*5225e6b1SAndroid Build Coastguard Worker fn accept_new(&mut self) -> bool {
63*5225e6b1SAndroid Build Coastguard Worker let efi_entry = self.socket.efi_entry;
64*5225e6b1SAndroid Build Coastguard Worker self.socket.poll();
65*5225e6b1SAndroid Build Coastguard Worker // If not listenining, start listening.
66*5225e6b1SAndroid Build Coastguard Worker // If not connected but it's been `DEFAULT_TIMEOUT_MS`, restart listening in case the remote
67*5225e6b1SAndroid Build Coastguard Worker // client disconnects in the middle of TCP handshake and leaves the socket in a half open
68*5225e6b1SAndroid Build Coastguard Worker // state.
69*5225e6b1SAndroid Build Coastguard Worker if !self.socket.is_listening_or_handshaking()
70*5225e6b1SAndroid Build Coastguard Worker || (!self.socket.check_active()
71*5225e6b1SAndroid Build Coastguard Worker && self.socket.time_since_last_listen() > DEFAULT_TIMEOUT_MS)
72*5225e6b1SAndroid Build Coastguard Worker {
73*5225e6b1SAndroid Build Coastguard Worker let _ = self
74*5225e6b1SAndroid Build Coastguard Worker .socket
75*5225e6b1SAndroid Build Coastguard Worker .listen(FASTBOOT_TCP_PORT)
76*5225e6b1SAndroid Build Coastguard Worker .inspect_err(|e| efi_println!(efi_entry, "TCP listen error: {:?}", e));
77*5225e6b1SAndroid Build Coastguard Worker
78*5225e6b1SAndroid Build Coastguard Worker // TODO(b/368647237): Enable only in Fuchsia context.
79*5225e6b1SAndroid Build Coastguard Worker self.socket.broadcast_fuchsia_fastboot_mdns();
80*5225e6b1SAndroid Build Coastguard Worker } else if self.socket.check_active() {
81*5225e6b1SAndroid Build Coastguard Worker self.socket.set_io_yield_threshold(1024 * 1024); // 1MB
82*5225e6b1SAndroid Build Coastguard Worker let remote = self.socket.get_socket().remote_endpoint().unwrap();
83*5225e6b1SAndroid Build Coastguard Worker efi_println!(efi_entry, "TCP connection from {}", remote);
84*5225e6b1SAndroid Build Coastguard Worker return true;
85*5225e6b1SAndroid Build Coastguard Worker }
86*5225e6b1SAndroid Build Coastguard Worker false
87*5225e6b1SAndroid Build Coastguard Worker }
88*5225e6b1SAndroid Build Coastguard Worker }
89*5225e6b1SAndroid Build Coastguard Worker
90*5225e6b1SAndroid Build Coastguard Worker /// `UsbTransport` implements the `fastboot::Transport` trait using USB interfaces from
91*5225e6b1SAndroid Build Coastguard Worker /// GBL_EFI_FASTBOOT_USB_PROTOCOL.
92*5225e6b1SAndroid Build Coastguard Worker pub struct UsbTransport<'a> {
93*5225e6b1SAndroid Build Coastguard Worker max_packet_size: usize,
94*5225e6b1SAndroid Build Coastguard Worker protocol: Protocol<'a, GblFastbootUsbProtocol>,
95*5225e6b1SAndroid Build Coastguard Worker io_yield_counter: YieldCounter,
96*5225e6b1SAndroid Build Coastguard Worker // Buffer for prefetching an incoming packet in `wait_for_packet()`.
97*5225e6b1SAndroid Build Coastguard Worker // Alternatively we can also consider adding an EFI event for packet arrive. But UEFI firmware
98*5225e6b1SAndroid Build Coastguard Worker // may be more complicated.
99*5225e6b1SAndroid Build Coastguard Worker prefetched: (Vec<u8>, usize),
100*5225e6b1SAndroid Build Coastguard Worker }
101*5225e6b1SAndroid Build Coastguard Worker
102*5225e6b1SAndroid Build Coastguard Worker impl<'a> UsbTransport<'a> {
new(max_packet_size: usize, protocol: Protocol<'a, GblFastbootUsbProtocol>) -> Self103*5225e6b1SAndroid Build Coastguard Worker fn new(max_packet_size: usize, protocol: Protocol<'a, GblFastbootUsbProtocol>) -> Self {
104*5225e6b1SAndroid Build Coastguard Worker Self {
105*5225e6b1SAndroid Build Coastguard Worker max_packet_size,
106*5225e6b1SAndroid Build Coastguard Worker protocol,
107*5225e6b1SAndroid Build Coastguard Worker io_yield_counter: YieldCounter::new(1024 * 1024),
108*5225e6b1SAndroid Build Coastguard Worker prefetched: (vec![0u8; max_packet_size], 0),
109*5225e6b1SAndroid Build Coastguard Worker }
110*5225e6b1SAndroid Build Coastguard Worker }
111*5225e6b1SAndroid Build Coastguard Worker
112*5225e6b1SAndroid Build Coastguard Worker /// Polls and cache the next USB packet.
113*5225e6b1SAndroid Build Coastguard Worker ///
114*5225e6b1SAndroid Build Coastguard Worker /// Returns Ok(true) if there is a new packet. Ok(false) if there is no incoming packet. Err()
115*5225e6b1SAndroid Build Coastguard Worker /// otherwise.
poll_next_packet(&mut self) -> Result<bool>116*5225e6b1SAndroid Build Coastguard Worker fn poll_next_packet(&mut self) -> Result<bool> {
117*5225e6b1SAndroid Build Coastguard Worker match &mut self.prefetched {
118*5225e6b1SAndroid Build Coastguard Worker (pkt, len) if *len == 0 => match self.protocol.fastboot_usb_receive(pkt) {
119*5225e6b1SAndroid Build Coastguard Worker Ok(out_size) => {
120*5225e6b1SAndroid Build Coastguard Worker *len = out_size;
121*5225e6b1SAndroid Build Coastguard Worker return Ok(true);
122*5225e6b1SAndroid Build Coastguard Worker }
123*5225e6b1SAndroid Build Coastguard Worker Err(Error::NotReady) => return Ok(false),
124*5225e6b1SAndroid Build Coastguard Worker Err(e) => return Err(e),
125*5225e6b1SAndroid Build Coastguard Worker },
126*5225e6b1SAndroid Build Coastguard Worker _ => Ok(true),
127*5225e6b1SAndroid Build Coastguard Worker }
128*5225e6b1SAndroid Build Coastguard Worker }
129*5225e6b1SAndroid Build Coastguard Worker }
130*5225e6b1SAndroid Build Coastguard Worker
131*5225e6b1SAndroid Build Coastguard Worker impl Transport for UsbTransport<'_> {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize>132*5225e6b1SAndroid Build Coastguard Worker async fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize> {
133*5225e6b1SAndroid Build Coastguard Worker let len = match &mut self.prefetched {
134*5225e6b1SAndroid Build Coastguard Worker (pkt, len) if *len > 0 => {
135*5225e6b1SAndroid Build Coastguard Worker let out = out.get_mut(..*len).ok_or(Error::BufferTooSmall(Some(*len)))?;
136*5225e6b1SAndroid Build Coastguard Worker let src = pkt.get(..*len).ok_or(Error::Other(Some("Invalid USB read size")))?;
137*5225e6b1SAndroid Build Coastguard Worker out.clone_from_slice(src);
138*5225e6b1SAndroid Build Coastguard Worker take(len)
139*5225e6b1SAndroid Build Coastguard Worker }
140*5225e6b1SAndroid Build Coastguard Worker _ => self.protocol.receive_packet(out).await?,
141*5225e6b1SAndroid Build Coastguard Worker };
142*5225e6b1SAndroid Build Coastguard Worker // Forces a yield to the executor if the data received/sent reaches a certain
143*5225e6b1SAndroid Build Coastguard Worker // threshold. This is to prevent the async code from holding up the CPU for too long
144*5225e6b1SAndroid Build Coastguard Worker // in case IO speed is high and the executor uses cooperative scheduling.
145*5225e6b1SAndroid Build Coastguard Worker self.io_yield_counter.increment(len.try_into().unwrap()).await;
146*5225e6b1SAndroid Build Coastguard Worker Ok(len)
147*5225e6b1SAndroid Build Coastguard Worker }
148*5225e6b1SAndroid Build Coastguard Worker
send_packet(&mut self, packet: &[u8]) -> Result<()>149*5225e6b1SAndroid Build Coastguard Worker async fn send_packet(&mut self, packet: &[u8]) -> Result<()> {
150*5225e6b1SAndroid Build Coastguard Worker let mut curr = &packet[..];
151*5225e6b1SAndroid Build Coastguard Worker while !curr.is_empty() {
152*5225e6b1SAndroid Build Coastguard Worker let to_send = min(curr.len(), self.max_packet_size);
153*5225e6b1SAndroid Build Coastguard Worker self.protocol.send_packet(&curr[..to_send], DEFAULT_TIMEOUT_MS).await?;
154*5225e6b1SAndroid Build Coastguard Worker // Forces a yield to the executor if the data received/sent reaches a certain
155*5225e6b1SAndroid Build Coastguard Worker // threshold. This is to prevent the async code from holding up the CPU for too long
156*5225e6b1SAndroid Build Coastguard Worker // in case IO speed is high and the executor uses cooperative scheduling.
157*5225e6b1SAndroid Build Coastguard Worker self.io_yield_counter.increment(to_send.try_into().unwrap()).await;
158*5225e6b1SAndroid Build Coastguard Worker curr = &curr[to_send..];
159*5225e6b1SAndroid Build Coastguard Worker }
160*5225e6b1SAndroid Build Coastguard Worker Ok(())
161*5225e6b1SAndroid Build Coastguard Worker }
162*5225e6b1SAndroid Build Coastguard Worker }
163*5225e6b1SAndroid Build Coastguard Worker
164*5225e6b1SAndroid Build Coastguard Worker impl GblUsbTransport for UsbTransport<'_> {
has_packet(&mut self) -> bool165*5225e6b1SAndroid Build Coastguard Worker fn has_packet(&mut self) -> bool {
166*5225e6b1SAndroid Build Coastguard Worker let efi_entry = self.protocol.efi_entry();
167*5225e6b1SAndroid Build Coastguard Worker self.poll_next_packet()
168*5225e6b1SAndroid Build Coastguard Worker .inspect_err(|e| efi_println!(efi_entry, "Error while polling next packet: {:?}", e))
169*5225e6b1SAndroid Build Coastguard Worker .unwrap_or(false)
170*5225e6b1SAndroid Build Coastguard Worker }
171*5225e6b1SAndroid Build Coastguard Worker }
172*5225e6b1SAndroid Build Coastguard Worker
173*5225e6b1SAndroid Build Coastguard Worker /// Initializes the Fastboot USB interface and returns a `UsbTransport`.
init_usb(efi_entry: &EfiEntry) -> Result<UsbTransport>174*5225e6b1SAndroid Build Coastguard Worker fn init_usb(efi_entry: &EfiEntry) -> Result<UsbTransport> {
175*5225e6b1SAndroid Build Coastguard Worker let protocol =
176*5225e6b1SAndroid Build Coastguard Worker efi_entry.system_table().boot_services().find_first_and_open::<GblFastbootUsbProtocol>()?;
177*5225e6b1SAndroid Build Coastguard Worker match protocol.fastboot_usb_interface_stop() {
178*5225e6b1SAndroid Build Coastguard Worker Err(e) if e != Error::NotStarted => Err(e),
179*5225e6b1SAndroid Build Coastguard Worker _ => Ok(UsbTransport::new(protocol.fastboot_usb_interface_start()?, protocol)),
180*5225e6b1SAndroid Build Coastguard Worker }
181*5225e6b1SAndroid Build Coastguard Worker }
182*5225e6b1SAndroid Build Coastguard Worker
183*5225e6b1SAndroid Build Coastguard Worker // Wrapper of vector of pinned futures.
184*5225e6b1SAndroid Build Coastguard Worker #[derive(Default)]
185*5225e6b1SAndroid Build Coastguard Worker struct VecPinFut<'a>(Vec<Pin<Box<dyn Future<Output = ()> + 'a>>>);
186*5225e6b1SAndroid Build Coastguard Worker
187*5225e6b1SAndroid Build Coastguard Worker impl<'a> PinFutContainer<'a> for VecPinFut<'a> {
add_with<F: Future<Output = ()> + 'a>(&mut self, f: impl FnOnce() -> F)188*5225e6b1SAndroid Build Coastguard Worker fn add_with<F: Future<Output = ()> + 'a>(&mut self, f: impl FnOnce() -> F) {
189*5225e6b1SAndroid Build Coastguard Worker self.0.push(Box::pin(f()));
190*5225e6b1SAndroid Build Coastguard Worker }
191*5225e6b1SAndroid Build Coastguard Worker
for_each_remove_if( &mut self, mut cb: impl FnMut(&mut Pin<&mut (dyn Future<Output = ()> + 'a)>) -> bool, )192*5225e6b1SAndroid Build Coastguard Worker fn for_each_remove_if(
193*5225e6b1SAndroid Build Coastguard Worker &mut self,
194*5225e6b1SAndroid Build Coastguard Worker mut cb: impl FnMut(&mut Pin<&mut (dyn Future<Output = ()> + 'a)>) -> bool,
195*5225e6b1SAndroid Build Coastguard Worker ) {
196*5225e6b1SAndroid Build Coastguard Worker for idx in (0..self.0.len()).rev() {
197*5225e6b1SAndroid Build Coastguard Worker cb(&mut self.0[idx].as_mut()).then(|| self.0.swap_remove(idx));
198*5225e6b1SAndroid Build Coastguard Worker }
199*5225e6b1SAndroid Build Coastguard Worker }
200*5225e6b1SAndroid Build Coastguard Worker }
201*5225e6b1SAndroid Build Coastguard Worker
fastboot(efi_gbl_ops: &mut Ops, bootimg_buf: &mut [u8]) -> Result<()>202*5225e6b1SAndroid Build Coastguard Worker pub fn fastboot(efi_gbl_ops: &mut Ops, bootimg_buf: &mut [u8]) -> Result<()> {
203*5225e6b1SAndroid Build Coastguard Worker let efi_entry = efi_gbl_ops.efi_entry;
204*5225e6b1SAndroid Build Coastguard Worker efi_println!(efi_entry, "Entering fastboot mode...");
205*5225e6b1SAndroid Build Coastguard Worker
206*5225e6b1SAndroid Build Coastguard Worker let usb = init_usb(efi_entry)
207*5225e6b1SAndroid Build Coastguard Worker .inspect(|_| efi_println!(efi_entry, "Started Fastboot over USB."))
208*5225e6b1SAndroid Build Coastguard Worker .inspect_err(|e| efi_println!(efi_entry, "Failed to start Fastboot over USB. {:?}.", e))
209*5225e6b1SAndroid Build Coastguard Worker .ok();
210*5225e6b1SAndroid Build Coastguard Worker
211*5225e6b1SAndroid Build Coastguard Worker let ts = AtomicU64::new(0);
212*5225e6b1SAndroid Build Coastguard Worker let mut net: EfiGblNetwork = Default::default();
213*5225e6b1SAndroid Build Coastguard Worker let mut tcp = net
214*5225e6b1SAndroid Build Coastguard Worker .init(efi_entry, &ts)
215*5225e6b1SAndroid Build Coastguard Worker .inspect(|v| {
216*5225e6b1SAndroid Build Coastguard Worker efi_println!(efi_entry, "Started Fastboot over TCP");
217*5225e6b1SAndroid Build Coastguard Worker efi_println!(efi_entry, "IP address:");
218*5225e6b1SAndroid Build Coastguard Worker v.interface().ip_addrs().iter().for_each(|v| {
219*5225e6b1SAndroid Build Coastguard Worker efi_println!(efi_entry, "\t{}", v.address());
220*5225e6b1SAndroid Build Coastguard Worker });
221*5225e6b1SAndroid Build Coastguard Worker })
222*5225e6b1SAndroid Build Coastguard Worker .inspect_err(|e| efi_println!(efi_entry, "Failed to start EFI network. {:?}.", e))
223*5225e6b1SAndroid Build Coastguard Worker .ok();
224*5225e6b1SAndroid Build Coastguard Worker let tcp = tcp.as_mut().map(|v| EfiFastbootTcpTransport::new(v));
225*5225e6b1SAndroid Build Coastguard Worker
226*5225e6b1SAndroid Build Coastguard Worker let download_buffers = vec![vec![0u8; 512 * 1024 * 1024]; 2].into();
227*5225e6b1SAndroid Build Coastguard Worker block_on(run_gbl_fastboot(
228*5225e6b1SAndroid Build Coastguard Worker efi_gbl_ops,
229*5225e6b1SAndroid Build Coastguard Worker &download_buffers,
230*5225e6b1SAndroid Build Coastguard Worker VecPinFut::default(),
231*5225e6b1SAndroid Build Coastguard Worker usb,
232*5225e6b1SAndroid Build Coastguard Worker tcp,
233*5225e6b1SAndroid Build Coastguard Worker bootimg_buf,
234*5225e6b1SAndroid Build Coastguard Worker ));
235*5225e6b1SAndroid Build Coastguard Worker
236*5225e6b1SAndroid Build Coastguard Worker efi_println!(efi_entry, "Leaving fastboot mode...");
237*5225e6b1SAndroid Build Coastguard Worker
238*5225e6b1SAndroid Build Coastguard Worker Ok(())
239*5225e6b1SAndroid Build Coastguard Worker }
240