Skip to content

Commit ba446c9

Browse files
committed
std: sys: net: uefi: tcp: Initial TcpListener support
Add support for binding and accepting TCP4 connections. While testing, the following network options were used with QEMU + OVMF: -nic user,hostfwd=tcp::12345-:12345 The default localhost address on qemu seems to be 10.0.2.15. UEFI spec does not seem to state that the TCP Handle returned by the Accept method has a ServiceBinding Protocol. So have made the ServiceBinding Protocol optional. Signed-off-by: Ayush Singh <[email protected]>
1 parent ae12bc2 commit ba446c9

File tree

4 files changed

+118
-47
lines changed

4 files changed

+118
-47
lines changed

library/std/src/sys/net/connection/uefi/mod.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,26 @@ pub struct TcpStream {
1616
}
1717

1818
impl TcpStream {
19+
fn new(inner: tcp::Tcp) -> Self {
20+
Self {
21+
inner,
22+
read_timeout: Arc::new(Mutex::new(None)),
23+
write_timeout: Arc::new(Mutex::new(None)),
24+
}
25+
}
26+
1927
pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
2028
return each_addr(addr, inner);
2129

2230
fn inner(addr: &SocketAddr) -> io::Result<TcpStream> {
2331
let inner = tcp::Tcp::connect(addr, None)?;
24-
Ok(TcpStream {
25-
inner,
26-
read_timeout: Arc::new(Mutex::new(None)),
27-
write_timeout: Arc::new(Mutex::new(None)),
28-
})
32+
Ok(TcpStream::new(inner))
2933
}
3034
}
3135

3236
pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> {
3337
let inner = tcp::Tcp::connect(addr, Some(timeout))?;
34-
Ok(Self {
35-
inner,
36-
read_timeout: Arc::new(Mutex::new(None)),
37-
write_timeout: Arc::new(Mutex::new(None)),
38-
})
38+
Ok(Self::new(inner))
3939
}
4040

4141
pub fn set_read_timeout(&self, t: Option<Duration>) -> io::Result<()> {
@@ -150,16 +150,23 @@ pub struct TcpListener {
150150
}
151151

152152
impl TcpListener {
153-
pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> {
154-
unsupported()
153+
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
154+
return each_addr(addr, inner);
155+
156+
fn inner(addr: &SocketAddr) -> io::Result<TcpListener> {
157+
let inner = tcp::Tcp::bind(addr)?;
158+
Ok(TcpListener { inner })
159+
}
155160
}
156161

157162
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
158-
unsupported()
163+
self.inner.socket_addr()
159164
}
160165

161166
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
162-
unsupported()
167+
let tcp = self.inner.accept()?;
168+
let addr = tcp.peer_addr()?;
169+
Ok((TcpStream::new(tcp), addr))
163170
}
164171

165172
pub fn duplicate(&self) -> io::Result<TcpListener> {

library/std/src/sys/net/connection/uefi/tcp.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,24 @@ impl Tcp {
1818
temp.connect(timeout)?;
1919
Ok(Tcp::V4(temp))
2020
}
21-
SocketAddr::V6(_) => todo!(),
21+
SocketAddr::V6(_) => unsupported(),
22+
}
23+
}
24+
25+
pub(crate) fn bind(addr: &SocketAddr) -> io::Result<Self> {
26+
match addr {
27+
SocketAddr::V4(x) => {
28+
let temp = tcp4::Tcp4::new()?;
29+
temp.configure(false, None, Some(x))?;
30+
Ok(Tcp::V4(temp))
31+
}
32+
SocketAddr::V6(_) => unsupported(),
33+
}
34+
}
35+
36+
pub(crate) fn accept(&self) -> io::Result<Self> {
37+
match self {
38+
Self::V4(client) => client.accept().map(Tcp::V4),
2239
}
2340
}
2441

library/std/src/sys/net/connection/uefi/tcp4.rs

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use r_efi::protocols::tcp4;
33

44
use crate::io;
55
use crate::net::SocketAddrV4;
6-
use crate::ptr::NonNull;
6+
use crate::ptr::{self, NonNull};
77
use crate::sync::atomic::{AtomicBool, Ordering};
88
use crate::sys::pal::helpers;
99
use crate::time::{Duration, Instant};
@@ -12,20 +12,21 @@ const TYPE_OF_SERVICE: u8 = 8;
1212
const TIME_TO_LIVE: u8 = 255;
1313

1414
pub(crate) struct Tcp4 {
15+
handle: NonNull<crate::ffi::c_void>,
1516
protocol: NonNull<tcp4::Protocol>,
1617
flag: AtomicBool,
17-
#[expect(dead_code)]
1818
service_binding: helpers::ServiceProtocol,
1919
}
2020

2121
const DEFAULT_ADDR: efi::Ipv4Address = efi::Ipv4Address { addr: [0u8; 4] };
2222

2323
impl Tcp4 {
2424
pub(crate) fn new() -> io::Result<Self> {
25-
let service_binding = helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?;
26-
let protocol = helpers::open_protocol(service_binding.child_handle(), tcp4::PROTOCOL_GUID)?;
25+
let (service_binding, handle) =
26+
helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?;
27+
let protocol = helpers::open_protocol(handle, tcp4::PROTOCOL_GUID)?;
2728

28-
Ok(Self { service_binding, protocol, flag: AtomicBool::new(false) })
29+
Ok(Self { service_binding, handle, protocol, flag: AtomicBool::new(false) })
2930
}
3031

3132
pub(crate) fn configure(
@@ -42,11 +43,14 @@ impl Tcp4 {
4243
(DEFAULT_ADDR, 0)
4344
};
4445

45-
// FIXME: Remove when passive connections with proper subnet handling are added
46-
assert!(station_address.is_none());
47-
let use_default_address = efi::Boolean::TRUE;
48-
let (station_address, station_port) = (DEFAULT_ADDR, 0);
49-
let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(0, 0, 0, 0));
46+
let use_default_address: r_efi::efi::Boolean = station_address.is_none().into();
47+
let (station_address, station_port) = if let Some(x) = station_address {
48+
(helpers::ipv4_to_r_efi(*x.ip()), x.port())
49+
} else {
50+
(DEFAULT_ADDR, 0)
51+
};
52+
let subnet_mask = crate::net::Ipv4Addr::new(255, 255, 255, 0);
53+
let subnet_mask = helpers::ipv4_to_r_efi(subnet_mask);
5054

5155
let mut config_data = tcp4::ConfigData {
5256
type_of_service: TYPE_OF_SERVICE,
@@ -60,7 +64,7 @@ impl Tcp4 {
6064
station_port,
6165
subnet_mask,
6266
},
63-
control_option: crate::ptr::null_mut(),
67+
control_option: ptr::null_mut(),
6468
};
6569

6670
let r = unsafe { ((*protocol).configure)(protocol, &mut config_data) };
@@ -74,17 +78,55 @@ impl Tcp4 {
7478
let r = unsafe {
7579
((*protocol).get_mode_data)(
7680
protocol,
77-
crate::ptr::null_mut(),
81+
ptr::null_mut(),
7882
&mut config_data,
79-
crate::ptr::null_mut(),
80-
crate::ptr::null_mut(),
81-
crate::ptr::null_mut(),
83+
ptr::null_mut(),
84+
ptr::null_mut(),
85+
ptr::null_mut(),
8286
)
8387
};
8488

8589
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(config_data) }
8690
}
8791

92+
pub(crate) fn accept(&self) -> io::Result<Self> {
93+
let evt = unsafe { self.create_evt() }?;
94+
let completion_token =
95+
tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
96+
let mut listen_token =
97+
tcp4::ListenToken { completion_token, new_child_handle: ptr::null_mut() };
98+
99+
let protocol = self.protocol.as_ptr();
100+
let r = unsafe { ((*protocol).accept)(protocol, &mut listen_token) };
101+
if r.is_error() {
102+
return Err(io::Error::from_raw_os_error(r.as_usize()));
103+
}
104+
105+
unsafe { self.wait_or_cancel(None, &mut listen_token.completion_token) }?;
106+
107+
if completion_token.status.is_error() {
108+
Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
109+
} else {
110+
// EDK2 internals seem to assume a single ServiceBinding Protocol for TCP4 and TCP6, and
111+
// thus does not use any service binding protocol data in destroying child sockets. It
112+
// does seem to suggest that we need to cleanup even the protocols created by accept. To
113+
// be on the safe side with other implementations, we will be using the same service
114+
// binding protocol as the parent TCP4 handle.
115+
//
116+
// https://github.com/tianocore/edk2/blob/f80580f56b267c96f16f985dbf707b2f96947da4/NetworkPkg/TcpDxe/TcpDriver.c#L938
117+
118+
let handle = NonNull::new(listen_token.new_child_handle).unwrap();
119+
let protocol = helpers::open_protocol(handle, tcp4::PROTOCOL_GUID)?;
120+
121+
Ok(Self {
122+
handle,
123+
service_binding: self.service_binding,
124+
protocol,
125+
flag: AtomicBool::new(false),
126+
})
127+
}
128+
}
129+
88130
pub(crate) fn connect(&self, timeout: Option<Duration>) -> io::Result<()> {
89131
let evt = unsafe { self.create_evt() }?;
90132
let completion_token =
@@ -263,6 +305,12 @@ impl Tcp4 {
263305
}
264306
}
265307

308+
impl Drop for Tcp4 {
309+
fn drop(&mut self) {
310+
let _ = self.service_binding.destroy_child(self.handle);
311+
}
312+
}
313+
266314
extern "efiapi" fn toggle_atomic_flag(_: r_efi::efi::Event, ctx: *mut crate::ffi::c_void) {
267315
let flag = unsafe { AtomicBool::from_ptr(ctx.cast()) };
268316
flag.store(true, Ordering::Relaxed);

library/std/src/sys/pal/uefi/helpers.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -646,14 +646,21 @@ pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDeviceP
646646

647647
/// Helper for UEFI Protocols which are created and destroyed using
648648
/// [EFI_SERVICE_BINDING_PROTOCOL](https://uefi.org/specs/UEFI/2.11/11_Protocols_UEFI_Driver_Model.html#efi-service-binding-protocol)
649+
///
650+
/// SAFETY: Copying ServiceProtocol is safe as long as handle is valid. For most service binding
651+
/// protocols, at least in edk2 implementations, the lifetime of these handles will be till UEFI is
652+
/// active, i.e. 'static
653+
#[derive(Clone, Copy)]
649654
pub(crate) struct ServiceProtocol {
650655
service_guid: r_efi::efi::Guid,
651656
handle: NonNull<crate::ffi::c_void>,
652-
child_handle: NonNull<crate::ffi::c_void>,
653657
}
654658

655659
impl ServiceProtocol {
656-
pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result<Self> {
660+
/// Open a child handle on a service_binding protocol.
661+
pub(crate) fn open(
662+
service_guid: r_efi::efi::Guid,
663+
) -> io::Result<(Self, NonNull<crate::ffi::c_void>)> {
657664
let handles = locate_handles(service_guid)?;
658665

659666
for handle in handles {
@@ -662,17 +669,13 @@ impl ServiceProtocol {
662669
continue;
663670
};
664671

665-
return Ok(Self { service_guid, handle, child_handle });
672+
return Ok((Self { service_guid, handle }, child_handle));
666673
}
667674
}
668675

669676
Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found"))
670677
}
671678

672-
pub(crate) fn child_handle(&self) -> NonNull<crate::ffi::c_void> {
673-
self.child_handle
674-
}
675-
676679
fn create_child(
677680
sbp: NonNull<service_binding::Protocol>,
678681
) -> io::Result<NonNull<crate::ffi::c_void>> {
@@ -687,17 +690,13 @@ impl ServiceProtocol {
687690
.ok_or(const_error!(io::ErrorKind::Other, "null child handle"))
688691
}
689692
}
690-
}
691693

692-
impl Drop for ServiceProtocol {
693-
fn drop(&mut self) {
694-
if let Ok(sbp) = open_protocol::<service_binding::Protocol>(self.handle, self.service_guid)
695-
{
696-
// SAFETY: Child handle must be allocated by the current service binding protocol.
697-
let _ = unsafe {
698-
((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), self.child_handle.as_ptr())
699-
};
700-
}
694+
pub(crate) fn destroy_child(&self, handle: NonNull<crate::ffi::c_void>) -> io::Result<()> {
695+
let sbp = open_protocol::<service_binding::Protocol>(self.handle, self.service_guid)?;
696+
697+
// SAFETY: Child handle must be allocated by the current service binding protocol.
698+
let r = unsafe { ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), handle.as_ptr()) };
699+
if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
701700
}
702701
}
703702

0 commit comments

Comments
 (0)