mirror of
https://github.com/rust-osdev/uefi-rs
synced 2026-06-08 17:17:36 +00:00
Add PXE Base Code protocol (#417)
This commit is contained in:
@@ -69,6 +69,7 @@ pub mod debug;
|
||||
pub mod device_path;
|
||||
pub mod loaded_image;
|
||||
pub mod media;
|
||||
pub mod network;
|
||||
pub mod pi;
|
||||
pub mod rng;
|
||||
pub mod shim;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//! Network access protocols.
|
||||
//!
|
||||
//! These protocols can be used to interact with network resources.
|
||||
|
||||
pub mod pxe;
|
||||
|
||||
/// Represents an IPv4/v6 address.
|
||||
///
|
||||
/// Corresponds to the `EFI_IP_ADDRESS` type in the C API.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(C, align(4))]
|
||||
pub struct IpAddress(pub [u8; 16]);
|
||||
|
||||
impl IpAddress {
|
||||
/// Construct a new IPv4 address.
|
||||
pub const fn new_v4(ip_addr: [u8; 4]) -> Self {
|
||||
let mut buffer = [0; 16];
|
||||
buffer[0] = ip_addr[0];
|
||||
buffer[1] = ip_addr[1];
|
||||
buffer[2] = ip_addr[2];
|
||||
buffer[3] = ip_addr[3];
|
||||
Self(buffer)
|
||||
}
|
||||
|
||||
/// Construct a new IPv6 address.
|
||||
pub const fn new_v6(ip_addr: [u8; 16]) -> Self {
|
||||
Self(ip_addr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a MAC (media access control) address.
|
||||
///
|
||||
/// Corresponds to the `EFI_MAC_ADDRESS` type in the C API.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(C)]
|
||||
pub struct MacAddress(pub [u8; 32]);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ pub fn test(image: Handle, st: &mut SystemTable<Boot>) {
|
||||
device_path::test(image, bt);
|
||||
loaded_image::test(image, bt);
|
||||
media::test(image, bt);
|
||||
network::test(image, bt);
|
||||
pi::test(bt);
|
||||
rng::test(image, bt);
|
||||
|
||||
@@ -60,6 +61,7 @@ mod debug;
|
||||
mod device_path;
|
||||
mod loaded_image;
|
||||
mod media;
|
||||
mod network;
|
||||
mod pi;
|
||||
mod rng;
|
||||
#[cfg(any(
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
use uefi::{
|
||||
prelude::BootServices,
|
||||
proto::network::{
|
||||
pxe::{BaseCode, DhcpV4Packet, IpFilter, IpFilters, UdpOpFlags},
|
||||
IpAddress,
|
||||
},
|
||||
table::boot::{OpenProtocolAttributes, OpenProtocolParams},
|
||||
CStr8, Handle,
|
||||
};
|
||||
|
||||
pub fn test(image: Handle, bt: &BootServices) {
|
||||
info!("Testing Network protocols");
|
||||
|
||||
if let Ok(handles) = bt.find_handles::<BaseCode>() {
|
||||
for handle in handles {
|
||||
let base_code = bt
|
||||
.open_protocol::<BaseCode>(
|
||||
OpenProtocolParams {
|
||||
handle,
|
||||
agent: image,
|
||||
controller: None,
|
||||
},
|
||||
OpenProtocolAttributes::Exclusive,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let base_code = unsafe { &mut *base_code.interface.get() };
|
||||
|
||||
info!("Starting PXE Base Code");
|
||||
base_code
|
||||
.start(false)
|
||||
.expect("failed to start PXE Base Code");
|
||||
base_code
|
||||
.dhcp(false)
|
||||
.expect("failed to complete a dhcpv4 handshake");
|
||||
|
||||
assert!(base_code.mode().dhcp_ack_received);
|
||||
let dhcp_ack: &DhcpV4Packet = base_code.mode().dhcp_ack.as_ref();
|
||||
let server_ip = dhcp_ack.bootp_si_addr;
|
||||
let server_ip = IpAddress::new_v4(server_ip);
|
||||
|
||||
const EXAMPLE_FILE_NAME: &[u8] = b"example-file.txt\0";
|
||||
const EXAMPLE_FILE_CONTENT: &[u8] = b"Hello world!";
|
||||
let example_file_name = CStr8::from_bytes_with_nul(EXAMPLE_FILE_NAME).unwrap();
|
||||
|
||||
info!("Getting remote file size");
|
||||
let file_size = base_code
|
||||
.tftp_get_file_size(&server_ip, example_file_name)
|
||||
.expect("failed to query file size");
|
||||
assert_eq!(file_size, EXAMPLE_FILE_CONTENT.len() as u64);
|
||||
|
||||
info!("Reading remote file");
|
||||
let mut buffer = [0; 512];
|
||||
let len = base_code
|
||||
.tftp_read_file(&server_ip, example_file_name, Some(&mut buffer))
|
||||
.expect("failed to read file");
|
||||
let len = usize::try_from(len).unwrap();
|
||||
assert_eq!(EXAMPLE_FILE_CONTENT, &buffer[..len]);
|
||||
|
||||
base_code
|
||||
.set_ip_filter(&IpFilter::new(IpFilters::STATION_IP, &[]))
|
||||
.expect("failed to set IP filter");
|
||||
|
||||
const EXAMPLE_SERVICE_PORT: u16 = 21572;
|
||||
|
||||
info!("Writing UDP packet to example service");
|
||||
|
||||
let payload = [1, 2, 3, 4];
|
||||
let header = [payload.len() as u8];
|
||||
let mut write_src_port = 0;
|
||||
base_code
|
||||
.udp_write(
|
||||
UdpOpFlags::ANY_SRC_PORT,
|
||||
&server_ip,
|
||||
EXAMPLE_SERVICE_PORT,
|
||||
None,
|
||||
None,
|
||||
Some(&mut write_src_port),
|
||||
Some(&header),
|
||||
&payload,
|
||||
)
|
||||
.expect("failed to write UDP packet");
|
||||
|
||||
info!("Reading UDP packet from example service");
|
||||
|
||||
let mut src_ip = server_ip;
|
||||
let mut src_port = EXAMPLE_SERVICE_PORT;
|
||||
let mut dest_ip = base_code.mode().station_ip;
|
||||
let mut dest_port = write_src_port;
|
||||
let mut header = [0; 1];
|
||||
let mut received = [0; 4];
|
||||
base_code
|
||||
.udp_read(
|
||||
UdpOpFlags::USE_FILTER,
|
||||
Some(&mut dest_ip),
|
||||
Some(&mut dest_port),
|
||||
Some(&mut src_ip),
|
||||
Some(&mut src_port),
|
||||
Some(&mut header),
|
||||
&mut received,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Check the header.
|
||||
assert_eq!(header[0] as usize, payload.len());
|
||||
// Check that we receive the reversed payload.
|
||||
received.reverse();
|
||||
assert_eq!(payload, received);
|
||||
|
||||
info!("Stopping PXE Base Code");
|
||||
base_code.stop().expect("failed to stop PXE Base Code");
|
||||
}
|
||||
} else {
|
||||
warn!("PXE Base Code protocol is not supported");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Hello world!
|
||||
@@ -1,6 +1,7 @@
|
||||
mod arch;
|
||||
mod cargo;
|
||||
mod disk;
|
||||
mod net;
|
||||
mod opt;
|
||||
mod qemu;
|
||||
mod util;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use std::net::UdpSocket;
|
||||
|
||||
/// Start a thread that listens on UDP port 21572 and simulates a simple echo
|
||||
/// service that reverses the incoming messages.
|
||||
pub fn start_reverse_echo_service() {
|
||||
std::thread::spawn(reverse_echo_service);
|
||||
}
|
||||
|
||||
fn reverse_echo_service() {
|
||||
let socket = UdpSocket::bind(("127.0.0.1", 21572)).expect("failed to bind to UDP socket");
|
||||
|
||||
let mut buffer = [0; 257];
|
||||
loop {
|
||||
// Receive a packet.
|
||||
let (len, addr) = socket
|
||||
.recv_from(&mut buffer)
|
||||
.expect("failed to receive packet");
|
||||
let buffer = &mut buffer[..len];
|
||||
|
||||
// Extract header information.
|
||||
let (payload_len, payload) = buffer.split_first_mut().unwrap();
|
||||
assert_eq!(usize::from(*payload_len), payload.len());
|
||||
|
||||
// Simulate processing the data: Reverse the payload.
|
||||
payload.reverse();
|
||||
|
||||
// Send a reply.
|
||||
socket.send_to(buffer, addr).expect("failed to send packet");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::arch::UefiArch;
|
||||
use crate::disk::{check_mbr_test_disk, create_mbr_test_disk};
|
||||
use crate::net;
|
||||
use crate::opt::QemuOpt;
|
||||
use crate::util::command_to_string;
|
||||
use anyhow::{bail, Context, Result};
|
||||
@@ -368,6 +369,13 @@ 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 with DHCP configured for PXE
|
||||
cmd.args(&[
|
||||
"-nic",
|
||||
"user,model=e1000,net=192.168.17.0/24,tftp=uefi-test-runner/tftp/,bootfile=fake-boot-file",
|
||||
]);
|
||||
net::start_reverse_echo_service();
|
||||
|
||||
println!("{}", command_to_string(&cmd));
|
||||
|
||||
cmd.stdin(Stdio::piped());
|
||||
|
||||
Reference in New Issue
Block a user