| // Copyright 2024, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use crate::defs::{ |
| EfiGuid, EfiMacAddress, EfiSimpleNetworkMode, EfiSimpleNetworkProtocol, EFI_STATUS_NOT_FOUND, |
| }; |
| use crate::protocol::{Protocol, ProtocolInfo}; |
| use crate::{efi_call, map_efi_err, EfiResult}; |
| use core::ffi::c_void; |
| use core::ptr::null_mut; |
| |
| /// EFI_SIMPLE_NETWORK_PROTOCOL |
| pub struct SimpleNetworkProtocol; |
| |
| impl ProtocolInfo for SimpleNetworkProtocol { |
| type InterfaceType = EfiSimpleNetworkProtocol; |
| |
| const GUID: EfiGuid = |
| EfiGuid::new(0xa19832b9, 0xac25, 0x11d3, [0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d]); |
| } |
| |
| impl<'a> Protocol<'a, SimpleNetworkProtocol> { |
| /// Wrapper of `EFI_SIMPLE_NETWORK.Start()` |
| pub fn start(&self) -> EfiResult<()> { |
| // SAFETY: |
| // `self.interface()?` guarantees to return a valid object pointer as established by |
| // `Protocol::new()`. |
| // `self.interface` outlives the call and will not be retained. |
| unsafe { efi_call!(self.interface()?.start, self.interface) } |
| } |
| |
| /// Wrapper of `EFI_SIMPLE_NETWORK.Stop()` |
| pub fn stop(&self) -> EfiResult<()> { |
| // SAFETY: See safety reasoning of `start()`. |
| unsafe { efi_call!(self.interface()?.stop, self.interface) } |
| } |
| |
| /// Wrapper of `EFI_SIMPLE_NETWORK.Initialize()` |
| pub fn initialize(&self, extra_rx_buf_size: usize, extra_tx_buf_size: usize) -> EfiResult<()> { |
| // SAFETY: See safety reasoning of `start()`. |
| unsafe { |
| efi_call!( |
| self.interface()?.initialize, |
| self.interface, |
| extra_rx_buf_size, |
| extra_tx_buf_size |
| ) |
| } |
| } |
| |
| /// Wrapper of `EFI_SIMPLE_NETWORK.Reset()` |
| pub fn reset(&self, extended_verification: bool) -> EfiResult<()> { |
| // SAFETY: See safety reasoning of `start()`. |
| unsafe { efi_call!(self.interface()?.reset, self.interface, extended_verification) } |
| } |
| |
| /// Wrapper of `EFI_SIMPLE_NETWORK.Shutdown()` |
| pub fn shutdown(&self) -> EfiResult<()> { |
| // SAFETY: See safety reasoning of `start()`. |
| unsafe { efi_call!(self.interface()?.shutdown, self.interface) } |
| } |
| |
| /// Wrapper of `EFI_SIMPLE_NETWORK.GetStatus()` |
| pub fn get_status( |
| &self, |
| interrupt_status: Option<&mut u32>, |
| recycle_buffer: Option<&mut *mut c_void>, |
| ) -> EfiResult<()> { |
| // SAFETY: |
| // See safety reasoning of `start()`. |
| // Pointers to `interrupt_status`, `recycled_buffer` are valid during the call and for |
| // writing output values only. |
| unsafe { |
| efi_call!( |
| self.interface()?.get_status, |
| self.interface, |
| option_ref_mut_to_pointer(interrupt_status), |
| option_ref_mut_to_pointer(recycle_buffer) |
| )?; |
| } |
| Ok(()) |
| } |
| |
| /// Wrapper of `EFI_SIMPLE_NETWORK.Transmit()` |
| /// |
| /// # Safety |
| /// |
| /// * `buf` needs to be a valid buffer. |
| /// * There should not be any existing references to memory pointed by `buf`. |
| /// * Because `buf` is internally retained by the network. `buf` should remain valid and not |
| /// dereferenced until either 1) the buffer address re-appears in `recycled_buffer` from |
| /// `Self::get_status()` or 2) Self::Shutdown() is called and returns either Ok(()) or |
| /// EFI_STATUS_NOT_STARTED. |
| pub unsafe fn transmit( |
| &self, |
| header_size: usize, |
| buf: *mut [u8], |
| mut src: EfiMacAddress, |
| mut dest: EfiMacAddress, |
| mut protocol: u16, |
| ) -> EfiResult<()> { |
| let buf = buf.as_mut().unwrap(); |
| // SAFETY: |
| // See safety reasoning of `start()`. |
| // All pointers passed are valid, outlive the call and are not retained by the call. |
| unsafe { |
| efi_call!( |
| self.interface()?.transmit, |
| self.interface, |
| header_size, |
| buf.len(), |
| buf.as_mut_ptr() as *mut _, |
| &mut src, |
| &mut dest, |
| &mut protocol |
| ) |
| } |
| } |
| |
| /// Wrapper of `EFI_SIMPLE_NETWORK.Receive()`. |
| pub fn receive( |
| &self, |
| header_size: Option<&mut usize>, |
| buf_size: Option<&mut usize>, |
| buf: &mut [u8], |
| src: Option<&mut EfiMacAddress>, |
| dest: Option<&mut EfiMacAddress>, |
| protocol: Option<&mut u16>, |
| ) -> EfiResult<()> { |
| // SAFETY: |
| // See safety reasoning of `start()`. |
| // All pointers passed are valid, outlive the call and are not retained by the call. |
| unsafe { |
| efi_call!( |
| self.interface()?.receive, |
| self.interface, |
| option_ref_mut_to_pointer(header_size), |
| option_ref_mut_to_pointer(buf_size), |
| buf.as_mut_ptr() as *mut _, |
| option_ref_mut_to_pointer(src), |
| option_ref_mut_to_pointer(dest), |
| option_ref_mut_to_pointer(protocol) |
| )?; |
| } |
| Ok(()) |
| } |
| |
| /// Returns `EFI_SIMPLE_NETWORK.Mode` structure |
| pub fn mode(&self) -> EfiResult<EfiSimpleNetworkMode> { |
| // SAFETY: Non-null pointer from UEFI interface points to valid object. |
| unsafe { self.interface()?.mode.as_ref() }.ok_or(EFI_STATUS_NOT_FOUND.into()).copied() |
| } |
| } |
| |
| /// A helper to convert an `Option<&mut T>` to `*mut T`. None maps to NULL. |
| fn option_ref_mut_to_pointer<T>(option: Option<&mut T>) -> *mut T { |
| option.map(|t| t as *mut _).unwrap_or(null_mut()) |
| } |