1 // Copyright 2024, 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 // This EFI application implements a demo for booting Android/Fuchsia from disk. See
16 // bootable/libbootloader/gbl/README.md for how to run the demo. See comments of
17 // `android_boot:android_boot_demo()` and `fuchsia_boot:fuchsia_boot_demo()` for
18 // supported/unsupported features at the moment.
19
20 use crate::error::{EfiAppError, GblEfiError, Result as GblResult};
21 use crate::net::{with_efi_network, EfiTcpSocket};
22 use crate::utils::{find_gpt_devices, loop_with_timeout};
23 use core::{fmt::Write, result::Result};
24 use efi::{
25 defs::{EFI_STATUS_NOT_READY, EFI_STATUS_NOT_STARTED},
26 efi_print, efi_println,
27 protocol::{android_boot::AndroidBootProtocol, Protocol},
28 EfiEntry,
29 };
30 use fastboot::{Fastboot, TcpStream, Transport, TransportError};
31 use libgbl::fastboot::GblFastboot;
32
33 const DEFAULT_TIMEOUT_MS: u64 = 5_000;
34 const FASTBOOT_TCP_PORT: u16 = 5554;
35
36 struct EfiFastbootTcpTransport<'a, 'b, 'c> {
37 last_err: GblResult<()>,
38 socket: &'c mut EfiTcpSocket<'a, 'b>,
39 }
40
41 impl<'a, 'b, 'c> EfiFastbootTcpTransport<'a, 'b, 'c> {
new(socket: &'c mut EfiTcpSocket<'a, 'b>) -> Self42 fn new(socket: &'c mut EfiTcpSocket<'a, 'b>) -> Self {
43 Self { last_err: Ok(()), socket: socket }
44 }
45 }
46
47 impl TcpStream for EfiFastbootTcpTransport<'_, '_, '_> {
48 /// Reads to `out` for exactly `out.len()` number bytes from the TCP connection.
read_exact(&mut self, out: &mut [u8]) -> Result<(), TransportError>49 fn read_exact(&mut self, out: &mut [u8]) -> Result<(), TransportError> {
50 self.last_err = self.socket.receive_exact(out, DEFAULT_TIMEOUT_MS);
51 self.last_err.as_ref().map_err(|_| TransportError::Others("TCP read error"))?;
52 Ok(())
53 }
54
55 /// Sends exactly `data.len()` number bytes from `data` to the TCP connection.
write_exact(&mut self, data: &[u8]) -> Result<(), TransportError>56 fn write_exact(&mut self, data: &[u8]) -> Result<(), TransportError> {
57 self.last_err = self.socket.send_exact(data, DEFAULT_TIMEOUT_MS);
58 self.last_err.as_ref().map_err(|_| TransportError::Others("TCP write error"))?;
59 Ok(())
60 }
61 }
62
63 /// `UsbTransport` implements the `fastboot::Transport` trait using USB interfaces from
64 /// EFI_ANDROID_BOOT_PROTOCOL.
65 pub struct UsbTransport<'a, 'b> {
66 last_err: GblResult<()>,
67 max_packet_size: usize,
68 protocol: &'b Protocol<'a, AndroidBootProtocol>,
69 }
70
71 impl<'a, 'b> UsbTransport<'a, 'b> {
new(max_packet_size: usize, protocol: &'b Protocol<'a, AndroidBootProtocol>) -> Self72 fn new(max_packet_size: usize, protocol: &'b Protocol<'a, AndroidBootProtocol>) -> Self {
73 Self { last_err: Ok(()), max_packet_size: max_packet_size, protocol: protocol }
74 }
75
76 /// Waits for the previous send to complete up to `DEFAULT_TIMEOUT_MS` timeout.
wait_for_send(&self) -> GblResult<()>77 fn wait_for_send(&self) -> GblResult<()> {
78 loop_with_timeout(self.protocol.efi_entry(), DEFAULT_TIMEOUT_MS, || {
79 match (|| -> GblResult<bool> {
80 Ok(self
81 .protocol
82 .efi_entry()
83 .system_table()
84 .boot_services()
85 .check_event(&self.protocol.wait_for_send_completion()?)?)
86 })() {
87 Ok(true) => Ok(Ok(())),
88 Ok(false) => Err(false),
89 Err(e) => Ok(Err(e)),
90 }
91 })?
92 .ok_or(EfiAppError::Timeout)??;
93 Ok(())
94 }
95 }
96
97 impl Transport for UsbTransport<'_, '_> {
receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError>98 fn receive_packet(&mut self, out: &mut [u8]) -> Result<usize, TransportError> {
99 let mut out_size = 0;
100 self.last_err = Ok(());
101 match self.protocol.fastboot_usb_receive(out, &mut out_size) {
102 Ok(()) => Ok(out_size),
103 Err(e) if e.is_efi_err(EFI_STATUS_NOT_READY) => Ok(0),
104 Err(e) => {
105 self.last_err = Err(e.into());
106 Err(TransportError::Others("USB receive error"))
107 }
108 }
109 }
110
send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError>111 fn send_packet(&mut self, packet: &[u8]) -> Result<(), TransportError> {
112 let mut sent = 0;
113 self.last_err = (|| -> GblResult<()> {
114 while sent < packet.len() {
115 let to_send = core::cmp::min(packet.len() - sent, self.max_packet_size);
116 let mut out_size = 0;
117 self.protocol.fastboot_usb_send(&packet[sent..][..to_send], &mut out_size)?;
118 self.wait_for_send()?;
119 sent += to_send;
120 }
121 Ok(())
122 })();
123 Ok(*self.last_err.as_ref().map_err(|_| TransportError::Others("USB send error"))?)
124 }
125 }
126
127 /// Loops and polls both USB and TCP transport. Runs Fastboot if any is available.
fastboot_loop( efi_entry: &EfiEntry, gbl_fb: &mut GblFastboot, fastboot: &mut Fastboot, mut socket: Option<&mut EfiTcpSocket>, mut usb: Option<&mut UsbTransport>, ) -> GblResult<()>128 fn fastboot_loop(
129 efi_entry: &EfiEntry,
130 gbl_fb: &mut GblFastboot,
131 fastboot: &mut Fastboot,
132 mut socket: Option<&mut EfiTcpSocket>,
133 mut usb: Option<&mut UsbTransport>,
134 ) -> GblResult<()> {
135 if socket.is_none() && usb.is_none() {
136 return Err(EfiAppError::Unsupported.into());
137 }
138
139 efi_println!(efi_entry, "Fastboot USB: {}", usb.as_ref().map_or("No", |_| "Yes"));
140 if let Some(socket) = socket.as_ref() {
141 efi_println!(efi_entry, "Fastboot TCP: Yes");
142 efi_println!(efi_entry, "Device IP addresses:");
143 socket.interface().ip_addrs().iter().for_each(|v| {
144 efi_println!(efi_entry, "\t{}", v.address());
145 });
146 } else {
147 efi_println!(efi_entry, "Fastboot TCP: No");
148 }
149
150 let mut listen_start_timestamp = EfiTcpSocket::timestamp(0);
151 loop {
152 // Checks and processes commands over USB.
153 if let Some(usb) = usb.as_mut() {
154 if fastboot.process_next_command(*usb, gbl_fb).is_err() {
155 efi_println!(efi_entry, "Fastboot USB error: {:?}", usb.last_err);
156 }
157 }
158
159 // Checks and processes commands over TCP.
160 if let Some(socket) = socket.as_mut() {
161 socket.poll();
162 let mut reset_socket = false;
163 if socket.check_active() {
164 let remote = socket.get_socket().remote_endpoint().unwrap();
165 efi_println!(efi_entry, "TCP connection from {}", remote);
166 let mut transport = EfiFastbootTcpTransport::new(socket);
167 let _ = fastboot.run_tcp_session(&mut transport, gbl_fb);
168 match transport.last_err {
169 Ok(()) | Err(GblEfiError::EfiAppError(EfiAppError::PeerClosed)) => {}
170 Err(e) => {
171 efi_println!(efi_entry, "Fastboot TCP error {:?}", e);
172 }
173 }
174 reset_socket = true;
175 } else if EfiTcpSocket::timestamp(listen_start_timestamp) > DEFAULT_TIMEOUT_MS {
176 // Reset once in a while in case a remote client disconnects in the middle of
177 // TCP handshake and leaves the socket in a half open state.
178 reset_socket = true;
179 }
180
181 if reset_socket {
182 listen_start_timestamp = EfiTcpSocket::timestamp(0);
183 if let Err(e) = socket.listen(FASTBOOT_TCP_PORT) {
184 efi_println!(efi_entry, "TCP listen error: {:?}", e);
185 }
186 }
187 }
188 }
189 }
190
191 /// Initializes the Fastboot USB interface and returns a `UsbTransport`.
init_usb<'a, 'b>( android_boot_protocol: &Option<&'b Protocol<'a, AndroidBootProtocol>>, ) -> GblResult<UsbTransport<'a, 'b>>192 fn init_usb<'a, 'b>(
193 android_boot_protocol: &Option<&'b Protocol<'a, AndroidBootProtocol>>,
194 ) -> GblResult<UsbTransport<'a, 'b>> {
195 let protocol = android_boot_protocol.ok_or(EfiAppError::Unsupported)?;
196 match protocol.fastboot_usb_interface_stop() {
197 Err(e) if !e.is_efi_err(EFI_STATUS_NOT_STARTED) => return Err(e.into()),
198 _ => {}
199 };
200 Ok(UsbTransport::new(protocol.fastboot_usb_interface_start()?, protocol))
201 }
202
203 /// Runs Fastboot.
run_fastboot( efi_entry: &EfiEntry, android_boot_protocol: Option<&Protocol<'_, AndroidBootProtocol>>, ) -> GblResult<()>204 pub fn run_fastboot(
205 efi_entry: &EfiEntry,
206 android_boot_protocol: Option<&Protocol<'_, AndroidBootProtocol>>,
207 ) -> GblResult<()> {
208 let mut gpt_devices = find_gpt_devices(efi_entry)?;
209 let mut gbl_fb = GblFastboot::new(&mut gpt_devices);
210 // TODO(b/328786603): Figure out where to get download buffer size.
211 let mut download_buffer = vec![0u8; 512 * 1024 * 1024];
212 let mut fastboot = Fastboot::new(&mut download_buffer[..]);
213
214 let mut usb = match init_usb(&android_boot_protocol) {
215 Ok(v) => Some(v),
216 Err(e) => {
217 efi_println!(efi_entry, "Failed to start Fastboot over USB. {:?}.", e);
218 None
219 }
220 };
221
222 match with_efi_network(efi_entry, |socket| -> GblResult<()> {
223 fastboot_loop(efi_entry, &mut gbl_fb, &mut fastboot, Some(socket), usb.as_mut())
224 }) {
225 Err(e) => {
226 efi_println!(efi_entry, "Failed to start EFI network. {:?}.", e);
227 fastboot_loop(efi_entry, &mut gbl_fb, &mut fastboot, None, usb.as_mut())
228 }
229 v => v?,
230 }
231 }
232