diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 004f6d413a1f3..b89c739713fb7 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -16,26 +16,26 @@ pub struct TcpStream { } impl TcpStream { + fn new(inner: tcp::Tcp) -> Self { + Self { + inner, + read_timeout: Arc::new(Mutex::new(None)), + write_timeout: Arc::new(Mutex::new(None)), + } + } + pub fn connect(addr: A) -> io::Result { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { let inner = tcp::Tcp::connect(addr, None)?; - Ok(TcpStream { - inner, - read_timeout: Arc::new(Mutex::new(None)), - write_timeout: Arc::new(Mutex::new(None)), - }) + Ok(TcpStream::new(inner)) } } pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { let inner = tcp::Tcp::connect(addr, Some(timeout))?; - Ok(Self { - inner, - read_timeout: Arc::new(Mutex::new(None)), - write_timeout: Arc::new(Mutex::new(None)), - }) + Ok(Self::new(inner)) } pub fn set_read_timeout(&self, t: Option) -> io::Result<()> { @@ -150,16 +150,23 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(_: A) -> io::Result { - unsupported() + pub fn bind(addr: A) -> io::Result { + return each_addr(addr, inner); + + fn inner(addr: &SocketAddr) -> io::Result { + let inner = tcp::Tcp::bind(addr)?; + Ok(TcpListener { inner }) + } } pub fn socket_addr(&self) -> io::Result { - unsupported() + self.inner.socket_addr() } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unsupported() + let tcp = self.inner.accept()?; + let addr = tcp.peer_addr()?; + Ok((TcpStream::new(tcp), addr)) } pub fn duplicate(&self) -> io::Result { diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs index aac97007bbfe5..5ddee975ce17d 100644 --- a/library/std/src/sys/net/connection/uefi/tcp.rs +++ b/library/std/src/sys/net/connection/uefi/tcp.rs @@ -18,7 +18,24 @@ impl Tcp { temp.connect(timeout)?; Ok(Tcp::V4(temp)) } - SocketAddr::V6(_) => todo!(), + SocketAddr::V6(_) => unsupported(), + } + } + + pub(crate) fn bind(addr: &SocketAddr) -> io::Result { + match addr { + SocketAddr::V4(x) => { + let temp = tcp4::Tcp4::new()?; + temp.configure(false, None, Some(x))?; + Ok(Tcp::V4(temp)) + } + SocketAddr::V6(_) => unsupported(), + } + } + + pub(crate) fn accept(&self) -> io::Result { + match self { + Self::V4(client) => client.accept().map(Tcp::V4), } } diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs index 75862ff247b4f..a7768e992c039 100644 --- a/library/std/src/sys/net/connection/uefi/tcp4.rs +++ b/library/std/src/sys/net/connection/uefi/tcp4.rs @@ -3,7 +3,7 @@ use r_efi::protocols::tcp4; use crate::io; use crate::net::SocketAddrV4; -use crate::ptr::NonNull; +use crate::ptr::{self, NonNull}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::pal::helpers; use crate::time::{Duration, Instant}; @@ -12,9 +12,9 @@ const TYPE_OF_SERVICE: u8 = 8; const TIME_TO_LIVE: u8 = 255; pub(crate) struct Tcp4 { + handle: NonNull, protocol: NonNull, flag: AtomicBool, - #[expect(dead_code)] service_binding: helpers::ServiceProtocol, } @@ -22,10 +22,11 @@ const DEFAULT_ADDR: efi::Ipv4Address = efi::Ipv4Address { addr: [0u8; 4] }; impl Tcp4 { pub(crate) fn new() -> io::Result { - let service_binding = helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?; - let protocol = helpers::open_protocol(service_binding.child_handle(), tcp4::PROTOCOL_GUID)?; + let (service_binding, handle) = + helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?; + let protocol = helpers::open_protocol(handle, tcp4::PROTOCOL_GUID)?; - Ok(Self { service_binding, protocol, flag: AtomicBool::new(false) }) + Ok(Self { service_binding, handle, protocol, flag: AtomicBool::new(false) }) } pub(crate) fn configure( @@ -42,11 +43,14 @@ impl Tcp4 { (DEFAULT_ADDR, 0) }; - // FIXME: Remove when passive connections with proper subnet handling are added - assert!(station_address.is_none()); - let use_default_address = efi::Boolean::TRUE; - let (station_address, station_port) = (DEFAULT_ADDR, 0); - let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(0, 0, 0, 0)); + let use_default_address: r_efi::efi::Boolean = station_address.is_none().into(); + let (station_address, station_port) = if let Some(x) = station_address { + (helpers::ipv4_to_r_efi(*x.ip()), x.port()) + } else { + (DEFAULT_ADDR, 0) + }; + let subnet_mask = crate::net::Ipv4Addr::new(255, 255, 255, 0); + let subnet_mask = helpers::ipv4_to_r_efi(subnet_mask); let mut config_data = tcp4::ConfigData { type_of_service: TYPE_OF_SERVICE, @@ -60,7 +64,7 @@ impl Tcp4 { station_port, subnet_mask, }, - control_option: crate::ptr::null_mut(), + control_option: ptr::null_mut(), }; let r = unsafe { ((*protocol).configure)(protocol, &mut config_data) }; @@ -74,17 +78,55 @@ impl Tcp4 { let r = unsafe { ((*protocol).get_mode_data)( protocol, - crate::ptr::null_mut(), + ptr::null_mut(), &mut config_data, - crate::ptr::null_mut(), - crate::ptr::null_mut(), - crate::ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), ) }; if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(config_data) } } + pub(crate) fn accept(&self) -> io::Result { + let evt = unsafe { self.create_evt() }?; + let completion_token = + tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; + let mut listen_token = + tcp4::ListenToken { completion_token, new_child_handle: ptr::null_mut() }; + + let protocol = self.protocol.as_ptr(); + let r = unsafe { ((*protocol).accept)(protocol, &mut listen_token) }; + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + unsafe { self.wait_or_cancel(None, &mut listen_token.completion_token) }?; + + if completion_token.status.is_error() { + Err(io::Error::from_raw_os_error(completion_token.status.as_usize())) + } else { + // EDK2 internals seem to assume a single ServiceBinding Protocol for TCP4 and TCP6, and + // thus does not use any service binding protocol data in destroying child sockets. It + // does seem to suggest that we need to cleanup even the protocols created by accept. To + // be on the safe side with other implementations, we will be using the same service + // binding protocol as the parent TCP4 handle. + // + // https://github.com/tianocore/edk2/blob/f80580f56b267c96f16f985dbf707b2f96947da4/NetworkPkg/TcpDxe/TcpDriver.c#L938 + + let handle = NonNull::new(listen_token.new_child_handle).unwrap(); + let protocol = helpers::open_protocol(handle, tcp4::PROTOCOL_GUID)?; + + Ok(Self { + handle, + service_binding: self.service_binding, + protocol, + flag: AtomicBool::new(false), + }) + } + } + pub(crate) fn connect(&self, timeout: Option) -> io::Result<()> { let evt = unsafe { self.create_evt() }?; let completion_token = @@ -263,6 +305,12 @@ impl Tcp4 { } } +impl Drop for Tcp4 { + fn drop(&mut self) { + let _ = self.service_binding.destroy_child(self.handle); + } +} + extern "efiapi" fn toggle_atomic_flag(_: r_efi::efi::Event, ctx: *mut crate::ffi::c_void) { let flag = unsafe { AtomicBool::from_ptr(ctx.cast()) }; flag.store(true, Ordering::Relaxed); diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index b50574de937aa..6698cd2158153 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -646,14 +646,21 @@ pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result, - child_handle: NonNull, } impl ServiceProtocol { - pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result { + /// Open a child handle on a service_binding protocol. + pub(crate) fn open( + service_guid: r_efi::efi::Guid, + ) -> io::Result<(Self, NonNull)> { let handles = locate_handles(service_guid)?; for handle in handles { @@ -662,17 +669,13 @@ impl ServiceProtocol { continue; }; - return Ok(Self { service_guid, handle, child_handle }); + return Ok((Self { service_guid, handle }, child_handle)); } } Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found")) } - pub(crate) fn child_handle(&self) -> NonNull { - self.child_handle - } - fn create_child( sbp: NonNull, ) -> io::Result> { @@ -687,17 +690,13 @@ impl ServiceProtocol { .ok_or(const_error!(io::ErrorKind::Other, "null child handle")) } } -} -impl Drop for ServiceProtocol { - fn drop(&mut self) { - if let Ok(sbp) = open_protocol::(self.handle, self.service_guid) - { - // SAFETY: Child handle must be allocated by the current service binding protocol. - let _ = unsafe { - ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), self.child_handle.as_ptr()) - }; - } + pub(crate) fn destroy_child(&self, handle: NonNull) -> io::Result<()> { + let sbp = open_protocol::(self.handle, self.service_guid)?; + + // SAFETY: Child handle must be allocated by the current service binding protocol. + let r = unsafe { ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), handle.as_ptr()) }; + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } }