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 use crate::{
16 error::{listen_to_unified, recv_to_unified, send_to_unified},
17 utils::{get_device_path, loop_with_timeout},
18 };
19 use alloc::{boxed::Box, vec::Vec};
20 use core::{
21 fmt::Write,
22 sync::atomic::{AtomicU64, Ordering},
23 time::Duration,
24 };
25 use efi::{
26 efi_print, efi_println,
27 protocol::{simple_network::SimpleNetworkProtocol, Protocol},
28 utils::Timeout,
29 DeviceHandle, EfiEntry, Event, EventNotify, EventType, Tpl,
30 };
31 use efi_types::{EfiEvent, EfiMacAddress, EFI_TIMER_DELAY_TIMER_PERIODIC};
32 use gbl_async::{yield_now, YieldCounter};
33 use liberror::{Error, Result};
34 use libgbl::fastboot::fuchsia_fastboot_mdns_packet;
35 use smoltcp::{
36 iface::{Config, Interface, SocketSet, SocketStorage},
37 phy,
38 phy::{Device, DeviceCapabilities, Medium},
39 socket::{
40 tcp::{Socket as TcpSocket, SocketBuffer, State},
41 udp::{PacketBuffer, Socket as UdpSocket, UdpMetadata},
42 },
43 storage::PacketMetadata,
44 time::Instant,
45 wire::{EthernetAddress, IpAddress, IpCidr, IpListenEndpoint, Ipv6Address},
46 };
47
48 /// Ethernet frame size for frame pool.
49 const ETHERNET_FRAME_SIZE: usize = 1536;
50 // Update period for `NETWORK_TIMESTAMP`.
51 const NETWORK_TIMESTAMP_UPDATE_PERIOD: Duration = Duration::from_millis(50);
52 // Size of the socket tx/rx application data buffer.
53 const SOCKET_TX_RX_BUFFER: usize = 256 * 1024;
54
55 /// Performs a shutdown and restart of the simple network protocol.
reset_simple_network<'a>(snp: &Protocol<'a, SimpleNetworkProtocol>) -> Result<()>56 fn reset_simple_network<'a>(snp: &Protocol<'a, SimpleNetworkProtocol>) -> Result<()> {
57 match snp.shutdown() {
58 Err(e) if e != Error::NotStarted => return Err(e),
59 _ => {}
60 };
61
62 match snp.start() {
63 Err(e) if e != Error::AlreadyStarted => return Err(e),
64 _ => {}
65 };
66 snp.initialize(0, 0)?;
67 Ok(snp.reset(true)?)
68 }
69
70 /// `EfiNetworkDevice` manages a frame pool and handles receiving/sending network frames.
71 pub struct EfiNetworkDevice<'a> {
72 protocol: Protocol<'a, SimpleNetworkProtocol>,
73 rx_frame: Box<[u8; ETHERNET_FRAME_SIZE]>,
74 tx_frames: Vec<*mut [u8; ETHERNET_FRAME_SIZE]>,
75 tx_frame_curr: usize, // Circular next index into tx_frames.
76 efi_entry: &'a EfiEntry,
77 }
78
79 impl<'a> EfiNetworkDevice<'a> {
80 /// Creates an new instance. Allocates `extra_tx_frames+1` number of TX frames.
new( protocol: Protocol<'a, SimpleNetworkProtocol>, extra_tx_frames: usize, efi_entry: &'a EfiEntry, ) -> Self81 pub fn new(
82 protocol: Protocol<'a, SimpleNetworkProtocol>,
83 extra_tx_frames: usize,
84 efi_entry: &'a EfiEntry,
85 ) -> Self {
86 let mut ret = Self {
87 protocol: protocol,
88 rx_frame: Box::new([0u8; ETHERNET_FRAME_SIZE]),
89 tx_frames: vec![core::ptr::null_mut(); extra_tx_frames + 1],
90 tx_frame_curr: 0,
91 efi_entry: efi_entry,
92 };
93 ret.tx_frames
94 .iter_mut()
95 .for_each(|v| *v = Box::into_raw(Box::new([0u8; ETHERNET_FRAME_SIZE])));
96 ret
97 }
98 }
99
100 impl Drop for EfiNetworkDevice<'_> {
drop(&mut self)101 fn drop(&mut self) {
102 if let Err(e) = self.protocol.shutdown() {
103 if e != Error::NotStarted {
104 // If shutdown fails, the protocol might still be operating on transmit buffers,
105 // which can cause undefined behavior. Thus we need to panic.
106 panic!("Failed to shutdown EFI network. {:?}", e);
107 }
108 }
109
110 // Deallocate TX frames.
111 self.tx_frames.iter_mut().for_each(|v| {
112 // SAFETY:
113 // Each pointer is created by `Box::new()` in `EfiNetworkDevice::new()`. Thus the
114 // pointer is valid and layout matches.
115 drop(unsafe { Box::<[u8; ETHERNET_FRAME_SIZE]>::from_raw(*v) });
116 });
117 }
118 }
119
120 // Implements network device trait backend for the `smoltcp` crate.
121 impl<'a> Device for EfiNetworkDevice<'a> {
122 type RxToken<'b>
123 = RxToken<'b>
124 where
125 Self: 'b;
126 type TxToken<'b>
127 = TxToken<'a, 'b>
128 where
129 Self: 'b;
130
capabilities(&self) -> DeviceCapabilities131 fn capabilities(&self) -> DeviceCapabilities {
132 // Taken from upstream example.
133 let mut res: DeviceCapabilities = Default::default();
134 res.max_transmission_unit = 65535;
135 res.medium = Medium::Ethernet;
136 res
137 }
138
receive(&mut self, _: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>139 fn receive(&mut self, _: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
140 let mut recv_size = self.rx_frame.len();
141 // Receive the next packet from the device.
142 self.protocol
143 .receive(None, Some(&mut recv_size), &mut self.rx_frame[..], None, None, None)
144 .ok()?;
145 match recv_size > 0 {
146 true => Some((
147 RxToken(&mut self.rx_frame[..recv_size]),
148 TxToken {
149 protocol: &self.protocol,
150 tx_frames: &mut self.tx_frames[..],
151 curr: &mut self.tx_frame_curr,
152 efi_entry: self.efi_entry,
153 },
154 )),
155 _ => None,
156 }
157 }
158
transmit(&mut self, _: Instant) -> Option<Self::TxToken<'_>>159 fn transmit(&mut self, _: Instant) -> Option<Self::TxToken<'_>> {
160 Some(TxToken {
161 protocol: &self.protocol,
162 tx_frames: &mut self.tx_frames[..],
163 curr: &mut self.tx_frame_curr,
164 efi_entry: self.efi_entry,
165 })
166 }
167 }
168
169 /// In smoltcp, a `RxToken` is used to receive/process a frame when consumed.
170 pub struct RxToken<'a>(&'a mut [u8]);
171
172 impl phy::RxToken for RxToken<'_> {
consume<R, F>(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R,173 fn consume<R, F>(self, f: F) -> R
174 where
175 F: FnOnce(&mut [u8]) -> R,
176 {
177 f(self.0)
178 }
179 }
180
181 /// In smoltcp, a `TxToken` is used to transmit a frame when consumed.
182 pub struct TxToken<'a: 'b, 'b> {
183 tx_frames: &'b mut [*mut [u8; ETHERNET_FRAME_SIZE]],
184 curr: &'b mut usize,
185 protocol: &'b Protocol<'a, SimpleNetworkProtocol>,
186 efi_entry: &'b EfiEntry,
187 }
188
189 impl TxToken<'_, '_> {
190 /// Tries to allocate a send buffer.
try_get_buffer(&mut self) -> Option<*mut [u8; ETHERNET_FRAME_SIZE]>191 fn try_get_buffer(&mut self) -> Option<*mut [u8; ETHERNET_FRAME_SIZE]> {
192 let mut ptr: *mut core::ffi::c_void = core::ptr::null_mut();
193 let mut interrupt_status = 0u32;
194 // Recyle a buffer or take one from `tx_frames`.
195 match self.protocol.get_status(Some(&mut interrupt_status), Some(&mut ptr)) {
196 Ok(()) if self.tx_frames.contains(&(ptr as *mut _)) => Some(ptr as *mut _),
197 _ if *self.curr < self.tx_frames.len() => {
198 // If we can't recycle a buffer, see if we can take one from the pool.
199 let res = *self.curr;
200 *self.curr = *self.curr + 1;
201 Some(self.tx_frames[res])
202 }
203 _ => None,
204 }
205 }
206 }
207
208 impl phy::TxToken for TxToken<'_, '_> {
consume<R, F>(mut self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R,209 fn consume<R, F>(mut self, len: usize, f: F) -> R
210 where
211 F: FnOnce(&mut [u8]) -> R,
212 {
213 loop {
214 match loop_with_timeout(self.efi_entry, Duration::from_secs(5), || {
215 self.try_get_buffer().ok_or(false)
216 }) {
217 Ok(Some(send_buffer)) => {
218 // SAFETY:
219 // * The pointer is confirmed to come from one of `self.tx_frames`. It's
220 // created via `Box::new()` in `EfiNetworkDevice::new()`. Thus it is properly
221 // aligned, dereferenceable and initialized.
222 // * The pointer is either recycled from `self.protocol.get_status` or newly
223 // allocated from `self.tx_frames`. Thus There's no other references to it.
224 // * The reference is only used for passing to `f` and goes out of scope
225 // immediately after.
226 let result = f(&mut unsafe { send_buffer.as_mut() }.unwrap()[..len]);
227
228 // SAFETY:
229 // * `send_buffer` comes from `EfiNetworkDevice::tx_frames`. It has a valid
230 // length at least `len`. `EfiNetworkDevice` shuts down network on drop. Thus
231 // the transmit buffer remains valid throughout the operation of the network
232 // protocol.
233 // * `send_buffer` is either recycled from `self.protocol.get_status()` or newly
234 // allocated from `self.tx_frames`. There's no other references to it.
235 // * `self.curr` stricly increases for each new allocation until
236 // `reset_simple_network()`. Thus there'll be no other references to the buffer
237 // until it is either recycled or `reset_simple_network()` is called.
238 let _ = unsafe {
239 self.protocol.transmit(
240 0,
241 send_buffer.as_mut().unwrap().get_mut(..len).unwrap(),
242 Default::default(), // Src mac address don't care
243 Default::default(), // Dest mac address don't care
244 0,
245 )
246 };
247
248 return result;
249 }
250 Ok(None) => {
251 // Some UEFI firmware has internal network service that also recycle buffers,
252 // in which case our buffer may be hijacked and will never be returned from our
253 // call. If we run into this case, shutdown and restart the network and try
254 // again. Shutting down network releases all pending send/receive buffers
255 // internally retained.
256 efi_println!(
257 self.efi_entry,
258 "Timeout recycling TX buffers. Resetting network."
259 );
260 // Panics if this fails, as we have effectively lost control over network's
261 // used of buffers.
262 reset_simple_network(self.protocol).unwrap();
263 *self.curr = 0;
264 }
265 _ => {} // `loop_with_timeout` failure. Try again.
266 };
267 }
268 }
269 }
270
271 /// Find the first available network device.
find_net_device(efi_entry: &EfiEntry) -> Result<DeviceHandle>272 fn find_net_device(efi_entry: &EfiEntry) -> Result<DeviceHandle> {
273 // Find the device whose path is the "smallest" lexicographically, this ensures that it's not
274 // any child network device of some other node. e1000 tends to add a child network device for
275 // ipv4 and ipv6 configuration information.
276 efi_entry
277 .system_table()
278 .boot_services()
279 .locate_handle_buffer_by_protocol::<SimpleNetworkProtocol>()?
280 .handles()
281 .iter()
282 .map(|handle| (*handle, get_device_path(efi_entry, *handle)))
283 // Ignore devices that fail to get device path.
284 .filter_map(|(handle, path)| path.ok().map(|v| (handle, v)))
285 // Ignore devices that have NULL path.
286 .filter_map(|(handle, path)| path.text().is_some().then(|| (handle, path)))
287 // Finds the minimum path lexicographically.
288 .min_by(|lhs, rhs| Ord::cmp(lhs.1.text().unwrap(), rhs.1.text().unwrap()))
289 .map(|(h, _)| h)
290 .ok_or(Error::NotFound.into())
291 }
292
293 /// Derives a link local ethernet mac address and IPv6 address from `EfiMacAddress`.
ll_mac_ip6_addr_from_efi_mac(mac: EfiMacAddress) -> (EthernetAddress, IpAddress)294 fn ll_mac_ip6_addr_from_efi_mac(mac: EfiMacAddress) -> (EthernetAddress, IpAddress) {
295 let ll_mac_bytes = &mac.addr[..6];
296 let mut ip6_bytes = [0u8; 16];
297 ip6_bytes[0] = 0xfe;
298 ip6_bytes[1] = 0x80;
299 ip6_bytes[8] = ll_mac_bytes[0] ^ 2;
300 ip6_bytes[9] = ll_mac_bytes[1];
301 ip6_bytes[10] = ll_mac_bytes[2];
302 ip6_bytes[11] = 0xff;
303 ip6_bytes[12] = 0xfe;
304 ip6_bytes[13] = ll_mac_bytes[3];
305 ip6_bytes[14] = ll_mac_bytes[4];
306 ip6_bytes[15] = ll_mac_bytes[5];
307
308 (
309 EthernetAddress::from_bytes(ll_mac_bytes),
310 IpAddress::Ipv6(Ipv6Address::from_bytes(&ip6_bytes[..])),
311 )
312 }
313
314 /// `EfiTcpSocket` groups together necessary components for performing TCP.
315 pub struct EfiTcpSocket<'a, 'b> {
316 pub(crate) efi_entry: &'a EfiEntry,
317 efi_net_dev: &'b mut EfiNetworkDevice<'a>,
318 interface: Interface,
319 socket_set: SocketSet<'b>,
320 io_yield_counter: YieldCounter,
321 last_listen_timestamp: Option<u64>,
322 _time_update_event: Event<'a, 'b>,
323 timestamp: &'b AtomicU64,
324 fuchsia_fastboot_mdns_packet: Vec<u8>,
325 }
326
327 impl<'a, 'b> EfiTcpSocket<'a, 'b> {
328 /// Resets the socket and starts listening for new TCP connection.
listen(&mut self, port: u16) -> Result<()>329 pub fn listen(&mut self, port: u16) -> Result<()> {
330 self.get_socket().abort();
331 self.get_socket().listen(port).map_err(listen_to_unified)?;
332 self.last_listen_timestamp = Some(self.timestamp(0).as_millis() as u64);
333 Ok(())
334 }
335
336 // Checks if the socket is listening or performing handshake.
is_listening_or_handshaking(&mut self) -> bool337 pub fn is_listening_or_handshaking(&mut self) -> bool {
338 matches!(self.get_socket().state(), State::Listen | State::SynReceived)
339 }
340
341 /// Returns the amount of time elapsed since last call to `Self::listen()`. If `listen()` has
342 /// never been called, `Duration::MAX` is returned.
time_since_last_listen(&mut self) -> Duration343 pub fn time_since_last_listen(&mut self) -> Duration {
344 self.last_listen_timestamp.map(|v| self.timestamp(v)).unwrap_or(Duration::MAX)
345 }
346
347 /// Polls network device.
poll(&mut self)348 pub fn poll(&mut self) {
349 self.interface.poll(self.instant(), self.efi_net_dev, &mut self.socket_set);
350 }
351
352 /// Polls network and check if the socket is in an active state.
check_active(&mut self) -> bool353 pub fn check_active(&mut self) -> bool {
354 self.poll();
355 self.get_socket().is_active()
356 }
357
358 /// Gets a reference to the smoltcp socket object.
get_socket(&mut self) -> &mut TcpSocket<'b>359 pub fn get_socket(&mut self) -> &mut TcpSocket<'b> {
360 // We only consider single socket use case for now.
361 let handle = self.socket_set.iter().next().unwrap().0;
362 self.socket_set.get_mut::<TcpSocket>(handle)
363 }
364
365 /// Checks whether a socket is closed.
is_closed(&mut self) -> bool366 fn is_closed(&mut self) -> bool {
367 return !self.get_socket().is_open() || self.get_socket().state() == State::CloseWait;
368 }
369
370 /// Sets the maximum number of bytes to read or write before a force await.
set_io_yield_threshold(&mut self, threshold: u64)371 pub fn set_io_yield_threshold(&mut self, threshold: u64) {
372 self.io_yield_counter = YieldCounter::new(threshold)
373 }
374
375 /// Receives exactly `out.len()` number of bytes to `out`.
receive_exact(&mut self, out: &mut [u8], timeout: Duration) -> Result<()>376 pub async fn receive_exact(&mut self, out: &mut [u8], timeout: Duration) -> Result<()> {
377 let timer = Timeout::new(self.efi_entry, timeout)?;
378 let mut curr = &mut out[..];
379 while !curr.is_empty() {
380 self.poll();
381 let mut has_progress = false;
382
383 if self.is_closed() {
384 return Err(Error::Disconnected);
385 } else if timer.check()? {
386 return Err(Error::Timeout);
387 } else if self.get_socket().can_recv() {
388 let recv_size = self.get_socket().recv_slice(curr).map_err(recv_to_unified)?;
389 curr = curr.get_mut(recv_size..).ok_or(Error::BadIndex(recv_size))?;
390 has_progress = recv_size > 0;
391 // Forces a yield to the executor if the data received/sent reaches a certain
392 // threshold. This is to prevent the async code from holding up the CPU for too long
393 // in case IO speed is high and the executor uses cooperative scheduling.
394 self.io_yield_counter.increment(recv_size.try_into().unwrap()).await;
395 }
396
397 match has_progress {
398 true => timer.reset(timeout)?,
399 _ => yield_now().await,
400 }
401 }
402 Ok(())
403 }
404
405 /// Sends exactly `data.len()` number of bytes from `data`.
send_exact(&mut self, data: &[u8], timeout: Duration) -> Result<()>406 pub async fn send_exact(&mut self, data: &[u8], timeout: Duration) -> Result<()> {
407 let timer = Timeout::new(self.efi_entry, timeout)?;
408 let mut curr = &data[..];
409 let mut last_send_queue = self.get_socket().send_queue();
410 loop {
411 self.poll();
412 if curr.is_empty() && self.get_socket().send_queue() == 0 {
413 return Ok(());
414 } else if self.is_closed() {
415 return Err(Error::Disconnected.into());
416 } else if timer.check()? {
417 return Err(Error::Timeout.into());
418 }
419
420 let mut has_progress = false;
421 // Checks if any data in the queue is sent.
422 if self.get_socket().send_queue() != last_send_queue {
423 last_send_queue = self.get_socket().send_queue();
424 has_progress = true;
425 }
426 // Checks if there are more data to be queued.
427 if self.get_socket().can_send() && !curr.is_empty() {
428 let sent = self.get_socket().send_slice(curr).map_err(send_to_unified)?;
429 curr = curr.get(sent..).ok_or(Error::BadIndex(sent))?;
430 // Forces a yield to the executor if the data received/sent reaches a certain
431 // threshold. This is to prevent the async code from holding up the CPU for too long
432 // in case IO speed is high and the executor uses cooperative scheduling.
433 self.io_yield_counter.increment(sent.try_into().unwrap()).await;
434 has_progress |= sent > 0;
435 }
436
437 match has_progress {
438 true => timer.reset(timeout)?,
439 _ => yield_now().await,
440 }
441 }
442 }
443
444 /// Gets the smoltcp `Interface` for this socket.
interface(&self) -> &Interface445 pub fn interface(&self) -> &Interface {
446 &self.interface
447 }
448
449 /// Returns the duration elapsed since the `base` timestamp.
timestamp(&self, base_in_millis: u64) -> Duration450 pub fn timestamp(&self, base_in_millis: u64) -> Duration {
451 let curr = self.timestamp.load(Ordering::Relaxed);
452 // Assume there can be at most one overflow.
453 Duration::from_millis(match curr < base_in_millis {
454 true => u64::MAX - (base_in_millis - curr),
455 false => curr - base_in_millis,
456 })
457 }
458
459 /// Returns a smoltcp time `Instant` value.
instant(&self) -> Instant460 fn instant(&self) -> Instant {
461 to_smoltcp_instant(self.timestamp(0).as_millis() as u64)
462 }
463
464 /// Broadcasts Fuchsia Fastboot MDNS service once.
broadcast_fuchsia_fastboot_mdns(&mut self)465 pub fn broadcast_fuchsia_fastboot_mdns(&mut self) {
466 const MDNS_PORT: u16 = 5353;
467 const IP6_BROADCAST_ADDR: &[u8] =
468 &[0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFB];
469 let ip6_broadcast = Ipv6Address::from_bytes(&IP6_BROADCAST_ADDR[..]);
470 let meta =
471 UdpMetadata { endpoint: (ip6_broadcast, MDNS_PORT).into(), meta: Default::default() };
472 let handle = self.socket_set.iter().nth(1).unwrap().0;
473 let socket = self.socket_set.get_mut::<UdpSocket>(handle);
474 if !socket.is_open() {
475 match socket.bind(IpListenEndpoint { addr: None, port: MDNS_PORT }) {
476 Err(e) => efi_println!(self.efi_entry, "bind error: {:?}", e),
477 _ => {}
478 }
479 }
480 if socket.can_send() {
481 match socket.send_slice(&self.fuchsia_fastboot_mdns_packet, meta) {
482 Err(e) => efi_println!(self.efi_entry, "UDP send error: {:?}", e),
483 _ => {}
484 }
485 }
486 }
487 }
488
489 /// Returns a smoltcp time `Instant` value from a u64 timestamp.
to_smoltcp_instant(ts: u64) -> Instant490 fn to_smoltcp_instant(ts: u64) -> Instant {
491 Instant::from_millis(i64::try_from(ts).unwrap())
492 }
493
494 /// Internal type that contains net driver interfaces and buffers for creating GBL network and
495 /// sockets.
496 ///
497 /// # Lifetimes
498 ///
499 /// * `'a`: Lifetime of [EfiEntry] borrowed.
500 /// * `'b`: Lifetime of [SocketStorage<'b>], which eventually refers to Self.
501 /// * `'c`: Lifetime of [AtomicU64] borrowed.
502 struct EfiGblNetworkInternal<'a, 'b, 'c> {
503 efi_entry: &'a EfiEntry,
504 tcp_tx_buffer: Vec<u8>,
505 tcp_rx_buffer: Vec<u8>,
506 udp_tx_payload_buffer: Vec<u8>,
507 udp_rx_payload_buffer: Vec<u8>,
508 udp_tx_metadata_buffer: Vec<PacketMetadata<UdpMetadata>>,
509 udp_rx_metadata_buffer: Vec<PacketMetadata<UdpMetadata>>,
510 socket_storage: [SocketStorage<'b>; 2],
511 efi_net_dev: EfiNetworkDevice<'a>,
512 timestamp: &'c AtomicU64,
513 notify_fn: Option<Box<dyn FnMut(EfiEvent) + Sync + 'c>>,
514 notify: Option<EventNotify<'b>>,
515 }
516
517 impl<'a, 'b, 'c> EfiGblNetworkInternal<'a, 'b, 'c> {
518 /// Creates a new instance of [EfiGblNetworkInternal].
new(efi_entry: &'a EfiEntry, timestamp: &'c AtomicU64) -> Result<Self>519 fn new(efi_entry: &'a EfiEntry, timestamp: &'c AtomicU64) -> Result<Self> {
520 // Creates and initializes simple network protocol.
521 let snp_dev = find_net_device(efi_entry)?;
522 let snp = efi_entry
523 .system_table()
524 .boot_services()
525 .open_protocol::<SimpleNetworkProtocol>(snp_dev)?;
526 reset_simple_network(&snp)?;
527
528 // The TCP stack requires ICMP6 solicitation for discovery. Enable promiscuous mode so that
529 // all uni/multicast packets can be captured.
530 match snp.set_promiscuous_mode() {
531 Err(e) => efi_println!(
532 efi_entry,
533 "Warning: Failed to set promiscuous mode {e:?}. Device may be undiscoverable",
534 ),
535 _ => {}
536 }
537
538 Ok(Self {
539 efi_entry,
540 tcp_tx_buffer: vec![0u8; SOCKET_TX_RX_BUFFER],
541 tcp_rx_buffer: vec![0u8; SOCKET_TX_RX_BUFFER],
542 udp_tx_payload_buffer: vec![0u8; ETHERNET_FRAME_SIZE],
543 udp_rx_payload_buffer: vec![0u8; ETHERNET_FRAME_SIZE],
544 udp_tx_metadata_buffer: vec![PacketMetadata::EMPTY; 1],
545 udp_rx_metadata_buffer: vec![PacketMetadata::EMPTY; 1],
546 socket_storage: Default::default(),
547 // Allocates 7(chosen randomly) extra TX frames. Revisits if it is not enough.
548 efi_net_dev: EfiNetworkDevice::new(snp, 7, &efi_entry),
549 timestamp,
550 notify_fn: None,
551 notify: None,
552 })
553 }
554
555 /// Creates an instance of [EfiTcpSocket].
create_socket(&'b mut self) -> Result<EfiTcpSocket<'a, 'b>>556 fn create_socket(&'b mut self) -> Result<EfiTcpSocket<'a, 'b>> {
557 // Resets network timestamp to 0.
558 let _ = self.timestamp.swap(0, Ordering::Relaxed);
559
560 // Initializes notification functions.
561 if self.notify_fn.is_none() {
562 self.notify_fn = Some(Box::new(|_: EfiEvent| {
563 self.timestamp.fetch_add(
564 NETWORK_TIMESTAMP_UPDATE_PERIOD.as_millis() as u64,
565 Ordering::Relaxed,
566 );
567 }));
568 self.notify = Some(EventNotify::new(Tpl::Callback, self.notify_fn.as_mut().unwrap()));
569 }
570
571 // Creates a timer event for updating the global timestamp.
572 let bs = self.efi_entry.system_table().boot_services();
573 // SAFETY: the notification callback in `notify_fn` initialized above never allocates,
574 // deallocates, or panics.
575 let _time_update_event = unsafe {
576 bs.create_event_with_notification(
577 EventType::TimerNotifySignal,
578 self.notify.as_mut().unwrap(),
579 )
580 }?;
581 bs.set_timer(
582 &_time_update_event,
583 EFI_TIMER_DELAY_TIMER_PERIODIC,
584 NETWORK_TIMESTAMP_UPDATE_PERIOD,
585 )?;
586
587 // Gets our MAC address and IPv6 address.
588 // We can also consider getting this from vendor configuration.
589 let (ll_mac, ll_ip6_addr) =
590 ll_mac_ip6_addr_from_efi_mac(self.efi_net_dev.protocol.mode()?.current_address);
591 // Configures smoltcp network interface.
592 let mut interface = Interface::new(
593 Config::new(ll_mac.into()),
594 &mut self.efi_net_dev,
595 to_smoltcp_instant(0),
596 );
597 interface.update_ip_addrs(|ip_addrs| ip_addrs.push(IpCidr::new(ll_ip6_addr, 64)).unwrap());
598
599 // Generates Fuchsia Fastboot MDNS packet.
600 let eth_mac = ll_mac.as_bytes();
601 let fuchsia_node_name = format!(
602 "fuchsia-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}",
603 eth_mac[0], eth_mac[1], eth_mac[2], eth_mac[3], eth_mac[4], eth_mac[5]
604 );
605 let fuchsia_fastboot_mdns_packet =
606 fuchsia_fastboot_mdns_packet(fuchsia_node_name.as_str(), ll_ip6_addr.as_bytes())?
607 .into();
608
609 // Creates sockets.
610 let mut socket_set = SocketSet::new(&mut self.socket_storage[..]);
611 // Creates a TCP socket for fastboot over TCP.
612 let tx_socket_buffer = SocketBuffer::new(&mut self.tcp_tx_buffer[..]);
613 let rx_socket_buffer = SocketBuffer::new(&mut self.tcp_rx_buffer[..]);
614 let tcp_socket = TcpSocket::new(rx_socket_buffer, tx_socket_buffer);
615 let _ = socket_set.add(tcp_socket);
616 // Creates a UDP socket for MDNS broadcast.
617 let udp_tx_packet_buffer = PacketBuffer::new(
618 &mut self.udp_tx_metadata_buffer[..],
619 &mut self.udp_tx_payload_buffer[..],
620 );
621 let udp_rx_packet_buffer = PacketBuffer::new(
622 &mut self.udp_rx_metadata_buffer[..],
623 &mut self.udp_rx_payload_buffer[..],
624 );
625 let udp_socket = UdpSocket::new(udp_rx_packet_buffer, udp_tx_packet_buffer);
626 let _ = socket_set.add(udp_socket);
627 Ok(EfiTcpSocket {
628 efi_entry: self.efi_entry,
629 efi_net_dev: &mut self.efi_net_dev,
630 interface,
631 socket_set,
632 io_yield_counter: YieldCounter::new(u64::MAX),
633 last_listen_timestamp: None,
634 _time_update_event,
635 timestamp: self.timestamp,
636 fuchsia_fastboot_mdns_packet,
637 })
638 }
639 }
640
641 /// The GBL network stack.
642 ///
643 /// # Lifetimes
644 ///
645 /// * `'a`: Lifetime of `efi_entry` borrowed.
646 /// * `'b`: Lifetime of Self.
647 /// * `'c`: Lifetime of external timestamp borrowed.
648 #[derive(Default)]
649 pub struct EfiGblNetwork<'a, 'b, 'c>(Option<EfiGblNetworkInternal<'a, 'b, 'c>>);
650
651 impl<'a, 'b, 'c: 'b> EfiGblNetwork<'a, 'b, 'c> {
652 /// Initializes GBL network and creates GBL sockets.
653 ///
654 /// # Args:
655 ///
656 /// * `efi_entry`: A [EfiEntry].
657 /// * `ts`: A reference to an [AtomicU64].
init( &'b mut self, efi_entry: &'a EfiEntry, timestamp: &'c AtomicU64, ) -> Result<EfiTcpSocket<'a, 'b>>658 pub fn init(
659 &'b mut self,
660 efi_entry: &'a EfiEntry,
661 timestamp: &'c AtomicU64,
662 ) -> Result<EfiTcpSocket<'a, 'b>> {
663 // Drops any existing network first to release the global event notify function.
664 self.0 = None;
665 self.0 = Some(EfiGblNetworkInternal::new(efi_entry, timestamp)?);
666 self.0.as_mut().unwrap().create_socket()
667 }
668 }
669