xref: /DragonOS/kernel/src/driver/firmware/efi/tables.rs (revision 0102d69fdd231e472d7bb3d609a41ae56a3799ee)
1 use core::{ffi::CStr, mem::size_of};
2 
3 use hashbrown::Equivalent;
4 use system_error::SystemError;
5 use uefi_raw::table::{
6     boot::{MemoryAttribute, MemoryType},
7     configuration::ConfigurationTable,
8 };
9 
10 use crate::{
11     arch::MMArch,
12     driver::firmware::efi::{
13         efi_manager,
14         guid::{DragonStubPayloadEFI, DRAGONSTUB_EFI_PAYLOAD_EFI_GUID},
15     },
16     mm::{
17         early_ioremap::EarlyIoRemap, memblock::mem_block_manager, MemoryManagementArch, PhysAddr,
18         VirtAddr,
19     },
20 };
21 
22 use super::{
23     guid::{
24         EFI_MEMORY_ATTRIBUTES_TABLE_GUID, EFI_MEMRESERVE_TABLE_GUID, EFI_SYSTEM_RESOURCE_TABLE_GUID,
25     },
26     EFIManager,
27 };
28 
29 /// 所有的要解析的表格的解析器
30 static TABLE_PARSERS: &[&TableMatcher] = &[
31     &TableMatcher::new(&MatchTableDragonStubPayloadEFI),
32     &TableMatcher::new(&MatchTableMemoryAttributes),
33     &TableMatcher::new(&MatchTableMemReserve),
34     &TableMatcher::new(&MatchTableEsrt),
35 ];
36 
37 impl EFIManager {
38     /// 显示EFI系统表头的信息
39     ///
40     /// ## 参数
41     ///
42     /// - header: system table表头
43     /// - firmware_vendor: firmware vendor字符串的物理地址
44     #[inline(never)]
45     pub fn report_systable_header(
46         &self,
47         header: &uefi_raw::table::Header,
48         firmware_vendor: PhysAddr,
49     ) {
50         const TMPBUF_SIZE: usize = 100;
51 
52         let mut tmp_buf = [0u8; TMPBUF_SIZE];
53 
54         let fw_ptr =
55             EarlyIoRemap::map_not_aligned(firmware_vendor, TMPBUF_SIZE * size_of::<u16>(), true);
56         if let Ok(fw_ptr) = fw_ptr {
57             let slice =
58                 unsafe { core::slice::from_raw_parts(fw_ptr.data() as *const u16, TMPBUF_SIZE) };
59             for i in 0..(TMPBUF_SIZE - 1) {
60                 let val = slice[i];
61 
62                 if (val & 0xff) == 0 {
63                     break;
64                 }
65                 tmp_buf[i] = (val & 0xff) as u8;
66             }
67 
68             EarlyIoRemap::unmap(fw_ptr).map_err(|e|{
69                 kerror!("report systable header: failed to unmap systable header, fw_ptr: {fw_ptr:?}, err: {e:?}");
70                 e
71             }).ok();
72         } else {
73             kwarn!("report systable header: failed to map systable header, err: {fw_ptr:?}");
74         }
75 
76         let s = CStr::from_bytes_with_nul(&tmp_buf)
77             .unwrap_or_else(|_| CStr::from_bytes_with_nul(b"Unknown\0").unwrap());
78         kinfo!("EFI version: {:?}, vendor: {:?}", header.revision, s);
79     }
80 
81     /// 解析EFI config table
82     pub fn parse_config_tables(&self, tables: &[ConfigurationTable]) -> Result<(), SystemError> {
83         for table in tables {
84             let mut flag = false;
85             'parser_loop: for parser in TABLE_PARSERS {
86                 if let Some(r) = parser.match_table(table) {
87                     // 有匹配结果
88                     if let Err(e) = r {
89                         kwarn!(
90                             "Failed to parse cfg table: '{}', err: {e:?}",
91                             parser.table.name()
92                         );
93                     }
94                     flag = true;
95                     break 'parser_loop;
96                 }
97             }
98 
99             if !flag {
100                 kwarn!("Cannot find parser for guid: {:?}", table.vendor_guid);
101             }
102         }
103 
104         // 如果存在mem reserve table
105         if let Some(mem_reserve) = efi_manager().inner_read().memreserve_table_paddr {
106             let mut prev_paddr = mem_reserve;
107             while !prev_paddr.is_null() {
108                 let vaddr = EarlyIoRemap::map_not_aligned(prev_paddr, MMArch::PAGE_SIZE, true)
109                     .map_err(|e| {
110                         kerror!(
111                             "Failed to map UEFI memreserve table, paddr: {prev_paddr:?}, err: {e:?}"
112                         );
113 
114                         SystemError::ENOMEM
115                     })?;
116 
117                 let p = unsafe {
118                     (vaddr.data() as *const LinuxEFIMemReserveTable)
119                         .as_ref()
120                         .unwrap()
121                 };
122 
123                 // reserve the entry itself
124                 let psize: usize = p.size.try_into().unwrap();
125                 mem_block_manager()
126                     .reserve_block(
127                         prev_paddr,
128                         size_of::<LinuxEFIMemReserveTable>()
129                             + size_of::<LinuxEFIMemReserveEntry>() * psize,
130                     )
131                     .map_err(|e| {
132                         kerror!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}");
133                         EarlyIoRemap::unmap(vaddr).unwrap();
134                         e
135                     })?;
136 
137                 let entries = unsafe {
138                     core::slice::from_raw_parts(
139                         (vaddr.data() as *const LinuxEFIMemReserveTable).add(1)
140                             as *const LinuxEFIMemReserveEntry,
141                         p.count as usize,
142                     )
143                 };
144                 // reserve the entries
145                 for entry in entries {
146                     mem_block_manager()
147                         .reserve_block(PhysAddr::new(entry.base), entry.size)
148                         .map_err(|e| {
149                             kerror!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}");
150                             EarlyIoRemap::unmap(vaddr).unwrap();
151                             e
152                         })?;
153                 }
154 
155                 prev_paddr = p.next_paddr;
156                 EarlyIoRemap::unmap(vaddr).unwrap();
157             }
158         }
159         return Ok(());
160     }
161 }
162 
163 /// A structure describing a region of memory.
164 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
165 #[repr(C)]
166 pub struct MemoryDescriptor {
167     /// Type of memory occupying this range.
168     pub ty: MemoryType,
169     /// Starting physical address.
170     pub phys_start: uefi_raw::PhysicalAddress,
171     /// Starting virtual address.
172     pub virt_start: uefi_raw::VirtualAddress,
173     /// Number of 4 KiB pages contained in this range.
174     pub page_count: u64,
175     /// The capability attributes of this memory range.
176     pub att: MemoryAttribute,
177 }
178 
179 #[allow(dead_code)]
180 impl MemoryDescriptor {
181     /// Memory descriptor version number.
182     pub const VERSION: u32 = 1;
183 
184     /// 当前内存描述符是否表示真实的内存
185     #[inline]
186     pub fn is_memory(&self) -> bool {
187         if self.att.contains(MemoryAttribute::WRITE_BACK)
188             || self.att.contains(MemoryAttribute::WRITE_THROUGH)
189             || self.att.contains(MemoryAttribute::WRITE_COMBINE)
190         {
191             return true;
192         }
193 
194         return false;
195     }
196 
197     /// 判断当前内存描述符所表示的区域是否能被作为系统内存使用
198     ///
199     /// ## 返回
200     ///
201     /// - `true` - 可以
202     /// - `false` - 不可以
203     pub fn is_usable_memory(&self) -> bool {
204         match self.ty {
205             MemoryType::LOADER_CODE
206             | MemoryType::LOADER_DATA
207             | MemoryType::ACPI_RECLAIM
208             | MemoryType::BOOT_SERVICES_CODE
209             | MemoryType::BOOT_SERVICES_DATA
210             | MemoryType::CONVENTIONAL
211             | MemoryType::PERSISTENT_MEMORY => {
212                 // SPECIAL_PURPOSE的内存是“软保留”的,这意味着它最初被留出,
213                 // 但在启动后可以通过热插拔再次使用,或者分配给dax驱动程序。
214                 if self.att.contains(MemoryAttribute::SPECIAL_PURPOSE) {
215                     return false;
216                 }
217 
218                 // 根据规范,在调用ExitBootServices()之后,这些区域就不再被保留了。
219                 // 然而,只有当它们可以被映射为WRITE_BACK缓存时,我们才能将它们用作系统内存
220                 return self.att.contains(MemoryAttribute::WRITE_BACK);
221             }
222             _ => {
223                 return false;
224             }
225         }
226     }
227 }
228 
229 impl Default for MemoryDescriptor {
230     fn default() -> MemoryDescriptor {
231         MemoryDescriptor {
232             ty: MemoryType::RESERVED,
233             phys_start: 0,
234             virt_start: 0,
235             page_count: 0,
236             att: MemoryAttribute::empty(),
237         }
238     }
239 }
240 
241 trait MatchTable: Send + Sync {
242     /// 配置表名(仅用于日志显示)
243     fn name(&self) -> &'static str;
244 
245     /// 当前table的guid
246     fn guid(&self) -> &'static uefi_raw::Guid;
247 
248     /// 匹配阶段时,匹配器要映射vendor_table的大小。
249     ///
250     /// 如果为0,则不映射
251     fn map_size(&self) -> usize;
252 
253     /// 当表格被映射后,调用这个函数
254     ///
255     /// ## 锁
256     ///
257     /// 进入该函数前,不得持有efi_manager().inner的任何锁
258     fn post_process(
259         &self,
260         vendor_table_vaddr: Option<VirtAddr>,
261         table_raw: &ConfigurationTable,
262     ) -> Result<(), SystemError>;
263 }
264 
265 /// `DRAGONSTUB_EFI_PAYLOAD_EFI_GUID` 的匹配器
266 struct MatchTableDragonStubPayloadEFI;
267 
268 impl MatchTable for MatchTableDragonStubPayloadEFI {
269     fn name(&self) -> &'static str {
270         "DragonStub Payload"
271     }
272 
273     fn guid(&self) -> &'static uefi_raw::Guid {
274         &DRAGONSTUB_EFI_PAYLOAD_EFI_GUID
275     }
276 
277     fn map_size(&self) -> usize {
278         core::mem::size_of::<DragonStubPayloadEFI>()
279     }
280 
281     fn post_process(
282         &self,
283         vendor_table_vaddr: Option<VirtAddr>,
284         _table_raw: &ConfigurationTable,
285     ) -> Result<(), SystemError> {
286         let vendor_table_vaddr = vendor_table_vaddr.unwrap();
287         let data = unsafe { *(vendor_table_vaddr.data() as *const DragonStubPayloadEFI) };
288 
289         efi_manager().inner_write().dragonstub_load_info = Some(data);
290 
291         return Ok(());
292     }
293 }
294 
295 struct MatchTableMemoryAttributes;
296 
297 impl MatchTable for MatchTableMemoryAttributes {
298     fn name(&self) -> &'static str {
299         "MemAttr"
300     }
301 
302     fn guid(&self) -> &'static uefi_raw::Guid {
303         &EFI_MEMORY_ATTRIBUTES_TABLE_GUID
304     }
305 
306     fn map_size(&self) -> usize {
307         // 不映射
308         0
309     }
310 
311     fn post_process(
312         &self,
313         _vendor_table_vaddr: Option<VirtAddr>,
314         table_raw: &ConfigurationTable,
315     ) -> Result<(), SystemError> {
316         efi_manager()
317             .inner
318             .write_irqsave()
319             .memory_attribute_table_paddr = Some(PhysAddr::new(table_raw.vendor_table as usize));
320         return Ok(());
321     }
322 }
323 
324 struct MatchTableMemReserve;
325 
326 impl MatchTable for MatchTableMemReserve {
327     fn name(&self) -> &'static str {
328         "MemReserve"
329     }
330 
331     fn guid(&self) -> &'static uefi_raw::Guid {
332         &EFI_MEMRESERVE_TABLE_GUID
333     }
334 
335     fn map_size(&self) -> usize {
336         // 不映射
337         0
338     }
339 
340     fn post_process(
341         &self,
342         _vendor_table_vaddr: Option<VirtAddr>,
343         table_raw: &ConfigurationTable,
344     ) -> Result<(), SystemError> {
345         efi_manager().inner.write_irqsave().memreserve_table_paddr =
346             Some(PhysAddr::new(table_raw.vendor_table as usize));
347         kdebug!(
348             "memreserve_table_paddr: {:#x}",
349             table_raw.vendor_table as usize
350         );
351         return Ok(());
352     }
353 }
354 
355 struct MatchTableEsrt;
356 
357 impl MatchTable for MatchTableEsrt {
358     fn name(&self) -> &'static str {
359         "ESRT"
360     }
361 
362     fn guid(&self) -> &'static uefi_raw::Guid {
363         &EFI_SYSTEM_RESOURCE_TABLE_GUID
364     }
365 
366     fn map_size(&self) -> usize {
367         0
368     }
369 
370     fn post_process(
371         &self,
372         _vendor_table_vaddr: Option<VirtAddr>,
373         table_raw: &ConfigurationTable,
374     ) -> Result<(), SystemError> {
375         efi_manager().inner.write_irqsave().esrt_table_paddr =
376             Some(PhysAddr::new(table_raw.vendor_table as usize));
377         kdebug!("esrt_table_paddr: {:#x}", table_raw.vendor_table as usize);
378         return Ok(());
379     }
380 }
381 
382 /// 用于匹配配置表的匹配器
383 struct TableMatcher {
384     table: &'static dyn MatchTable,
385 }
386 
387 impl TableMatcher {
388     const fn new(table: &'static dyn MatchTable) -> Self {
389         Self { table }
390     }
391 
392     /// 判断配置表与当前匹配器是否匹配
393     #[inline(never)]
394     fn match_table(&self, table: &ConfigurationTable) -> Option<Result<(), SystemError>> {
395         if !table.vendor_guid.equivalent(self.table.guid()) {
396             return None;
397         }
398 
399         let table_map_size = self.table.map_size();
400 
401         let vendor_table_vaddr: Option<VirtAddr> = if table_map_size > 0 {
402             let table_paddr: PhysAddr = PhysAddr::new(table.vendor_table as usize);
403             let vaddr = EarlyIoRemap::map_not_aligned(table_paddr, table_map_size, true);
404 
405             if let Err(e) = vaddr {
406                 return Some(Err(e));
407             }
408 
409             Some(vaddr.unwrap())
410         } else {
411             None
412         };
413 
414         let r = self.table.post_process(vendor_table_vaddr, table);
415 
416         if let Some(vaddr) = vendor_table_vaddr {
417             EarlyIoRemap::unmap(vaddr).unwrap();
418         }
419         return Some(r);
420     }
421 }
422 
423 #[repr(C)]
424 #[derive(Debug)]
425 struct LinuxEFIMemReserveTable {
426     /// allocated size of the array
427     size: i32,
428     /// number of entries used
429     count: i32,
430     /// pa of next struct instance
431     next_paddr: PhysAddr,
432     entry: [LinuxEFIMemReserveEntry; 0],
433 }
434 
435 #[repr(C)]
436 #[derive(Debug)]
437 struct LinuxEFIMemReserveEntry {
438     base: usize,
439     size: usize,
440 }
441