Skip to content

Commit c6f8c48

Browse files
committed
library: std: sys: net: uefi: tcp: Implement write_vectored
- A working vectored write implementation for TCP4. - Also introduces a small helper UefiBox intended to be used with heap allocated UEFI DSTs. - Tested on OVMF Signed-off-by: Ayush Singh <[email protected]>
1 parent f13ef0d commit c6f8c48

File tree

4 files changed

+82
-16
lines changed

4 files changed

+82
-16
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,11 @@ impl TcpStream {
7777
}
7878

7979
pub fn write_vectored(&self, buf: &[IoSlice<'_>]) -> io::Result<usize> {
80-
// FIXME: UEFI does support vectored write, so implement that.
81-
crate::io::default_write_vectored(|b| self.write(b), buf)
80+
self.inner.write_vectored(buf, self.write_timeout()?)
8281
}
8382

8483
pub fn is_write_vectored(&self) -> bool {
85-
false
84+
true
8685
}
8786

8887
pub fn peer_addr(&self) -> io::Result<SocketAddr> {

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::tcp4;
2-
use crate::io;
2+
use crate::io::{self, IoSlice};
33
use crate::net::SocketAddr;
44
use crate::ptr::NonNull;
55
use crate::sys::{helpers, unsupported};
@@ -28,6 +28,16 @@ impl Tcp {
2828
}
2929
}
3030

31+
pub(crate) fn write_vectored(
32+
&self,
33+
buf: &[IoSlice<'_>],
34+
timeout: Option<Duration>,
35+
) -> io::Result<usize> {
36+
match self {
37+
Self::V4(client) => client.write_vectored(buf, timeout),
38+
}
39+
}
40+
3141
pub(crate) fn read(&self, buf: &mut [u8], timeout: Option<Duration>) -> io::Result<usize> {
3242
match self {
3343
Self::V4(client) => client.read(buf, timeout),

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

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use r_efi::efi::{self, Status};
22
use r_efi::protocols::tcp4;
33

4-
use crate::io;
4+
use crate::io::{self, IoSlice};
55
use crate::net::SocketAddrV4;
66
use crate::ptr::NonNull;
77
use crate::sync::atomic::{AtomicBool, Ordering};
@@ -108,11 +108,7 @@ impl Tcp4 {
108108
}
109109

110110
pub(crate) fn write(&self, buf: &[u8], timeout: Option<Duration>) -> io::Result<usize> {
111-
let evt = unsafe { self.create_evt() }?;
112-
let completion_token =
113-
tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
114111
let data_len = u32::try_from(buf.len()).unwrap_or(u32::MAX);
115-
116112
let fragment = tcp4::FragmentData {
117113
fragment_length: data_len,
118114
fragment_buffer: buf.as_ptr().cast::<crate::ffi::c_void>().cast_mut(),
@@ -125,14 +121,57 @@ impl Tcp4 {
125121
fragment_table: [fragment],
126122
};
127123

128-
let protocol = self.protocol.as_ptr();
129-
let mut token = tcp4::IoToken {
130-
completion_token,
131-
packet: tcp4::IoTokenPacket {
132-
tx_data: (&raw mut tx_data).cast::<tcp4::TransmitData<0>>(),
133-
},
124+
self.write_inner((&raw mut tx_data).cast(), timeout).map(|_| data_len as usize)
125+
}
126+
127+
pub(crate) fn write_vectored(
128+
&self,
129+
buf: &[IoSlice<'_>],
130+
timeout: Option<Duration>,
131+
) -> io::Result<usize> {
132+
let mut data_len = 0u32;
133+
let mut fragment_count = 0u32;
134+
135+
// Calculate how many IoSlice in buf can be transmitted.
136+
for i in buf {
137+
match data_len.checked_add(i.as_slice().len() as u32) {
138+
Some(x) => data_len = x,
139+
None => break,
140+
}
141+
fragment_count += 1;
142+
}
143+
144+
let tx_data_size = crate::mem::size_of::<tcp4::TransmitData<0>>()
145+
+ crate::mem::size_of::<tcp4::FragmentData>() * (fragment_count as usize);
146+
let mut tx_data = helpers::UefiBox::<tcp4::TransmitData>::new(tx_data_size);
147+
unsafe {
148+
(*tx_data.as_mut_ptr()).push = r_efi::efi::Boolean::FALSE;
149+
(*tx_data.as_mut_ptr()).urgent = r_efi::efi::Boolean::FALSE;
150+
(*tx_data.as_mut_ptr()).data_length = data_len;
151+
(*tx_data.as_mut_ptr()).fragment_count = fragment_count;
152+
// SAFETY: IoSlice and FragmentData are guaranteed to have same layout.
153+
crate::ptr::copy_nonoverlapping(
154+
buf.as_ptr().cast(),
155+
(*tx_data.as_mut_ptr()).fragment_table.as_mut_ptr(),
156+
fragment_count as usize,
157+
);
134158
};
135159

160+
self.write_inner(tx_data.as_mut_ptr(), timeout).map(|_| data_len as usize)
161+
}
162+
163+
fn write_inner(
164+
&self,
165+
tx_data: *mut tcp4::TransmitData,
166+
timeout: Option<Duration>,
167+
) -> io::Result<()> {
168+
let evt = unsafe { self.create_evt() }?;
169+
let completion_token =
170+
tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
171+
172+
let protocol = self.protocol.as_ptr();
173+
let mut token = tcp4::IoToken { completion_token, packet: tcp4::IoTokenPacket { tx_data } };
174+
136175
let r = unsafe { ((*protocol).transmit)(protocol, &mut token) };
137176
if r.is_error() {
138177
return Err(io::Error::from_raw_os_error(r.as_usize()));
@@ -143,7 +182,7 @@ impl Tcp4 {
143182
if completion_token.status.is_error() {
144183
Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
145184
} else {
146-
Ok(data_len as usize)
185+
Ok(())
147186
}
148187
}
149188

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,21 @@ pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Addres
765765
pub(crate) const fn ipv4_from_r_efi(ip: efi::Ipv4Address) -> crate::net::Ipv4Addr {
766766
crate::net::Ipv4Addr::new(ip.addr[0], ip.addr[1], ip.addr[2], ip.addr[3])
767767
}
768+
769+
/// This type is intended for use with ZSTs. Since such types are unsized, a reference to such types
770+
/// is not valid in Rust. Thus, only pointers should be used when interacting with such types.
771+
#[repr(transparent)]
772+
pub(crate) struct UefiBox<T> {
773+
inner: Box<[u8]>,
774+
phantom: PhantomData<T>,
775+
}
776+
777+
impl<T> UefiBox<T> {
778+
pub(crate) fn new(len: usize) -> Self {
779+
Self { inner: vec![0u8; len].into_boxed_slice(), phantom: PhantomData }
780+
}
781+
782+
pub(crate) fn as_mut_ptr(&mut self) -> *mut T {
783+
self.inner.as_mut_ptr().cast()
784+
}
785+
}

0 commit comments

Comments
 (0)