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