1 //! PXE Base Code protocol. 2 3 use core::ffi::c_void; 4 use core::fmt::{self, Debug, Display, Formatter}; 5 use core::iter::from_fn; 6 use core::mem::MaybeUninit; 7 use core::ptr::{null, null_mut}; 8 9 use crate::polyfill::maybe_uninit_slice_as_mut_ptr; 10 use crate::proto::unsafe_protocol; 11 use crate::util::ptr_write_unaligned_and_add; 12 use bitflags::bitflags; 13 use ptr_meta::Pointee; 14 15 use crate::{CStr8, Char8, Result, Status, StatusExt}; 16 17 use super::{IpAddress, MacAddress}; 18 19 /// PXE Base Code protocol 20 #[derive(Debug)] 21 #[repr(C)] 22 #[unsafe_protocol("03c4e603-ac28-11d3-9a2d-0090273fc14d")] 23 #[allow(clippy::type_complexity)] 24 pub struct BaseCode { 25 revision: u64, 26 start: extern "efiapi" fn(this: &Self, use_ipv6: bool) -> Status, 27 stop: extern "efiapi" fn(this: &Self) -> Status, 28 dhcp: extern "efiapi" fn(this: &Self, sort_offers: bool) -> Status, 29 discover: extern "efiapi" fn( 30 this: &Self, 31 ty: BootstrapType, 32 layer: &mut u16, 33 use_bis: bool, 34 info: *const FfiDiscoverInfo, 35 ) -> Status, 36 mtftp: unsafe extern "efiapi" fn( 37 this: &Self, 38 operation: TftpOpcode, 39 buffer: *mut c_void, 40 overwrite: bool, 41 buffer_size: &mut u64, 42 block_size: Option<&usize>, 43 server_ip: &IpAddress, 44 filename: *const Char8, 45 info: Option<&MtftpInfo>, 46 dont_use_buffer: bool, 47 ) -> Status, 48 udp_write: unsafe extern "efiapi" fn( 49 this: &Self, 50 op_flags: UdpOpFlags, 51 dest_ip: &IpAddress, 52 dest_port: &u16, 53 gateway_ip: Option<&IpAddress>, 54 src_ip: Option<&IpAddress>, 55 src_port: Option<&mut u16>, 56 header_size: Option<&usize>, 57 header_ptr: *const c_void, 58 buffer_size: &usize, 59 buffer_ptr: *const c_void, 60 ) -> Status, 61 udp_read: unsafe extern "efiapi" fn( 62 this: &Self, 63 op_flags: UdpOpFlags, 64 dest_ip: Option<&mut IpAddress>, 65 dest_port: Option<&mut u16>, 66 src_ip: Option<&mut IpAddress>, 67 src_port: Option<&mut u16>, 68 header_size: Option<&usize>, 69 header_ptr: *mut c_void, 70 buffer_size: &mut usize, 71 buffer_ptr: *mut c_void, 72 ) -> Status, 73 set_ip_filter: extern "efiapi" fn(this: &Self, new_filter: &IpFilter) -> Status, 74 arp: extern "efiapi" fn( 75 this: &Self, 76 ip_addr: &IpAddress, 77 mac_addr: Option<&mut MacAddress>, 78 ) -> Status, 79 set_parameters: extern "efiapi" fn( 80 this: &Self, 81 new_auto_arp: Option<&bool>, 82 new_send_guid: Option<&bool>, 83 new_ttl: Option<&u8>, 84 new_tos: Option<&u8>, 85 new_make_callback: Option<&bool>, 86 ) -> Status, 87 set_station_ip: extern "efiapi" fn( 88 this: &Self, 89 new_station_ip: Option<&IpAddress>, 90 new_subnet_mask: Option<&IpAddress>, 91 ) -> Status, 92 set_packets: extern "efiapi" fn( 93 this: &Self, 94 new_dhcp_discover_valid: Option<&bool>, 95 new_dhcp_ack_received: Option<&bool>, 96 new_proxy_offer_received: Option<&bool>, 97 new_pxe_discover_valid: Option<&bool>, 98 new_pxe_reply_received: Option<&bool>, 99 new_pxe_bis_reply_received: Option<&bool>, 100 new_dhcp_discover: Option<&Packet>, 101 new_dhcp_ack: Option<&Packet>, 102 new_proxy_offer: Option<&Packet>, 103 new_pxe_discover: Option<&Packet>, 104 new_pxe_reply: Option<&Packet>, 105 new_pxe_bis_reply: Option<&Packet>, 106 ) -> Status, 107 mode: *const Mode, 108 } 109 110 impl BaseCode { 111 /// Enables the use of the PXE Base Code Protocol functions. start(&mut self, use_ipv6: bool) -> Result112 pub fn start(&mut self, use_ipv6: bool) -> Result { 113 (self.start)(self, use_ipv6).to_result() 114 } 115 116 /// Disables the use of the PXE Base Code Protocol functions. stop(&mut self) -> Result117 pub fn stop(&mut self) -> Result { 118 (self.stop)(self).to_result() 119 } 120 121 /// Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / 122 /// acknowledge) or DHCPv6 S.A.R.R (solicit / advertise / request / reply) sequence. dhcp(&mut self, sort_offers: bool) -> Result123 pub fn dhcp(&mut self, sort_offers: bool) -> Result { 124 (self.dhcp)(self, sort_offers).to_result() 125 } 126 127 /// Attempts to complete the PXE Boot Server and/or boot image discovery 128 /// sequence. discover( &mut self, ty: BootstrapType, layer: &mut u16, use_bis: bool, info: Option<&DiscoverInfo>, ) -> Result129 pub fn discover( 130 &mut self, 131 ty: BootstrapType, 132 layer: &mut u16, 133 use_bis: bool, 134 info: Option<&DiscoverInfo>, 135 ) -> Result { 136 let info: *const FfiDiscoverInfo = info 137 .map(|info| { 138 let info_ptr: *const DiscoverInfo = info; 139 info_ptr.cast() 140 }) 141 .unwrap_or(null()); 142 143 (self.discover)(self, ty, layer, use_bis, info).to_result() 144 } 145 146 /// Returns the size of a file located on a TFTP server. tftp_get_file_size(&mut self, server_ip: &IpAddress, filename: &CStr8) -> Result<u64>147 pub fn tftp_get_file_size(&mut self, server_ip: &IpAddress, filename: &CStr8) -> Result<u64> { 148 let mut buffer_size = 0; 149 150 let status = unsafe { 151 (self.mtftp)( 152 self, 153 TftpOpcode::TftpGetFileSize, 154 null_mut(), 155 false, 156 &mut buffer_size, 157 None, 158 server_ip, 159 filename.as_ptr(), 160 None, 161 false, 162 ) 163 }; 164 status.to_result_with_val(|| buffer_size) 165 } 166 167 /// Reads a file located on a TFTP server. tftp_read_file( &mut self, server_ip: &IpAddress, filename: &CStr8, buffer: Option<&mut [u8]>, ) -> Result<u64>168 pub fn tftp_read_file( 169 &mut self, 170 server_ip: &IpAddress, 171 filename: &CStr8, 172 buffer: Option<&mut [u8]>, 173 ) -> Result<u64> { 174 let (buffer_ptr, mut buffer_size, dont_use_buffer) = if let Some(buffer) = buffer { 175 let buffer_size = u64::try_from(buffer.len()).unwrap(); 176 ((&mut buffer[0] as *mut u8).cast(), buffer_size, false) 177 } else { 178 (null_mut(), 0, true) 179 }; 180 181 let status = unsafe { 182 (self.mtftp)( 183 self, 184 TftpOpcode::TftpReadFile, 185 buffer_ptr, 186 false, 187 &mut buffer_size, 188 None, 189 server_ip, 190 filename.as_ptr(), 191 None, 192 dont_use_buffer, 193 ) 194 }; 195 status.to_result_with_val(|| buffer_size) 196 } 197 198 /// Writes to a file located on a TFTP server. tftp_write_file( &mut self, server_ip: &IpAddress, filename: &CStr8, overwrite: bool, buffer: &[u8], ) -> Result199 pub fn tftp_write_file( 200 &mut self, 201 server_ip: &IpAddress, 202 filename: &CStr8, 203 overwrite: bool, 204 buffer: &[u8], 205 ) -> Result { 206 let buffer_ptr = (&buffer[0] as *const u8 as *mut u8).cast(); 207 let mut buffer_size = u64::try_from(buffer.len()).expect("buffer length should fit in u64"); 208 209 unsafe { 210 (self.mtftp)( 211 self, 212 TftpOpcode::TftpWriteFile, 213 buffer_ptr, 214 overwrite, 215 &mut buffer_size, 216 None, 217 server_ip, 218 filename.as_ptr(), 219 None, 220 false, 221 ) 222 } 223 .to_result() 224 } 225 226 /// Reads a directory listing of a directory on a TFTP server. tftp_read_dir<'a>( &self, server_ip: &IpAddress, directory_name: &CStr8, buffer: &'a mut [u8], ) -> Result<impl Iterator<Item = core::result::Result<TftpFileInfo<'a>, ReadDirParseError>> + 'a>227 pub fn tftp_read_dir<'a>( 228 &self, 229 server_ip: &IpAddress, 230 directory_name: &CStr8, 231 buffer: &'a mut [u8], 232 ) -> Result<impl Iterator<Item = core::result::Result<TftpFileInfo<'a>, ReadDirParseError>> + 'a> 233 { 234 let buffer_ptr = (&buffer[0] as *const u8 as *mut u8).cast(); 235 let mut buffer_size = u64::try_from(buffer.len()).expect("buffer length should fit in u64"); 236 237 let status = unsafe { 238 (self.mtftp)( 239 self, 240 TftpOpcode::TftpReadDirectory, 241 buffer_ptr, 242 false, 243 &mut buffer_size, 244 None, 245 server_ip, 246 directory_name.as_ptr(), 247 None, 248 false, 249 ) 250 }; 251 status.to_result()?; 252 253 let buffer_size = usize::try_from(buffer_size).expect("buffer length should fit in usize"); 254 let buffer = &buffer[..buffer_size]; 255 256 let mut iterator = buffer.split_inclusive(|b| *b == 0); 257 let mut parse_next = move || { 258 let filename = iterator.next().ok_or(ReadDirParseError)?; 259 if filename == [0] { 260 // This is the final entry. 261 return Ok(None); 262 } 263 let filename = CStr8::from_bytes_with_nul(filename).unwrap(); 264 265 let information_string = iterator.next().ok_or(ReadDirParseError)?; 266 let (_null_terminator, information_string) = information_string.split_last().unwrap(); 267 let information_string = 268 core::str::from_utf8(information_string).map_err(|_| ReadDirParseError)?; 269 270 let (size, rest) = information_string 271 .split_once(' ') 272 .ok_or(ReadDirParseError)?; 273 let (year, rest) = rest.split_once('-').ok_or(ReadDirParseError)?; 274 let (month, rest) = rest.split_once('-').ok_or(ReadDirParseError)?; 275 let (day, rest) = rest.split_once(' ').ok_or(ReadDirParseError)?; 276 let (hour, rest) = rest.split_once(':').ok_or(ReadDirParseError)?; 277 let (minute, second) = rest.split_once(':').ok_or(ReadDirParseError)?; 278 279 let size = size.parse().map_err(|_| ReadDirParseError)?; 280 let year = year.parse().map_err(|_| ReadDirParseError)?; 281 let month = month.parse().map_err(|_| ReadDirParseError)?; 282 let day = day.parse().map_err(|_| ReadDirParseError)?; 283 let hour = hour.parse().map_err(|_| ReadDirParseError)?; 284 let minute = minute.parse().map_err(|_| ReadDirParseError)?; 285 let second = second.parse().map_err(|_| ReadDirParseError)?; 286 287 Ok(Some(TftpFileInfo { 288 filename, 289 size, 290 year, 291 month, 292 day, 293 hour, 294 minute, 295 second, 296 })) 297 }; 298 Ok(from_fn(move || parse_next().transpose()).fuse()) 299 } 300 301 /// Returns the size of a file located on a MTFTP server. mtftp_get_file_size( &mut self, server_ip: &IpAddress, filename: &CStr8, info: &MtftpInfo, ) -> Result<u64>302 pub fn mtftp_get_file_size( 303 &mut self, 304 server_ip: &IpAddress, 305 filename: &CStr8, 306 info: &MtftpInfo, 307 ) -> Result<u64> { 308 let mut buffer_size = 0; 309 310 let status = unsafe { 311 (self.mtftp)( 312 self, 313 TftpOpcode::MtftpGetFileSize, 314 null_mut(), 315 false, 316 &mut buffer_size, 317 None, 318 server_ip, 319 filename.as_ptr(), 320 Some(info), 321 false, 322 ) 323 }; 324 status.to_result_with_val(|| buffer_size) 325 } 326 327 /// Reads a file located on a MTFTP server. mtftp_read_file( &mut self, server_ip: &IpAddress, filename: &CStr8, buffer: Option<&mut [u8]>, info: &MtftpInfo, ) -> Result<u64>328 pub fn mtftp_read_file( 329 &mut self, 330 server_ip: &IpAddress, 331 filename: &CStr8, 332 buffer: Option<&mut [u8]>, 333 info: &MtftpInfo, 334 ) -> Result<u64> { 335 let (buffer_ptr, mut buffer_size, dont_use_buffer) = if let Some(buffer) = buffer { 336 let buffer_size = u64::try_from(buffer.len()).unwrap(); 337 ((&mut buffer[0] as *mut u8).cast(), buffer_size, false) 338 } else { 339 (null_mut(), 0, true) 340 }; 341 342 let status = unsafe { 343 (self.mtftp)( 344 self, 345 TftpOpcode::MtftpReadFile, 346 buffer_ptr, 347 false, 348 &mut buffer_size, 349 None, 350 server_ip, 351 filename.as_ptr(), 352 Some(info), 353 dont_use_buffer, 354 ) 355 }; 356 status.to_result_with_val(|| buffer_size) 357 } 358 359 /// Reads a directory listing of a directory on a MTFTP server. mtftp_read_dir<'a>( &self, server_ip: &IpAddress, buffer: &'a mut [u8], info: &MtftpInfo, ) -> Result<impl Iterator<Item = core::result::Result<MtftpFileInfo<'a>, ReadDirParseError>> + 'a>360 pub fn mtftp_read_dir<'a>( 361 &self, 362 server_ip: &IpAddress, 363 buffer: &'a mut [u8], 364 info: &MtftpInfo, 365 ) -> Result<impl Iterator<Item = core::result::Result<MtftpFileInfo<'a>, ReadDirParseError>> + 'a> 366 { 367 let buffer_ptr = (&buffer[0] as *const u8 as *mut u8).cast(); 368 let mut buffer_size = u64::try_from(buffer.len()).expect("buffer length should fit in u64"); 369 370 let status = unsafe { 371 (self.mtftp)( 372 self, 373 TftpOpcode::MtftpReadDirectory, 374 buffer_ptr, 375 false, 376 &mut buffer_size, 377 None, 378 server_ip, 379 null_mut(), 380 Some(info), 381 false, 382 ) 383 }; 384 status.to_result()?; 385 386 let buffer_size = usize::try_from(buffer_size).expect("buffer length should fit in usize"); 387 let buffer = &buffer[..buffer_size]; 388 389 let mut iterator = buffer.split_inclusive(|b| *b == 0); 390 let mut parse_next = move || { 391 let filename = iterator.next().ok_or(ReadDirParseError)?; 392 if filename == [0] { 393 // This is the final entry. 394 return Ok(None); 395 } 396 let filename = CStr8::from_bytes_with_nul(filename).unwrap(); 397 398 let multicast_ip = iterator.next().ok_or(ReadDirParseError)?; 399 let (_null_terminator, multicast_ip) = multicast_ip.split_last().unwrap(); 400 let multicast_ip = core::str::from_utf8(multicast_ip).map_err(|_| ReadDirParseError)?; 401 let mut octets = multicast_ip.split('.'); 402 let mut buffer = [0; 4]; 403 for b in buffer.iter_mut() { 404 let octet = octets.next().ok_or(ReadDirParseError)?; 405 let octet = octet.parse().map_err(|_| ReadDirParseError)?; 406 *b = octet; 407 } 408 if octets.next().is_some() { 409 // The IP should have exact 4 octets, not more. 410 return Err(ReadDirParseError); 411 } 412 let ip_address = IpAddress::new_v4(buffer); 413 414 let information_string = iterator.next().ok_or(ReadDirParseError)?; 415 let (_null_terminator, information_string) = information_string.split_last().unwrap(); 416 let information_string = 417 core::str::from_utf8(information_string).map_err(|_| ReadDirParseError)?; 418 419 let (size, rest) = information_string 420 .split_once(' ') 421 .ok_or(ReadDirParseError)?; 422 let (year, rest) = rest.split_once('-').ok_or(ReadDirParseError)?; 423 let (month, rest) = rest.split_once('-').ok_or(ReadDirParseError)?; 424 let (day, rest) = rest.split_once(' ').ok_or(ReadDirParseError)?; 425 let (hour, rest) = rest.split_once(':').ok_or(ReadDirParseError)?; 426 let (minute, second) = rest.split_once(':').ok_or(ReadDirParseError)?; 427 428 let size = size.parse().map_err(|_| ReadDirParseError)?; 429 let year = year.parse().map_err(|_| ReadDirParseError)?; 430 let month = month.parse().map_err(|_| ReadDirParseError)?; 431 let day = day.parse().map_err(|_| ReadDirParseError)?; 432 let hour = hour.parse().map_err(|_| ReadDirParseError)?; 433 let minute = minute.parse().map_err(|_| ReadDirParseError)?; 434 let second = second.parse().map_err(|_| ReadDirParseError)?; 435 436 Ok(Some(MtftpFileInfo { 437 filename, 438 ip_address, 439 size, 440 year, 441 month, 442 day, 443 hour, 444 minute, 445 second, 446 })) 447 }; 448 Ok(from_fn(move || parse_next().transpose()).fuse()) 449 } 450 451 /// Writes a UDP packet to the network interface. 452 #[allow(clippy::too_many_arguments)] udp_write( &mut self, op_flags: UdpOpFlags, dest_ip: &IpAddress, dest_port: u16, gateway_ip: Option<&IpAddress>, src_ip: Option<&IpAddress>, src_port: Option<&mut u16>, header: Option<&[u8]>, buffer: &[u8], ) -> Result453 pub fn udp_write( 454 &mut self, 455 op_flags: UdpOpFlags, 456 dest_ip: &IpAddress, 457 dest_port: u16, 458 gateway_ip: Option<&IpAddress>, 459 src_ip: Option<&IpAddress>, 460 src_port: Option<&mut u16>, 461 header: Option<&[u8]>, 462 buffer: &[u8], 463 ) -> Result { 464 let header_size_tmp; 465 let (header_size, header_ptr) = if let Some(header) = header { 466 header_size_tmp = header.len(); 467 (Some(&header_size_tmp), (&header[0] as *const u8).cast()) 468 } else { 469 (None, null()) 470 }; 471 472 unsafe { 473 (self.udp_write)( 474 self, 475 op_flags, 476 dest_ip, 477 &dest_port, 478 gateway_ip, 479 src_ip, 480 src_port, 481 header_size, 482 header_ptr, 483 &buffer.len(), 484 (&buffer[0] as *const u8).cast(), 485 ) 486 } 487 .to_result() 488 } 489 490 /// Reads a UDP packet from the network interface. 491 #[allow(clippy::too_many_arguments)] udp_read( &mut self, op_flags: UdpOpFlags, dest_ip: Option<&mut IpAddress>, dest_port: Option<&mut u16>, src_ip: Option<&mut IpAddress>, src_port: Option<&mut u16>, header: Option<&mut [u8]>, buffer: &mut [u8], ) -> Result<usize>492 pub fn udp_read( 493 &mut self, 494 op_flags: UdpOpFlags, 495 dest_ip: Option<&mut IpAddress>, 496 dest_port: Option<&mut u16>, 497 src_ip: Option<&mut IpAddress>, 498 src_port: Option<&mut u16>, 499 header: Option<&mut [u8]>, 500 buffer: &mut [u8], 501 ) -> Result<usize> { 502 let header_size_tmp; 503 let (header_size, header_ptr) = if let Some(header) = header { 504 header_size_tmp = header.len(); 505 (Some(&header_size_tmp), (&mut header[0] as *mut u8).cast()) 506 } else { 507 (None, null_mut()) 508 }; 509 510 let mut buffer_size = buffer.len(); 511 512 let status = unsafe { 513 (self.udp_read)( 514 self, 515 op_flags, 516 dest_ip, 517 dest_port, 518 src_ip, 519 src_port, 520 header_size, 521 header_ptr, 522 &mut buffer_size, 523 (&mut buffer[0] as *mut u8).cast(), 524 ) 525 }; 526 status.to_result_with_val(|| buffer_size) 527 } 528 529 /// Updates the IP receive filters of a network device and enables software 530 /// filtering. set_ip_filter(&mut self, new_filter: &IpFilter) -> Result531 pub fn set_ip_filter(&mut self, new_filter: &IpFilter) -> Result { 532 (self.set_ip_filter)(self, new_filter).to_result() 533 } 534 535 /// Uses the ARP protocol to resolve a MAC address. arp(&mut self, ip_addr: &IpAddress, mac_addr: Option<&mut MacAddress>) -> Result536 pub fn arp(&mut self, ip_addr: &IpAddress, mac_addr: Option<&mut MacAddress>) -> Result { 537 (self.arp)(self, ip_addr, mac_addr).to_result() 538 } 539 540 /// Updates the parameters that affect the operation of the PXE Base Code 541 /// Protocol. set_parameters( &mut self, new_auto_arp: Option<bool>, new_send_guid: Option<bool>, new_ttl: Option<u8>, new_tos: Option<u8>, new_make_callback: Option<bool>, ) -> Result542 pub fn set_parameters( 543 &mut self, 544 new_auto_arp: Option<bool>, 545 new_send_guid: Option<bool>, 546 new_ttl: Option<u8>, 547 new_tos: Option<u8>, 548 new_make_callback: Option<bool>, 549 ) -> Result { 550 (self.set_parameters)( 551 self, 552 new_auto_arp.as_ref(), 553 new_send_guid.as_ref(), 554 new_ttl.as_ref(), 555 new_tos.as_ref(), 556 new_make_callback.as_ref(), 557 ) 558 .to_result() 559 } 560 561 /// Updates the station IP address and/or subnet mask values of a network 562 /// device. set_station_ip( &mut self, new_station_ip: Option<&IpAddress>, new_subnet_mask: Option<&IpAddress>, ) -> Result563 pub fn set_station_ip( 564 &mut self, 565 new_station_ip: Option<&IpAddress>, 566 new_subnet_mask: Option<&IpAddress>, 567 ) -> Result { 568 (self.set_station_ip)(self, new_station_ip, new_subnet_mask).to_result() 569 } 570 571 /// Updates the contents of the cached DHCP and Discover packets. 572 #[allow(clippy::too_many_arguments)] set_packets( &mut self, new_dhcp_discover_valid: Option<bool>, new_dhcp_ack_received: Option<bool>, new_proxy_offer_received: Option<bool>, new_pxe_discover_valid: Option<bool>, new_pxe_reply_received: Option<bool>, new_pxe_bis_reply_received: Option<bool>, new_dhcp_discover: Option<&Packet>, new_dhcp_ack: Option<&Packet>, new_proxy_offer: Option<&Packet>, new_pxe_discover: Option<&Packet>, new_pxe_reply: Option<&Packet>, new_pxe_bis_reply: Option<&Packet>, ) -> Result573 pub fn set_packets( 574 &mut self, 575 new_dhcp_discover_valid: Option<bool>, 576 new_dhcp_ack_received: Option<bool>, 577 new_proxy_offer_received: Option<bool>, 578 new_pxe_discover_valid: Option<bool>, 579 new_pxe_reply_received: Option<bool>, 580 new_pxe_bis_reply_received: Option<bool>, 581 new_dhcp_discover: Option<&Packet>, 582 new_dhcp_ack: Option<&Packet>, 583 new_proxy_offer: Option<&Packet>, 584 new_pxe_discover: Option<&Packet>, 585 new_pxe_reply: Option<&Packet>, 586 new_pxe_bis_reply: Option<&Packet>, 587 ) -> Result { 588 (self.set_packets)( 589 self, 590 new_dhcp_discover_valid.as_ref(), 591 new_dhcp_ack_received.as_ref(), 592 new_proxy_offer_received.as_ref(), 593 new_pxe_discover_valid.as_ref(), 594 new_pxe_reply_received.as_ref(), 595 new_pxe_bis_reply_received.as_ref(), 596 new_dhcp_discover, 597 new_dhcp_ack, 598 new_proxy_offer, 599 new_pxe_discover, 600 new_pxe_reply, 601 new_pxe_bis_reply, 602 ) 603 .to_result() 604 } 605 606 /// Returns a reference to the `Mode` struct. 607 #[must_use] mode(&self) -> &Mode608 pub const fn mode(&self) -> &Mode { 609 unsafe { &*self.mode } 610 } 611 } 612 613 /// A type of bootstrap to perform in [`BaseCode::discover`]. 614 /// 615 /// Corresponds to the `EFI_PXE_BASE_CODE_BOOT_` constants in the C API. 616 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 617 #[repr(u16)] 618 #[allow(missing_docs)] 619 pub enum BootstrapType { 620 Bootstrap = 0, 621 MsWinntRis = 1, 622 IntelLcm = 2, 623 DosUndi = 3, 624 NecEsmpro = 4, 625 IbmWsoD = 5, 626 IbmLccm = 6, 627 CaUnicenterTng = 7, 628 HpOpenview = 8, 629 Altiris9 = 9, 630 Altiris10 = 10, 631 Altiris11 = 11, 632 // NOT_USED_12 = 12, 633 RedhatInstall = 13, 634 RedhatBoot = 14, 635 Rembo = 15, 636 Beoboot = 16, 637 // 638 // Values 17 through 32767 are reserved. 639 // Values 32768 through 65279 are for vendor use. 640 // Values 65280 through 65534 are reserved. 641 // 642 PxeTest = 65535, 643 } 644 645 opaque_type! { 646 /// Opaque type that should be used to represent a pointer to a [`DiscoverInfo`] in 647 /// foreign function interfaces. This type produces a thin pointer, unlike 648 /// [`DiscoverInfo`]. 649 pub struct FfiDiscoverInfo; 650 } 651 652 /// This struct contains optional parameters for [`BaseCode::discover`]. 653 /// 654 /// Corresponds to the `EFI_PXE_BASE_CODE_DISCOVER_INFO` type in the C API. 655 #[repr(C)] 656 #[derive(Debug, Pointee)] 657 pub struct DiscoverInfo { 658 use_m_cast: bool, 659 use_b_cast: bool, 660 use_u_cast: bool, 661 must_use_list: bool, 662 server_m_cast_ip: IpAddress, 663 ip_cnt: u16, 664 srv_list: [Server], 665 } 666 667 impl DiscoverInfo { 668 /// Create a `DiscoverInfo`. new_in_buffer<'buf>( buffer: &'buf mut [MaybeUninit<u8>], use_m_cast: bool, use_b_cast: bool, use_u_cast: bool, must_use_list: bool, server_m_cast_ip: IpAddress, srv_list: &[Server], ) -> Result<&'buf mut Self>669 pub fn new_in_buffer<'buf>( 670 buffer: &'buf mut [MaybeUninit<u8>], 671 use_m_cast: bool, 672 use_b_cast: bool, 673 use_u_cast: bool, 674 must_use_list: bool, 675 server_m_cast_ip: IpAddress, 676 srv_list: &[Server], 677 ) -> Result<&'buf mut Self> { 678 let server_count = srv_list.len(); 679 assert!(server_count <= u16::MAX as usize, "too many servers"); 680 681 let required_size = core::mem::size_of::<bool>() * 4 682 + core::mem::size_of::<IpAddress>() 683 + core::mem::size_of::<u16>() 684 + core::mem::size_of_val(srv_list); 685 686 if buffer.len() < required_size { 687 return Err(Status::BUFFER_TOO_SMALL.into()); 688 } 689 690 let mut ptr: *mut u8 = maybe_uninit_slice_as_mut_ptr(buffer); 691 unsafe { 692 ptr_write_unaligned_and_add(&mut ptr, use_m_cast); 693 ptr_write_unaligned_and_add(&mut ptr, use_b_cast); 694 ptr_write_unaligned_and_add(&mut ptr, use_u_cast); 695 ptr_write_unaligned_and_add(&mut ptr, must_use_list); 696 ptr_write_unaligned_and_add(&mut ptr, server_m_cast_ip); 697 ptr_write_unaligned_and_add(&mut ptr, server_count as u16); 698 699 ptr = ptr.add(2); // Align server list (4-byte alignment). 700 core::ptr::copy(srv_list.as_ptr(), ptr.cast(), server_count); 701 702 let ptr: *mut Self = 703 ptr_meta::from_raw_parts_mut(buffer.as_mut_ptr().cast(), server_count); 704 Ok(&mut *ptr) 705 } 706 } 707 } 708 709 impl DiscoverInfo { 710 /// Returns whether discovery should use multicast. 711 #[must_use] use_m_cast(&self) -> bool712 pub const fn use_m_cast(&self) -> bool { 713 self.use_m_cast 714 } 715 716 /// Returns whether discovery should use broadcast. 717 #[must_use] use_b_cast(&self) -> bool718 pub const fn use_b_cast(&self) -> bool { 719 self.use_b_cast 720 } 721 722 /// Returns whether discovery should use unicast. 723 #[must_use] use_u_cast(&self) -> bool724 pub const fn use_u_cast(&self) -> bool { 725 self.use_u_cast 726 } 727 728 /// Returns whether discovery should only accept boot servers in the server 729 /// list (boot server verification). 730 #[must_use] must_use_list(&self) -> bool731 pub const fn must_use_list(&self) -> bool { 732 self.must_use_list 733 } 734 735 /// Returns the address used in multicast discovery. 736 #[must_use] server_m_cast_ip(&self) -> &IpAddress737 pub const fn server_m_cast_ip(&self) -> &IpAddress { 738 &self.server_m_cast_ip 739 } 740 741 /// Returns the amount of Boot Server. 742 #[must_use] ip_cnt(&self) -> u16743 pub const fn ip_cnt(&self) -> u16 { 744 self.ip_cnt 745 } 746 747 /// Returns the Boot Server list used for unicast discovery or boot server 748 /// verification. 749 #[must_use] srv_list(&self) -> &[Server]750 pub const fn srv_list(&self) -> &[Server] { 751 &self.srv_list 752 } 753 } 754 755 /// An entry in the Boot Server list 756 /// 757 /// Corresponds to the `EFI_PXE_BASE_CODE_SRVLIST` type in the C API. 758 #[repr(C)] 759 #[derive(Debug)] 760 pub struct Server { 761 /// The type of Boot Server reply 762 pub ty: u16, 763 accept_any_response: bool, 764 _reserved: u8, 765 /// The IP address of the server 766 ip_addr: IpAddress, 767 } 768 769 impl Server { 770 /// Construct a `Server` for a Boot Server reply type. If `ip_addr` is not 771 /// `None` only Boot Server replies with matching the IP address will be 772 /// accepted. 773 #[must_use] new(ty: u16, ip_addr: Option<IpAddress>) -> Self774 pub fn new(ty: u16, ip_addr: Option<IpAddress>) -> Self { 775 Self { 776 ty, 777 accept_any_response: ip_addr.is_none(), 778 _reserved: 0, 779 ip_addr: ip_addr.unwrap_or(IpAddress([0; 16])), 780 } 781 } 782 783 /// Returns a `None` if the any response should be accepted or the IP 784 /// address of a Boot Server whose responses should be accepted. 785 #[must_use] ip_addr(&self) -> Option<&IpAddress>786 pub const fn ip_addr(&self) -> Option<&IpAddress> { 787 if self.accept_any_response { 788 None 789 } else { 790 Some(&self.ip_addr) 791 } 792 } 793 } 794 795 /// Corresponds to the `EFI_PXE_BASE_CODE_TFTP_OPCODE` type in the C API. 796 #[repr(C)] 797 enum TftpOpcode { 798 TftpGetFileSize = 1, 799 TftpReadFile, 800 TftpWriteFile, 801 TftpReadDirectory, 802 MtftpGetFileSize, 803 MtftpReadFile, 804 MtftpReadDirectory, 805 } 806 807 /// MTFTP connection parameters 808 /// 809 /// Corresponds to the `EFI_PXE_BASE_CODE_MTFTP_INFO` type in the C API. 810 #[derive(Clone, Copy, Debug)] 811 #[repr(C)] 812 pub struct MtftpInfo { 813 /// File multicast IP address. This is the IP address to which the server 814 /// will send the requested file. 815 pub m_cast_ip: IpAddress, 816 /// Client multicast listening port. This is the UDP port to which the 817 /// server will send the requested file. 818 pub c_port: u16, 819 /// Server multicast listening port. This is the UDP port on which the 820 /// server listens for multicast open requests and data acks. 821 pub s_port: u16, 822 /// The number of seconds a client should listen for an active multicast 823 /// session before requesting a new multicast session. 824 pub listen_timeout: u16, 825 /// The number of seconds a client should wait for a packet from the server 826 /// before retransmitting the previous open request or data ack packet. 827 pub transmit_timeout: u16, 828 } 829 830 // No corresponding type in the UEFI spec, it just uses UINT16. 831 bitflags! { 832 /// Flags for UDP read and write operations. 833 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] 834 #[repr(transparent)] 835 pub struct UdpOpFlags: u16 { 836 /// Receive a packet sent from any IP address in UDP read operations. 837 const ANY_SRC_IP = 0x0001; 838 /// Receive a packet sent from any UDP port in UDP read operations. If 839 /// the source port is no specified in UDP write operations, the 840 /// source port will be automatically selected. 841 const ANY_SRC_PORT = 0x0002; 842 /// Receive a packet sent to any IP address in UDP read operations. 843 const ANY_DEST_IP = 0x0004; 844 /// Receive a packet sent to any UDP port in UDP read operations. 845 const ANY_DEST_PORT = 0x0008; 846 /// The software filter is used in UDP read operations. 847 const USE_FILTER = 0x0010; 848 /// If required, a UDP write operation may be broken up across multiple packets. 849 const MAY_FRAGMENT = 0x0020; 850 } 851 } 852 853 /// IP receive filter settings 854 /// 855 /// Corresponds to the `EFI_PXE_BASE_CODE_IP_FILTER` type in the C API. 856 #[repr(C)] 857 #[derive(Debug)] 858 pub struct IpFilter { 859 /// A set of filters. 860 pub filters: IpFilters, 861 ip_cnt: u8, 862 _reserved: u16, 863 ip_list: [IpAddress; 8], 864 } 865 866 impl IpFilter { 867 /// Construct a new `IpFilter`. 868 /// 869 /// # Panics 870 /// 871 /// Panics if `ip_list` contains more than 8 entries. 872 #[must_use] new(filters: IpFilters, ip_list: &[IpAddress]) -> Self873 pub fn new(filters: IpFilters, ip_list: &[IpAddress]) -> Self { 874 assert!(ip_list.len() <= 8); 875 876 let ip_cnt = ip_list.len() as u8; 877 let mut buffer = [IpAddress([0; 16]); 8]; 878 buffer[..ip_list.len()].copy_from_slice(ip_list); 879 880 Self { 881 filters, 882 ip_cnt, 883 _reserved: 0, 884 ip_list: buffer, 885 } 886 } 887 888 /// A list of IP addresses other than the Station Ip that should be 889 /// enabled. Maybe be multicast or unicast. 890 #[must_use] ip_list(&self) -> &[IpAddress]891 pub fn ip_list(&self) -> &[IpAddress] { 892 &self.ip_list[..usize::from(self.ip_cnt)] 893 } 894 } 895 896 bitflags! { 897 /// IP receive filters. 898 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] 899 #[repr(transparent)] 900 pub struct IpFilters: u8 { 901 /// Enable the Station IP address. 902 const STATION_IP = 0x01; 903 /// Enable IPv4 broadcast addresses. 904 const BROADCAST = 0x02; 905 /// Enable all addresses. 906 const PROMISCUOUS = 0x04; 907 /// Enable all multicast addresses. 908 const PROMISCUOUS_MULTICAST = 0x08; 909 } 910 } 911 912 /// A network packet. 913 /// 914 /// Corresponds to the `EFI_PXE_BASE_CODE_PACKET` type in the C API. 915 #[repr(C)] 916 pub union Packet { 917 raw: [u8; 1472], 918 dhcpv4: DhcpV4Packet, 919 dhcpv6: DhcpV6Packet, 920 } 921 922 impl Debug for Packet { fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result923 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 924 write!(f, "<binary data>") 925 } 926 } 927 928 impl AsRef<[u8; 1472]> for Packet { as_ref(&self) -> &[u8; 1472]929 fn as_ref(&self) -> &[u8; 1472] { 930 unsafe { &self.raw } 931 } 932 } 933 934 impl AsRef<DhcpV4Packet> for Packet { as_ref(&self) -> &DhcpV4Packet935 fn as_ref(&self) -> &DhcpV4Packet { 936 unsafe { &self.dhcpv4 } 937 } 938 } 939 940 impl AsRef<DhcpV6Packet> for Packet { as_ref(&self) -> &DhcpV6Packet941 fn as_ref(&self) -> &DhcpV6Packet { 942 unsafe { &self.dhcpv6 } 943 } 944 } 945 946 /// A Dhcpv4 Packet. 947 /// 948 /// Corresponds to the `EFI_PXE_BASE_CODE_DHCPV4_PACKET` type in the C API. 949 #[repr(C)] 950 #[derive(Clone, Copy, Debug)] 951 pub struct DhcpV4Packet { 952 /// Packet op code / message type. 953 pub bootp_opcode: u8, 954 /// Hardware address type. 955 pub bootp_hw_type: u8, 956 /// Hardware address length. 957 pub bootp_hw_addr_len: u8, 958 /// Client sets to zero, optionally used by gateways in cross-gateway booting. 959 pub bootp_gate_hops: u8, 960 bootp_ident: u32, 961 bootp_seconds: u16, 962 bootp_flags: u16, 963 /// Client IP address, filled in by client in bootrequest if known. 964 pub bootp_ci_addr: [u8; 4], 965 /// 'your' (client) IP address; filled by server if client doesn't know its own address (`bootp_ci_addr` was 0). 966 pub bootp_yi_addr: [u8; 4], 967 /// Server IP address, returned in bootreply by server. 968 pub bootp_si_addr: [u8; 4], 969 /// Gateway IP address, used in optional cross-gateway booting. 970 pub bootp_gi_addr: [u8; 4], 971 /// Client hardware address, filled in by client. 972 pub bootp_hw_addr: [u8; 16], 973 /// Optional server host name, null terminated string. 974 pub bootp_srv_name: [u8; 64], 975 /// Boot file name, null terminated string, 'generic' name or null in 976 /// bootrequest, fully qualified directory-path name in bootreply. 977 pub bootp_boot_file: [u8; 128], 978 dhcp_magik: u32, 979 /// Optional vendor-specific area, e.g. could be hardware type/serial on request, or 'capability' / remote file system handle on reply. This info may be set aside for use by a third phase bootstrap or kernel. 980 pub dhcp_options: [u8; 56], 981 } 982 983 impl DhcpV4Packet { 984 /// The expected value for [`Self::dhcp_magik`]. 985 pub const DHCP_MAGIK: u32 = 0x63825363; 986 987 /// Transaction ID, a random number, used to match this boot request with the responses it generates. 988 #[must_use] bootp_ident(&self) -> u32989 pub const fn bootp_ident(&self) -> u32 { 990 u32::from_be(self.bootp_ident) 991 } 992 993 /// Filled in by client, seconds elapsed since client started trying to boot. 994 #[must_use] bootp_seconds(&self) -> u16995 pub const fn bootp_seconds(&self) -> u16 { 996 u16::from_be(self.bootp_seconds) 997 } 998 999 /// The flags. 1000 #[must_use] bootp_flags(&self) -> DhcpV4Flags1001 pub const fn bootp_flags(&self) -> DhcpV4Flags { 1002 DhcpV4Flags::from_bits_truncate(u16::from_be(self.bootp_flags)) 1003 } 1004 1005 /// A magic cookie, should be [`Self::DHCP_MAGIK`]. 1006 #[must_use] dhcp_magik(&self) -> u321007 pub const fn dhcp_magik(&self) -> u32 { 1008 u32::from_be(self.dhcp_magik) 1009 } 1010 } 1011 1012 bitflags! { 1013 /// Represents the 'flags' field for a [`DhcpV4Packet`]. 1014 #[repr(transparent)] 1015 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] 1016 pub struct DhcpV4Flags: u16 { 1017 /// Should be set when the client cannot receive unicast IP datagrams 1018 /// until its protocol software has been configured with an IP address. 1019 const BROADCAST = 1; 1020 } 1021 } 1022 1023 /// A Dhcpv6 Packet. 1024 /// 1025 /// Corresponds to the `EFI_PXE_BASE_CODE_DHCPV6_PACKET` type in the C API. 1026 #[repr(C)] 1027 #[derive(Clone, Copy, Debug)] 1028 pub struct DhcpV6Packet { 1029 /// The message type. 1030 pub message_type: u8, 1031 transaction_id: [u8; 3], 1032 /// A byte array containing dhcp options. 1033 pub dhcp_options: [u8; 1024], 1034 } 1035 1036 impl DhcpV6Packet { 1037 /// The transaction id. 1038 #[must_use] transaction_id(&self) -> u321039 pub fn transaction_id(&self) -> u32 { 1040 u32::from(self.transaction_id[0]) << 16 1041 | u32::from(self.transaction_id[1]) << 8 1042 | u32::from(self.transaction_id[2]) 1043 } 1044 } 1045 1046 /// The data values in this structure are read-only and are updated by the 1047 /// [`BaseCode`]. 1048 /// 1049 /// Corresponds to the `EFI_PXE_BASE_CODE_MODE` type in the C API. 1050 #[repr(C)] 1051 #[derive(Debug)] 1052 pub struct Mode { 1053 /// `true` if this device has been started by calling [`BaseCode::start`]. 1054 /// This field is set to `true` by [`BaseCode::start`] and to `false` by 1055 /// the [`BaseCode::stop`] function. 1056 pub started: bool, 1057 /// `true` if the UNDI protocol supports IPv6 1058 pub ipv6_available: bool, 1059 /// `true` if this PXE Base Code Protocol implementation supports IPv6. 1060 pub ipv6_supported: bool, 1061 /// `true` if this device is currently using IPv6. This field is set by 1062 /// [`BaseCode::start`]. 1063 pub using_ipv6: bool, 1064 /// `true` if this PXE Base Code implementation supports Boot Integrity 1065 /// Services (BIS). This field is set by [`BaseCode::start`]. 1066 pub bis_supported: bool, 1067 /// `true` if this device and the platform support Boot Integrity Services 1068 /// (BIS). This field is set by [`BaseCode::start`]. 1069 pub bis_detected: bool, 1070 /// `true` for automatic ARP packet generation, `false` otherwise. This 1071 /// field is initialized to `true` by [`BaseCode::start`] and can be 1072 /// modified with [`BaseCode::set_parameters`]. 1073 pub auto_arp: bool, 1074 /// This field is used to change the Client Hardware Address (chaddr) field 1075 /// in the DHCP and Discovery packets. Set to `true` to send the SystemGuid 1076 /// (if one is available). Set to `false` to send the client NIC MAC 1077 /// address. This field is initialized to `false` by [`BaseCode::start`] 1078 /// and can be modified with [`BaseCode::set_parameters`]. 1079 pub send_guid: bool, 1080 /// This field is initialized to `false` by [`BaseCode::start`] and set to 1081 /// `true` when [`BaseCode::dhcp`] completes successfully. When `true`, 1082 /// [`Self::dhcp_discover`] is valid. This field can also be changed by 1083 /// [`BaseCode::set_packets`]. 1084 pub dhcp_discover_valid: bool, 1085 /// This field is initialized to `false` by [`BaseCode::start`] and set to 1086 /// `true` when [`BaseCode::dhcp`] completes successfully. When `true`, 1087 /// [`Self::dhcp_ack`] is valid. This field can also be changed by 1088 /// [`BaseCode::set_packets`]. 1089 pub dhcp_ack_received: bool, 1090 /// This field is initialized to `false` by [`BaseCode::start`] and set to 1091 /// `true` when [`BaseCode::dhcp`] completes successfully and a proxy DHCP 1092 /// offer packet was received. When `true`, [`Self::proxy_offer`] is valid. 1093 /// This field can also be changed by [`BaseCode::set_packets`]. 1094 pub proxy_offer_received: bool, 1095 /// When `true`, [`Self::pxe_discover`] is valid. This field is set to 1096 /// `false` by [`BaseCode::start`] and [`BaseCode::dhcp`], and can be set 1097 /// to `true` or `false` by [`BaseCode::discover`] and 1098 /// [`BaseCode::set_packets`]. 1099 pub pxe_discover_valid: bool, 1100 /// When `true`, [`Self::pxe_reply`] is valid. This field is set to `false` 1101 /// by [`BaseCode::start`] and [`BaseCode::dhcp`], and can be set to `true` 1102 /// or `false` by [`BaseCode::discover`] and [`BaseCode::set_packets`]. 1103 pub pxe_reply_received: bool, 1104 /// When `true`, [`Self::pxe_bis_reply`] is valid. This field is set to 1105 /// `false` by [`BaseCode::start`] and [`BaseCode::dhcp`], and can be set 1106 /// to `true` or `false` by the [`BaseCode::discover`] and 1107 /// [`BaseCode::set_packets`]. 1108 pub pxe_bis_reply_received: bool, 1109 /// Indicates whether [`Self::icmp_error`] has been updated. This field is 1110 /// reset to `false` by [`BaseCode::start`], [`BaseCode::dhcp`], 1111 /// [`BaseCode::discover`],[`BaseCode::udp_read`], [`BaseCode::udp_write`], 1112 /// [`BaseCode::arp`] and any of the TFTP/MTFTP operations. If an ICMP 1113 /// error is received, this field will be set to `true` after 1114 /// [`Self::icmp_error`] is updated. 1115 pub icmp_error_received: bool, 1116 /// Indicates whether [`Self::tftp_error`] has been updated. This field is 1117 /// reset to `false` by [`BaseCode::start`] and any of the TFTP/MTFTP 1118 /// operations. If a TFTP error is received, this field will be set to 1119 /// `true` after [`Self::tftp_error`] is updated. 1120 pub tftp_error_received: bool, 1121 /// When `false`, callbacks will not be made. When `true`, make callbacks 1122 /// to the PXE Base Code Callback Protocol. This field is reset to `false` 1123 /// by [`BaseCode::start`] if the PXE Base Code Callback Protocol is not 1124 /// available. It is reset to `true` by [`BaseCode::start`] if the PXE Base 1125 /// Code Callback Protocol is available. 1126 pub make_callbacks: bool, 1127 /// The "time to live" field of the IP header. This field is initialized to 1128 /// `16` by [`BaseCode::start`] and can be modified by 1129 /// [`BaseCode::set_parameters`]. 1130 pub ttl: u8, 1131 /// The type of service field of the IP header. This field is initialized 1132 /// to `0` by [`BaseCode::start`], and can be modified with 1133 /// [`BaseCode::set_parameters`]. 1134 pub tos: u8, 1135 /// The device’s current IP address. This field is initialized to a zero 1136 /// address by Start(). This field is set when [`BaseCode::dhcp`] completes 1137 /// successfully. This field can also be set by 1138 /// [`BaseCode::set_station_ip`]. This field must be set to a valid IP 1139 /// address by either [`BaseCode::dhcp`] or [`BaseCode::set_station_ip`] 1140 /// before [`BaseCode::discover`], [`BaseCode::udp_read`], 1141 /// [`BaseCode::udp_write`], [`BaseCode::arp`] and any of the TFTP/MTFTP 1142 /// operations are called. 1143 pub station_ip: IpAddress, 1144 /// The device's current subnet mask. This field is initialized to a zero 1145 /// address by [`BaseCode::start`]. This field is set when 1146 /// [`BaseCode::dhcp`] completes successfully. This field can also be set 1147 /// by [`BaseCode::set_station_ip`]. This field must be set to a valid 1148 /// subnet mask by either [`BaseCode::dhcp`] or 1149 /// [`BaseCode::set_station_ip`] before [`BaseCode::discover`], 1150 /// [`BaseCode::udp_read`], [`BaseCode::udp_write`], 1151 /// [`BaseCode::arp`] or any of the TFTP/MTFTP operations are called. 1152 pub subnet_mask: IpAddress, 1153 /// Cached DHCP Discover packet. This field is zero-filled by the 1154 /// [`BaseCode::start`] function, and is set when [`BaseCode::dhcp`] 1155 /// completes successfully. The contents of this field can replaced by 1156 /// [`BaseCode::set_packets`]. 1157 pub dhcp_discover: Packet, 1158 /// Cached DHCP Ack packet. This field is zero-filled by 1159 /// [`BaseCode::start`], and is set when [`BaseCode::dhcp`] completes 1160 /// successfully. The contents of this field can be replaced by 1161 /// [`BaseCode::set_packets`]. 1162 pub dhcp_ack: Packet, 1163 /// Cached Proxy Offer packet. This field is zero-filled by 1164 /// [`BaseCode::start`], and is set when [`BaseCode::dhcp`] completes 1165 /// successfully. The contents of this field can be replaced by 1166 /// [`BaseCode::set_packets`]. 1167 pub proxy_offer: Packet, 1168 /// Cached PXE Discover packet. This field is zero-filled by 1169 /// [`BaseCode::start`], and is set when [`BaseCode::discover`] completes 1170 /// successfully. The contents of this field can be replaced by 1171 /// [`BaseCode::set_packets`]. 1172 pub pxe_discover: Packet, 1173 /// Cached PXE Reply packet. This field is zero-filled by 1174 /// [`BaseCode::start`], and is set when [`BaseCode::discover`] completes 1175 /// successfully. The contents of this field can be replaced by the 1176 /// [`BaseCode::set_packets`] function. 1177 pub pxe_reply: Packet, 1178 /// Cached PXE BIS Reply packet. This field is zero-filled by 1179 /// [`BaseCode::start`], and is set when [`BaseCode::discover`] completes 1180 /// successfully. This field can be replaced by [`BaseCode::set_packets`]. 1181 pub pxe_bis_reply: Packet, 1182 /// The current IP receive filter settings. The receive filter is disabled 1183 /// and the number of IP receive filters is set to zero by 1184 /// [`BaseCode::start`], and is set by [`BaseCode::set_ip_filter`]. 1185 pub ip_filter: IpFilter, 1186 /// The number of valid entries in the ARP cache. This field is reset to 1187 /// zero by [`BaseCode::start`]. 1188 pub arp_cache_entries: u32, 1189 /// Array of cached ARP entries. 1190 pub arp_cache: [ArpEntry; 8], 1191 /// The number of valid entries in the current route table. This field is 1192 /// reset to zero by [`BaseCode::start`]. 1193 pub route_table_entries: u32, 1194 /// Array of route table entries. 1195 pub route_table: [RouteEntry; 8], 1196 /// ICMP error packet. This field is updated when an ICMP error is received 1197 /// and is undefined until the first ICMP error is received. This field is 1198 /// zero-filled by [`BaseCode::start`]. 1199 pub icmp_error: IcmpError, 1200 /// TFTP error packet. This field is updated when a TFTP error is received 1201 /// and is undefined until the first TFTP error is received. This field is 1202 /// zero-filled by the [`BaseCode::start`] function. 1203 pub tftp_error: TftpError, 1204 } 1205 1206 /// An entry for the ARP cache found in [`Mode::arp_cache`] 1207 /// 1208 /// Corresponds to the `EFI_PXE_BASE_CODE_ARP_ENTRY` type in the C API. 1209 #[repr(C)] 1210 #[derive(Debug)] 1211 pub struct ArpEntry { 1212 /// The IP address. 1213 pub ip_addr: IpAddress, 1214 /// The mac address of the device that is addressed by [`Self::ip_addr`]. 1215 pub mac_addr: MacAddress, 1216 } 1217 1218 /// An entry for the route table found in [`Mode::route_table`] 1219 /// 1220 /// Corresponds to the `EFI_PXE_BASE_CODE_ROUTE_ENTRY` type in the C API. 1221 #[repr(C)] 1222 #[allow(missing_docs)] 1223 #[derive(Debug)] 1224 pub struct RouteEntry { 1225 pub ip_addr: IpAddress, 1226 pub subnet_mask: IpAddress, 1227 pub gw_addr: IpAddress, 1228 } 1229 1230 /// An ICMP error packet. 1231 /// 1232 /// Corresponds to the `EFI_PXE_BASE_CODE_ICMP_ERROR` type in the C API. 1233 #[repr(C)] 1234 #[allow(missing_docs)] 1235 #[derive(Debug)] 1236 pub struct IcmpError { 1237 pub ty: u8, 1238 pub code: u8, 1239 pub checksum: u16, 1240 pub u: IcmpErrorUnion, 1241 pub data: [u8; 494], 1242 } 1243 1244 impl Display for IcmpError { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result1245 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 1246 write!(f, "{self:?}") 1247 } 1248 } 1249 1250 #[cfg(feature = "unstable")] 1251 impl core::error::Error for IcmpError {} 1252 1253 /// Corresponds to the anonymous union inside 1254 /// `EFI_PXE_BASE_CODE_ICMP_ERROR` in the C API. 1255 #[repr(C)] 1256 #[allow(missing_docs)] 1257 pub union IcmpErrorUnion { 1258 pub reserved: u32, 1259 pub mtu: u32, 1260 pub pointer: u32, 1261 pub echo: IcmpErrorEcho, 1262 } 1263 1264 impl Debug for IcmpErrorUnion { fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result1265 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 1266 write!(f, "<binary data>") 1267 } 1268 } 1269 1270 /// Corresponds to the `Echo` field in the anonymous union inside 1271 /// `EFI_PXE_BASE_CODE_ICMP_ERROR` in the C API. 1272 #[repr(C)] 1273 #[derive(Clone, Copy, Debug)] 1274 #[allow(missing_docs)] 1275 pub struct IcmpErrorEcho { 1276 pub identifier: u16, 1277 pub sequence: u16, 1278 } 1279 1280 /// A TFTP error packet. 1281 /// 1282 /// Corresponds to the `EFI_PXE_BASE_CODE_TFTP_ERROR` type in the C API. 1283 #[repr(C)] 1284 #[allow(missing_docs)] 1285 #[derive(Debug)] 1286 pub struct TftpError { 1287 pub error_code: u8, 1288 pub error_string: [u8; 127], 1289 } 1290 1291 impl Display for TftpError { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result1292 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 1293 write!(f, "{self:?}") 1294 } 1295 } 1296 1297 #[cfg(feature = "unstable")] 1298 impl core::error::Error for TftpError {} 1299 1300 /// Returned by [`BaseCode::tftp_read_dir`]. 1301 #[allow(missing_docs)] 1302 #[derive(Debug)] 1303 pub struct TftpFileInfo<'a> { 1304 pub filename: &'a CStr8, 1305 pub size: u64, 1306 pub year: u16, 1307 pub month: u8, 1308 pub day: u8, 1309 pub hour: u8, 1310 pub minute: u8, 1311 pub second: f32, 1312 } 1313 1314 /// Returned by [`BaseCode::mtftp_read_dir`]. 1315 #[allow(missing_docs)] 1316 #[derive(Debug)] 1317 pub struct MtftpFileInfo<'a> { 1318 pub filename: &'a CStr8, 1319 pub ip_address: IpAddress, 1320 pub size: u64, 1321 pub year: u16, 1322 pub month: u8, 1323 pub day: u8, 1324 pub hour: u8, 1325 pub minute: u8, 1326 pub second: f32, 1327 } 1328 1329 /// Returned if a server sends a malformed response in 1330 /// [`BaseCode::tftp_read_dir`] or [`BaseCode::mtftp_read_dir`]. 1331 #[derive(Clone, Copy, Debug)] 1332 pub struct ReadDirParseError; 1333 1334 impl Display for ReadDirParseError { fmt(&self, f: &mut Formatter<'_>) -> fmt::Result1335 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 1336 write!(f, "{self:?}") 1337 } 1338 } 1339 1340 #[cfg(feature = "unstable")] 1341 impl core::error::Error for ReadDirParseError {} 1342