1use core::mem::size_of;
18use scroll::Pread;
19
20use crate::pi::hob::*;
21
22const SIZE_4G: u64 = 0x100000000u64;
23
24pub fn align_to_next_hob_offset(cap: usize, offset: usize, length: u16) -> Option<usize> {
26 if length == 0 || length > (u16::MAX - 7) {
27 None
28 } else {
29 let offset = offset.checked_add((length as usize + 7) / 8 * 8)?;
30 if offset < cap {
31 Some(offset)
32 } else {
33 None
34 }
35 }
36}
37
38pub fn seek_to_next_hob(hob_list: &'_ [u8]) -> Option<&'_ [u8]> {
40 let header: Header = hob_list.pread(0).ok()?;
41 let offset = align_to_next_hob_offset(hob_list.len(), 0, header.length)?;
42
43 Some(&hob_list[offset..])
44}
45
46pub fn check_hob_length(hob: &[u8], hob_length: usize) -> Option<&[u8]> {
48 let phit: HandoffInfoTable = hob.pread(0).ok()?;
49 let end: u64 = phit.efi_end_of_hob_list;
50 if phit.header.r#type == HOB_TYPE_HANDOFF
51 && phit.header.length as usize >= size_of::<HandoffInfoTable>()
52 {
53 if hob_length as u64 == end.checked_sub(hob.as_ptr() as u64)? {
54 Some(&hob[0..hob_length])
55 } else {
56 None
57 }
58 } else {
59 None
60 }
61}
62
63pub fn check_hob_integrity(hob_list: &[u8]) -> Option<&[u8]> {
66 let mut offset = 0;
67 let hob_list_len = hob_list.len();
68
69 if hob_list_len < size_of::<HandoffInfoTable>() {
71 return None;
72 }
73 speculation_barrier();
74
75 loop {
76 let hob = &hob_list[offset..];
77 if offset.checked_add(size_of::<Header>())? > hob_list_len {
78 return None;
79 }
80 speculation_barrier();
81
82 let header: Header = hob.pread(0).ok()?;
83
84 if header.length == 0 || header.length as usize > hob.len() || header.reserved != 0 {
86 return None;
87 }
88 speculation_barrier();
89
90 match header.r#type {
91 HOB_TYPE_HANDOFF => {
92 if header.length as usize != size_of::<HandoffInfoTable>()
93 || offset + size_of::<HandoffInfoTable>() > hob_list_len
94 {
95 return None;
96 }
97 let phit_hob: HandoffInfoTable = hob.pread(0).ok()?;
98
99 if phit_hob.efi_memory_top % 0x1000 != 0 {
101 log::info!(
102 "PHIT HOB does not hold a 4-KB aligned EFI memory top address: {:x}\n",
103 phit_hob.efi_memory_top
104 );
105 return None;
106 }
107 }
108 HOB_TYPE_END_OF_HOB_LIST => {
109 let hob_length = offset + size_of::<Header>();
110
111 return check_hob_length(hob_list, hob_length);
112 }
113 HOB_TYPE_RESOURCE_DESCRIPTOR => {
114 if header.length as usize != size_of::<ResourceDescription>() {
115 return None;
116 }
117 let resource_hob: ResourceDescription = hob.pread(0).ok()?;
118 if resource_hob.resource_type >= RESOURCE_MAX_MEMORY_TYPE
119 || resource_hob.resource_attribute & (!RESOURCE_ATTRIBUTE_ALL) != 0
120 {
121 log::info!("Invalid resource type or attributes:\n");
122 resource_hob.dump();
123 return None;
124 }
125
126 resource_hob
127 .physical_start
128 .checked_add(resource_hob.resource_length)?;
129 }
130 HOB_TYPE_MEMORY_ALLOCATION => {
131 if header.length as usize != size_of::<MemoryAllocation>() {
132 return None;
133 }
134 }
135 HOB_TYPE_FV => {
136 if header.length as usize != size_of::<FirmwareVolume>() {
137 return None;
138 }
139 }
140 HOB_TYPE_FV2 => {
141 if header.length as usize != size_of::<FirmwareVolume2>() {
142 return None;
143 }
144 }
145 HOB_TYPE_FV3 => {
146 if header.length as usize != size_of::<FirmwareVolume3>() {
147 return None;
148 }
149 }
150 HOB_TYPE_CPU => {
151 if header.length as usize != size_of::<Cpu>()
152 || offset + size_of::<Cpu>() > hob_list_len
153 {
154 return None;
155 }
156
157 let cpu_hob: Cpu = hob.pread(0).ok()?;
158 if cpu_hob.reserved != [0u8; 6] {
160 return None;
161 }
162 }
163 HOB_TYPE_GUID_EXTENSION => {
164 }
166 _ => return None,
168 }
169 offset = align_to_next_hob_offset(hob_list_len, offset, header.length)?;
170 speculation_barrier();
171 }
172}
173
174pub fn dump_hob(hob_list: &[u8]) -> Option<()> {
176 let mut offset = 0;
177
178 loop {
179 let hob = &hob_list[offset..];
180 let header: Header = hob.pread(0).ok()?;
181
182 match header.r#type {
183 HOB_TYPE_HANDOFF => {
184 let phit_hob: HandoffInfoTable = hob.pread(0).ok()?;
185 phit_hob.dump();
186 }
187 HOB_TYPE_RESOURCE_DESCRIPTOR => {
188 let resource_hob: ResourceDescription = hob.pread(0).ok()?;
189 resource_hob.dump();
190 }
191 HOB_TYPE_MEMORY_ALLOCATION => {
192 let allocation_hob: MemoryAllocation = hob.pread(0).ok()?;
193 allocation_hob.dump();
194 }
195 HOB_TYPE_FV => {
196 let fv_hob: FirmwareVolume = hob.pread(0).ok()?;
197 fv_hob.dump();
198 }
199 HOB_TYPE_CPU => {
200 let cpu_hob: Cpu = hob.pread(0).ok()?;
201 cpu_hob.dump();
202 }
203 HOB_TYPE_END_OF_HOB_LIST => return Some(()),
204 _ => header.dump(),
205 }
206
207 offset = align_to_next_hob_offset(hob_list.len(), offset, header.length)?;
208 }
209}
210
211pub fn get_system_memory_size_below_4gb(hob_list: &[u8]) -> Option<u64> {
215 let mut low_mem_top = 0u64; let mut offset = 0;
217
218 loop {
219 let hob = &hob_list[offset..];
220 let header: Header = hob.pread(0).ok()?;
221
222 match header.r#type {
223 HOB_TYPE_RESOURCE_DESCRIPTOR => {
224 let resource_hob: ResourceDescription = hob.pread(0).ok()?;
225 if resource_hob.resource_type == RESOURCE_SYSTEM_MEMORY {
226 let end = resource_hob
227 .physical_start
228 .checked_add(resource_hob.resource_length)?;
229 if end < SIZE_4G && end > low_mem_top {
230 low_mem_top = end;
231 }
232 }
233 }
234 HOB_TYPE_END_OF_HOB_LIST => break,
235 _ => {}
236 }
237
238 offset = align_to_next_hob_offset(hob_list.len(), offset, header.length)?;
239 }
240
241 Some(low_mem_top)
242}
243
244pub fn get_total_memory_top(hob_list: &[u8]) -> Option<u64> {
246 let mut mem_top = 0; let mut offset = 0;
248
249 loop {
250 let hob = &hob_list[offset..];
251 let header: Header = hob.pread(0).ok()?;
252
253 match header.r#type {
254 HOB_TYPE_RESOURCE_DESCRIPTOR => {
255 let resource_hob: ResourceDescription = hob.pread(0).ok()?;
256 if resource_hob.resource_type == RESOURCE_SYSTEM_MEMORY
258 || resource_hob.resource_type == RESOURCE_MEMORY_MAPPED_IO
259 {
260 let end = resource_hob
261 .physical_start
262 .checked_add(resource_hob.resource_length)?;
263 if end > mem_top {
264 mem_top = end;
265 }
266 }
267 }
268 HOB_TYPE_END_OF_HOB_LIST => break,
269 _ => {}
270 }
271 offset = align_to_next_hob_offset(hob_list.len(), offset, header.length)?;
272 }
273
274 Some(mem_top)
275}
276
277pub fn get_fv(hob_list: &[u8]) -> Option<FirmwareVolume> {
278 let mut offset = 0;
279
280 loop {
281 let hob = &hob_list[offset..];
282 let header: Header = hob.pread(0).ok()?;
283 match header.r#type {
284 HOB_TYPE_FV => {
285 let fv_hob: FirmwareVolume = hob.pread(0).ok()?;
286 return Some(fv_hob);
287 }
288 HOB_TYPE_END_OF_HOB_LIST => break,
289 _ => {}
290 }
291 offset = align_to_next_hob_offset(hob_list.len(), offset, header.length)?;
292 }
293
294 None
295}
296
297pub fn get_next_extension_guid_hob<'a>(hob_list: &'a [u8], guid: &[u8]) -> Option<&'a [u8]> {
299 let mut offset = 0;
300
301 loop {
302 let hob = &hob_list[offset..];
303 let header: Header = hob.pread(0).ok()?;
304
305 match header.r#type {
306 HOB_TYPE_GUID_EXTENSION => {
307 let guid_hob: GuidExtension = hob.pread(0).ok()?;
308 if guid_hob.name == guid {
309 return Some(hob);
310 }
311 }
312 HOB_TYPE_END_OF_HOB_LIST => break,
313 _ => {}
314 }
315 offset = align_to_next_hob_offset(hob_list.len(), offset, header.length)?;
316 }
317 None
318}
319
320pub fn get_guid_data(hob_list: &[u8]) -> Option<&[u8]> {
322 let guid_hob: GuidExtension = hob_list.pread(0).ok()?;
323 let offset = size_of::<GuidExtension>();
324 let end = guid_hob.header.length as usize;
325
326 if end >= offset && end <= hob_list.len() {
327 Some(&hob_list[offset..end])
328 } else {
329 None
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336 use crate::pi::guid::Guid;
337 use core::ptr::slice_from_raw_parts;
338
339 #[test]
340 fn test_align_to_next_hob() {
341 assert!(align_to_next_hob_offset(usize::MAX, 0, 0).is_none());
342 assert!(align_to_next_hob_offset(8, 8, 1).is_none());
343 assert_eq!(align_to_next_hob_offset(usize::MAX, 8, 1), Some(16));
344 assert_eq!(align_to_next_hob_offset(usize::MAX, 8, 9), Some(24));
345 assert_eq!(
346 align_to_next_hob_offset(usize::MAX, 0, u16::MAX - 8),
347 Some(u16::MAX as usize - 7)
348 );
349 assert_eq!(
350 align_to_next_hob_offset(usize::MAX, 0, u16::MAX - 7),
351 Some(u16::MAX as usize - 7)
352 );
353 assert_eq!(
354 align_to_next_hob_offset(usize::MAX, 8, u16::MAX - 7),
355 Some(u16::MAX as usize + 1)
356 );
357 assert!(align_to_next_hob_offset(usize::MAX, 0, u16::MAX - 6).is_none());
358 assert!(align_to_next_hob_offset(usize::MAX, 8, u16::MAX).is_none());
359 }
360
361 #[test]
362 fn test_dump_hob() {
363 assert!(dump_hob(&[]).is_none());
364 assert!(dump_hob(&[0u8]).is_none());
365
366 let memory_allocation_hob_header = Header {
367 r#type: HOB_TYPE_MEMORY_ALLOCATION,
368 length: size_of::<MemoryAllocation>() as u16,
369 reserved: 0x0,
370 };
371 let memory_allocation_header = MemoryAllocationHeader {
372 name: [0; 16],
373 memory_base_address: 0x0,
374 memory_length: 0x0,
375 memory_type: 0x0,
376 reserved: [0; 4],
377 };
378 let _ = memory_allocation_header.as_bytes();
379 let hob = MemoryAllocation {
380 header: memory_allocation_hob_header,
381 alloc_descriptor: memory_allocation_header,
382 };
383 assert!(dump_hob(hob.as_bytes()).is_none());
384
385 let fv_hob_header = Header {
386 r#type: HOB_TYPE_FV,
387 length: size_of::<FirmwareVolume>() as u16,
388 reserved: 0x0,
389 };
390 let hob = FirmwareVolume {
391 header: fv_hob_header,
392 base_address: 0x0,
393 length: 0x0,
394 };
395 assert!(dump_hob(hob.as_bytes()).is_none());
396
397 let cpu_hob_header = Header {
398 r#type: HOB_TYPE_CPU,
399 length: size_of::<Cpu>() as u16,
400 reserved: 0x0,
401 };
402 let hob = Cpu {
403 header: cpu_hob_header,
404 size_of_memory_space: 0x0,
405 size_of_io_space: 0x0,
406 reserved: [0; 6],
407 };
408 assert!(dump_hob(hob.as_bytes()).is_none());
409 }
410
411 #[test]
412 fn test_check_hob_length() {
413 let hob_header = Header {
414 r#type: HOB_TYPE_HANDOFF,
415 length: size_of::<HandoffInfoTable>() as u16,
416 reserved: 0x0,
417 };
418
419 let mut hob = HandoffInfoTable {
420 header: hob_header,
421 version: 0,
422 boot_mode: 0,
423 efi_memory_top: 0,
424 efi_memory_bottom: 0,
425 efi_free_memory_top: 0,
426 efi_free_memory_bottom: 0,
427 efi_end_of_hob_list: 0,
428 };
429 hob.efi_end_of_hob_list =
430 size_of::<HandoffInfoTable>() as u64 + hob.as_bytes().as_ptr() as u64;
431 assert!(check_hob_length(hob.as_bytes(), size_of::<HandoffInfoTable>() as usize).is_some());
432 hob.efi_end_of_hob_list = hob.as_bytes().as_ptr() as u64 - 1;
434 assert!(check_hob_length(hob.as_bytes(), size_of::<HandoffInfoTable>() as usize).is_none());
435 hob.efi_end_of_hob_list =
437 size_of::<HandoffInfoTable>() as u64 + hob.as_bytes().as_ptr() as u64;
438 assert!(check_hob_length(hob.as_bytes(), u64::MAX as usize).is_none());
439
440 let hob_header = Header {
442 r#type: HOB_TYPE_MEMORY_ALLOCATION,
443 length: size_of::<HandoffInfoTable>() as u16,
444 reserved: 0x0,
445 };
446 let mut hob = HandoffInfoTable {
447 header: hob_header,
448 version: 0,
449 boot_mode: 0,
450 efi_memory_top: 0,
451 efi_memory_bottom: 0,
452 efi_free_memory_top: 0,
453 efi_free_memory_bottom: 0,
454 efi_end_of_hob_list: 0,
455 };
456 hob.efi_end_of_hob_list =
457 size_of::<HandoffInfoTable>() as u64 + hob.as_bytes().as_ptr() as u64;
458 assert!(check_hob_length(hob.as_bytes(), size_of::<HandoffInfoTable>() as usize).is_none());
459
460 let hob_header = Header {
462 r#type: HOB_TYPE_HANDOFF,
463 length: size_of::<HandoffInfoTable>() as u16 - 1,
464 reserved: 0x0,
465 };
466 let mut hob = HandoffInfoTable {
467 header: hob_header,
468 version: 0,
469 boot_mode: 0,
470 efi_memory_top: 0,
471 efi_memory_bottom: 0,
472 efi_free_memory_top: 0,
473 efi_free_memory_bottom: 0,
474 efi_end_of_hob_list: 0,
475 };
476 hob.efi_end_of_hob_list =
477 size_of::<HandoffInfoTable>() as u64 + hob.as_bytes().as_ptr() as u64;
478 assert!(check_hob_length(hob.as_bytes(), size_of::<HandoffInfoTable>() as usize).is_none());
479 }
480
481 #[test]
482 fn test_check_hob_integrity() {
483 const EFI_END_OF_HOB_LIST_OFFSET: usize = 48;
484 let hob = &include_bytes!("../../fuzz/seeds/hob_parser/hob_buffer")[..];
485 let mut test_hob = hob.to_vec();
486 let ptr = test_hob.as_ptr() as u64;
487 if test_hob.len() >= size_of::<HandoffInfoTable>() {
488 test_hob[EFI_END_OF_HOB_LIST_OFFSET..size_of::<HandoffInfoTable>()]
489 .copy_from_slice(&u64::to_le_bytes(ptr + hob.len() as u64)[..]);
490 }
491
492 assert!(check_hob_integrity(&test_hob).is_some());
493 assert!(dump_hob(&test_hob).is_some());
494 }
495
496 #[test]
497 fn test_get_total_memory_top() {
498 let hob = &include_bytes!("../../fuzz/seeds/hob_parser/hob_buffer")[..];
499
500 assert!(get_total_memory_top(hob).is_some());
501 }
502
503 #[test]
504 fn test_seek_to_next_hob() {
505 let hob = &include_bytes!("../../fuzz/seeds/hob_parser/hob_buffer")[..];
506
507 assert!(seek_to_next_hob(hob).is_some());
508 }
509
510 #[test]
511 fn test_get_system_memory_size_below_4gb() {
512 assert!(get_system_memory_size_below_4gb(&[]).is_none());
513
514 let mut buf = [0u8; 1024];
515 let res = ResourceDescription {
516 header: Header {
517 r#type: HOB_TYPE_RESOURCE_DESCRIPTOR,
518 length: size_of::<ResourceDescription>() as u16,
519 reserved: 0,
520 },
521 owner: [0u8; 16],
522 resource_type: RESOURCE_SYSTEM_MEMORY,
523 resource_attribute: 0,
524 physical_start: 0,
525 resource_length: 0x200_0000,
526 };
527 let buf1 = unsafe {
528 &*slice_from_raw_parts(
529 &res as *const ResourceDescription as *const u8,
530 size_of::<ResourceDescription>(),
531 )
532 };
533 buf[..size_of::<ResourceDescription>()].copy_from_slice(buf1);
534 let res = ResourceDescription {
535 header: Header {
536 r#type: HOB_TYPE_RESOURCE_DESCRIPTOR,
537 length: size_of::<ResourceDescription>() as u16,
538 reserved: 0,
539 },
540 owner: [0u8; 16],
541 resource_type: RESOURCE_SYSTEM_MEMORY,
542 resource_attribute: 0,
543 physical_start: 0x1000_0000,
544 resource_length: 0x200_0000,
545 };
546 let buf1 = unsafe {
547 &*slice_from_raw_parts(
548 &res as *const ResourceDescription as *const u8,
549 size_of::<ResourceDescription>(),
550 )
551 };
552 buf[size_of::<ResourceDescription>()..2 * size_of::<ResourceDescription>()]
553 .copy_from_slice(buf1);
554 let end = Header {
555 r#type: HOB_TYPE_END_OF_HOB_LIST,
556 length: 0,
557 reserved: 0,
558 };
559 let buf2 = unsafe {
560 &*slice_from_raw_parts(&end as *const Header as *const u8, size_of::<Header>())
561 };
562 buf[2 * size_of::<ResourceDescription>()
563 ..2 * size_of::<ResourceDescription>() + size_of::<Header>()]
564 .copy_from_slice(buf2);
565 assert_eq!(get_system_memory_size_below_4gb(&buf), Some(0x1200_0000));
566
567 let res = ResourceDescription {
568 header: Header {
569 r#type: HOB_TYPE_RESOURCE_DESCRIPTOR,
570 length: 0,
571 reserved: 0,
572 },
573 owner: [0u8; 16],
574 resource_type: RESOURCE_SYSTEM_MEMORY,
575 resource_attribute: 0,
576 physical_start: 0,
577 resource_length: 0x200_0000,
578 };
579 let buf1 = unsafe {
580 &*slice_from_raw_parts(
581 &res as *const ResourceDescription as *const u8,
582 size_of::<ResourceDescription>(),
583 )
584 };
585 buf[..size_of::<ResourceDescription>()].copy_from_slice(buf1);
586 assert!(get_system_memory_size_below_4gb(&buf).is_none());
587 }
588
589 #[test]
590 fn test_get_fv() {
591 assert!(get_fv(&[]).is_none());
592
593 let mut buf = [0u8; 1024];
594 let res = FirmwareVolume {
595 header: Header {
596 r#type: HOB_TYPE_FV,
597 length: size_of::<FirmwareVolume>() as u16,
598 reserved: 0,
599 },
600 base_address: 0x1000000,
601 length: 0,
602 };
603 let buf1 = unsafe {
604 &*slice_from_raw_parts(
605 &res as *const FirmwareVolume as *const u8,
606 size_of::<FirmwareVolume>(),
607 )
608 };
609 buf[..size_of::<FirmwareVolume>()].copy_from_slice(buf1);
610 let end = Header {
611 r#type: HOB_TYPE_END_OF_HOB_LIST,
612 length: 0,
613 reserved: 0,
614 };
615 let buf2 = unsafe {
616 &*slice_from_raw_parts(&end as *const Header as *const u8, size_of::<Header>())
617 };
618 buf[size_of::<FirmwareVolume>()..size_of::<FirmwareVolume>() + size_of::<Header>()]
619 .copy_from_slice(buf2);
620 assert!(get_fv(&buf).is_some());
621
622 let res = FirmwareVolume {
623 header: Header {
624 r#type: HOB_TYPE_FV2,
625 length: u16::MAX,
626 reserved: 0,
627 },
628 base_address: 0x1000000,
629 length: 0,
630 };
631 let buf1 = unsafe {
632 &*slice_from_raw_parts(
633 &res as *const FirmwareVolume as *const u8,
634 size_of::<FirmwareVolume>(),
635 )
636 };
637 buf[..size_of::<FirmwareVolume>()].copy_from_slice(buf1);
638 assert!(get_fv(&buf).is_none());
639 }
640
641 #[test]
642 fn test_get_guid() {
643 let guid = Guid::from_bytes(&[0u8; 16]);
644 assert!(get_next_extension_guid_hob(&[], guid.as_bytes()).is_none());
645
646 let mut buf = [0xaau8; 128];
647 let res = GuidExtension {
648 header: Header {
649 r#type: HOB_TYPE_GUID_EXTENSION,
650 length: size_of::<GuidExtension>() as u16 + 16,
651 reserved: 0,
652 },
653 name: [0xa5u8; 16],
654 };
655 let buf1 = unsafe {
656 &*slice_from_raw_parts(
657 &res as *const GuidExtension as *const u8,
658 size_of::<GuidExtension>(),
659 )
660 };
661 assert_eq!(buf1, res.as_bytes());
662 buf[..size_of::<GuidExtension>()].copy_from_slice(buf1);
663 let end = Header {
664 r#type: HOB_TYPE_END_OF_HOB_LIST,
665 length: 0,
666 reserved: 0,
667 };
668 let buf2 = unsafe {
669 &*slice_from_raw_parts(&end as *const Header as *const u8, size_of::<Header>())
670 };
671 buf[size_of::<GuidExtension>() + 16..size_of::<GuidExtension>() + 16 + size_of::<Header>()]
672 .copy_from_slice(buf2);
673 let guid = get_next_extension_guid_hob(&buf, &[0xa5u8; 16]).unwrap();
674 let data = get_guid_data(guid).unwrap();
675 assert_eq!(data, &[0xaa; 16]);
676 }
677}
678
679fn speculation_barrier() {
682 unsafe { core::arch::asm!("lfence") }
683}