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