kernel/device/
fdt.rs

1//! Flattened Device Tree (FDT) management module.
2//! 
3//! This module provides functionality for managing the Flattened Device Tree (FDT),
4//! which is a data structure used to describe the hardware components of a system.
5//!
6//! # Overview
7//!
8//! The FDT is passed by the bootloader to the kernel and contains information about
9//! the hardware configuration of the system. This module parses and provides access
10//! to that information.
11//!
12//! # Core Components
13//!
14//! - `FdtManager`: A singleton that manages access to the parsed FDT
15//! - `init_fdt()`: Function to initialize the FDT subsystem
16//!
17//! # Usage
18//!
19//! Before using the FDT functions, you must:
20//! 1. Set the FDT address using `FdtManager::set_fdt_addr()`
21//! 2. Call `init_fdt()` to parse the FDT
22//!
23//! After initialization, you can access the FDT using the static manager:
24//! ```
25//! let fdt_manager = FdtManager::get_manager();
26//! if let Some(fdt) = fdt_manager.get_fdt() {
27//!     // Use the FDT data
28//! }
29//! ```
30//!
31//! # Implementation Details
32//!
33//! The module uses the `fdt` crate to parse the device tree. It maintains a static
34//! global manager instance to provide access throughout the kernel. The FDT address
35//! is stored in a static variable that must be set before initialization.
36
37
38use core::panic;
39use core::result::Result;
40
41use fdt::{Fdt, FdtError};
42
43use crate::early_println;
44use crate::vm::vmem::MemoryArea;
45
46#[unsafe(link_section = ".data")]
47static mut FDT_ADDR: usize = 0;
48
49static mut MANAGER: FdtManager = FdtManager::new();
50
51
52pub struct FdtManager<'a> {
53    fdt: Option<Fdt<'a>>,
54    relocated: bool,
55}
56
57impl<'a> FdtManager<'a> {
58    const fn new() -> Self {
59        FdtManager {
60            fdt: None,
61            relocated: false,
62        }
63    }
64
65    pub fn init(&mut self, ptr: *const u8) -> Result<(), FdtError> {
66        match unsafe { Fdt::from_ptr(ptr) } {
67            Ok(fdt) => {
68                self.fdt = Some(fdt);
69            }
70            Err(e) => return Err(e),
71        }
72        Ok(())
73    }
74
75    /// Sets the FDT address.
76    /// 
77    /// # Safety
78    /// This function modifies a static variable that holds the FDT address.
79    /// Ensure that this function is called before any other FDT-related functions
80    /// to avoid undefined behavior.
81    /// 
82    /// # Arguments
83    /// 
84    /// * `addr`: The address of the FDT.
85    /// 
86    /// # Notes
87    /// 
88    /// This function must be called before initializing the FDT manager.
89    /// After once FdtManager is initialized, you cannot change the address.
90    /// 
91    pub unsafe fn set_fdt_addr(addr: usize) {
92        unsafe {
93            FDT_ADDR = addr;
94        }
95    }
96    
97    pub fn get_fdt(&self) -> Option<&Fdt<'a>> {
98        self.fdt.as_ref()
99    }
100
101    /// Returns a mutable reference to the FdtManager.
102    /// This is unsafe because it allows mutable access to a static variable.
103    /// It should be used with caution to avoid data races.
104    /// 
105    /// # Safety
106    /// This function provides mutable access to the static FdtManager instance.
107    /// Ensure that no other references to the manager are active to prevent data races.
108    /// 
109    /// # Returns
110    /// A mutable reference to the static FdtManager instance.
111    #[allow(static_mut_refs)]
112    pub unsafe fn get_mut_manager() -> &'static mut FdtManager<'a> {
113        unsafe { &mut MANAGER }
114    }
115
116    /// Returns a reference to the FdtManager.
117    /// 
118    /// # Returns
119    /// A reference to the static FdtManager instance.
120    #[allow(static_mut_refs)]
121    pub fn get_manager() -> &'static FdtManager<'a> {
122        unsafe { &MANAGER }
123    }
124
125    /// Relocates the FDT to a new address.
126    /// 
127    /// # Safety
128    /// This function modifies the static FDT address and reinitializes the FdtManager.
129    /// Ensure that the new address is valid
130    /// and does not cause memory corruption.
131    ///
132    /// # Parameters
133    /// - `ptr`: The pointer to the new FDT address.
134    ///
135    /// # Panics
136    /// 
137    /// This function will panic if the FDT has already been relocated.
138    /// 
139    unsafe fn relocate_fdt(&mut self, ptr: *mut u8) {
140        if self.relocated {
141            panic!("FDT already relocated");
142        }
143        // Copy the FDT to the new address
144        let size = self.get_fdt().unwrap().total_size();
145        let old_ptr = unsafe { FDT_ADDR } as *const u8;
146        unsafe { core::ptr::copy_nonoverlapping(old_ptr, ptr, size) };
147
148        // Reinitialize the FDT with the new address
149        match self.init(ptr) {
150            Ok(_) => {
151                self.relocated = true;
152                early_println!("FDT relocated to address: {:#x}", ptr as usize);
153            }
154            Err(e) => {
155                panic!("Failed to relocate FDT: {:?}", e);
156            }
157        }
158    }
159
160    /// Get the initramfs memory area from the device tree
161    ///
162    /// This function searches for the initramfs memory region in the device tree.
163    /// It looks for the initrd-start/end or linux,initrd-start/end properties
164    /// in the /chosen node.
165    ///
166    /// # Returns
167    /// Option<MemoryArea>: If the initramfs region is found, returns Some(MemoryArea),
168    /// otherwise returns None.
169    pub fn get_initramfs(&self) -> Option<MemoryArea> {
170        let fdt = self.get_fdt()?;
171        
172        // Find the /chosen node which contains initramfs information
173        let chosen_node = fdt.find_node("/chosen")?;
174        
175        // Try to find initramfs start address
176        // First check for "linux,initrd-start" property
177        let start_addr = if let Some(prop) = chosen_node.property("linux,initrd-start") {
178            if prop.value.len() == 8 {
179                let val = u64::from_be_bytes([
180                    prop.value[0],
181                    prop.value[1],
182                    prop.value[2],
183                    prop.value[3],
184                    prop.value[4],
185                    prop.value[5],
186                    prop.value[6],
187                    prop.value[7],
188                ]);
189                Some(val as usize)
190            } else if prop.value.len() == 4 {
191                let val = u32::from_be_bytes([
192                    prop.value[0],
193                    prop.value[1],
194                    prop.value[2],
195                    prop.value[3],
196                ]);
197                Some(val as usize)
198            } else {
199                None
200            }
201        // Then check for "initrd-start" property
202        } else if let Some(prop) = chosen_node.property("initrd-start") {
203            if prop.value.len() >= 4 {
204                let val = u32::from_be_bytes([
205                    prop.value[0],
206                    prop.value[1],
207                    prop.value[2],
208                    prop.value[3],
209                ]);
210                Some(val as usize)
211            } else {
212                None
213            }
214        } else {
215            None
216        };
217        
218        // Try to find initramfs end address
219        // First check for "linux,initrd-end" property
220        let end_addr = if let Some(prop) = chosen_node.property("linux,initrd-end") {
221            if prop.value.len() == 8 {
222                let val = u64::from_be_bytes([
223                    prop.value[0],
224                    prop.value[1],
225                    prop.value[2],
226                    prop.value[3],
227                    prop.value[4],
228                    prop.value[5],
229                    prop.value[6],
230                    prop.value[7],
231                ]);
232                Some(val as usize)
233            } else if prop.value.len() == 4 {
234                let val = u32::from_be_bytes([
235                    prop.value[0],
236                    prop.value[1],
237                    prop.value[2],
238                    prop.value[3],
239                ]);
240                Some(val as usize)
241            } else {
242                None
243            }
244        // Then check for "initrd-end" property
245        } else if let Some(prop) = chosen_node.property("initrd-end") {
246            if prop.value.len() >= 4 {
247                let val = u32::from_be_bytes([
248                    prop.value[0],
249                    prop.value[1],
250                    prop.value[2],
251                    prop.value[3],
252                ]);
253                Some(val as usize)
254            } else {
255                None
256            }
257        } else {
258            None
259        };
260
261        // If we have both start and end addresses, create a memory area
262        if let (Some(start), Some(end)) = (start_addr, end_addr) {
263            if end <= start {
264                return None;
265            }
266            
267            let size = end - start;
268            early_println!("[InitRamFS] Found initramfs: start={:#x}, end={:#x}, size={} bytes", 
269                start, end, size);
270            
271            let memory_area = MemoryArea::new(start, end - 1);
272            return Some(memory_area);
273        }
274        
275        early_println!("[InitRamFS] No initramfs found in device tree");
276        None
277    }
278
279    pub fn get_dram_memoryarea(&self) -> Option<MemoryArea> {
280        let fdt = self.get_fdt()?;
281        let memory_node = fdt.find_node("/memory")?;
282        
283        
284        let reg = memory_node.property("reg")?;
285        if reg.value.len() < 16 {
286            return None;
287        }
288        let reg_start = u64::from_be_bytes([
289            reg.value[0],
290            reg.value[1],
291            reg.value[2],
292            reg.value[3],
293            reg.value[4],
294            reg.value[5],
295            reg.value[6],
296            reg.value[7],
297        ]);
298        let start = reg_start as usize;
299        let size = u64::from_be_bytes([
300            reg.value[8],
301            reg.value[9],
302            reg.value[10],
303            reg.value[11],
304            reg.value[12],
305            reg.value[13],
306            reg.value[14],
307            reg.value[15],
308        ]) as usize;
309        Some(
310            MemoryArea::new(start as usize, start + size - 1) // end is inclusive
311        )
312    }
313
314}
315
316/// Initializes the FDT subsystem.
317pub fn init_fdt() {
318    let fdt_manager = unsafe { FdtManager::get_mut_manager() };
319    let fdt_ptr = unsafe { FDT_ADDR as *const u8 };
320    match fdt_manager.init(fdt_ptr) {
321        Ok(_) => {
322            early_println!("FDT initialized");
323            let fdt =  fdt_manager.get_fdt().unwrap();
324            
325            match fdt.chosen().bootargs() {
326                Some(bootargs) => early_println!("Bootargs: {}", bootargs),
327                None => early_println!("No bootargs found"),
328            }
329            let model = fdt.root().model();
330            early_println!("Model: {}", model);
331        }
332        Err(e) => {
333            early_println!("FDT error: {:?}", e);
334        }
335    }
336}
337
338/// Relocates the FDT to safe memory.
339/// 
340/// This function allocates memory for the FDT and relocates it to that address.
341/// 
342/// # Panic
343/// 
344/// This function will panic if the FDT has already been relocated or if
345/// the memory allocation fails.
346/// 
347pub fn relocate_fdt(dest_ptr: *mut u8) -> MemoryArea {
348    let fdt_manager = unsafe { FdtManager::get_mut_manager() };
349    if fdt_manager.relocated {
350        panic!("FDT already relocated");
351    }
352    let size = fdt_manager.get_fdt().unwrap().total_size();
353    unsafe { fdt_manager.relocate_fdt(dest_ptr) };
354    MemoryArea::new(dest_ptr as usize, dest_ptr as usize + size - 1) // return the memory area
355}