Skip to content

Implement SIMPLE_NETWORK_PROTOCOL #606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions uefi-test-runner/src/proto/network/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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::<BaseCode>() {
for handle in handles {
Expand Down
124 changes: 124 additions & 0 deletions uefi-test-runner/src/proto/network/snp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use uefi::prelude::BootServices;
use uefi::proto::network::snp::{InterruptStatus, ReceiveFlags, SimpleNetwork};
use uefi::proto::network::MacAddress;
use uefi::Status;

pub fn test(bt: &BootServices) {
info!("Testing the simple network protocol");

let handles = bt.find_handles::<SimpleNetwork>().unwrap_or_default();

for handle in handles {
let simple_network = bt.open_protocol_exclusive::<SimpleNetwork>(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(0, 0)
.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(
ReceiveFlags::UNICAST | ReceiveFlags::MULTICAST | ReceiveFlags::BROADCAST,
ReceiveFlags::empty(),
false,
None,
)
.expect("Failed to set receive filters");

// Check media
if !simple_network.mode().media_present_supported || !simple_network.mode().media_present {
continue;
}

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()
.contains(InterruptStatus::TRANSMIT));

// 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()
.contains(InterruptStatus::TRANSMIT)
{}

// Attempt to receive a frame
let mut buffer = [0u8; 1500];

info!("Waiting for the reception");
if simple_network.receive(&mut buffer, None, None, None, None)
== Err(Status::NOT_READY.into())
{
bt.stall(1_000_000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we could use the wait_for_packet here. I recall from the last PR attempting this that we couldn't get that event to fire... any idea what the problem could be there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This event didn't seem to fire on QEMU at all, despite the packet being received. I thought I was doing something wrong, but if with the same code I try to wait for a wait_for_key event, that event resolves fine...

At this point, I think either the QEMU/OVMF implementation and the EFI spec disagree, QEMU doesn't implement this event for whatever reason (I found this thread from 10 years ago that seems to imply that the event not being implemented is fairly common, but I am not sure how I would verify), or there was something else wrong with my code that I don't undersand. ~30m of digging got me nowhere last time I tried.

This is also why the event is not exposed, since I haven't managed to get it to work :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dug around in the EDK2 and OVMF source a bit but don't have much to add. It does look like the WaitForPacket Event and all related machinery have been implemented for at least the virtio-net-pci driver in QEMU. However, even while using this driver, the event never seems to be signaled.

I'll try to plug gdb into qemu tomorrow and see what might be going on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also why the event is not exposed, since I haven't managed to get it to work :)

I would recommend exposing it anyway; it may well work on some non-QEMU/EDK2 environments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I exposed it with a comment that cautions the user to check before using it :)


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");
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);
}
}
1 change: 1 addition & 0 deletions uefi/src/proto/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
Loading