From 8b61a4c39e98f9cff9a83486486fccf2ae45ed8e Mon Sep 17 00:00:00 2001 From: demilade Date: Thu, 27 Oct 2022 11:11:00 +0100 Subject: [PATCH 1/7] done with definitions of snp structs --- uefi/src/proto/network/mod.rs | 1 + uefi/src/proto/network/snp.rs | 134 ++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 uefi/src/proto/network/snp.rs diff --git a/uefi/src/proto/network/mod.rs b/uefi/src/proto/network/mod.rs index debfde916..69316b1c9 100644 --- a/uefi/src/proto/network/mod.rs +++ b/uefi/src/proto/network/mod.rs @@ -3,6 +3,7 @@ //! These protocols can be used to interact with network resources. pub mod pxe; +pub mod snp; /// Represents an IPv4/v6 address. /// diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs new file mode 100644 index 000000000..3cb283d74 --- /dev/null +++ b/uefi/src/proto/network/snp.rs @@ -0,0 +1,134 @@ +//! Simple Network Protocol + +use core::ffi::c_void; +use crate::Status; +use crate::data_types::Event; +use super::{IpAddress, MacAddress}; + +/// The Simple Network Protocol +#[repr(C)] +#[unsafe_guid("a19832b9-ac25-11d3-9a2d-0090273fc14d")] +#[derive(Protocol)] +pub struct SimpleNetwork { + revision: u64, + start: extern "efiapi" fn(this: &Self) -> Status, + stop: extern "efiapi" fn(this: &Self) -> Status, + initialize: extern "efiapi" fn( + this: &Self, + extra_recv_buffer_size: Option, + extra_transmit_buffer_size: Option + ) -> Status, + reset: extern "efiapi" fn(this: &Self, extended_verification: bool) -> Status, + shutdown: extern "efiapi" fn(this: &Self) -> Status, + receive_filters: extern "efiapi" fn( + this: &Self, + enable: u32, + disable: u32, + reset_mcast_filter: bool, + mcast_filter_count: Option, + mcast_filter: Option<*const [MacAddress]> + ) -> Status, + station_address: extern "efiapi" fn(this: &Self, reset: bool, new: Option) -> Status, + statistics: extern "efiapi" fn( + this: &Self, + reset: bool, + stats_size: Option<&mut usize>, + stats_table: Option<&mut NetworkStats> + ) -> Status, + mcast_ip_to_mac: extern "efiapi" fn( + this: &Self, + ipv6: bool, + ip: &IpAdddress, + mac: &mut MacAddress + ) -> Status, + nv_data: extern "efiapi" fn( + this: &Self, + read_write: bool, + offset: usize, + buffer_size: usize, + buffer: *mut c_void + ) -> Status, + get_status: extern "efiapi" fn( + this: &Self, + interrupt_status: Option<&mut u32>, + tx_buf: Option<&mut *mut c_void> + ) -> Status, + transmit: extern "efiapi" fn( + this: &Self, + header_size: usize, + buffer_size: usize, + buffer: *mut c_void, + src_addr: Option<&mut MacAddress>, + dest_addr: Option<&mut MacAddress>, + protocol: Option<&mut u16> + ) -> Status, + receive: extern "efiapi" fn( + this: &Self, + header_size: Option<&mut usize>, + buffer_size: &mut usize, + buffer: *mut c_void, + src_addr: Option<&mut MacAddress>, + dest_addr: Option<&mut MacAddress>, + protocol: Option<&mut u16> + ) -> Status, + wait_for_packet: Event, + mode: *const NetworkMode, +} + +/// Network Statistics +/// +/// The description of statistics on the network with the SNP's `statistics` function +/// is returned in this structure +#[repr(C)] +pub struct NetworkStats { + total_frames_rx: u64, + good_frames_rx: u64, + undersize_frames_rx: u64, + oversize_frames_rx: u64, + dropped_frames_rx: u64, + unicast_frames_rx: u64, + broadcast_frames_rx: u64, + multicast_frames_rx: u64, + crc_error_frames_rx: u64, + total_bytes_rx: u64, + total_frames_tx: u64, + good_frames_tx: u64, + undersize_frames_tx: u64, + oversize_frames_tx: u64, + dropped_frames_tx: u64, + unicast_frames_tx: u64, + broadcast_frames_tx: u64, + multicast_frames_tx: u64, + crc_error_frames_tx: u64, + total_bytes_tx: u64, + collisions: u64, + unsupported_protocol: u64, + duplicated_frames_rx: u64, + decrypt_error_frames_rx: u64, + error_frames_tx: u64, + retry_frames_tx: u64 +} + +#[repr(C)] +#[derive(Debug)] +pub struct NetworkMode { + state: u32, + hw_address_size: u32, + media_header_size: u32, + max_packet_size: u32, + nv_ram_size: u32, + nv_ram_access_size: u32, + receive_filter_mask: u32, + receive_filter_setting: u32, + max_mcast_filter_count: u32, + mcast_filter_count: u32, + mcast_filter: [MacAddress; 16], + current_address: MacAddress, + broadcast_address: MacAddress, + permanent_address: MacAddress, + if_type: u8, + mac_address_changeable: bool, + multiple_tx_supported: bool, + media_present_supported: bool, + media_present: bool +} \ No newline at end of file From ac664bef2359d4eb9e9a45036ecda62aef9704a9 Mon Sep 17 00:00:00 2001 From: demilade Date: Thu, 27 Oct 2022 13:32:42 +0100 Subject: [PATCH 2/7] wrapped SNP functions --- uefi/src/proto/network/snp.rs | 244 ++++++++++++++++++++++++++++++++-- 1 file changed, 235 insertions(+), 9 deletions(-) diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs index 3cb283d74..2e137ca08 100644 --- a/uefi/src/proto/network/snp.rs +++ b/uefi/src/proto/network/snp.rs @@ -1,7 +1,16 @@ //! Simple Network Protocol +//! +//! Provides a packet level interface to a network adapter. +//! Once the adapter is initialized, the protocol provides services that allows +//! packets to be transmitted and received. +//! +//! No interface function must be called until `SimpleNetwork.start` is successfully +//! called first. use core::ffi::c_void; -use crate::Status; +use core::ptr; +use uefi_macros::{unsafe_guid, Protocol}; +use crate::{Status, Result}; use crate::data_types::Event; use super::{IpAddress, MacAddress}; @@ -28,7 +37,7 @@ pub struct SimpleNetwork { mcast_filter_count: Option, mcast_filter: Option<*const [MacAddress]> ) -> Status, - station_address: extern "efiapi" fn(this: &Self, reset: bool, new: Option) -> Status, + station_address: extern "efiapi" fn(this: &Self, reset: bool, new: Option<&MacAddress>) -> Status, statistics: extern "efiapi" fn( this: &Self, reset: bool, @@ -38,7 +47,7 @@ pub struct SimpleNetwork { mcast_ip_to_mac: extern "efiapi" fn( this: &Self, ipv6: bool, - ip: &IpAdddress, + ip: &IpAddress, mac: &mut MacAddress ) -> Status, nv_data: extern "efiapi" fn( @@ -50,7 +59,7 @@ pub struct SimpleNetwork { ) -> Status, get_status: extern "efiapi" fn( this: &Self, - interrupt_status: Option<&mut u32>, + interrupt_status: Option<&mut InterruptStatus>, tx_buf: Option<&mut *mut c_void> ) -> Status, transmit: extern "efiapi" fn( @@ -58,9 +67,9 @@ pub struct SimpleNetwork { header_size: usize, buffer_size: usize, buffer: *mut c_void, - src_addr: Option<&mut MacAddress>, - dest_addr: Option<&mut MacAddress>, - protocol: Option<&mut u16> + src_addr: Option<&MacAddress>, + dest_addr: Option<&MacAddress>, + protocol: Option<&u16> ) -> Status, receive: extern "efiapi" fn( this: &Self, @@ -75,11 +84,219 @@ pub struct SimpleNetwork { mode: *const NetworkMode, } +impl SimpleNetwork { + /// Changes the state of a network from "Stopped" to "Started" + pub fn start(&self) -> Result { + (self.start)(self).into() + } + + /// Changes the state of a network interface from "Started" to "Stopped" + pub fn stop(&self) -> Result { + (self.stop)(self).into() + } + + /// Resets a network adapter and allocates the transmit and receive buffers + /// required by the network interface; optionally, also requests allocation of + /// additional transmit and receive buffers + pub fn initialize( + &self, + extra_rx_buffer_size: Option, + extra_tx_buffer_size: Option + ) -> Result { + (self.initialize)(self, extra_rx_buffer_size, extra_tx_buffer_size).into() + } + + /// Resets a network adapter and reinitializes it with the parameters that were + /// provided in the previous call to `initialize` + pub fn reset(&self, extended_verification: bool) -> Result { + (self.reset)(self, extended_verification).into() + } + + /// Resets a network adapter and leaves it in a state that is safe + /// for another driver to initialize + pub fn shutdown(&self) -> Result { + (self.shutdown)(self).into() + } + + /// Manages the multicast receive filters of a network + pub fn receive_filters( + &self, + enable: u32, + disable: u32, + reset_mcast_filter: bool, + mcast_filter_count: Option, + mcast_filter: Option<*const [MacAddress]> + ) -> Result { + (self.receive_filters)( + self, + enable, + disable, + reset_mcast_filter, + mcast_filter_count, + mcast_filter + ).into() + } + + /// Modifies or resets the current station address, if supported + pub fn station_address(&self, reset: bool, new: Option<&MacAddress>) -> Result { + (self.station_address)( + self, + reset, + new + ).into() + } + + /// Resets statistics on a network interface + pub fn reset_statistics(&self) -> Result { + (self.statistics)(self, true, None, None).into() + } + + /// Collects statistics on a network interface + pub fn collect_statistics(&self) -> Result { + let mut stats_table: NetworkStats = Default::default(); + let mut stats_size = core::mem::size_of::(); + let status = (self.statistics)(self, false, Some(&mut stats_size), Some(&mut stats_table)); + Result::from(status)?; + Ok(stats_table) + } + + /// Converts a multicast IP address to a multicast HW MAC Address + pub fn mcast_ip_to_mac(&self, ipv6: bool, ip: IpAddress) -> Result { + let mut mac_address = MacAddress([0; 32]); + let status = (self.mcast_ip_to_mac)(self, ipv6, &ip, &mut mac_address); + Result::from(status)?; + Ok(mac_address) + } + + /// Performs read operations on the NVRAM device attached to + /// a network interface + pub fn read_nv_data(&self, offset: usize, buffer_size: usize, buffer: *mut c_void) -> Result { + (self.nv_data)( + self, + true, + offset, + buffer_size, + buffer + ).into() + } + + /// Performs write operations on the NVRAM device attached to a network interface + pub fn write_nv_data(&self, offset: usize, buffer_size: usize, buffer: *mut c_void) -> Result { + (self.nv_data)( + self, + false, + offset, + buffer_size, + buffer + ).into() + } + + /// Reads the current interrupt status and recycled transmit buffer + /// status from a network interface + pub fn get_interrupt_status(&self) -> Result { + let mut interrupt_status = InterruptStatus::new(); + let status = (self.get_status)(self, Some(&mut interrupt_status), None); + Result::from(status)?; + Ok(interrupt_status) + } + + /// Reads the current recycled transmit buffer status from a + /// network interface + pub fn get_recycled_transmit_buffer_status(&self) -> Result> { + let mut tx_buf: *mut c_void = ptr::null_mut(); + let status = (self.get_status)(self, None, Some(&mut tx_buf)); + Result::from(status)?; + if tx_buf == ptr::null_mut() { + Ok(None) + } else { + Ok(Some(tx_buf.cast())) + } + } + + /// Places a packet in the transmit queue of a network interface + pub fn transmit( + &self, + header_size: usize, + buffer: &mut [u8], + src_addr: Option<&MacAddress>, + dest_addr: Option<&MacAddress>, + protocol: Option<&u16> + ) -> Result { + (self.transmit)( + self, + header_size, + buffer.len(), + buffer.as_mut_ptr().cast(), + src_addr, + dest_addr, + protocol + ).into() + } + + /// Receives a packet from a network interface + /// + /// On success, returns the size of bytes of the received packet + pub fn receive( + &self, + buffer: &mut [u8], + header_size: Option<&mut usize>, + src_addr: Option<&mut MacAddress>, + dest_addr: Option<&mut MacAddress>, + protocol: Option<&mut u16> + ) -> Result { + let mut buffer_size = buffer.len(); + let status = (self.receive)( + self, + header_size, + &mut buffer_size, + buffer.as_mut_ptr().cast(), + src_addr, + dest_addr, + protocol + ); + Result::from(status)?; + Ok(buffer_size) + } + + /// Returns a reference to the Simple Network mode + pub fn mode(&self) -> &NetworkMode { + unsafe { &*self.mode } + } +} + +/// A bitmask of currently active interrupts +#[repr(transparent)] +pub struct InterruptStatus(u32); + +impl InterruptStatus { + /// Creates a new InterruptStatus instance with all bits unset + pub fn new() -> Self { + Self(0) + } + /// The receive interrupt bit + pub fn receive_interrupt(&self) -> bool { + self.0 & 0x01 == 1 + } + /// The transmit interrupt bit + pub fn transmit_interrupt(&self) -> bool { + self.0 & 0x02 == 0x02 + } + /// The command interrupt bit + pub fn command_interrupt(&self) -> bool { + self.0 & 0x04 == 0x04 + } + /// The software interrupt bit + pub fn software_interrupt(&self) -> bool { + self.0 & 0x08 == 0x08 + } +} + /// Network Statistics /// /// The description of statistics on the network with the SNP's `statistics` function /// is returned in this structure #[repr(C)] +#[derive(Default)] pub struct NetworkStats { total_frames_rx: u64, good_frames_rx: u64, @@ -109,10 +326,10 @@ pub struct NetworkStats { retry_frames_tx: u64 } +/// The Simple Network Mode #[repr(C)] -#[derive(Debug)] pub struct NetworkMode { - state: u32, + state: NetworkState, hw_address_size: u32, media_header_size: u32, max_packet_size: u32, @@ -131,4 +348,13 @@ pub struct NetworkMode { multiple_tx_supported: bool, media_present_supported: bool, media_present: bool +} + +newtype_enum! { + pub enum NetworkState: u32 => { + Stopped = 0, + Started = 1, + Initialized = 2, + MaxState = 4 + } } \ No newline at end of file From ffac49d3c518e4037585f8824845882e018f23c9 Mon Sep 17 00:00:00 2001 From: demilade Date: Thu, 27 Oct 2022 13:43:59 +0100 Subject: [PATCH 3/7] fixed an error in the snp code --- uefi/src/proto/network/snp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs index 2e137ca08..acbf4dbd3 100644 --- a/uefi/src/proto/network/snp.rs +++ b/uefi/src/proto/network/snp.rs @@ -355,6 +355,6 @@ newtype_enum! { Stopped = 0, Started = 1, Initialized = 2, - MaxState = 4 + MaxState = 4, } } \ No newline at end of file From bf204d7f40b621b990f12406efb1da7859d99a90 Mon Sep 17 00:00:00 2001 From: demilade Date: Thu, 27 Oct 2022 14:29:49 +0100 Subject: [PATCH 4/7] fixed an error in the snp module --- uefi/src/proto/network/snp.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs index acbf4dbd3..30dce3d62 100644 --- a/uefi/src/proto/network/snp.rs +++ b/uefi/src/proto/network/snp.rs @@ -351,10 +351,15 @@ pub struct NetworkMode { } newtype_enum! { + /// The state of a network interface pub enum NetworkState: u32 => { - Stopped = 0, - Started = 1, - Initialized = 2, - MaxState = 4, + /// The interface has been stopped + STOPPED = 0, + /// The interface has been started + STARTED = 1, + /// The interface has been initialized + INITIALIZED = 2, + /// No state can have a number higher than this + MAX_STATE = 4, } } \ No newline at end of file From 1975acf20e512557f3be0f30058d4b76a7f709fb Mon Sep 17 00:00:00 2001 From: demilade Date: Sun, 30 Oct 2022 20:03:59 +0100 Subject: [PATCH 5/7] gave network statistics a better interface; refactored a little --- uefi-test-runner/src/proto/network/mod.rs | 11 + .../src/proto/{network.rs => network/pxe.rs} | 2 +- uefi-test-runner/src/proto/network/snp.rs | 89 ++++++ uefi/src/proto/network/snp.rs | 293 ++++++++++++++---- 4 files changed, 342 insertions(+), 53 deletions(-) create mode 100644 uefi-test-runner/src/proto/network/mod.rs rename uefi-test-runner/src/proto/{network.rs => network/pxe.rs} (98%) create mode 100644 uefi-test-runner/src/proto/network/snp.rs diff --git a/uefi-test-runner/src/proto/network/mod.rs b/uefi-test-runner/src/proto/network/mod.rs new file mode 100644 index 000000000..436d78635 --- /dev/null +++ b/uefi-test-runner/src/proto/network/mod.rs @@ -0,0 +1,11 @@ +use uefi::prelude::*; + +pub fn test(bt: &BootServices) { + info!("Testing Network protocols"); + + pxe::test(bt); + snp::test(bt); +} + +mod pxe; +mod snp; diff --git a/uefi-test-runner/src/proto/network.rs b/uefi-test-runner/src/proto/network/pxe.rs similarity index 98% rename from uefi-test-runner/src/proto/network.rs rename to uefi-test-runner/src/proto/network/pxe.rs index f19ba9026..17b00416e 100644 --- a/uefi-test-runner/src/proto/network.rs +++ b/uefi-test-runner/src/proto/network/pxe.rs @@ -8,7 +8,7 @@ use uefi::{ }; pub fn test(bt: &BootServices) { - info!("Testing Network protocols"); + info!("Testing The PXE base code protocol"); if let Ok(handles) = bt.find_handles::() { for handle in handles { diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs new file mode 100644 index 000000000..7ca9ccf4f --- /dev/null +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -0,0 +1,89 @@ +use uefi::prelude::BootServices; +use uefi::proto::network::snp::SimpleNetwork; +use uefi::proto::network::MacAddress; + + +pub fn test(bt: &BootServices) { + info!("Testing the simple network protocol"); + + let handles = bt + .find_handles::() + .expect("Failed to get handles for `SimpleNetwork` protocol"); + + for handle in handles { + + let simple_network = bt.open_protocol_exclusive::(handle); + if simple_network.is_err() { continue; } + let simple_network = simple_network.unwrap(); + + // Check shutdown + simple_network.shutdown().expect("Failed to shutdown Simple Network"); + + // Check stop + simple_network.stop().expect("Failed to stop Simple Network"); + + // Check start + simple_network.start().expect("Failed to start Simple Network"); + + // Check initialize + simple_network.initialize(None, None) + .expect("Failed to initialize Simple Network"); + + simple_network.reset_statistics().unwrap(); + + // Reading the interrupt status clears it + simple_network.get_interrupt_status().unwrap(); + + // Set receive filters + simple_network.receive_filters(0x01 | 0x02 | 0x04 | 0x08 | 0x10, 0, false, None, None) + .expect("Failed to set receive filters"); + + // Check media + if !simple_network.mode().media_present_supported || !simple_network.mode().media_present { + continue; + } + + let payload = &[0u8; 46]; + + let dest_addr = MacAddress([0xffu8;32]); + assert!(!simple_network.get_interrupt_status().unwrap().transmit_interrupt()); + // Send the frame + simple_network.transmit( + simple_network.mode().media_header_size as usize, + payload, + None, + Some(&dest_addr), + Some(&0x0800), + ) + .expect("Failed to transmit frame"); + + info!("Waiting for the transmit"); + while !simple_network.get_interrupt_status().unwrap().transmit_interrupt() {} + + // Attempt to receive a frame + let mut buffer = [0u8; 1500]; + + let mut count = 0; + + info!("Waiting for the reception"); + while count < 1_000 { + let result = simple_network.receive( + &mut buffer, + None, + None, + None, + None + ); + if result.is_ok() { break; } + count += 1; + } + + // Get stats + let stats = simple_network.collect_statistics().expect("Failed to collect statistics"); + info!("Stats: {:?}", stats); + + // One frame should have been transmitted and one received + assert_eq!(stats.tx_total_frames().unwrap(), 1); + assert_eq!(stats.rx_total_frames().unwrap(), 1); + } +} diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs index 30dce3d62..acaadc2a5 100644 --- a/uefi/src/proto/network/snp.rs +++ b/uefi/src/proto/network/snp.rs @@ -66,7 +66,7 @@ pub struct SimpleNetwork { this: &Self, header_size: usize, buffer_size: usize, - buffer: *mut c_void, + buffer: *const c_void, src_addr: Option<&MacAddress>, dest_addr: Option<&MacAddress>, protocol: Option<&u16> @@ -217,7 +217,7 @@ impl SimpleNetwork { pub fn transmit( &self, header_size: usize, - buffer: &mut [u8], + buffer: &[u8], src_addr: Option<&MacAddress>, dest_addr: Option<&MacAddress>, protocol: Option<&u16> @@ -225,8 +225,8 @@ impl SimpleNetwork { (self.transmit)( self, header_size, - buffer.len(), - buffer.as_mut_ptr().cast(), + buffer.len() + header_size, + buffer.as_ptr().cast(), src_addr, dest_addr, protocol @@ -265,6 +265,7 @@ impl SimpleNetwork { } /// A bitmask of currently active interrupts +#[derive(Debug)] #[repr(transparent)] pub struct InterruptStatus(u32); @@ -275,19 +276,19 @@ impl InterruptStatus { } /// The receive interrupt bit pub fn receive_interrupt(&self) -> bool { - self.0 & 0x01 == 1 + self.0 & 0x01 != 0 } /// The transmit interrupt bit pub fn transmit_interrupt(&self) -> bool { - self.0 & 0x02 == 0x02 + self.0 & 0x02 != 0 } /// The command interrupt bit pub fn command_interrupt(&self) -> bool { - self.0 & 0x04 == 0x04 + self.0 & 0x04 != 0 } /// The software interrupt bit pub fn software_interrupt(&self) -> bool { - self.0 & 0x08 == 0x08 + self.0 & 0x08 != 0 } } @@ -295,59 +296,247 @@ impl InterruptStatus { /// /// The description of statistics on the network with the SNP's `statistics` function /// is returned in this structure +/// +/// Any of these statistics may or may not be available on the device. So, all the +/// retriever functions of the statistics return `None` when a statistic is not supported #[repr(C)] -#[derive(Default)] +#[derive(Default, Debug)] pub struct NetworkStats { - total_frames_rx: u64, - good_frames_rx: u64, - undersize_frames_rx: u64, - oversize_frames_rx: u64, - dropped_frames_rx: u64, - unicast_frames_rx: u64, - broadcast_frames_rx: u64, - multicast_frames_rx: u64, - crc_error_frames_rx: u64, - total_bytes_rx: u64, - total_frames_tx: u64, - good_frames_tx: u64, - undersize_frames_tx: u64, - oversize_frames_tx: u64, - dropped_frames_tx: u64, - unicast_frames_tx: u64, - broadcast_frames_tx: u64, - multicast_frames_tx: u64, - crc_error_frames_tx: u64, - total_bytes_tx: u64, + rx_total_frames: u64, + rx_good_frames: u64, + rx_undersize_frames: u64, + rx_oversize_frames: u64, + rx_dropped_frames: u64, + rx_unicast_frames: u64, + rx_broadcast_frames: u64, + rx_multicast_frames: u64, + rx_crc_error_frames: u64, + rx_total_bytes: u64, + tx_total_frames: u64, + tx_good_frames: u64, + tx_undersize_frames: u64, + tx_oversize_frames: u64, + tx_dropped_frames: u64, + tx_unicast_frames: u64, + tx_broadcast_frames: u64, + tx_multicast_frames: u64, + tx_crc_error_frames: u64, + tx_total_bytes: u64, collisions: u64, unsupported_protocol: u64, - duplicated_frames_rx: u64, - decrypt_error_frames_rx: u64, - error_frames_tx: u64, - retry_frames_tx: u64 + rx_duplicated_frames: u64, + rx_decrypt_error_frames: u64, + tx_error_frames: u64, + tx_retry_frames: u64 +} + +impl NetworkStats { + /// Any statistic value of -1 is not available + fn available(&self, stat: u64) -> bool { + stat as i64 != -1 + } + + /// Takes a statistic and converts it to an option + /// + /// When the statistic is not available, `None` is returned + fn to_option(&self, stat: u64) -> Option { + match self.available(stat) { + true => Some(stat), + false => None + } + } + + /// The total number of frames received, including error frames + /// and dropped frames + pub fn rx_total_frames(&self) -> Option { + self.to_option(self.rx_total_frames) + } + + /// The total number of good frames received and copied + /// into receive buffers + pub fn rx_good_frames(&self) -> Option { + self.to_option(self.rx_good_frames) + } + + /// The number of frames below the minimum length for the + /// communications device + pub fn rx_undersize_frames(&self) -> Option { + self.to_option(self.rx_undersize_frames) + } + + /// The number of frames longer than the maximum length for + /// the communications length device + pub fn rx_oversize_frames(&self) -> Option { + self.to_option(self.rx_oversize_frames) + } + + /// The number of valid frames that were dropped because + /// the receive buffers were full + pub fn rx_dropped_frames(&self) -> Option { + self.to_option(self.rx_dropped_frames) + } + + /// The number of valid unicast frames received and not dropped + pub fn rx_unicast_frames(&self) -> Option { + self.to_option(self.rx_unicast_frames) + } + + /// The number of valid broadcast frames received and not dropped + pub fn rx_broadcast_frames(&self) -> Option { + self.to_option(self.rx_broadcast_frames) + } + + /// The number of valid multicast frames received and not dropped + pub fn rx_multicast_frames(&self) -> Option { + self.to_option(self.rx_multicast_frames) + } + + /// Number of frames with CRC or alignment errors + pub fn rx_crc_error_frames(&self) -> Option { + self.to_option(self.rx_crc_error_frames) + } + + /// The total number of bytes received including frames with errors + /// and dropped frames + pub fn rx_total_bytes(&self) -> Option { + self.to_option(self.rx_total_bytes) + } + + /// The total number of frames transmitted including frames + /// with errors and dropped frames + pub fn tx_total_frames(&self) -> Option { + self.to_option(self.tx_total_frames) + } + + /// The total number of valid frames transmitted and copied + /// into receive buffers + pub fn tx_good_frames(&self) -> Option { + self.to_option(self.tx_good_frames) + } + + /// The number of frames below the minimum length for + /// the media. This would be less than 64 for Ethernet + pub fn tx_undersize_frames(&self) -> Option { + self.to_option(self.tx_undersize_frames) + } + + /// The number of frames longer than the maximum length for + /// the media. This would be 1500 for Ethernet + pub fn tx_oversize_frames(&self) -> Option { + self.to_option(self.tx_oversize_frames) + } + + /// The number of valid frames that were dropped because + /// received buffers were full + pub fn tx_dropped_frames(&self) -> Option { + self.to_option(self.tx_dropped_frames) + } + + /// The number of valid unicast frames transmitted and not + /// dropped + pub fn tx_unicast_frames(&self) -> Option { + self.to_option(self.tx_unicast_frames) + } + + /// The number of valid broadcast frames transmitted and + /// not dropped + pub fn tx_broadcast_frames(&self) -> Option { + self.to_option(self.tx_broadcast_frames) + } + + /// The number of valid multicast frames transmitted + /// and not dropped + pub fn tx_multicast_frames(&self) -> Option { + self.to_option(self.tx_multicast_frames) + } + + /// The number of transmitted frames with CRC or + /// alignment errors + pub fn tx_crc_error_frames(&self) -> Option { + self.to_option(self.tx_crc_error_frames) + } + + /// The total number of bytes transmitted including + /// error frames and dropped frames + pub fn tx_total_bytes(&self) -> Option { + self.to_option(self.tx_total_bytes) + } + + /// The number of collisions detected on this subnet + pub fn collisions(&self) -> Option { + self.to_option(self.collisions) + } + + /// The number of frames destined for unsupported protocol + pub fn unsupported_protocol(&self) -> Option { + self.to_option(self.unsupported_protocol) + } + + /// The number of valid frames received that were duplicated + pub fn rx_duplicated_frames(&self) -> Option { + self.to_option(self.rx_duplicated_frames) + } + + /// The number of encrypted frames received that failed + /// to decrypt + pub fn rx_decrypt_error_frames(&self) -> Option { + self.to_option(self.rx_decrypt_error_frames) + } + + /// The number of frames that failed to transmit after + /// exceeding the retry limit + pub fn tx_error_frames(&self) -> Option { + self.to_option(self.tx_error_frames) + } + + /// The number of frames that transmitted successfully + /// after more than one attempt + pub fn tx_retry_frames(&self) -> Option { + self.to_option(self.tx_retry_frames) + } } /// The Simple Network Mode #[repr(C)] pub struct NetworkMode { - state: NetworkState, - hw_address_size: u32, - media_header_size: u32, - max_packet_size: u32, - nv_ram_size: u32, - nv_ram_access_size: u32, - receive_filter_mask: u32, - receive_filter_setting: u32, - max_mcast_filter_count: u32, - mcast_filter_count: u32, - mcast_filter: [MacAddress; 16], - current_address: MacAddress, - broadcast_address: MacAddress, - permanent_address: MacAddress, - if_type: u8, - mac_address_changeable: bool, - multiple_tx_supported: bool, - media_present_supported: bool, - media_present: bool + /// Reports the current state of the network interface + pub state: NetworkState, + /// The size of the network interface's hardware address in bytes + pub hw_address_size: u32, + /// The size of the network interface's media header in bytes + pub media_header_size: u32, + /// The maximum size of the packets supported by the network interface in bytes + pub max_packet_size: u32, + /// The size of the NVRAM device attached to the network interface in bytes + pub nv_ram_size: u32, + /// The size that must be used for all NVRAM reads and writes + pub nv_ram_access_size: u32, + /// The multicast receive filter settings supported by the network interface + pub receive_filter_mask: u32, + /// The current multicast receive filter settings + pub receive_filter_setting: u32, + /// The maximum number of multicast address receive filters supported by the driver + pub max_mcast_filter_count: u32, + /// The current number of multicast address receive filters + pub mcast_filter_count: u32, + /// The array containing the addresses of the current multicast address receive filters + pub mcast_filter: [MacAddress; 16], + /// The current hardware MAC address for the network interface + pub current_address: MacAddress, + /// The current hardware MAC address for broadcast packets + pub broadcast_address: MacAddress, + /// The permanent hardware MAC address for the network interface + pub permanent_address: MacAddress, + /// The interface type of the network interface + pub if_type: u8, + /// Tells if the MAC address can be changed + pub mac_address_changeable: bool, + /// Tells if the network interface can transmit more than one packet at a time + pub multiple_tx_supported: bool, + /// Tells if the presence of the media can be determined + pub media_present_supported: bool, + /// Tells if media are connected to the network interface + pub media_present: bool } newtype_enum! { From 0e003b28b233bfaf0e399b809c6f4db65454d7a8 Mon Sep 17 00:00:00 2001 From: Luca Versari Date: Fri, 9 Dec 2022 01:27:37 +0100 Subject: [PATCH 6/7] Clean up implementation of SNP and fix test code. --- uefi-test-runner/src/proto/network/snp.rs | 115 ++++++++++------ uefi/src/proto/network/snp.rs | 159 +++++++++++++--------- xtask/src/util.rs | 2 +- 3 files changed, 174 insertions(+), 102 deletions(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 7ca9ccf4f..0007636af 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -1,32 +1,41 @@ use uefi::prelude::BootServices; use uefi::proto::network::snp::SimpleNetwork; +use uefi::proto::network::snp::EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; +use uefi::proto::network::snp::EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; +use uefi::proto::network::snp::EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; use uefi::proto::network::MacAddress; - +use uefi::Status; pub fn test(bt: &BootServices) { info!("Testing the simple network protocol"); - let handles = bt - .find_handles::() - .expect("Failed to get handles for `SimpleNetwork` protocol"); + let handles = bt.find_handles::().unwrap_or_default(); for handle in handles { - let simple_network = bt.open_protocol_exclusive::(handle); - if simple_network.is_err() { continue; } + if simple_network.is_err() { + continue; + } let simple_network = simple_network.unwrap(); // Check shutdown - simple_network.shutdown().expect("Failed to shutdown Simple Network"); + simple_network + .shutdown() + .expect("Failed to shutdown Simple Network"); // Check stop - simple_network.stop().expect("Failed to stop Simple Network"); + simple_network + .stop() + .expect("Failed to stop Simple Network"); // Check start - simple_network.start().expect("Failed to start Simple Network"); + simple_network + .start() + .expect("Failed to start Simple Network"); // Check initialize - simple_network.initialize(None, None) + simple_network + .initialize(0, 0) .expect("Failed to initialize Simple Network"); simple_network.reset_statistics().unwrap(); @@ -35,7 +44,15 @@ pub fn test(bt: &BootServices) { simple_network.get_interrupt_status().unwrap(); // Set receive filters - simple_network.receive_filters(0x01 | 0x02 | 0x04 | 0x08 | 0x10, 0, false, None, None) + simple_network + .receive_filters( + EFI_SIMPLE_NETWORK_RECEIVE_UNICAST + | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST + | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST, + 0, + false, + None, + ) .expect("Failed to set receive filters"); // Check media @@ -43,43 +60,65 @@ pub fn test(bt: &BootServices) { continue; } - let payload = &[0u8; 46]; - - let dest_addr = MacAddress([0xffu8;32]); - assert!(!simple_network.get_interrupt_status().unwrap().transmit_interrupt()); + let payload = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + \x45\x00\ + \x00\x21\ + \x00\x01\ + \x00\x00\ + \x10\ + \x11\ + \x07\x6a\ + \xc0\xa8\x11\x0f\ + \xc0\xa8\x11\x02\ + \x54\x45\ + \x54\x44\ + \x00\x0d\ + \xa9\xe4\ + \x04\x01\x02\x03\x04"; + + let dest_addr = MacAddress([0xffu8; 32]); + assert!(!simple_network + .get_interrupt_status() + .unwrap() + .transmit_interrupt()); // Send the frame - simple_network.transmit( - simple_network.mode().media_header_size as usize, - payload, - None, - Some(&dest_addr), - Some(&0x0800), - ) - .expect("Failed to transmit frame"); + simple_network + .transmit( + simple_network.mode().media_header_size as usize, + payload, + None, + Some(&dest_addr), + Some(&0x0800), + ) + .expect("Failed to transmit frame"); info!("Waiting for the transmit"); - while !simple_network.get_interrupt_status().unwrap().transmit_interrupt() {} + while !simple_network + .get_interrupt_status() + .unwrap() + .transmit_interrupt() + {} // Attempt to receive a frame let mut buffer = [0u8; 1500]; - - let mut count = 0; - + info!("Waiting for the reception"); - while count < 1_000 { - let result = simple_network.receive( - &mut buffer, - None, - None, - None, - None - ); - if result.is_ok() { break; } - count += 1; + if simple_network.receive(&mut buffer, None, None, None, None) + == Err(Status::NOT_READY.into()) + { + bt.stall(1_000_000); + + simple_network + .receive(&mut buffer, None, None, None, None) + .unwrap(); } + assert_eq!(buffer[42..47], [4, 4, 3, 2, 1]); + // Get stats - let stats = simple_network.collect_statistics().expect("Failed to collect statistics"); + let stats = simple_network + .collect_statistics() + .expect("Failed to collect statistics"); info!("Stats: {:?}", stats); // One frame should have been transmitted and one received diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs index acaadc2a5..aa090147e 100644 --- a/uefi/src/proto/network/snp.rs +++ b/uefi/src/proto/network/snp.rs @@ -7,12 +7,13 @@ //! No interface function must be called until `SimpleNetwork.start` is successfully //! called first. +use super::{IpAddress, MacAddress}; +use crate::data_types::Event; +use crate::{Result, Status}; use core::ffi::c_void; use core::ptr; +use core::ptr::NonNull; use uefi_macros::{unsafe_guid, Protocol}; -use crate::{Status, Result}; -use crate::data_types::Event; -use super::{IpAddress, MacAddress}; /// The Simple Network Protocol #[repr(C)] @@ -24,8 +25,8 @@ pub struct SimpleNetwork { stop: extern "efiapi" fn(this: &Self) -> Status, initialize: extern "efiapi" fn( this: &Self, - extra_recv_buffer_size: Option, - extra_transmit_buffer_size: Option + extra_recv_buffer_size: usize, + extra_transmit_buffer_size: usize, ) -> Status, reset: extern "efiapi" fn(this: &Self, extended_verification: bool) -> Status, shutdown: extern "efiapi" fn(this: &Self) -> Status, @@ -34,33 +35,30 @@ pub struct SimpleNetwork { enable: u32, disable: u32, reset_mcast_filter: bool, - mcast_filter_count: Option, - mcast_filter: Option<*const [MacAddress]> + mcast_filter_count: usize, + mcast_filter: Option>, ) -> Status, - station_address: extern "efiapi" fn(this: &Self, reset: bool, new: Option<&MacAddress>) -> Status, + station_address: + extern "efiapi" fn(this: &Self, reset: bool, new: Option<&MacAddress>) -> Status, statistics: extern "efiapi" fn( this: &Self, reset: bool, stats_size: Option<&mut usize>, - stats_table: Option<&mut NetworkStats> - ) -> Status, - mcast_ip_to_mac: extern "efiapi" fn( - this: &Self, - ipv6: bool, - ip: &IpAddress, - mac: &mut MacAddress + stats_table: Option<&mut NetworkStats>, ) -> Status, + mcast_ip_to_mac: + extern "efiapi" fn(this: &Self, ipv6: bool, ip: &IpAddress, mac: &mut MacAddress) -> Status, nv_data: extern "efiapi" fn( this: &Self, read_write: bool, offset: usize, buffer_size: usize, - buffer: *mut c_void + buffer: *mut c_void, ) -> Status, get_status: extern "efiapi" fn( this: &Self, interrupt_status: Option<&mut InterruptStatus>, - tx_buf: Option<&mut *mut c_void> + tx_buf: Option<&mut *mut c_void>, ) -> Status, transmit: extern "efiapi" fn( this: &Self, @@ -69,7 +67,7 @@ pub struct SimpleNetwork { buffer: *const c_void, src_addr: Option<&MacAddress>, dest_addr: Option<&MacAddress>, - protocol: Option<&u16> + protocol: Option<&u16>, ) -> Status, receive: extern "efiapi" fn( this: &Self, @@ -78,8 +76,9 @@ pub struct SimpleNetwork { buffer: *mut c_void, src_addr: Option<&mut MacAddress>, dest_addr: Option<&mut MacAddress>, - protocol: Option<&mut u16> + protocol: Option<&mut u16>, ) -> Status, + // On QEMU, this event seems to never fire. wait_for_packet: Event, mode: *const NetworkMode, } @@ -98,11 +97,7 @@ impl SimpleNetwork { /// Resets a network adapter and allocates the transmit and receive buffers /// required by the network interface; optionally, also requests allocation of /// additional transmit and receive buffers - pub fn initialize( - &self, - extra_rx_buffer_size: Option, - extra_tx_buffer_size: Option - ) -> Result { + pub fn initialize(&self, extra_rx_buffer_size: usize, extra_tx_buffer_size: usize) -> Result { (self.initialize)(self, extra_rx_buffer_size, extra_tx_buffer_size).into() } @@ -124,26 +119,26 @@ impl SimpleNetwork { enable: u32, disable: u32, reset_mcast_filter: bool, - mcast_filter_count: Option, - mcast_filter: Option<*const [MacAddress]> + mcast_filter: Option<&[MacAddress]>, ) -> Result { - (self.receive_filters)( - self, - enable, - disable, - reset_mcast_filter, - mcast_filter_count, - mcast_filter - ).into() + if let Some(mcast_filter) = mcast_filter { + (self.receive_filters)( + self, + enable, + disable, + reset_mcast_filter, + mcast_filter.len(), + NonNull::new(mcast_filter.as_ptr() as *mut _), + ) + .into() + } else { + (self.receive_filters)(self, enable, disable, reset_mcast_filter, 0, None).into() + } } /// Modifies or resets the current station address, if supported pub fn station_address(&self, reset: bool, new: Option<&MacAddress>) -> Result { - (self.station_address)( - self, - reset, - new - ).into() + (self.station_address)(self, reset, new).into() } /// Resets statistics on a network interface @@ -171,24 +166,12 @@ impl SimpleNetwork { /// Performs read operations on the NVRAM device attached to /// a network interface pub fn read_nv_data(&self, offset: usize, buffer_size: usize, buffer: *mut c_void) -> Result { - (self.nv_data)( - self, - true, - offset, - buffer_size, - buffer - ).into() + (self.nv_data)(self, true, offset, buffer_size, buffer).into() } /// Performs write operations on the NVRAM device attached to a network interface pub fn write_nv_data(&self, offset: usize, buffer_size: usize, buffer: *mut c_void) -> Result { - (self.nv_data)( - self, - false, - offset, - buffer_size, - buffer - ).into() + (self.nv_data)(self, false, offset, buffer_size, buffer).into() } /// Reads the current interrupt status and recycled transmit buffer @@ -206,7 +189,7 @@ impl SimpleNetwork { let mut tx_buf: *mut c_void = ptr::null_mut(); let status = (self.get_status)(self, None, Some(&mut tx_buf)); Result::from(status)?; - if tx_buf == ptr::null_mut() { + if tx_buf.is_null() { Ok(None) } else { Ok(Some(tx_buf.cast())) @@ -220,7 +203,7 @@ impl SimpleNetwork { buffer: &[u8], src_addr: Option<&MacAddress>, dest_addr: Option<&MacAddress>, - protocol: Option<&u16> + protocol: Option<&u16>, ) -> Result { (self.transmit)( self, @@ -229,8 +212,9 @@ impl SimpleNetwork { buffer.as_ptr().cast(), src_addr, dest_addr, - protocol - ).into() + protocol, + ) + .into() } /// Receives a packet from a network interface @@ -242,7 +226,7 @@ impl SimpleNetwork { header_size: Option<&mut usize>, src_addr: Option<&mut MacAddress>, dest_addr: Option<&mut MacAddress>, - protocol: Option<&mut u16> + protocol: Option<&mut u16>, ) -> Result { let mut buffer_size = buffer.len(); let status = (self.receive)( @@ -252,18 +236,30 @@ impl SimpleNetwork { buffer.as_mut_ptr().cast(), src_addr, dest_addr, - protocol + protocol, ); Result::from(status)?; Ok(buffer_size) } /// Returns a reference to the Simple Network mode + #[must_use] pub fn mode(&self) -> &NetworkMode { unsafe { &*self.mode } } } +/// Receive unicast packets. +pub const EFI_SIMPLE_NETWORK_RECEIVE_UNICAST: u32 = 0x01; +/// Receive multicast packets. +pub const EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST: u32 = 0x02; +/// Receive broadcast packets. +pub const EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST: u32 = 0x04; +/// Receive packets in promiscuous mode. +pub const EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS: u32 = 0x08; +/// Receive packets in promiscuous multicast mode. +pub const EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST: u32 = 0x10; + /// A bitmask of currently active interrupts #[derive(Debug)] #[repr(transparent)] @@ -271,27 +267,38 @@ pub struct InterruptStatus(u32); impl InterruptStatus { /// Creates a new InterruptStatus instance with all bits unset + #[must_use] pub fn new() -> Self { Self(0) } /// The receive interrupt bit + #[must_use] pub fn receive_interrupt(&self) -> bool { self.0 & 0x01 != 0 } /// The transmit interrupt bit + #[must_use] pub fn transmit_interrupt(&self) -> bool { self.0 & 0x02 != 0 } /// The command interrupt bit + #[must_use] pub fn command_interrupt(&self) -> bool { self.0 & 0x04 != 0 } /// The software interrupt bit + #[must_use] pub fn software_interrupt(&self) -> bool { self.0 & 0x08 != 0 } } +impl Default for InterruptStatus { + fn default() -> Self { + Self::new() + } +} + /// Network Statistics /// /// The description of statistics on the network with the SNP's `statistics` function @@ -327,7 +334,7 @@ pub struct NetworkStats { rx_duplicated_frames: u64, rx_decrypt_error_frames: u64, tx_error_frames: u64, - tx_retry_frames: u64 + tx_retry_frames: u64, } impl NetworkStats { @@ -342,155 +349,181 @@ impl NetworkStats { fn to_option(&self, stat: u64) -> Option { match self.available(stat) { true => Some(stat), - false => None + false => None, } } /// The total number of frames received, including error frames /// and dropped frames + #[must_use] pub fn rx_total_frames(&self) -> Option { self.to_option(self.rx_total_frames) } /// The total number of good frames received and copied /// into receive buffers + #[must_use] pub fn rx_good_frames(&self) -> Option { self.to_option(self.rx_good_frames) } /// The number of frames below the minimum length for the /// communications device + #[must_use] pub fn rx_undersize_frames(&self) -> Option { self.to_option(self.rx_undersize_frames) } /// The number of frames longer than the maximum length for /// the communications length device + #[must_use] pub fn rx_oversize_frames(&self) -> Option { self.to_option(self.rx_oversize_frames) } /// The number of valid frames that were dropped because /// the receive buffers were full + #[must_use] pub fn rx_dropped_frames(&self) -> Option { self.to_option(self.rx_dropped_frames) } /// The number of valid unicast frames received and not dropped + #[must_use] pub fn rx_unicast_frames(&self) -> Option { self.to_option(self.rx_unicast_frames) } /// The number of valid broadcast frames received and not dropped + #[must_use] pub fn rx_broadcast_frames(&self) -> Option { self.to_option(self.rx_broadcast_frames) } /// The number of valid multicast frames received and not dropped + #[must_use] pub fn rx_multicast_frames(&self) -> Option { self.to_option(self.rx_multicast_frames) } /// Number of frames with CRC or alignment errors + #[must_use] pub fn rx_crc_error_frames(&self) -> Option { self.to_option(self.rx_crc_error_frames) } /// The total number of bytes received including frames with errors /// and dropped frames + #[must_use] pub fn rx_total_bytes(&self) -> Option { self.to_option(self.rx_total_bytes) } /// The total number of frames transmitted including frames /// with errors and dropped frames + #[must_use] pub fn tx_total_frames(&self) -> Option { self.to_option(self.tx_total_frames) } /// The total number of valid frames transmitted and copied /// into receive buffers + #[must_use] pub fn tx_good_frames(&self) -> Option { self.to_option(self.tx_good_frames) } /// The number of frames below the minimum length for /// the media. This would be less than 64 for Ethernet + #[must_use] pub fn tx_undersize_frames(&self) -> Option { self.to_option(self.tx_undersize_frames) } /// The number of frames longer than the maximum length for /// the media. This would be 1500 for Ethernet + #[must_use] pub fn tx_oversize_frames(&self) -> Option { self.to_option(self.tx_oversize_frames) } /// The number of valid frames that were dropped because /// received buffers were full + #[must_use] pub fn tx_dropped_frames(&self) -> Option { self.to_option(self.tx_dropped_frames) } /// The number of valid unicast frames transmitted and not /// dropped + #[must_use] pub fn tx_unicast_frames(&self) -> Option { self.to_option(self.tx_unicast_frames) } /// The number of valid broadcast frames transmitted and /// not dropped + #[must_use] pub fn tx_broadcast_frames(&self) -> Option { self.to_option(self.tx_broadcast_frames) } /// The number of valid multicast frames transmitted /// and not dropped + #[must_use] pub fn tx_multicast_frames(&self) -> Option { self.to_option(self.tx_multicast_frames) } /// The number of transmitted frames with CRC or /// alignment errors + #[must_use] pub fn tx_crc_error_frames(&self) -> Option { self.to_option(self.tx_crc_error_frames) } /// The total number of bytes transmitted including /// error frames and dropped frames + #[must_use] pub fn tx_total_bytes(&self) -> Option { self.to_option(self.tx_total_bytes) } /// The number of collisions detected on this subnet + #[must_use] pub fn collisions(&self) -> Option { self.to_option(self.collisions) } /// The number of frames destined for unsupported protocol + #[must_use] pub fn unsupported_protocol(&self) -> Option { self.to_option(self.unsupported_protocol) } /// The number of valid frames received that were duplicated + #[must_use] pub fn rx_duplicated_frames(&self) -> Option { self.to_option(self.rx_duplicated_frames) } /// The number of encrypted frames received that failed /// to decrypt + #[must_use] pub fn rx_decrypt_error_frames(&self) -> Option { self.to_option(self.rx_decrypt_error_frames) } /// The number of frames that failed to transmit after /// exceeding the retry limit + #[must_use] pub fn tx_error_frames(&self) -> Option { self.to_option(self.tx_error_frames) } /// The number of frames that transmitted successfully /// after more than one attempt + #[must_use] pub fn tx_retry_frames(&self) -> Option { self.to_option(self.tx_retry_frames) } @@ -536,7 +569,7 @@ pub struct NetworkMode { /// Tells if the presence of the media can be determined pub media_present_supported: bool, /// Tells if media are connected to the network interface - pub media_present: bool + pub media_present: bool, } newtype_enum! { @@ -551,4 +584,4 @@ newtype_enum! { /// No state can have a number higher than this MAX_STATE = 4, } -} \ No newline at end of file +} diff --git a/xtask/src/util.rs b/xtask/src/util.rs index 747eee28d..33b8f3c08 100644 --- a/xtask/src/util.rs +++ b/xtask/src/util.rs @@ -51,7 +51,7 @@ mod tests { #[test] fn test_command_to_string() { let mut cmd = Command::new("MyCommand"); - cmd.args(&["abc", "123"]).envs([ + cmd.args(["abc", "123"]).envs([ ("VAR1", "val1"), ("VAR2", "val2"), ("PATH", "pathval"), From 56db749254f4452e8fdf9f3a20a04898866f7384 Mon Sep 17 00:00:00 2001 From: Luca Versari Date: Sat, 10 Dec 2022 17:07:52 +0100 Subject: [PATCH 7/7] Review comments. --- uefi-test-runner/src/proto/network/snp.rs | 20 +-- uefi/src/proto/network/snp.rs | 182 +++++++++++----------- 2 files changed, 100 insertions(+), 102 deletions(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 0007636af..196fd5cc5 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -1,8 +1,5 @@ use uefi::prelude::BootServices; -use uefi::proto::network::snp::SimpleNetwork; -use uefi::proto::network::snp::EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; -use uefi::proto::network::snp::EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST; -use uefi::proto::network::snp::EFI_SIMPLE_NETWORK_RECEIVE_UNICAST; +use uefi::proto::network::snp::{InterruptStatus, ReceiveFlags, SimpleNetwork}; use uefi::proto::network::MacAddress; use uefi::Status; @@ -46,10 +43,8 @@ pub fn test(bt: &BootServices) { // Set receive filters simple_network .receive_filters( - EFI_SIMPLE_NETWORK_RECEIVE_UNICAST - | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST - | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST, - 0, + ReceiveFlags::UNICAST | ReceiveFlags::MULTICAST | ReceiveFlags::BROADCAST, + ReceiveFlags::empty(), false, None, ) @@ -80,15 +75,16 @@ pub fn test(bt: &BootServices) { assert!(!simple_network .get_interrupt_status() .unwrap() - .transmit_interrupt()); + .contains(InterruptStatus::TRANSMIT)); + // Send the frame simple_network .transmit( simple_network.mode().media_header_size as usize, payload, None, - Some(&dest_addr), - Some(&0x0800), + Some(dest_addr), + Some(0x0800), ) .expect("Failed to transmit frame"); @@ -96,7 +92,7 @@ pub fn test(bt: &BootServices) { while !simple_network .get_interrupt_status() .unwrap() - .transmit_interrupt() + .contains(InterruptStatus::TRANSMIT) {} // Attempt to receive a frame diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs index aa090147e..6554455c3 100644 --- a/uefi/src/proto/network/snp.rs +++ b/uefi/src/proto/network/snp.rs @@ -10,6 +10,7 @@ use super::{IpAddress, MacAddress}; use crate::data_types::Event; use crate::{Result, Status}; +use bitflags::bitflags; use core::ffi::c_void; use core::ptr; use core::ptr::NonNull; @@ -84,69 +85,70 @@ pub struct SimpleNetwork { } impl SimpleNetwork { - /// Changes the state of a network from "Stopped" to "Started" + /// Change the state of a network from "Stopped" to "Started". pub fn start(&self) -> Result { (self.start)(self).into() } - /// Changes the state of a network interface from "Started" to "Stopped" + /// Change the state of a network interface from "Started" to "Stopped". pub fn stop(&self) -> Result { (self.stop)(self).into() } - /// Resets a network adapter and allocates the transmit and receive buffers - /// required by the network interface; optionally, also requests allocation of - /// additional transmit and receive buffers + /// Reset a network adapter and allocate the transmit and receive buffers + /// required by the network interface; optionally, also request allocation of + /// additional transmit and receive buffers. pub fn initialize(&self, extra_rx_buffer_size: usize, extra_tx_buffer_size: usize) -> Result { (self.initialize)(self, extra_rx_buffer_size, extra_tx_buffer_size).into() } - /// Resets a network adapter and reinitializes it with the parameters that were - /// provided in the previous call to `initialize` + /// Reset a network adapter and reinitialize it with the parameters that were + /// provided in the previous call to `initialize`. pub fn reset(&self, extended_verification: bool) -> Result { (self.reset)(self, extended_verification).into() } - /// Resets a network adapter and leaves it in a state that is safe + /// Reset a network adapter, leaving it in a state that is safe /// for another driver to initialize pub fn shutdown(&self) -> Result { (self.shutdown)(self).into() } - /// Manages the multicast receive filters of a network + /// Manage the multicast receive filters of a network. pub fn receive_filters( &self, - enable: u32, - disable: u32, + enable: ReceiveFlags, + disable: ReceiveFlags, reset_mcast_filter: bool, mcast_filter: Option<&[MacAddress]>, ) -> Result { if let Some(mcast_filter) = mcast_filter { (self.receive_filters)( self, - enable, - disable, + enable.bits, + disable.bits, reset_mcast_filter, mcast_filter.len(), NonNull::new(mcast_filter.as_ptr() as *mut _), ) .into() } else { - (self.receive_filters)(self, enable, disable, reset_mcast_filter, 0, None).into() + (self.receive_filters)(self, enable.bits, disable.bits, reset_mcast_filter, 0, None) + .into() } } - /// Modifies or resets the current station address, if supported + /// Modify or reset the current station address, if supported. pub fn station_address(&self, reset: bool, new: Option<&MacAddress>) -> Result { (self.station_address)(self, reset, new).into() } - /// Resets statistics on a network interface + /// Reset statistics on a network interface. pub fn reset_statistics(&self) -> Result { (self.statistics)(self, true, None, None).into() } - /// Collects statistics on a network interface + /// Collect statistics on a network interface. pub fn collect_statistics(&self) -> Result { let mut stats_table: NetworkStats = Default::default(); let mut stats_size = core::mem::size_of::(); @@ -155,7 +157,7 @@ impl SimpleNetwork { Ok(stats_table) } - /// Converts a multicast IP address to a multicast HW MAC Address + /// Convert a multicast IP address to a multicast HW MAC Address. pub fn mcast_ip_to_mac(&self, ipv6: bool, ip: IpAddress) -> Result { let mut mac_address = MacAddress([0; 32]); let status = (self.mcast_ip_to_mac)(self, ipv6, &ip, &mut mac_address); @@ -163,63 +165,73 @@ impl SimpleNetwork { Ok(mac_address) } - /// Performs read operations on the NVRAM device attached to - /// a network interface - pub fn read_nv_data(&self, offset: usize, buffer_size: usize, buffer: *mut c_void) -> Result { - (self.nv_data)(self, true, offset, buffer_size, buffer).into() + /// Perform read operations on the NVRAM device attached to + /// a network interface. + pub fn read_nv_data(&self, offset: usize, buffer: &[u8]) -> Result { + (self.nv_data)( + self, + true, + offset, + buffer.len(), + buffer.as_ptr() as *mut c_void, + ) + .into() } - /// Performs write operations on the NVRAM device attached to a network interface - pub fn write_nv_data(&self, offset: usize, buffer_size: usize, buffer: *mut c_void) -> Result { - (self.nv_data)(self, false, offset, buffer_size, buffer).into() + /// Perform write operations on the NVRAM device attached to a network interface. + pub fn write_nv_data(&self, offset: usize, buffer: &mut [u8]) -> Result { + (self.nv_data)( + self, + false, + offset, + buffer.len(), + buffer.as_mut_ptr().cast(), + ) + .into() } - /// Reads the current interrupt status and recycled transmit buffer - /// status from a network interface + /// Read the current interrupt status and recycled transmit buffer + /// status from a network interface. pub fn get_interrupt_status(&self) -> Result { - let mut interrupt_status = InterruptStatus::new(); + let mut interrupt_status = InterruptStatus::empty(); let status = (self.get_status)(self, Some(&mut interrupt_status), None); Result::from(status)?; Ok(interrupt_status) } - /// Reads the current recycled transmit buffer status from a - /// network interface - pub fn get_recycled_transmit_buffer_status(&self) -> Result> { + /// Read the current recycled transmit buffer status from a + /// network interface. + pub fn get_recycled_transmit_buffer_status(&self) -> Result>> { let mut tx_buf: *mut c_void = ptr::null_mut(); let status = (self.get_status)(self, None, Some(&mut tx_buf)); Result::from(status)?; - if tx_buf.is_null() { - Ok(None) - } else { - Ok(Some(tx_buf.cast())) - } + Ok(NonNull::new(tx_buf.cast())) } - /// Places a packet in the transmit queue of a network interface + /// Place a packet in the transmit queue of a network interface. pub fn transmit( &self, header_size: usize, buffer: &[u8], - src_addr: Option<&MacAddress>, - dest_addr: Option<&MacAddress>, - protocol: Option<&u16>, + src_addr: Option, + dest_addr: Option, + protocol: Option, ) -> Result { (self.transmit)( self, header_size, buffer.len() + header_size, buffer.as_ptr().cast(), - src_addr, - dest_addr, - protocol, + src_addr.as_ref(), + dest_addr.as_ref(), + protocol.as_ref(), ) .into() } - /// Receives a packet from a network interface + /// Receive a packet from a network interface. /// - /// On success, returns the size of bytes of the received packet + /// On success, returns the size of bytes of the received packet. pub fn receive( &self, buffer: &mut [u8], @@ -242,60 +254,50 @@ impl SimpleNetwork { Ok(buffer_size) } - /// Returns a reference to the Simple Network mode + /// Event that fires once a packet is available to be received. + /// + /// On QEMU, this event seems to never fire; it is suggested to verify that your implementation + /// of UEFI properly implements this event before using it. + #[must_use] + pub fn wait_for_packet(&self) -> &Event { + &self.wait_for_packet + } + + /// Returns a reference to the Simple Network mode. #[must_use] pub fn mode(&self) -> &NetworkMode { unsafe { &*self.mode } } } -/// Receive unicast packets. -pub const EFI_SIMPLE_NETWORK_RECEIVE_UNICAST: u32 = 0x01; -/// Receive multicast packets. -pub const EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST: u32 = 0x02; -/// Receive broadcast packets. -pub const EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST: u32 = 0x04; -/// Receive packets in promiscuous mode. -pub const EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS: u32 = 0x08; -/// Receive packets in promiscuous multicast mode. -pub const EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST: u32 = 0x10; - -/// A bitmask of currently active interrupts -#[derive(Debug)] -#[repr(transparent)] -pub struct InterruptStatus(u32); - -impl InterruptStatus { - /// Creates a new InterruptStatus instance with all bits unset - #[must_use] - pub fn new() -> Self { - Self(0) - } - /// The receive interrupt bit - #[must_use] - pub fn receive_interrupt(&self) -> bool { - self.0 & 0x01 != 0 - } - /// The transmit interrupt bit - #[must_use] - pub fn transmit_interrupt(&self) -> bool { - self.0 & 0x02 != 0 - } - /// The command interrupt bit - #[must_use] - pub fn command_interrupt(&self) -> bool { - self.0 & 0x04 != 0 - } - /// The software interrupt bit - #[must_use] - pub fn software_interrupt(&self) -> bool { - self.0 & 0x08 != 0 +bitflags! { + /// Flags to pass to receive_filters to enable/disable reception of some kinds of packets. + pub struct ReceiveFlags : u32 { + /// Receive unicast packets. + const UNICAST = 0x01; + /// Receive multicast packets. + const MULTICAST = 0x02; + /// Receive broadcast packets. + const BROADCAST = 0x04; + /// Receive packets in promiscuous mode. + const PROMISCUOUS = 0x08; + /// Receive packets in promiscuous multicast mode. + const PROMISCUOUS_MULTICAST = 0x10; } } -impl Default for InterruptStatus { - fn default() -> Self { - Self::new() +bitflags! { + /// Flags returned by get_interrupt_status to indicate which interrupts have fired on the + /// interace since the last call. + pub struct InterruptStatus : u32 { + /// Packet received. + const RECEIVE = 0x01; + /// Packet transmitted. + const TRANSMIT = 0x02; + /// Command interrupt fired. + const COMMAND = 0x04; + /// Software interrupt fired. + const SOFTWARE = 0x08; } }