xref: /DragonOS/kernel/src/arch/x86_64/init/multiboot2.rs (revision a8753f8fffb992e4d3bbd21eda431081b395af6b)
1 use core::hint::spin_loop;
2 
3 use acpi::rsdp::Rsdp;
4 use alloc::string::{String, ToString};
5 use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType, RsdpV1Tag};
6 use system_error::SystemError;
7 
8 use crate::{
9     arch::mm::x86_64_set_kernel_load_base_paddr,
10     driver::{
11         serial::serial8250::send_to_default_serial8250_port,
12         video::fbdev::{
13             base::{BootTimeScreenInfo, BootTimeVideoType},
14             vesafb::vesafb_early_map,
15         },
16     },
17     init::{
18         boot::{register_boot_callbacks, BootCallbacks, BootloaderAcpiArg},
19         boot_params,
20     },
21     libs::lazy_init::Lazy,
22     mm::{memblock::mem_block_manager, PhysAddr},
23 };
24 
25 pub(super) const MULTIBOOT2_ENTRY_MAGIC: u32 = multiboot2::MAGIC;
26 static MB2_INFO: Lazy<BootInformation> = Lazy::new();
27 const MB2_RAW_INFO_MAX_SIZE: usize = 4096;
28 
29 static mut MB2_RAW_INFO: [u8; MB2_RAW_INFO_MAX_SIZE] = [0u8; MB2_RAW_INFO_MAX_SIZE];
30 
31 fn mb2_rsdp_v1_tag_to_rsdp_struct(tag: &RsdpV1Tag) -> Rsdp {
32     Rsdp {
33         signature: tag.signature,
34         checksum: tag.checksum,
35         oem_id: tag.oem_id,
36         revision: tag.revision,
37         rsdt_address: tag.rsdt_address,
38         length: 0,
39         xsdt_address: 0,
40         ext_checksum: 0,
41         reserved: [0u8; 3],
42     }
43 }
44 
45 fn mb2_rsdp_v2_tag_to_rsdp_struct(tag: &multiboot2::RsdpV2Tag) -> Rsdp {
46     Rsdp {
47         signature: tag.signature,
48         checksum: tag.checksum,
49         oem_id: tag.oem_id,
50         revision: tag.revision,
51         rsdt_address: tag.rsdt_address,
52         length: tag.length,
53         xsdt_address: tag.xsdt_address,
54         ext_checksum: tag.ext_checksum,
55         reserved: tag._reserved,
56     }
57 }
58 struct Mb2Callback;
59 
60 impl BootCallbacks for Mb2Callback {
61     fn init_bootloader_name(&self) -> Result<Option<String>, SystemError> {
62         let name = MB2_INFO
63             .get()
64             .boot_loader_name_tag()
65             .expect("MB2: Bootloader name tag not found!")
66             .name()
67             .expect("Failed to parse bootloader name!")
68             .to_string();
69         Ok(Some(name))
70     }
71 
72     fn init_acpi_args(&self) -> Result<BootloaderAcpiArg, SystemError> {
73         if let Some(v1_tag) = MB2_INFO.get().rsdp_v1_tag() {
74             Ok(BootloaderAcpiArg::Rsdt(mb2_rsdp_v1_tag_to_rsdp_struct(
75                 v1_tag,
76             )))
77         } else if let Some(v2_tag) = MB2_INFO.get().rsdp_v2_tag() {
78             Ok(BootloaderAcpiArg::Xsdt(mb2_rsdp_v2_tag_to_rsdp_struct(
79                 v2_tag,
80             )))
81         } else {
82             Ok(BootloaderAcpiArg::NotProvided)
83         }
84     }
85 
86     fn init_kernel_cmdline(&self) -> Result<(), SystemError> {
87         let cmdline = MB2_INFO
88             .get()
89             .command_line_tag()
90             .expect("Mb2: Command line tag not found!")
91             .cmdline()
92             .expect("Mb2: Failed to parse command line!");
93         boot_params()
94             .write_irqsave()
95             .boot_cmdline_append(cmdline.as_bytes());
96         Ok(())
97     }
98 
99     fn early_init_framebuffer_info(
100         &self,
101         scinfo: &mut BootTimeScreenInfo,
102     ) -> Result<(), SystemError> {
103         let Some(Ok(fb_tag)) = MB2_INFO.get().framebuffer_tag() else {
104             return Err(SystemError::ENODEV);
105         };
106         let width = fb_tag.width();
107         let height = fb_tag.height();
108         scinfo.is_vga = true;
109         scinfo.lfb_base = PhysAddr::new(fb_tag.address() as usize);
110 
111         let fb_type = fb_tag.buffer_type().unwrap();
112         match fb_type {
113             multiboot2::FramebufferType::Indexed { palette: _ } => todo!(),
114             multiboot2::FramebufferType::RGB { red, green, blue } => {
115                 scinfo.lfb_width = width;
116                 scinfo.lfb_height = height;
117                 scinfo.video_type = BootTimeVideoType::Vlfb;
118                 scinfo.lfb_depth = fb_tag.bpp();
119                 scinfo.red_pos = red.position;
120                 scinfo.red_size = red.size;
121                 scinfo.green_pos = green.position;
122                 scinfo.green_size = green.size;
123                 scinfo.blue_pos = blue.position;
124                 scinfo.blue_size = blue.size;
125             }
126             multiboot2::FramebufferType::Text => {
127                 scinfo.origin_video_cols = width as u8;
128                 scinfo.origin_video_lines = height as u8;
129                 scinfo.video_type = BootTimeVideoType::Mda;
130                 scinfo.lfb_depth = 8;
131             }
132         };
133 
134         scinfo.lfb_size = (width * height * ((scinfo.lfb_depth as u32 + 7) / 8)) as usize;
135 
136         scinfo.lfb_virt_base = Some(vesafb_early_map(scinfo.lfb_base, scinfo.lfb_size)?);
137 
138         return Ok(());
139     }
140 
141     fn early_init_memory_blocks(&self) -> Result<(), SystemError> {
142         let mb2_info = MB2_INFO.get();
143         send_to_default_serial8250_port("init_memory_area_from_multiboot2\n\0".as_bytes());
144 
145         let mem_regions_tag = mb2_info
146             .memory_map_tag()
147             .expect("MB2: Memory map tag not found!");
148         let mut total_mem_size = 0usize;
149         let mut usable_mem_size = 0usize;
150         for region in mem_regions_tag.memory_areas() {
151             let start = PhysAddr::new(region.start_address() as usize);
152             let size = region.size() as usize;
153             let area_typ = MemoryAreaType::from(region.typ());
154             total_mem_size += size;
155 
156             match area_typ {
157                 MemoryAreaType::Available => {
158                     usable_mem_size += size;
159                     mem_block_manager()
160                         .add_block(start, size)
161                         .unwrap_or_else(|e| {
162                             log::warn!(
163                                 "Failed to add memory block: base={:?}, size={:#x}, error={:?}",
164                                 start,
165                                 size,
166                                 e
167                             );
168                         });
169                 }
170 
171                 _ => {
172                     mem_block_manager()
173                         .reserve_block(start, size)
174                         .unwrap_or_else(|e| {
175                             log::warn!(
176                                 "Failed to reserve memory block: base={:?}, size={:#x}, error={:?}",
177                                 start,
178                                 size,
179                                 e
180                             );
181                         });
182                 }
183             }
184         }
185         send_to_default_serial8250_port("init_memory_area_from_multiboot2 end\n\0".as_bytes());
186         log::info!(
187             "Total memory size: {:#x}, Usable memory size: {:#x}",
188             total_mem_size,
189             usable_mem_size
190         );
191 
192         // Add the boot module region since Grub does not specify it.
193         let mb2_module_tag = mb2_info.module_tags();
194         for module in mb2_module_tag {
195             let start = PhysAddr::new(module.start_address() as usize);
196             let size = module.module_size() as usize;
197             mem_block_manager()
198                 .reserve_block(start, size)
199                 .unwrap_or_else(|e| {
200                     log::warn!(
201                         "Failed to reserve memory block for mb2 modules: base={:?}, size={:#x}, error={:?}",
202                         start,
203                         size,
204                         e
205                     );
206                 });
207         }
208 
209         // setup kernel load base
210         self.setup_kernel_load_base();
211 
212         Ok(())
213     }
214 }
215 
216 impl Mb2Callback {
217     fn setup_kernel_load_base(&self) {
218         let mb2_info = MB2_INFO.get();
219         let kernel_start = mb2_info
220             .load_base_addr_tag()
221             .expect("MB2: Load base address tag not found!")
222             .load_base_addr();
223         let loadbase = PhysAddr::new(kernel_start as usize);
224         x86_64_set_kernel_load_base_paddr(loadbase);
225     }
226 }
227 pub(super) fn early_multiboot2_init(boot_magic: u32, boot_info: u64) -> Result<(), SystemError> {
228     assert_eq!(boot_magic, MULTIBOOT2_ENTRY_MAGIC);
229     let bi_ptr = boot_info as usize as *const BootInformationHeader;
230     let bi_size = unsafe { (*bi_ptr).total_size() as usize };
231     assert!(bi_size <= MB2_RAW_INFO_MAX_SIZE);
232     unsafe {
233         core::ptr::copy_nonoverlapping(bi_ptr as *const u8, MB2_RAW_INFO.as_mut_ptr(), bi_size);
234     }
235 
236     let boot_info =
237         unsafe { BootInformation::load(MB2_RAW_INFO.as_mut_ptr() as *const BootInformationHeader) }
238             .inspect_err(|_| loop {
239                 spin_loop();
240             })
241             .unwrap();
242 
243     MB2_INFO.init(boot_info);
244 
245     register_boot_callbacks(&Mb2Callback);
246 
247     return Ok(());
248 }
249