Auto merge of #150644 - matthiaskrgr:rollup-ey900sr, r=matthiaskrgr

Rollup of 2 pull requests

Successful merges:

 - rust-lang/rust#145339 (std: sys: net: uefi: tcp: Initial TcpListener support)
 - rust-lang/rust#150641 (std: remove manual bindings on NetBSD)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2026-01-03 15:04:57 +00:00
commit 1d22b964df
5 changed files with 132 additions and 69 deletions

View file

@ -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<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
return each_addr(addr, inner);
fn inner(addr: &SocketAddr) -> io::Result<TcpStream> {
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<TcpStream> {
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<Duration>) -> io::Result<()> {
@ -148,16 +148,23 @@ pub struct TcpListener {
}
impl TcpListener {
pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> {
unsupported()
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
return each_addr(addr, inner);
fn inner(addr: &SocketAddr) -> io::Result<TcpListener> {
let inner = tcp::Tcp::bind(addr)?;
Ok(TcpListener { inner })
}
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
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<TcpListener> {

View file

@ -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<Self> {
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<Self> {
match self {
Self::V4(client) => client.accept().map(Tcp::V4),
}
}

View file

@ -3,7 +3,7 @@ use r_efi::protocols::tcp4;
use crate::io::{self, IoSlice, IoSliceMut};
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<crate::ffi::c_void>,
protocol: NonNull<tcp4::Protocol>,
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<Self> {
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_or(|addr| addr.ip().is_unspecified()).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 = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(255, 255, 255, 0));
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<Self> {
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<Duration>) -> io::Result<()> {
let evt = unsafe { self.create_evt() }?;
let completion_token =
@ -352,6 +394,12 @@ impl Tcp4 {
}
}
impl Drop for Tcp4 {
fn drop(&mut self) {
let _ = unsafe { 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);

View file

@ -651,34 +651,38 @@ pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDeviceP
/// Helper for UEFI Protocols which are created and destroyed using
/// [EFI_SERVICE_BINDING_PROTOCOL](https://uefi.org/specs/UEFI/2.11/11_Protocols_UEFI_Driver_Model.html#efi-service-binding-protocol)
///
/// # Invariant
/// - `handle` must always be a valid UEFI handle corresponding to the `service_guid`.
/// - Copying `ServiceProtocol` is sound as long as `handle` remains valid.
/// - For most service binding protocols (in edk2 implementations), such handles remain valid
/// for the lifetime of the UEFI environment — effectively `'static`.
#[derive(Clone, Copy)]
pub(crate) struct ServiceProtocol {
service_guid: r_efi::efi::Guid,
handle: NonNull<crate::ffi::c_void>,
child_handle: NonNull<crate::ffi::c_void>,
}
impl ServiceProtocol {
pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result<Self> {
/// Open a child handle on a service_binding protocol.
pub(crate) fn open(
service_guid: r_efi::efi::Guid,
) -> io::Result<(Self, NonNull<crate::ffi::c_void>)> {
let handles = locate_handles(service_guid)?;
for handle in handles {
if let Ok(protocol) = open_protocol::<service_binding::Protocol>(handle, service_guid) {
let Ok(child_handle) = Self::create_child(protocol) else {
continue;
};
return Ok(Self { service_guid, handle, child_handle });
if let Ok(child_handle) = unsafe { Self::create_child(protocol) } {
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<crate::ffi::c_void> {
self.child_handle
}
fn create_child(
// SAFETY: sbp must be a valid service binding protocol pointer
unsafe fn create_child(
sbp: NonNull<service_binding::Protocol>,
) -> io::Result<NonNull<crate::ffi::c_void>> {
let mut child_handle: r_efi::efi::Handle = crate::ptr::null_mut();
@ -692,17 +696,17 @@ 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::<service_binding::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())
};
}
// SAFETY: Child handle must be allocated by the current service binding protocol and must be
// valid.
pub(crate) unsafe fn destroy_child(
&self,
handle: NonNull<crate::ffi::c_void>,
) -> io::Result<()> {
let sbp = open_protocol::<service_binding::Protocol>(self.handle, self.service_guid)?;
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(()) }
}
}

View file

@ -2,24 +2,11 @@
// separate modules for each platform.
#![cfg(target_os = "netbsd")]
use libc::{_lwp_self, CLOCK_MONOTONIC, c_long, clockid_t, lwpid_t, time_t, timespec};
use libc::{_lwp_park, _lwp_self, _lwp_unpark, CLOCK_MONOTONIC, c_long, lwpid_t, time_t, timespec};
use crate::ffi::{c_int, c_void};
use crate::ptr;
use crate::time::Duration;
unsafe extern "C" {
fn ___lwp_park60(
clock_id: clockid_t,
flags: c_int,
ts: *mut timespec,
unpark: lwpid_t,
hint: *const c_void,
unparkhint: *const c_void,
) -> c_int;
fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
}
pub type ThreadId = lwpid_t;
#[inline]
@ -30,7 +17,7 @@ pub fn current() -> ThreadId {
#[inline]
pub fn park(hint: usize) {
unsafe {
___lwp_park60(0, 0, ptr::null_mut(), 0, ptr::without_provenance(hint), ptr::null());
_lwp_park(0, 0, ptr::null_mut(), 0, ptr::without_provenance(hint), ptr::null_mut());
}
}
@ -45,13 +32,13 @@ pub fn park_timeout(dur: Duration, hint: usize) {
// Timeout needs to be mutable since it is modified on NetBSD 9.0 and
// above.
unsafe {
___lwp_park60(
_lwp_park(
CLOCK_MONOTONIC,
0,
&mut timeout,
0,
ptr::without_provenance(hint),
ptr::null(),
ptr::null_mut(),
);
}
}