Add PXE Base Code protocol (#417)

This commit is contained in:
Tom Dohrmann
2022-05-07 15:13:09 +00:00
committed by GitHub
parent d351f24af6
commit 16f7f18474
9 changed files with 1448 additions and 0 deletions
+1
View File
@@ -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;
+36
View File
@@ -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
+2
View File
@@ -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(
+116
View File
@@ -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");
}
}
+1
View File
@@ -0,0 +1 @@
Hello world!
+1
View File
@@ -1,6 +1,7 @@
mod arch;
mod cargo;
mod disk;
mod net;
mod opt;
mod qemu;
mod util;
+30
View File
@@ -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");
}
}
+8
View File
@@ -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());