From 6ebacd99054fb43d5d8bca5e6d085f146125bc7c Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 3 Nov 2021 22:00:55 -0500 Subject: [PATCH 1/4] Initial attempt: it seems to be working! --- src/proto/mod.rs | 1 + src/proto/network/mod.rs | 5 + src/proto/network/snp.rs | 172 ++++++++++++++++++++++++++++++ uefi-test-runner/src/proto/mod.rs | 2 + 4 files changed, 180 insertions(+) create mode 100644 src/proto/network/mod.rs create mode 100644 src/proto/network/snp.rs diff --git a/src/proto/mod.rs b/src/proto/mod.rs index cf32cfd23..c5041f700 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -33,5 +33,6 @@ pub mod debug; pub mod device_path; pub mod loaded_image; pub mod media; +pub mod network; pub mod pi; pub mod shim; diff --git a/src/proto/network/mod.rs b/src/proto/network/mod.rs new file mode 100644 index 000000000..63626d82a --- /dev/null +++ b/src/proto/network/mod.rs @@ -0,0 +1,5 @@ +//! Network access protocols. +//! +//! These protocols can be used to interact with network resources. + +pub mod snp; diff --git a/src/proto/network/snp.rs b/src/proto/network/snp.rs new file mode 100644 index 000000000..40fb3892f --- /dev/null +++ b/src/proto/network/snp.rs @@ -0,0 +1,172 @@ +//! Network I/O protocols. + +use crate::proto::Protocol; +use crate::{unsafe_guid, Result, Status}; + +/// The SNP protocol. +#[repr(C)] +#[unsafe_guid("a19832b9-ac25-11d3-9a2d-0090273fc14d")] +#[derive(Protocol)] +pub struct SNP { + pub revision: u64, + start : extern "efiapi" fn(this: &SNP) -> Status, + stop : extern "efiapi" fn(this: &SNP) -> Status, + initialize : extern "efiapi" fn(this: &SNP, extra_rx_buffer_size: usize, extra_tx_buffer_size: usize) -> Status, + reset : extern "efiapi" fn(this: &SNP, extended_verification: bool) -> Status, + shutdown : extern "efiapi" fn(this: &SNP) -> Status, + receive_filters : extern "efiapi" fn(this: &SNP, enable: u32, disable: u32, reset_mcast_filter: bool, mcast_filter_count: usize, mcast_filter: *const MacAddress) -> Status, + station_address : extern "efiapi" fn(this: &SNP, reset: bool, new: *const MacAddress) -> Status, + statistics : extern "efiapi" fn(this: &SNP, reset: bool, statistics_size: *mut usize, statistics_table: *mut NetworkStatistics) -> Status, + mcast_ip_to_mac : extern "efiapi" fn(this: &SNP, ipv6: bool, ip: *const IpAddress) -> Status, + nv_data : extern "efiapi" fn(this: &SNP, read_write: bool, offset: usize, buffer_size: usize, buffer: *mut u8) -> Status, + get_status : extern "efiapi" fn(this: &SNP, interrupt_status: *mut u32, tx_buf: *mut *mut u8) -> Status, + transmit : extern "efiapi" fn(this: &SNP, header_size: usize, buffer_size: usize, buffer: *const u8, src_addr: *const MacAddress, dest_addr: *const MacAddress, protocol: u16) -> Status, + receive : extern "efiapi" fn(this: &SNP, header_size: *const usize, buffer_size: *mut usize, buffer: *mut u8, src_addr: *mut MacAddress, dest_addr: *mut MacAddress, protocol: *mut u16) -> Status, + wait_for_packet : usize, + mode: *const NetworkMode, +} + +impl SNP { + + /// Changes the state of a network interface from “stopped” to “started”. + /// + /// # Errors + /// * `uefi::Status::ALREADY_STARTED` The network interface is already in the started state. + /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::DEVICE_ERROR` This function is not supported by the network interface. + pub fn start(&self) -> Result { + (self.start)(self).into() + } + + /// Changes the state of a network interface from “started” to “stopped”. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::DEVICE_ERROR` This function is not supported by the network interface. + pub fn stop(&mut self) -> Result { + (self.stop)(self).into() + } + + /// Resets a network adapter and allocates the transmit and receive buffers. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::EFI_OUT_OF_RESOURCES` There was not enough memory for the transmit and receive buffers + /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` The increased buffer size feature is not supported. + 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 or collects the statistics on a network interface. + /// + /// # Errors + pub fn statistics(&self, reset: bool, statistics_size: *mut usize, statistics_table: *mut NetworkStatistics) -> Result { + (self.statistics)(self, reset, statistics_size, statistics_table).into() + } + + /// Places a packet in the transmit queue of a network interface. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::EFI_OUT_OF_RESOURCES` There was not enough memory for the transmit and receive buffers + /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` The increased buffer size feature is not supported. + pub fn transmit(&self, header_size: usize, buffer: &[u8], src_addr: *const MacAddress, dest_addr: *const MacAddress, protocol: u16) -> Result { + (self.transmit)(self, header_size, buffer.len(), buffer.as_ptr(), src_addr, dest_addr, protocol).into() + } + + /// Pointer for network mode. + pub fn mode(&self) -> &NetworkMode { + unsafe { &*self.mode } + } +} + +/// Network statistics structure +#[repr(C)] +#[derive(Debug)] +pub struct NetworkStatistics { + 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, + rx_duplicated_frames: u64, + rx_decrypt_error_frames: u64, + tx_error_frames: u64, + tx_retry_frames: u64, +} + +/// EFI_MAC_ADDRESS +#[repr(C)] +#[derive(Debug)] +pub struct MacAddress { + pub addr: [u8; 32], +} + +/// EFI_IP_ADDRESS +#[repr(C)] +#[derive(Debug)] +pub struct IpAddress { + addr: [u8; 4], +} + +/// Network mode structure +#[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, +} + +impl NetworkMode { + + /// + pub fn current_address(&self) -> &MacAddress { + &self.current_address + } + + /// + pub fn if_type(&self) -> u8 { + self.if_type + } +} diff --git a/uefi-test-runner/src/proto/mod.rs b/uefi-test-runner/src/proto/mod.rs index 38c92a902..f5a6b2001 100644 --- a/uefi-test-runner/src/proto/mod.rs +++ b/uefi-test-runner/src/proto/mod.rs @@ -15,6 +15,7 @@ pub fn test(image: Handle, st: &mut SystemTable) { debug::test(image, bt); device_path::test(image, bt); media::test(image, bt); + network::test(bt); pi::test(bt); #[cfg(any( @@ -57,6 +58,7 @@ mod console; mod debug; mod device_path; mod media; +mod network; mod pi; #[cfg(any( target_arch = "i386", From 7b94dc609f24062a98d289c8ca9e1f3a25a7557a Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Thu, 4 Nov 2021 22:43:11 -0500 Subject: [PATCH 2/4] Add first test case --- src/proto/network/snp.rs | 353 +++++++++++++++++----- uefi-test-runner/src/proto/network/mod.rs | 56 ++++ 2 files changed, 338 insertions(+), 71 deletions(-) create mode 100644 uefi-test-runner/src/proto/network/mod.rs diff --git a/src/proto/network/snp.rs b/src/proto/network/snp.rs index 40fb3892f..7b0cba87f 100644 --- a/src/proto/network/snp.rs +++ b/src/proto/network/snp.rs @@ -3,31 +3,69 @@ use crate::proto::Protocol; use crate::{unsafe_guid, Result, Status}; -/// The SNP protocol. +/// The Snp protocol. #[repr(C)] #[unsafe_guid("a19832b9-ac25-11d3-9a2d-0090273fc14d")] #[derive(Protocol)] -pub struct SNP { +pub struct Snp { pub revision: u64, - start : extern "efiapi" fn(this: &SNP) -> Status, - stop : extern "efiapi" fn(this: &SNP) -> Status, - initialize : extern "efiapi" fn(this: &SNP, extra_rx_buffer_size: usize, extra_tx_buffer_size: usize) -> Status, - reset : extern "efiapi" fn(this: &SNP, extended_verification: bool) -> Status, - shutdown : extern "efiapi" fn(this: &SNP) -> Status, - receive_filters : extern "efiapi" fn(this: &SNP, enable: u32, disable: u32, reset_mcast_filter: bool, mcast_filter_count: usize, mcast_filter: *const MacAddress) -> Status, - station_address : extern "efiapi" fn(this: &SNP, reset: bool, new: *const MacAddress) -> Status, - statistics : extern "efiapi" fn(this: &SNP, reset: bool, statistics_size: *mut usize, statistics_table: *mut NetworkStatistics) -> Status, - mcast_ip_to_mac : extern "efiapi" fn(this: &SNP, ipv6: bool, ip: *const IpAddress) -> Status, - nv_data : extern "efiapi" fn(this: &SNP, read_write: bool, offset: usize, buffer_size: usize, buffer: *mut u8) -> Status, - get_status : extern "efiapi" fn(this: &SNP, interrupt_status: *mut u32, tx_buf: *mut *mut u8) -> Status, - transmit : extern "efiapi" fn(this: &SNP, header_size: usize, buffer_size: usize, buffer: *const u8, src_addr: *const MacAddress, dest_addr: *const MacAddress, protocol: u16) -> Status, - receive : extern "efiapi" fn(this: &SNP, header_size: *const usize, buffer_size: *mut usize, buffer: *mut u8, src_addr: *mut MacAddress, dest_addr: *mut MacAddress, protocol: *mut u16) -> Status, - wait_for_packet : usize, + start: extern "efiapi" fn(this: &Snp) -> Status, + stop: extern "efiapi" fn(this: &Snp) -> Status, + initialize: extern "efiapi" fn( + this: &Snp, + extra_rx_buffer_size: usize, + extra_tx_buffer_size: usize, + ) -> Status, + reset: extern "efiapi" fn(this: &Snp, extended_verification: bool) -> Status, + shutdown: extern "efiapi" fn(this: &Snp) -> Status, + receive_filters: extern "efiapi" fn( + this: &Snp, + enable: u32, + disable: u32, + reset_mcast_filter: bool, + mcast_filter_count: usize, + mcast_filter: *const MacAddress, + ) -> Status, + station_address: extern "efiapi" fn(this: &Snp, reset: bool, new: *const MacAddress) -> Status, + statistics: extern "efiapi" fn( + this: &Snp, + reset: bool, + statistics_size: *mut usize, + statistics_table: *mut NetworkStatistics, + ) -> Status, + mcast_ip_to_mac: extern "efiapi" fn(this: &Snp, ipv6: bool, ip: *const IpAddress, mac: *mut MacAddress) -> Status, + nv_data: extern "efiapi" fn( + this: &Snp, + read_write: bool, + offset: usize, + buffer_size: usize, + buffer: *mut [u8], + ) -> Status, + get_status: + extern "efiapi" fn(this: &Snp, interrupt_status: *mut u32, tx_buf: *mut *mut [u8]) -> Status, + transmit: extern "efiapi" fn( + this: &Snp, + header_size: usize, + buffer_size: usize, + buffer: *const [u8], + src_addr: *const MacAddress, + dest_addr: *const MacAddress, + protocol: u16, + ) -> Status, + receive: extern "efiapi" fn( + this: &Snp, + header_size: *const usize, + buffer_size: *mut usize, + buffer: *mut [u8], + src_addr: *mut MacAddress, + dest_addr: *mut MacAddress, + protocol: *mut u16, + ) -> Status, + wait_for_packet: usize, mode: *const NetworkMode, } -impl SNP { - +impl Snp { /// Changes the state of a network interface from “stopped” to “started”. /// /// # Errors @@ -62,13 +100,105 @@ impl SNP { (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(). + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` This function is not supported by the network interface. + 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. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::EFI_OUT_OF_RESOURCES` There was not enough memory for the transmit and receive buffers + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + pub fn shutdown(&self) -> Result { + (self.shutdown)(self).into() + } + + /// Modifies or resets the current station address, if supported. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` This function is not supported by the network interface. + pub fn station_address( + &self, + reset: bool, + new: *const MacAddress, + ) -> Result { + (self.station_address)(self, reset, new).into() + } + /// Resets or collects the statistics on a network interface. /// /// # Errors - pub fn statistics(&self, reset: bool, statistics_size: *mut usize, statistics_table: *mut NetworkStatistics) -> Result { + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` This function is not supported by the network interface. + pub fn statistics( + &self, + reset: bool, + statistics_size: *mut usize, + statistics_table: *mut NetworkStatistics, + ) -> Result { (self.statistics)(self, reset, statistics_size, statistics_table).into() } + /// Converts a multicast IP address to a multicast HW MAC address. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` This function is not supported by the network interface. + pub fn mcast_ip_to_mac( + &self, + ipv6: bool, + ip: *const IpAddress, + mac: *mut MacAddress, + ) -> Result { + (self.mcast_ip_to_mac)(self, ipv6, ip, mac).into() + } + + /// Performs read and write operations on the NVRAM device attached to a network interface. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` This function is not supported by the network interface. + pub fn nv_data( + &self, + read_write: bool, + offset: usize, + buffer_size: usize, + buffer: *mut [u8], + ) -> Result { + (self.nv_data)(self, read_write, offset, buffer_size, buffer).into() + } + + /// Reads the current interrupt status and recycled transmit buffer status from a network interface. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + pub fn get_status( + &self, + interrupt_status: *mut u32, + tx_buf: *mut *mut [u8], + ) -> Result { + (self.get_status)(self, interrupt_status, tx_buf).into() + } + /// Places a packet in the transmit queue of a network interface. /// /// # Errors @@ -77,8 +207,54 @@ impl SNP { /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. /// * `uefi::Status::UNSUPPORTED` The increased buffer size feature is not supported. - pub fn transmit(&self, header_size: usize, buffer: &[u8], src_addr: *const MacAddress, dest_addr: *const MacAddress, protocol: u16) -> Result { - (self.transmit)(self, header_size, buffer.len(), buffer.as_ptr(), src_addr, dest_addr, protocol).into() + pub fn transmit( + &self, + header_size: usize, + buffer_size: usize, + buffer: *const [u8], + src_addr: *const MacAddress, + dest_addr: *const MacAddress, + protocol: u16, + ) -> Result { + (self.transmit)( + self, + header_size, + buffer_size, + buffer, + src_addr, + dest_addr, + protocol, + ) + .into() + } + + /// Receives a packet from a network interface. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::NOT_READY` No packets have been received on the network interface. + /// * `uefi::Status::BUFFER_TOO_SMALL` BufferSize is too small for the received packets. BufferSize has been updated to the required size. + /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + pub fn receive( + &self, + header_size: *mut usize, + buffer_size: *mut usize, + buffer: *mut [u8], + src_addr: *mut MacAddress, + dest_addr: *mut MacAddress, + protocol: *mut u16, + ) -> Result { + (self.receive)( + self, + header_size, + buffer_size, + buffer, + src_addr, + dest_addr, + protocol, + ) + .into() } /// Pointer for network mode. @@ -87,85 +263,120 @@ impl SNP { } } -/// Network statistics structure +newtype_enum! { + /// EFI_SIMPLE_NETWORK_STATE + pub enum NetworkState: u32 => { + NETWORK_STOPPED = 0x00, + NETWORK_STARTED = 0x01, + NETWORK_INITIALIZED = 0x02, + NETWORK_MAX_STATE = 0x03, + } +} + +/// EFI_NETWORK_STATISTICS #[repr(C)] #[derive(Debug)] pub struct NetworkStatistics { 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, - rx_duplicated_frames: u64, - rx_decrypt_error_frames: u64, - tx_error_frames: u64, - tx_retry_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, + rx_duplicated_frames: u64, + rx_decrypt_error_frames: u64, + tx_error_frames: u64, + tx_retry_frames: u64, } /// EFI_MAC_ADDRESS #[repr(C)] #[derive(Debug)] pub struct MacAddress { - pub addr: [u8; 32], + addr: [u8; 32], +} + +impl MacAddress { + pub fn new(mac: [u8; 6]) -> MacAddress { + let mut data = [0u8; 32]; + for (a, b) in data.iter_mut().zip(mac.iter()) { + *a = *b; + } + MacAddress{addr: data} + } } /// EFI_IP_ADDRESS #[repr(C)] #[derive(Debug)] pub struct IpAddress { - addr: [u8; 4], + addr: [u8; 4], } -/// Network mode structure +/// EFI_SIMPLE_NETWORK_MODE #[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, + 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, } impl NetworkMode { - /// + /// Reports the current state of the network interface. + pub fn state(&self) -> NetworkState { + self.state + } + + /// The size, in bytes, of the network interface’s HW address. + pub fn hw_address_size(&self) -> u32 { + self.hw_address_size + } + + /// The size, in bytes, of the network interface’s media header. + pub fn media_header_size(&self) -> u32 { + self.media_header_size + } + + /// The current HW MAC address for the network interface. pub fn current_address(&self) -> &MacAddress { &self.current_address } - /// + /// The interface type of the network interface. See RFC 3232, section "Number Hardware Type." pub fn if_type(&self) -> u8 { self.if_type } 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..9dd7368ea --- /dev/null +++ b/uefi-test-runner/src/proto/network/mod.rs @@ -0,0 +1,56 @@ +use core::ptr; +use uefi::prelude::*; +use uefi::proto::network::snp::MacAddress; +use uefi::proto::network::snp::NetworkStatistics; +use uefi::proto::network::snp::Snp; + +pub fn test(bt: &BootServices) { + info!("Testing Network protocols"); + + let handles = bt + .find_handles::() + .expect_success("Failed to get handles for `Snp` protocol"); + + for handle in handles { + let nic = bt.handle_protocol::(handle).expect_success("Unknown"); + let nic = unsafe { &*nic.get() }; + + // Check start + nic.start().expect_success("Failed to start NIC"); + + // Check initialize + nic.initialize(4096, 4096).expect_success("Failed to initialize NIC"); + + // Prepare to send a frame + let mut header_size = 14usize; + let mut buffer_size = 15usize; + let mut buffer = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 10u8, + ]; + let mut src_addr = nic.mode().current_address(); + let mut dest_addr = nic.mode().current_address(); + let mut protocol = 2048u16; + + // Send a frame with one byte payload + nic.transmit( + header_size, + buffer_size, + &buffer, + src_addr as *const MacAddress, + dest_addr as *const MacAddress, + protocol, + ) + .expect_success("Failed to transmit packet"); + + // Receive the frame + nic.receive( + header_size as *mut usize, + buffer_size as *mut usize, + &mut buffer, + &mut *src_addr, + &mut *dest_addr, + protocol as *mut u16, + ) + .expect_success("Failed to receive packet"); + } +} From 3df95b8ac2d6ba021bf9c08bb68cee38ea9dc8b4 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Tue, 25 Jan 2022 22:18:21 -0600 Subject: [PATCH 3/4] Improve names and documentation --- src/proto/network/snp.rs | 270 ++++++++++++++++++++++++--------------- xtask/src/qemu.rs | 3 + 2 files changed, 168 insertions(+), 105 deletions(-) diff --git a/src/proto/network/snp.rs b/src/proto/network/snp.rs index 7b0cba87f..1da2cd28c 100644 --- a/src/proto/network/snp.rs +++ b/src/proto/network/snp.rs @@ -1,76 +1,75 @@ -//! Network I/O protocols. +//! Simple Network Protocol (SNP). use crate::proto::Protocol; use crate::{unsafe_guid, Result, Status}; -/// The Snp protocol. +/// The SimpleNetwork protocol provides a packet level interface to network adapters. #[repr(C)] #[unsafe_guid("a19832b9-ac25-11d3-9a2d-0090273fc14d")] #[derive(Protocol)] -pub struct Snp { - pub revision: u64, - start: extern "efiapi" fn(this: &Snp) -> Status, - stop: extern "efiapi" fn(this: &Snp) -> Status, +pub struct SimpleNetwork { + revision: u64, + start: extern "efiapi" fn(this: &SimpleNetwork) -> Status, + stop: extern "efiapi" fn(this: &SimpleNetwork) -> Status, initialize: extern "efiapi" fn( - this: &Snp, + this: &SimpleNetwork, extra_rx_buffer_size: usize, extra_tx_buffer_size: usize, ) -> Status, - reset: extern "efiapi" fn(this: &Snp, extended_verification: bool) -> Status, - shutdown: extern "efiapi" fn(this: &Snp) -> Status, + reset: extern "efiapi" fn(this: &SimpleNetwork, extended_verification: bool) -> Status, + shutdown: extern "efiapi" fn(this: &SimpleNetwork) -> Status, receive_filters: extern "efiapi" fn( - this: &Snp, + this: &SimpleNetwork, enable: u32, disable: u32, reset_mcast_filter: bool, mcast_filter_count: usize, mcast_filter: *const MacAddress, ) -> Status, - station_address: extern "efiapi" fn(this: &Snp, reset: bool, new: *const MacAddress) -> Status, + station_address: extern "efiapi" fn(this: &SimpleNetwork, reset: bool, new: *const MacAddress) -> Status, statistics: extern "efiapi" fn( - this: &Snp, + this: &SimpleNetwork, reset: bool, statistics_size: *mut usize, - statistics_table: *mut NetworkStatistics, + statistics_table: *mut SimpleNetworkStatistics, ) -> Status, - mcast_ip_to_mac: extern "efiapi" fn(this: &Snp, ipv6: bool, ip: *const IpAddress, mac: *mut MacAddress) -> Status, + mcast_ip_to_mac: extern "efiapi" fn(this: &SimpleNetwork, ipv6: bool, ip: *const IpAddress, mac: *mut MacAddress) -> Status, nv_data: extern "efiapi" fn( - this: &Snp, + this: &SimpleNetwork, read_write: bool, offset: usize, buffer_size: usize, buffer: *mut [u8], ) -> Status, get_status: - extern "efiapi" fn(this: &Snp, interrupt_status: *mut u32, tx_buf: *mut *mut [u8]) -> Status, + extern "efiapi" fn(this: &SimpleNetwork, interrupt_status: *mut u32, tx_buf: *mut *mut u8) -> Status, transmit: extern "efiapi" fn( - this: &Snp, + this: &SimpleNetwork, header_size: usize, buffer_size: usize, - buffer: *const [u8], + buffer: *const core::ffi::c_void, src_addr: *const MacAddress, dest_addr: *const MacAddress, - protocol: u16, + protocol: *const u16, ) -> Status, receive: extern "efiapi" fn( - this: &Snp, + this: &SimpleNetwork, header_size: *const usize, buffer_size: *mut usize, - buffer: *mut [u8], + buffer: *mut core::ffi::c_void, src_addr: *mut MacAddress, dest_addr: *mut MacAddress, protocol: *mut u16, ) -> Status, wait_for_packet: usize, - mode: *const NetworkMode, + mode: *const SimpleNetworkMode, } -impl Snp { +impl SimpleNetwork { /// Changes the state of a network interface from “stopped” to “started”. /// /// # Errors /// * `uefi::Status::ALREADY_STARTED` The network interface is already in the started state. - /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. /// * `uefi::Status::DEVICE_ERROR` This function is not supported by the network interface. pub fn start(&self) -> Result { @@ -81,10 +80,9 @@ impl Snp { /// /// # Errors /// * `uefi::Status::NOT_STARTED` The network interface has not been started. - /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. /// * `uefi::Status::DEVICE_ERROR` This function is not supported by the network interface. - pub fn stop(&mut self) -> Result { + pub fn stop(&self) -> Result { (self.stop)(self).into() } @@ -93,7 +91,6 @@ impl Snp { /// # Errors /// * `uefi::Status::NOT_STARTED` The network interface has not been started. /// * `uefi::Status::EFI_OUT_OF_RESOURCES` There was not enough memory for the transmit and receive buffers - /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. /// * `uefi::Status::UNSUPPORTED` The increased buffer size feature is not supported. pub fn initialize(&self, extra_rx_buffer_size: usize, extra_tx_buffer_size: usize) -> Result { @@ -121,6 +118,24 @@ impl Snp { (self.shutdown)(self).into() } + /// Manages the multicast receive filters of a network interface. + /// + /// # Errors + /// * `uefi::Status::NOT_STARTED` The network interface has not been started. + /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. + /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. + /// * `uefi::Status::UNSUPPORTED` This function is not supported by the network interface. + pub fn receive_filters( + &self, + enable: u32, + disable: u32, + reset_mcast_filter: bool, + mcast_filter_cnt: usize, + mcast_filter: *const MacAddress, + ) -> Result { + (self.receive_filters)(self, enable, disable, reset_mcast_filter, mcast_filter_cnt, mcast_filter).into() + } + /// Modifies or resets the current station address, if supported. /// /// # Errors @@ -147,7 +162,7 @@ impl Snp { &self, reset: bool, statistics_size: *mut usize, - statistics_table: *mut NetworkStatistics, + statistics_table: *mut SimpleNetworkStatistics, ) -> Result { (self.statistics)(self, reset, statistics_size, statistics_table).into() } @@ -189,12 +204,11 @@ impl Snp { /// /// # Errors /// * `uefi::Status::NOT_STARTED` The network interface has not been started. - /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure. /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. pub fn get_status( &self, interrupt_status: *mut u32, - tx_buf: *mut *mut [u8], + tx_buf: *mut *mut u8, ) -> Result { (self.get_status)(self, interrupt_status, tx_buf).into() } @@ -204,7 +218,6 @@ impl Snp { /// # Errors /// * `uefi::Status::NOT_STARTED` The network interface has not been started. /// * `uefi::Status::EFI_OUT_OF_RESOURCES` There was not enough memory for the transmit and receive buffers - /// * `uefi::Status::INVALID_PARAMETER` This parameter was NULL or did not point to a valid EFI_SIMPLE_NETWORK_PROTOCOL structure /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. /// * `uefi::Status::UNSUPPORTED` The increased buffer size feature is not supported. pub fn transmit( @@ -214,13 +227,13 @@ impl Snp { buffer: *const [u8], src_addr: *const MacAddress, dest_addr: *const MacAddress, - protocol: u16, + protocol: *const u16, ) -> Result { (self.transmit)( self, header_size, buffer_size, - buffer, + buffer as *const _ as *const core::ffi::c_void, src_addr, dest_addr, protocol, @@ -249,7 +262,7 @@ impl Snp { self, header_size, buffer_size, - buffer, + buffer as *mut _ as *mut core::ffi::c_void, src_addr, dest_addr, protocol, @@ -258,14 +271,14 @@ impl Snp { } /// Pointer for network mode. - pub fn mode(&self) -> &NetworkMode { + pub fn mode(&self) -> &SimpleNetworkMode { unsafe { &*self.mode } } } newtype_enum! { /// EFI_SIMPLE_NETWORK_STATE - pub enum NetworkState: u32 => { + pub enum SimpleNetworkState: u32 => { NETWORK_STOPPED = 0x00, NETWORK_STARTED = 0x01, NETWORK_INITIALIZED = 0x02, @@ -276,43 +289,80 @@ newtype_enum! { /// EFI_NETWORK_STATISTICS #[repr(C)] #[derive(Debug)] -pub struct NetworkStatistics { - 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, - rx_duplicated_frames: u64, - rx_decrypt_error_frames: u64, - tx_error_frames: u64, - tx_retry_frames: u64, +pub struct SimpleNetworkStatistics { + pub rx_total_frames: u64, + pub rx_good_frames: u64, + pub rx_undersize_frames: u64, + pub rx_oversize_frames: u64, + pub rx_dropped_frames: u64, + pub rx_unicast_frames: u64, + pub rx_broadcast_frames: u64, + pub rx_multicast_frames: u64, + pub rx_crc_error_frames: u64, + pub rx_total_bytes: u64, + pub tx_total_frames: u64, + pub tx_good_frames: u64, + pub tx_undersize_frames: u64, + pub tx_oversize_frames: u64, + pub tx_dropped_frames: u64, + pub tx_unicast_frames: u64, + pub tx_broadcast_frames: u64, + pub tx_multicast_frames: u64, + pub tx_crc_error_frames: u64, + pub tx_total_bytes: u64, + pub collisions: u64, + pub unsupported_protocol: u64, + pub rx_duplicated_frames: u64, + pub rx_decrypt_error_frames: u64, + pub tx_error_frames: u64, + pub tx_retry_frames: u64, +} + +impl SimpleNetworkStatistics { + + // Create a blank `SimpleNetworkStatistics`. + pub fn new() -> SimpleNetworkStatistics { + SimpleNetworkStatistics { + rx_total_frames: 0, + rx_good_frames: 0, + rx_undersize_frames: 0, + rx_oversize_frames: 0, + rx_dropped_frames: 0, + rx_unicast_frames: 0, + rx_broadcast_frames: 0, + rx_multicast_frames: 0, + rx_crc_error_frames: 0, + rx_total_bytes: 0, + tx_total_frames: 0, + tx_good_frames: 0, + tx_undersize_frames: 0, + tx_oversize_frames: 0, + tx_dropped_frames: 0, + tx_unicast_frames: 0, + tx_broadcast_frames: 0, + tx_multicast_frames: 0, + tx_crc_error_frames: 0, + tx_total_bytes: 0, + collisions: 0, + unsupported_protocol: 0, + rx_duplicated_frames: 0, + rx_decrypt_error_frames: 0, + tx_error_frames: 0, + tx_retry_frames: 0, + } + } } /// EFI_MAC_ADDRESS #[repr(C)] -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct MacAddress { - addr: [u8; 32], + pub addr: [u8; 32], } impl MacAddress { + + // Create a `MacAddress` from the given 6 bytes. pub fn new(mac: [u8; 6]) -> MacAddress { let mut data = [0u8; 32]; for (a, b) in data.iter_mut().zip(mac.iter()) { @@ -326,58 +376,68 @@ impl MacAddress { #[repr(C)] #[derive(Debug)] pub struct IpAddress { - addr: [u8; 4], + pub addr: [u8; 4], } /// EFI_SIMPLE_NETWORK_MODE #[repr(C)] #[derive(Debug)] -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, -} - -impl NetworkMode { +pub struct SimpleNetworkMode { - /// Reports the current state of the network interface. - pub fn state(&self) -> NetworkState { - self.state - } + /// The current state of the network interface. + pub state: SimpleNetworkState, /// The size, in bytes, of the network interface’s HW address. - pub fn hw_address_size(&self) -> u32 { - self.hw_address_size - } + pub hw_address_size: u32, /// The size, in bytes, of the network interface’s media header. - pub fn media_header_size(&self) -> u32 { - self.media_header_size - } + pub media_header_size: u32, + + /// The maximum size, in bytes, of the packets supported by the network interface. + pub max_packet_size: u32, + + /// The size, in bytes, of the NVRAM device attached to the network interface. + 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, + + /// Array containing the addresses of the current multicast address receive filters. + pub mcast_filter: [MacAddress; 16], /// The current HW MAC address for the network interface. - pub fn current_address(&self) -> &MacAddress { - &self.current_address - } + pub current_address: MacAddress, + + /// The current HW MAC address for broadcast packets. + pub broadcast_address: MacAddress, + + /// The permanent HW MAC address for the network interface. + pub permanent_address: MacAddress, /// The interface type of the network interface. See RFC 3232, section "Number Hardware Type." - pub fn if_type(&self) -> u8 { - self.if_type - } + pub if_type: u8, + + /// Whether the HW MAC address can be changed. + pub mac_address_changeable: bool, + + /// Whether the network interface can transmit more than one packet at a time. + pub multiple_tx_supported: bool, + + /// Whether the presence of media can be determined. + pub media_present_supported: bool, + + /// Whether media are connected to the network interface. + pub media_present: bool, } diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index cf52ee4ea..e60a1c2c5 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -345,6 +345,9 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> { // Map the QEMU monitor to a pair of named pipes cmd.args(&["-qmp", &qemu_monitor_pipe.qemu_arg]); + // Attach network device + cmd.args(&["-nic", "model=e1000"]); + println!("{}", command_to_string(&cmd)); cmd.stdin(Stdio::piped()); From 80fb3ac1f6bcadd6349a60206a8898d47802a682 Mon Sep 17 00:00:00 2001 From: Tyler Cook Date: Wed, 26 Jan 2022 23:07:32 -0600 Subject: [PATCH 4/4] Update tests to use ARP frame instead of DHCP --- src/proto/network/snp.rs | 50 ++++--- uefi-test-runner/src/proto/network/mod.rs | 162 +++++++++++++++++----- 2 files changed, 158 insertions(+), 54 deletions(-) diff --git a/src/proto/network/snp.rs b/src/proto/network/snp.rs index 1da2cd28c..a5e3ef31b 100644 --- a/src/proto/network/snp.rs +++ b/src/proto/network/snp.rs @@ -26,14 +26,20 @@ pub struct SimpleNetwork { mcast_filter_count: usize, mcast_filter: *const MacAddress, ) -> Status, - station_address: extern "efiapi" fn(this: &SimpleNetwork, reset: bool, new: *const MacAddress) -> Status, + station_address: + extern "efiapi" fn(this: &SimpleNetwork, reset: bool, new: *const MacAddress) -> Status, statistics: extern "efiapi" fn( this: &SimpleNetwork, reset: bool, statistics_size: *mut usize, statistics_table: *mut SimpleNetworkStatistics, ) -> Status, - mcast_ip_to_mac: extern "efiapi" fn(this: &SimpleNetwork, ipv6: bool, ip: *const IpAddress, mac: *mut MacAddress) -> Status, + mcast_ip_to_mac: extern "efiapi" fn( + this: &SimpleNetwork, + ipv6: bool, + ip: *const IpAddress, + mac: *mut MacAddress, + ) -> Status, nv_data: extern "efiapi" fn( this: &SimpleNetwork, read_write: bool, @@ -41,8 +47,11 @@ pub struct SimpleNetwork { buffer_size: usize, buffer: *mut [u8], ) -> Status, - get_status: - extern "efiapi" fn(this: &SimpleNetwork, interrupt_status: *mut u32, tx_buf: *mut *mut u8) -> Status, + get_status: extern "efiapi" fn( + this: &SimpleNetwork, + interrupt_status: *mut u32, + tx_buf: *mut *mut u8, + ) -> Status, transmit: extern "efiapi" fn( this: &SimpleNetwork, header_size: usize, @@ -133,7 +142,15 @@ impl SimpleNetwork { mcast_filter_cnt: usize, mcast_filter: *const MacAddress, ) -> Result { - (self.receive_filters)(self, enable, disable, reset_mcast_filter, mcast_filter_cnt, mcast_filter).into() + (self.receive_filters)( + self, + enable, + disable, + reset_mcast_filter, + mcast_filter_cnt, + mcast_filter, + ) + .into() } /// Modifies or resets the current station address, if supported. @@ -143,11 +160,7 @@ impl SimpleNetwork { /// * `uefi::Status::INVALID_PARAMETER` A parameter was invalid. /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. /// * `uefi::Status::UNSUPPORTED` This function is not supported by the network interface. - pub fn station_address( - &self, - reset: bool, - new: *const MacAddress, - ) -> Result { + pub fn station_address(&self, reset: bool, new: *const MacAddress) -> Result { (self.station_address)(self, reset, new).into() } @@ -161,10 +174,10 @@ impl SimpleNetwork { pub fn statistics( &self, reset: bool, - statistics_size: *mut usize, statistics_table: *mut SimpleNetworkStatistics, ) -> Result { - (self.statistics)(self, reset, statistics_size, statistics_table).into() + let mut stats_size = core::mem::size_of::(); + (self.statistics)(self, reset, &mut stats_size, statistics_table).into() } /// Converts a multicast IP address to a multicast HW MAC address. @@ -183,7 +196,7 @@ impl SimpleNetwork { (self.mcast_ip_to_mac)(self, ipv6, ip, mac).into() } - /// Performs read and write operations on the NVRAM device attached to a network interface. + /// Performs read and write operations on the NVRAM device attached to a network interface. /// /// # Errors /// * `uefi::Status::NOT_STARTED` The network interface has not been started. @@ -205,11 +218,7 @@ impl SimpleNetwork { /// # Errors /// * `uefi::Status::NOT_STARTED` The network interface has not been started. /// * `uefi::Status::DEVICE_ERROR` The command could not be sent to the network interface. - pub fn get_status( - &self, - interrupt_status: *mut u32, - tx_buf: *mut *mut u8, - ) -> Result { + pub fn get_status(&self, interrupt_status: *mut u32, tx_buf: *mut *mut u8) -> Result { (self.get_status)(self, interrupt_status, tx_buf).into() } @@ -319,7 +328,6 @@ pub struct SimpleNetworkStatistics { } impl SimpleNetworkStatistics { - // Create a blank `SimpleNetworkStatistics`. pub fn new() -> SimpleNetworkStatistics { SimpleNetworkStatistics { @@ -361,14 +369,13 @@ pub struct MacAddress { } impl MacAddress { - // Create a `MacAddress` from the given 6 bytes. pub fn new(mac: [u8; 6]) -> MacAddress { let mut data = [0u8; 32]; for (a, b) in data.iter_mut().zip(mac.iter()) { *a = *b; } - MacAddress{addr: data} + MacAddress { addr: data } } } @@ -383,7 +390,6 @@ pub struct IpAddress { #[repr(C)] #[derive(Debug)] pub struct SimpleNetworkMode { - /// The current state of the network interface. pub state: SimpleNetworkState, diff --git a/uefi-test-runner/src/proto/network/mod.rs b/uefi-test-runner/src/proto/network/mod.rs index 9dd7368ea..4938514f3 100644 --- a/uefi-test-runner/src/proto/network/mod.rs +++ b/uefi-test-runner/src/proto/network/mod.rs @@ -1,56 +1,154 @@ use core::ptr; use uefi::prelude::*; -use uefi::proto::network::snp::MacAddress; -use uefi::proto::network::snp::NetworkStatistics; -use uefi::proto::network::snp::Snp; + +use uefi::proto::network::snp::SimpleNetwork; +use uefi::proto::network::snp::SimpleNetworkStatistics; pub fn test(bt: &BootServices) { info!("Testing Network protocols"); let handles = bt - .find_handles::() - .expect_success("Failed to get handles for `Snp` protocol"); + .find_handles::() + .expect_success("Failed to get handles for `SimpleNetwork` protocol"); for handle in handles { - let nic = bt.handle_protocol::(handle).expect_success("Unknown"); + let nic = bt + .handle_protocol::(handle) + .expect_success("Unknown error"); let nic = unsafe { &*nic.get() }; + // Check shutdown + nic.shutdown().expect_success("Failed to shutdown NIC"); + + // Check stop + nic.stop().expect_success("Failed to stop NIC"); + // Check start nic.start().expect_success("Failed to start NIC"); // Check initialize - nic.initialize(4096, 4096).expect_success("Failed to initialize NIC"); + nic.initialize(0, 0) + .expect_success("Failed to initialize NIC"); + + // Set receive filters + nic.receive_filters(0x01 | 0x04 | 0x08, 0, false, 0, core::ptr::null()) + .expect_success("Failed to set receive filters"); + + // Check media + if nic.mode().media_present_supported && !nic.mode().media_present { + continue; + } + + let src_addr = nic.mode().current_address; - // Prepare to send a frame - let mut header_size = 14usize; - let mut buffer_size = 15usize; - let mut buffer = [ - 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 10u8, + // Hand-craft an ARP probe to use as an example frame + let arp_request: [u8; 42] = [ + // ETHERNET HEADER + // Destination MAC + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + // Source MAC + src_addr.addr[0], + src_addr.addr[1], + src_addr.addr[2], + src_addr.addr[3], + src_addr.addr[4], + src_addr.addr[5], + // EtherType + 0x08, + 0x06, + // ARP HEADER + // Hardware type + 0x00, + 0x01, + // Protocol type + 0x08, + 0x00, + // Hardware address length and protocol address length + 0x06, + 0x04, + // Operation + 0x00, + 0x01, + // Sender hardware address + src_addr.addr[0], + src_addr.addr[1], + src_addr.addr[2], + src_addr.addr[3], + src_addr.addr[4], + src_addr.addr[5], + // Sender protocol address + 0x01, + 0x01, + 0x01, + 0x01, + // Target hardware address + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + // Target protocol address + 0x00, + 0x00, + 0x00, + 0x00, ]; - let mut src_addr = nic.mode().current_address(); - let mut dest_addr = nic.mode().current_address(); - let mut protocol = 2048u16; - // Send a frame with one byte payload + // Send the frame nic.transmit( - header_size, - buffer_size, - &buffer, - src_addr as *const MacAddress, - dest_addr as *const MacAddress, - protocol, + 0, + 14 + 28, // Ethernet header plus arp + &arp_request, + core::ptr::null(), + core::ptr::null(), + core::ptr::null(), ) - .expect_success("Failed to transmit packet"); + .expect_success("Failed to transmit frame"); + + // Get status + let mut interrupt_status = 0u32; + let mut tx_buf: *mut u8 = core::ptr::null_mut(); + let tx_buf_ptr: *mut *mut u8 = &mut tx_buf; + + for j in 1..3 { + nic.get_status(&mut interrupt_status, tx_buf_ptr) + .expect_success("Failed to get status"); + info!("interrupt_status: {}", interrupt_status); + info!("tx_buf: {}", unsafe { **tx_buf_ptr }); + bt.stall(5_000); + } + + // Attempt to receive a frame + let mut buffer_size = 1500usize; + let mut buffer = [0u8; 1500]; // Receive the frame - nic.receive( - header_size as *mut usize, - buffer_size as *mut usize, - &mut buffer, - &mut *src_addr, - &mut *dest_addr, - protocol as *mut u16, - ) - .expect_success("Failed to receive packet"); + for j in 1..3 { + let res = nic.receive( + ptr::null_mut(), + &mut buffer_size as *mut usize, + &mut buffer, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ); + info!("receive: {:?}", res); + bt.stall(5_000_000); + } + + // Get stats + let mut stats = SimpleNetworkStatistics::new(); + nic.statistics(false, &mut stats); + info!("Stats: {:?}", stats); + + // We should probably see some transmit and receive stats + assert!(stats.tx_total_frames > 0, "tx_total_frames is zero"); + assert!(stats.rx_total_frames > 0, "rx_total_frames is zero"); } }