1use crate::boot::entry::{KernelArgs, MemoryKind, MemoryRegion};
26
27const MAX_DTB_MEMORY_REGIONS: usize = 128;
29
30const MAX_CSTRING_LEN: usize = 512;
32
33const DEFAULT_HHDM_OFFSET: u64 = 0x1_0000_0000;
35
36static mut DTB_MEMORY_MAP: [MemoryRegion; MAX_DTB_MEMORY_REGIONS] = [MemoryRegion {
41 base: 0,
42 size: 0,
43 kind: MemoryKind::Reserved,
44}; MAX_DTB_MEMORY_REGIONS];
45
46#[cfg_attr(not(test), allow(static_mut_refs))]
52pub unsafe fn build_kernel_args_from_dtb(dtb_ptr: u64) -> KernelArgs {
53 let mut args = KernelArgs {
54 magic: strat9_abi::boot::STRAT9_BOOT_MAGIC,
55 abi_version: strat9_abi::boot::STRAT9_BOOT_ABI_VERSION,
56 kernel_base: 0,
57 kernel_size: 0,
58 acpi_rsdp_base: 0,
59 memory_map_base: 0,
60 memory_map_size: 0,
61 framebuffer_addr: 0,
62 framebuffer_width: 0,
63 framebuffer_height: 0,
64 framebuffer_stride: 0,
65 framebuffer_bpp: 0,
66 framebuffer_red_mask_size: 0,
67 framebuffer_red_mask_shift: 0,
68 framebuffer_green_mask_size: 0,
69 framebuffer_green_mask_shift: 0,
70 framebuffer_blue_mask_size: 0,
71 framebuffer_blue_mask_shift: 0,
72 hhdm_offset: 0,
73 cmdline_ptr: 0,
74 cmdline_len: 0,
75 modules_base: 0,
76 modules_size: 0,
77 bss_virt_base: 0,
78 bss_virt_size: 0,
79 };
80
81 if dtb_ptr == 0 {
82 crate::serial_println!("[fdt] ERROR: null DTB pointer");
83 return args;
84 }
85
86 let fdt = match parse_fdt_header(dtb_ptr) {
87 Ok(fdt) => fdt,
88 Err(e) => {
89 crate::serial_println!("[fdt] ERROR: {}", e);
90 return args;
91 }
92 };
93
94 crate::serial_println!("[fdt] DTB at {:#x}, version {}", dtb_ptr, fdt.version);
95
96 let (addr_cells, size_cells) = parse_root_properties(&fdt);
98 crate::serial_println!(
99 "[fdt] Root: #address-cells={} #size-cells={}",
100 addr_cells,
101 size_cells
102 );
103
104 let regions = parse_memory_nodes(&fdt, addr_cells, size_cells);
106 let count = regions.len();
107 args.memory_map_base = regions.as_ptr() as u64;
108 args.memory_map_size = (count * core::mem::size_of::<MemoryRegion>()) as u64;
109
110 args.hhdm_offset = derive_hhdm_offset(regions);
112
113 let mut efi_systab_addr = 0u64;
115 parse_chosen_node(&fdt, &mut args, &mut efi_systab_addr);
116
117 parse_framebuffer(&fdt, &mut args, addr_cells);
119
120 if efi_systab_addr != 0 && args.acpi_rsdp_base == 0 {
122 args.acpi_rsdp_base = find_rsdp_from_efi_systab(efi_systab_addr);
123 }
124
125 args
126}
127
128struct FdtHeader {
133 off_dt_struct: u32,
134 off_dt_strings: u32,
135 version: u32,
136 size_dt_struct: u32,
137 base_ptr: u64,
138}
139
140unsafe fn parse_fdt_header(dtb_ptr: u64) -> Result<FdtHeader, &'static str> {
141 let ptr = dtb_ptr as *const u8;
142
143 if read_be32(ptr) != 0xd00dfeed {
144 return Err("invalid FDT magic number");
145 }
146
147 Ok(FdtHeader {
148 off_dt_struct: read_be32(ptr.add(8)),
149 off_dt_strings: read_be32(ptr.add(12)),
150 version: read_be32(ptr.add(20)),
151 size_dt_struct: read_be32(ptr.add(32)),
152 base_ptr: dtb_ptr,
153 })
154}
155
156unsafe fn parse_root_properties(fdt: &FdtHeader) -> (u32, u32) {
162 let struct_base = fdt.base_ptr + fdt.off_dt_struct as u64;
163 let strings_base = fdt.base_ptr + fdt.off_dt_strings as u64;
164 let struct_end = struct_base + fdt.size_dt_struct as u64;
165
166 let mut pos = struct_base;
167 let mut addr_cells = 2u32;
168 let mut size_cells = 2u32;
169 let mut in_root = false;
170
171 while pos < struct_end {
172 let token = read_be32(pos as *const u8);
173 pos += 4;
174
175 match token {
176 0x1 => {
177 let name = read_cstring(pos as *const u8);
179 let name_len = name.len() + 1;
180 pos += (name_len as u64 + 3) & !3;
181
182 if name.is_empty() {
183 in_root = true;
184 } else {
185 break;
186 }
187 }
188 0x2 if in_root => break,
189 0x3 => {
190 let prop_len = read_be32(pos as *const u8) as u64;
192 pos += 4;
193 let prop_nameoff = read_be32(pos as *const u8) as u64;
194 pos += 4;
195 let prop_ptr = pos as *const u8;
196 pos += (prop_len + 3) & !3;
197
198 if !in_root {
199 continue;
200 }
201
202 let prop_name = read_cstring((strings_base + prop_nameoff) as *const u8);
203 match prop_name.as_bytes() {
204 b"#address-cells" if prop_len >= 4 => addr_cells = read_be32(prop_ptr),
205 b"#size-cells" if prop_len >= 4 => size_cells = read_be32(prop_ptr),
206 _ => {}
207 }
208 }
209 0x4 => {} 0x9 => break,
211 _ => break,
212 }
213 }
214
215 (addr_cells, size_cells)
216}
217
218#[allow(static_mut_refs)]
226unsafe fn parse_memory_nodes(
227 fdt: &FdtHeader,
228 addr_cells: u32,
229 size_cells: u32,
230) -> &'static [MemoryRegion] {
231 let struct_base = fdt.base_ptr + fdt.off_dt_struct as u64;
232 let strings_base = fdt.base_ptr + fdt.off_dt_strings as u64;
233 let struct_end = struct_base + fdt.size_dt_struct as u64;
234
235 let mut pos = struct_base;
236 let mut count = 0usize;
237 let mut depth = 0u32;
238 let mut in_memory_node = false;
239
240 crate::serial_println!("[fdt] Scanning for /memory nodes...");
241
242 while pos < struct_end && count < MAX_DTB_MEMORY_REGIONS {
243 let token = read_be32(pos as *const u8);
244 pos += 4;
245
246 match token {
247 0x1 => {
248 depth += 1;
250 let name = read_cstring(pos as *const u8);
251 let name_len = (name.len() + 1) as u64;
252 pos += (name_len + 3) & !3;
253
254 if depth == 1 && name.starts_with("memory") {
255 in_memory_node = true;
256 crate::serial_println!("[fdt] Found node: {}", name);
257 }
258 }
259 0x2 => {
260 if in_memory_node && depth == 1 {
261 in_memory_node = false;
262 }
263 depth = depth.saturating_sub(1);
264 }
265 0x3 => {
266 let prop_len = read_be32(pos as *const u8) as u64;
268 pos += 4;
269 let prop_nameoff = read_be32(pos as *const u8) as u64;
270 pos += 4;
271 let prop_ptr = pos as *const u8;
272 pos += (prop_len + 3) & !3;
273
274 if in_memory_node {
275 let prop_name = read_cstring((strings_base + prop_nameoff) as *const u8);
276 if prop_name == "reg" {
277 let (base, size) =
278 read_reg_property(prop_ptr, prop_len, addr_cells, size_cells);
279 if base != 0 && size != 0 {
280 DTB_MEMORY_MAP[count] = MemoryRegion {
281 base,
282 size,
283 kind: MemoryKind::Free,
284 };
285 crate::serial_println!(
286 "[fdt] Memory region: base={:#x} size={:#x}",
287 base,
288 size
289 );
290 count += 1;
291 }
292 }
293 }
294 }
295 0x4 => {}
296 0x9 => break,
297 _ => {
298 crate::serial_println!("[fdt] Unknown token: {:#x}", token);
299 break;
300 }
301 }
302 }
303
304 crate::serial_println!("[fdt] Found {} memory regions", count);
305
306 unsafe { core::slice::from_raw_parts(DTB_MEMORY_MAP.as_ptr(), count) }
308}
309
310unsafe fn read_reg_property(
312 ptr: *const u8,
313 len: u64,
314 addr_cells: u32,
315 size_cells: u32,
316) -> (u64, u64) {
317 let addr_bytes = (addr_cells * 4) as usize;
318 let size_bytes = (size_cells * 4) as usize;
319 let total = addr_bytes + size_bytes;
320
321 if (len as usize) < total {
322 return (0, 0);
323 }
324
325 let mut base = 0u64;
326 for i in 0..addr_bytes {
327 base = (base << 8) | *ptr.add(i) as u64;
328 }
329
330 let mut size = 0u64;
331 for i in addr_bytes..total {
332 size = (size << 8) | *ptr.add(i) as u64;
333 }
334
335 (base, size)
336}
337
338fn derive_hhdm_offset(regions: &[MemoryRegion]) -> u64 {
346 let max_addr = regions
347 .iter()
348 .filter(|r| matches!(r.kind, MemoryKind::Free))
349 .map(|r| r.base + r.size)
350 .max()
351 .unwrap_or(0);
352
353 crate::serial_println!(
354 "[fdt] Memory map: max_addr={:#x}, default HHDM={:#x}",
355 max_addr,
356 DEFAULT_HHDM_OFFSET
357 );
358
359 DEFAULT_HHDM_OFFSET
360}
361
362unsafe fn parse_chosen_node(fdt: &FdtHeader, args: &mut KernelArgs, efi_systab_addr: &mut u64) {
368 let struct_base = fdt.base_ptr + fdt.off_dt_struct as u64;
369 let strings_base = fdt.base_ptr + fdt.off_dt_strings as u64;
370 let struct_end = struct_base + fdt.size_dt_struct as u64;
371
372 let mut pos = struct_base;
373 let mut depth = 0u32;
374 let mut in_chosen = false;
375
376 crate::serial_println!("[fdt] Scanning for /chosen node...");
377
378 while pos < struct_end {
379 let token = read_be32(pos as *const u8);
380 pos += 4;
381
382 match token {
383 0x1 => {
384 depth += 1;
385 let name = read_cstring(pos as *const u8);
386 let name_len = name.len() + 1;
387 pos += (name_len as u64 + 3) & !3;
388
389 if depth == 1 && name == "chosen" {
390 in_chosen = true;
391 crate::serial_println!("[fdt] Found /chosen node");
392 }
393 }
394 0x2 => {
395 if in_chosen && depth == 1 {
396 in_chosen = false;
397 }
398 depth = depth.saturating_sub(1);
399 }
400 0x3 => {
401 let prop_len = read_be32(pos as *const u8) as u64;
403 pos += 4;
404 let prop_nameoff = read_be32(pos as *const u8) as u64;
405 pos += 4;
406 let prop_ptr = pos as *const u8;
407 pos += (prop_len + 3) & !3;
408
409 if !in_chosen {
410 continue;
411 }
412
413 let prop_name = read_cstring((strings_base + prop_nameoff) as *const u8);
414
415 match prop_name.as_bytes() {
416 b"bootargs" => {
417 let bootargs = read_cstring(prop_ptr);
418 crate::serial_println!("[fdt] bootargs: '{}'", bootargs);
419 args.cmdline_ptr = prop_ptr as u64;
420 args.cmdline_len = bootargs.len() as u64 + 1;
421 }
422 b"linux,initrd-start" => {
423 let addr = read_prop_u64(prop_ptr, prop_len);
424 crate::serial_println!(
425 "[fdt] initrd-start: {:#x} (FDT boot, module table not yet supported)",
426 addr
427 );
428 }
431 b"linux,initrd-end" => {
432 let addr = read_prop_u64(prop_ptr, prop_len);
433 crate::serial_println!("[fdt] initrd-end: {:#x}", addr);
434 }
435 b"stdout-path" => {
436 crate::serial_println!("[fdt] stdout-path: {}", read_cstring(prop_ptr));
437 }
438 b"uefi-systab" => {
439 let addr = read_prop_u64(prop_ptr, prop_len);
441 crate::serial_println!("[fdt] uefi-systab (EFI System Table): {:#x}", addr);
442 *efi_systab_addr = addr;
443 }
444 _ => {}
445 }
446 }
447 0x4 => {}
448 0x9 => break,
449 _ => break,
450 }
451 }
452}
453
454unsafe fn parse_framebuffer(fdt: &FdtHeader, args: &mut KernelArgs, addr_cells: u32) {
460 let struct_base = fdt.base_ptr + fdt.off_dt_struct as u64;
461 let strings_base = fdt.base_ptr + fdt.off_dt_strings as u64;
462 let struct_end = struct_base + fdt.size_dt_struct as u64;
463
464 let mut pos = struct_base;
465 let mut depth = 0u32;
466 let mut in_framebuffer = false;
467
468 while pos < struct_end {
469 let token = read_be32(pos as *const u8);
470 pos += 4;
471
472 match token {
473 0x1 => {
474 depth += 1;
475 let name = read_cstring(pos as *const u8);
476 let name_len = name.len() + 1;
477 pos += (name_len as u64 + 3) & !3;
478
479 if name.contains("framebuffer") || name.contains("display") {
480 in_framebuffer = true;
481 crate::serial_println!("[fdt] Found framebuffer node: {}", name);
482 }
483 }
484 0x2 => {
485 if in_framebuffer {
486 in_framebuffer = false;
487 }
488 depth = depth.saturating_sub(1);
489 }
490 0x3 => {
491 let prop_len = read_be32(pos as *const u8) as u64;
492 pos += 4;
493 let prop_nameoff = read_be32(pos as *const u8) as u64;
494 pos += 4;
495 let prop_ptr = pos as *const u8;
496 pos += (prop_len + 3) & !3;
497
498 if !in_framebuffer {
499 continue;
500 }
501
502 let prop_name = read_cstring((strings_base + prop_nameoff) as *const u8);
503
504 match prop_name.as_bytes() {
505 b"reg" => {
506 let addr_bytes = (addr_cells * 4) as u64;
507 if prop_len >= addr_bytes {
508 args.framebuffer_addr = read_be_int(prop_ptr, addr_bytes as usize);
509 }
510 }
511 b"width" if prop_len >= 4 => args.framebuffer_width = read_be32(prop_ptr),
512 b"height" if prop_len >= 4 => args.framebuffer_height = read_be32(prop_ptr),
513 b"stride" if prop_len >= 4 => args.framebuffer_stride = read_be32(prop_ptr),
514 b"bpp" if prop_len >= 4 => args.framebuffer_bpp = read_be32(prop_ptr) as u16,
515 _ => {}
516 }
517 }
518 0x4 => {}
519 0x9 => break,
520 _ => break,
521 }
522 }
523
524 if args.framebuffer_bpp == 0 && args.framebuffer_addr != 0 {
526 args.framebuffer_bpp = 32;
527 args.framebuffer_red_mask_size = 8;
528 args.framebuffer_red_mask_shift = 16;
529 args.framebuffer_green_mask_size = 8;
530 args.framebuffer_green_mask_shift = 8;
531 args.framebuffer_blue_mask_size = 8;
532 args.framebuffer_blue_mask_shift = 0;
533 }
534}
535
536unsafe fn find_rsdp_from_efi_systab(efi_systab_addr: u64) -> u64 {
546 crate::serial_println!(
547 "[fdt] Searching for RSDP via EFI System Table at {:#x}",
548 efi_systab_addr
549 );
550
551 let systab = efi_systab_addr as *const u8;
552
553 let num_entries = read_be64(systab.add(0x68));
557 let config_table_ptr = read_be64(systab.add(0x70));
558
559 crate::serial_println!(
560 "[fdt] EFI Config Table: {} entries at {:#x}",
561 num_entries,
562 config_table_ptr
563 );
564
565 if config_table_ptr == 0 || num_entries == 0 {
566 crate::serial_println!("[fdt] No EFI configuration tables found");
567 return 0;
568 }
569
570 let rsdp_guid: [u64; 2] = [0x47d4_0409_ac03_114e, 0xa1f5_d3dd_9645_c2a7];
572
573 let config_table = config_table_ptr as *const u8;
575
576 for i in 0..num_entries as usize {
577 let entry = config_table.add(i * 24);
578 let guid_lo = read_be64(entry);
579 let guid_hi = read_be64(entry.add(8));
580 let table_ptr = read_be64(entry.add(16));
581
582 if guid_lo == rsdp_guid[0] && guid_hi == rsdp_guid[1] && table_ptr != 0 {
583 crate::serial_println!("[fdt] RSDP found at {:#x}", table_ptr);
584 return table_ptr;
585 }
586 }
587
588 crate::serial_println!("[fdt] RSDP not found in EFI configuration tables");
589 0
590}
591
592#[inline]
598unsafe fn read_be32(ptr: *const u8) -> u32 {
599 u32::from_be_bytes([
600 core::ptr::read_volatile(ptr),
601 core::ptr::read_volatile(ptr.add(1)),
602 core::ptr::read_volatile(ptr.add(2)),
603 core::ptr::read_volatile(ptr.add(3)),
604 ])
605}
606
607#[inline]
609unsafe fn read_be64(ptr: *const u8) -> u64 {
610 u64::from_be_bytes([
611 core::ptr::read_volatile(ptr),
612 core::ptr::read_volatile(ptr.add(1)),
613 core::ptr::read_volatile(ptr.add(2)),
614 core::ptr::read_volatile(ptr.add(3)),
615 core::ptr::read_volatile(ptr.add(4)),
616 core::ptr::read_volatile(ptr.add(5)),
617 core::ptr::read_volatile(ptr.add(6)),
618 core::ptr::read_volatile(ptr.add(7)),
619 ])
620}
621
622unsafe fn read_be_int(ptr: *const u8, num_bytes: usize) -> u64 {
624 let mut val = 0u64;
625 for i in 0..num_bytes {
626 val = (val << 8) | *ptr.add(i) as u64;
627 }
628 val
629}
630
631unsafe fn read_prop_u64(ptr: *const u8, prop_len: u64) -> u64 {
633 if prop_len >= 8 {
634 read_be64(ptr)
635 } else {
636 read_be32(ptr) as u64
637 }
638}
639
640unsafe fn read_cstring(ptr: *const u8) -> &'static str {
642 let mut len = 0;
643 while len < MAX_CSTRING_LEN && core::ptr::read_volatile(ptr.add(len)) != 0 {
644 len += 1;
645 }
646 let slice = core::slice::from_raw_parts(ptr, len);
647 core::str::from_utf8(slice).unwrap_or("")
648}