Skip to content

Commit ff1b91f

Browse files
committed
add ip4 config2 protocol
Signed-off-by: Gerd Hoffmann <[email protected]>
1 parent 6ab3d14 commit ff1b91f

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

uefi/src/proto/network/ip4config2.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
#![cfg(feature = "alloc")]
4+
5+
//! IP4 Config2 Protocol.
6+
7+
extern crate alloc;
8+
use alloc::vec;
9+
use alloc::vec::Vec;
10+
use core::ffi::c_void;
11+
12+
use uefi::boot::ScopedProtocol;
13+
use uefi::prelude::*;
14+
use uefi::proto::unsafe_protocol;
15+
use uefi::{print, println};
16+
use uefi_raw::protocol::network::ip4_config2::{
17+
Ip4Config2DataType, Ip4Config2InterfaceInfo, Ip4Config2Policy, Ip4Config2Protocol,
18+
};
19+
use uefi_raw::Ipv4Address;
20+
21+
/// IP4 Config2 [`Protocol`]. Configure IPv4 networking.
22+
///
23+
/// [`Protocol`]: uefi::proto::Protocol
24+
#[derive(Debug)]
25+
#[unsafe_protocol(Ip4Config2Protocol::GUID)]
26+
pub struct Ip4Config2(pub Ip4Config2Protocol);
27+
28+
impl Ip4Config2 {
29+
/// Open IP4 Config2 protocol for the given NIC handle.
30+
pub fn new(nic_handle: Handle) -> uefi::Result<ScopedProtocol<Ip4Config2>> {
31+
let protocol;
32+
unsafe {
33+
protocol = boot::open_protocol::<Ip4Config2>(
34+
boot::OpenProtocolParams {
35+
handle: nic_handle,
36+
agent: boot::image_handle(),
37+
controller: None,
38+
},
39+
boot::OpenProtocolAttributes::GetProtocol,
40+
)?;
41+
}
42+
Ok(protocol)
43+
}
44+
45+
/// Set configuration data. It is recommended to type-specific set_* helpers instead of calling this directly.
46+
pub fn set_data(&mut self, data_type: Ip4Config2DataType, data: &mut [u8]) -> uefi::Result<()> {
47+
let status = unsafe {
48+
let data_ptr = data.as_mut_ptr() as *mut c_void;
49+
(self.0.set_data)(&mut self.0, data_type, data.len(), data_ptr)
50+
};
51+
match status {
52+
Status::SUCCESS => Ok(()),
53+
_ => Err(status.into()),
54+
}
55+
}
56+
57+
/// Get configuration data. It is recommended to type-specific get_* helpers instead of calling this directly.
58+
pub fn get_data(&mut self, data_type: Ip4Config2DataType) -> uefi::Result<Vec<u8>> {
59+
let mut data_size = 0;
60+
61+
// call #1: figure return buffer size
62+
let status = unsafe {
63+
let null = core::ptr::null_mut();
64+
(self.0.get_data)(&mut self.0, data_type, &mut data_size, null)
65+
};
66+
if status != Status::BUFFER_TOO_SMALL {
67+
return Err(status.into());
68+
}
69+
70+
// call #2: get data
71+
let mut data = vec![0; data_size];
72+
let status = unsafe {
73+
let data_ptr = data.as_mut_ptr() as *mut c_void;
74+
(self.0.get_data)(&mut self.0, data_type, &mut data_size, data_ptr)
75+
};
76+
match status {
77+
Status::SUCCESS => Ok(data),
78+
_ => Err(status.into()),
79+
}
80+
}
81+
82+
/// Set config policy (static vs. dhcp).
83+
pub fn set_policy(&mut self, policy: Ip4Config2Policy) -> uefi::Result<()> {
84+
let mut data: [u8; 4] = policy.0.to_ne_bytes();
85+
self.set_data(Ip4Config2DataType::POLICY, &mut data)
86+
}
87+
88+
/// Get current interface configuration.
89+
pub fn get_interface_info(&mut self) -> uefi::Result<Ip4Config2InterfaceInfo> {
90+
let data = self.get_data(Ip4Config2DataType::INTERFACE_INFO)?;
91+
let info: &Ip4Config2InterfaceInfo =
92+
unsafe { &*(data.as_ptr() as *const Ip4Config2InterfaceInfo) };
93+
Ok(Ip4Config2InterfaceInfo {
94+
name: info.name,
95+
if_type: info.if_type,
96+
hw_addr_size: info.hw_addr_size,
97+
hw_addr: info.hw_addr,
98+
station_addr: info.station_addr,
99+
subnet_mask: info.subnet_mask,
100+
route_table_size: 0,
101+
route_table: core::ptr::null_mut(),
102+
})
103+
}
104+
105+
fn print_info(info: &Ip4Config2InterfaceInfo) {
106+
println!(
107+
"addr v4: {}.{}.{}.{}",
108+
info.station_addr.0[0],
109+
info.station_addr.0[1],
110+
info.station_addr.0[2],
111+
info.station_addr.0[3],
112+
);
113+
}
114+
115+
/// Bring up network interface. Does nothing in case the network
116+
/// is already set up. Otherwise turns on DHCP and waits until an
117+
/// IPv4 address has been assigned. Reports progress on the
118+
/// console if verbose is set to true. Returns TIMEOUT error in
119+
/// case DHCP configuration does not finish within 30 seconds.
120+
pub fn ifup(&mut self, verbose: bool) -> uefi::Result<()> {
121+
let no_address = Ipv4Address::default();
122+
123+
let info = self.get_interface_info()?;
124+
if info.station_addr != no_address {
125+
if verbose {
126+
print!("Network is already up: ");
127+
Ip4Config2::print_info(&info);
128+
}
129+
return Ok(());
130+
}
131+
132+
if verbose {
133+
print!("DHCP ");
134+
}
135+
self.set_policy(Ip4Config2Policy::DHCP)?;
136+
137+
for _ in 0..30 {
138+
if verbose {
139+
print!(".");
140+
}
141+
boot::stall(1_000_000);
142+
let info = self.get_interface_info()?;
143+
if info.station_addr != no_address {
144+
if verbose {
145+
print!(" OK: ");
146+
Ip4Config2::print_info(&info);
147+
}
148+
return Ok(());
149+
}
150+
}
151+
152+
Err(Status::TIMEOUT.into())
153+
}
154+
}

uefi/src/proto/network/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//!
55
//! These protocols can be used to interact with network resources.
66
7+
pub mod ip4config2;
78
pub mod pxe;
89
pub mod snp;
910

0 commit comments

Comments
 (0)