Skip to content

Commit 5eef85c

Browse files
authored
Rollup merge of rust-lang#146301 - Ayush1325:uefi-box, r=joboet
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 cc ```@nicholasbishop```
2 parents 149eb1a + 471f2ba commit 5eef85c

File tree

4 files changed

+107
-16
lines changed

4 files changed

+107
-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
@@ -82,12 +82,11 @@ impl TcpStream {
8282
}
8383

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

8988
pub fn is_write_vectored(&self) -> bool {
90-
false
89+
true
9190
}
9291

9392
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: 57 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,63 @@ 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_length = 0u32;
133+
let mut fragment_count = 0u32;
134+
135+
// Calculate how many IoSlice in buf can be transmitted.
136+
for i in buf {
137+
// IoSlice length is always <= u32::MAX in UEFI.
138+
match data_length
139+
.checked_add(u32::try_from(i.as_slice().len()).expect("value is stored as a u32"))
140+
{
141+
Some(x) => data_length = x,
142+
None => break,
143+
}
144+
fragment_count += 1;
145+
}
146+
147+
let tx_data_size = size_of::<tcp4::TransmitData<0>>()
148+
+ size_of::<tcp4::FragmentData>() * (fragment_count as usize);
149+
let mut tx_data = helpers::UefiBox::<tcp4::TransmitData>::new(tx_data_size)?;
150+
tx_data.write(tcp4::TransmitData {
151+
push: r_efi::efi::Boolean::FALSE,
152+
urgent: r_efi::efi::Boolean::FALSE,
153+
data_length,
154+
fragment_count,
155+
fragment_table: [],
156+
});
157+
unsafe {
158+
// SAFETY: IoSlice and FragmentData are guaranteed to have same layout.
159+
crate::ptr::copy_nonoverlapping(
160+
buf.as_ptr().cast(),
161+
(*tx_data.as_mut_ptr()).fragment_table.as_mut_ptr(),
162+
fragment_count as usize,
163+
);
134164
};
135165

166+
self.write_inner(tx_data.as_mut_ptr(), timeout).map(|_| data_length as usize)
167+
}
168+
169+
fn write_inner(
170+
&self,
171+
tx_data: *mut tcp4::TransmitData,
172+
timeout: Option<Duration>,
173+
) -> io::Result<()> {
174+
let evt = unsafe { self.create_evt() }?;
175+
let completion_token =
176+
tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
177+
178+
let protocol = self.protocol.as_ptr();
179+
let mut token = tcp4::IoToken { completion_token, packet: tcp4::IoTokenPacket { tx_data } };
180+
136181
let r = unsafe { ((*protocol).transmit)(protocol, &mut token) };
137182
if r.is_error() {
138183
return Err(io::Error::from_raw_os_error(r.as_usize()));
@@ -143,7 +188,7 @@ impl Tcp4 {
143188
if completion_token.status.is_error() {
144189
Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
145190
} else {
146-
Ok(data_len as usize)
191+
Ok(())
147192
}
148193
}
149194

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use r_efi::efi::{self, Guid};
1313
use r_efi::protocols::{device_path, device_path_to_text, service_binding, shell};
1414

15+
use crate::alloc::Layout;
1516
use crate::ffi::{OsStr, OsString};
1617
use crate::io::{self, const_error};
1718
use crate::marker::PhantomData;
@@ -769,3 +770,39 @@ pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Addres
769770
pub(crate) const fn ipv4_from_r_efi(ip: efi::Ipv4Address) -> crate::net::Ipv4Addr {
770771
crate::net::Ipv4Addr::new(ip.addr[0], ip.addr[1], ip.addr[2], ip.addr[3])
771772
}
773+
774+
/// This type is intended for use with ZSTs. Since such types are unsized, a reference to such types
775+
/// is not valid in Rust. Thus, only pointers should be used when interacting with such types.
776+
pub(crate) struct UefiBox<T> {
777+
inner: NonNull<T>,
778+
size: usize,
779+
}
780+
781+
impl<T> UefiBox<T> {
782+
pub(crate) fn new(len: usize) -> io::Result<Self> {
783+
assert!(len >= size_of::<T>());
784+
// UEFI always expects types to be 8 byte aligned.
785+
let layout = Layout::from_size_align(len, 8).unwrap();
786+
let ptr = unsafe { crate::alloc::alloc(layout) };
787+
788+
match NonNull::new(ptr.cast()) {
789+
Some(inner) => Ok(Self { inner, size: len }),
790+
None => Err(io::Error::new(io::ErrorKind::OutOfMemory, "Allocation failed")),
791+
}
792+
}
793+
794+
pub(crate) fn write(&mut self, data: T) {
795+
unsafe { self.inner.write(data) }
796+
}
797+
798+
pub(crate) fn as_mut_ptr(&mut self) -> *mut T {
799+
self.inner.as_ptr().cast()
800+
}
801+
}
802+
803+
impl<T> Drop for UefiBox<T> {
804+
fn drop(&mut self) {
805+
let layout = Layout::from_size_align(self.size, 8).unwrap();
806+
unsafe { crate::alloc::dealloc(self.inner.as_ptr().cast(), layout) };
807+
}
808+
}

0 commit comments

Comments
 (0)