riscv: 完成UEFI初始化,能正确设置memblock的信息 (#501)

* riscv: 完成UEFI初始化,能正确设置memblock的信息

* sbi增加reset功能

* 把虚拟CPU修改为sifive-u54,使qemu能更正确地模拟硬件行为

* 修复内存页面映射未设置“DIRTY”、”ACCESSED“、”GLOBAL“位,导致真机page fault的问题
This commit is contained in:
LoGin
2024-01-26 18:08:39 +08:00
committed by GitHub
parent a381e482cb
commit 9284987850
22 changed files with 754 additions and 130 deletions

View File

@ -0,0 +1,35 @@
use core::{fmt, mem};
use uefi_raw::Guid;
/// 由DragonStub设置的用于描述内核被放置在的地址的GUID
pub static DRAGONSTUB_EFI_PAYLOAD_EFI_GUID: Guid = Guid::new(
unsafe { mem::transmute_copy(&0xddf1d47cu32) },
unsafe { mem::transmute_copy(&0x102cu32) },
unsafe { mem::transmute_copy(&0xaaf9u32) },
0xce,
0x34,
[0xbc, 0xef, 0x98, 0x12, 0x00, 0x31],
);
/// 表示内核被加载到的地址的信息。
///
/// 对应 `DRAGONSTUB_EFI_PAYLOAD_EFI_GUID`
#[derive(Clone, Copy)]
#[repr(C)]
pub struct DragonStubPayloadEFI {
/// 内核文件被加载到的物理地址
pub paddr: u64,
/// 占用的空间的大小
pub size: u64,
}
impl fmt::Debug for DragonStubPayloadEFI {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DragonStubPayloadEFI")
.field("paddr", &format_args!("0x{:x}", self.paddr))
.field("size", &self.size)
.finish()
}
}

View File

@ -1,11 +1,16 @@
use core::{intrinsics::unlikely, mem::size_of};
use system_error::SystemError;
use uefi_raw::table::boot::{MemoryAttribute, MemoryType};
use crate::{
driver::firmware::efi::EFIInitFlags,
libs::align::page_align_down,
mm::{early_ioremap::EarlyIoRemap, PhysAddr, VirtAddr},
arch::MMArch,
driver::{firmware::efi::EFIInitFlags, open_firmware::fdt::open_firmware_fdt_driver},
libs::align::{page_align_down, page_align_up},
mm::{
allocator::page_frame::PhysPageFrame, early_ioremap::EarlyIoRemap,
memblock::mem_block_manager, MemoryManagementArch, PhysAddr, VirtAddr,
},
};
use super::efi_manager;
@ -13,6 +18,7 @@ use super::efi_manager;
#[allow(dead_code)]
#[inline(never)]
pub fn efi_init() {
kinfo!("Initializing efi...");
let data_from_fdt = efi_manager()
.get_fdt_params()
.expect("Failed to get fdt params");
@ -22,7 +28,7 @@ pub fn efi_init() {
return;
}
kdebug!("to map memory table");
// kdebug!("to map memory table");
// 映射mmap table
if efi_manager().memmap_init_early(&data_from_fdt).is_err() {
@ -42,15 +48,36 @@ pub fn efi_init() {
kwarn!("Unexpected EFI memory map version: {}", desc_version);
}
// todo: 映射table初始化runtime services
let r = uefi_init(PhysAddr::new(data_from_fdt.systable.unwrap() as usize));
if let Err(r) = r {
kerror!("Failed to initialize UEFI: {:?}", r);
if let Err(e) = r {
kerror!("Failed to initialize UEFI: {:?}", e);
efi_manager().efi_memmap_unmap();
return;
}
loop {}
reserve_memory_regions();
// todo: 由于上面的`uefi_init`里面按照UEFI的数据初始化了内存块
// 但是UEFI给的数据可能不全这里Linux会再次从设备树检测可用内存从而填补完全相应的内存信息
// 并且Linux还对EFI BootService提供的Mokvar表进行了检测以及空间保留。
// todo: 模仿Linux的行为做好接下来的几步工作
// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/firmware/efi/efi-init.c#217
// 保留mmap table的内存
let base = page_align_down(data_from_fdt.mmap_base.unwrap() as usize);
let offset = data_from_fdt.mmap_base.unwrap() as usize - base;
mem_block_manager()
.reserve_block(
PhysAddr::new(base),
data_from_fdt.mmap_size.unwrap() as usize + offset,
)
.expect("Failed to reserve memory for EFI mmap table");
// todo: Initialize screen info
kinfo!("UEFI init done!");
}
#[inline(never)]
@ -69,18 +96,11 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> {
// 映射system table
let st_size = size_of::<uefi_raw::table::system::SystemTable>();
kdebug!("system table: {system_table:?}, size: {st_size}");
let st_map_phy_base = PhysAddr::new(page_align_down(system_table.data()));
let st_map_offset = system_table.data() - st_map_phy_base.data();
let st_map_size = st_size + st_map_offset;
let (st_vaddr, _st_map_size) =
EarlyIoRemap::map(st_map_phy_base, st_map_size, true).map_err(|e| {
kwarn!("Unable to map EFI system table, e:{e:?}");
e
})?;
let st_vaddr = st_vaddr + st_map_offset;
let st_vaddr = EarlyIoRemap::map_not_aligned(system_table, st_size, true).map_err(|e| {
kwarn!("Unable to map EFI system table, e:{e:?}");
e
})?;
efi_manager()
.inner
@ -94,8 +114,6 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> {
.init_flags
.set(EFIInitFlags::EFI_64BIT, true);
kdebug!("to parse EFI system table: p: {st_vaddr:?}");
if st_vaddr.is_null() {
return Err(SystemError::EINVAL);
}
@ -109,21 +127,113 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> {
e
})?;
kdebug!("parse ok!");
let mut inner_write_guard = efi_manager().inner.write();
let st_ref = unsafe { st_ptr.as_ref().unwrap() };
inner_write_guard.runtime_paddr = Some(PhysAddr::new(st_ref.runtime_services as usize));
let runtime_service_paddr = efi_vaddr_2_paddr(st_ref.runtime_services as usize);
let mut inner_write_guard = efi_manager().inner.write();
inner_write_guard.runtime_paddr = Some(runtime_service_paddr);
inner_write_guard.runtime_service_version = Some(st_ref.header.revision);
kdebug!(
"runtime service paddr: {:?}",
inner_write_guard.runtime_paddr.unwrap()
);
kdebug!(
"runtime service version: {}",
inner_write_guard.runtime_service_version.unwrap()
drop(inner_write_guard);
efi_manager().report_systable_header(
&st_ref.header,
efi_vaddr_2_paddr(st_ref.firmware_vendor as usize),
);
unimplemented!("report header");
// return Ok(());
{
// 映射configuration table
let table_size = st_ref.number_of_configuration_table_entries
* size_of::<uefi_raw::table::configuration::ConfigurationTable>();
let config_table_vaddr = EarlyIoRemap::map_not_aligned(
efi_vaddr_2_paddr(st_ref.configuration_table as usize),
table_size,
true,
)
.map_err(|e| {
kwarn!("Unable to map EFI configuration table, e:{e:?}");
err_unmap_systable(st_vaddr);
e
})?;
let cfg_tables = unsafe {
core::slice::from_raw_parts(
config_table_vaddr.data()
as *const uefi_raw::table::configuration::ConfigurationTable,
st_ref.number_of_configuration_table_entries,
)
};
// 解析configuration table
let r = efi_manager().parse_config_tables(cfg_tables);
EarlyIoRemap::unmap(config_table_vaddr).expect("Failed to unmap EFI config table");
return r;
}
}
/// 把EFI固件提供的虚拟地址转换为物理地址。
///
/// 因为在调用SetVirtualAddressMap()之后,`EFI SystemTable` 的一些数据成员会被虚拟重映射
///
/// ## 锁
///
/// 在进入该函数前,请不要持有`efi_manager().inner`的写锁
fn efi_vaddr_2_paddr(efi_vaddr: usize) -> PhysAddr {
let guard = efi_manager().inner.read();
let mmap = &guard.mmap;
let efi_vaddr: u64 = efi_vaddr as u64;
for md in mmap.iter() {
if !md.att.contains(MemoryAttribute::RUNTIME) {
continue;
}
if md.virt_start == 0 {
// no virtual mapping has been installed by the DragonStub
break;
}
if md.virt_start <= efi_vaddr
&& ((efi_vaddr - md.virt_start) < (md.page_count << (MMArch::PAGE_SHIFT as u64)))
{
return PhysAddr::new((md.phys_start + (efi_vaddr - md.virt_start)) as usize);
}
}
return PhysAddr::new(efi_vaddr as usize);
}
/// 根据UEFI提供的内存描述符的信息填写内存区域信息
fn reserve_memory_regions() {
// 忽略之前已经发现的任何内存块。因为之前发现的内存块来自平坦设备树,
// 但是UEFI有自己的内存映射表我们以UEFI提供的为准
mem_block_manager()
.remove_block(PhysAddr::new(0), PhysAddr::MAX.data())
.expect("Failed to remove all memblocks!");
let inner_guard = efi_manager().inner.read_irqsave();
for md in inner_guard.mmap.iter() {
let page_count = (PhysPageFrame::new(PhysAddr::new(page_align_up(
(md.phys_start + md.page_count << (MMArch::PAGE_SHIFT as u64)) as usize,
)))
.ppn()
- PhysPageFrame::new(PhysAddr::new(page_align_down(md.phys_start as usize))).ppn())
as u64;
let phys_start = page_align_down(md.phys_start as usize);
let size = (page_count << (MMArch::PAGE_SHIFT as u64)) as usize;
if md.is_memory() {
open_firmware_fdt_driver().early_init_dt_add_memory(phys_start as u64, size as u64);
if !md.is_usable_memory() {
mem_block_manager()
.mark_nomap(PhysAddr::new(phys_start), size)
.unwrap();
}
// keep ACPI reclaim memory intact for kexec etc.
if md.ty == MemoryType::ACPI_RECLAIM {
mem_block_manager()
.reserve_block(PhysAddr::new(phys_start), size)
.unwrap();
}
}
}
}

View File

@ -1,3 +1,5 @@
use core::{intrinsics::unlikely, mem::size_of};
use system_error::SystemError;
use crate::{
@ -6,7 +8,7 @@ use crate::{
mm::{early_ioremap::EarlyIoRemap, PhysAddr, VirtAddr},
};
use super::{fdt::EFIFdtParams, EFIManager};
use super::{fdt::EFIFdtParams, tables::MemoryDescriptor, EFIManager};
#[derive(Debug)]
pub struct EFIMemoryMapInfo {
@ -22,6 +24,11 @@ pub struct EFIMemoryMapInfo {
pub(super) desc_size: usize,
/// EFI Memory Map的描述信息的版本
pub(super) desc_version: usize,
/// 当前是否在内存管理已经完成初始化后,对该结构体进行操作
///
/// true: 内存管理已经完成初始化
/// false: 内存管理还未完成初始化
pub(super) late: bool,
}
impl EFIMemoryMapInfo {
@ -32,6 +39,7 @@ impl EFIMemoryMapInfo {
nr_map: 0,
desc_size: 0,
desc_version: 0,
late: false,
};
/// 获取EFI Memory Map的虚拟的结束地址
@ -39,6 +47,43 @@ impl EFIMemoryMapInfo {
pub fn map_end_vaddr(&self) -> Option<VirtAddr> {
return self.vaddr.map(|v| v + self.size);
}
/// 迭代所有的内存描述符
pub fn iter(&self) -> EFIMemoryDescIter {
EFIMemoryDescIter::new(self)
}
}
/// UEFI 内存描述符的迭代器
pub struct EFIMemoryDescIter<'a> {
inner: &'a EFIMemoryMapInfo,
offset: usize,
}
impl<'a> EFIMemoryDescIter<'a> {
fn new(inner: &'a EFIMemoryMapInfo) -> Self {
Self { inner, offset: 0 }
}
}
impl<'a> Iterator for EFIMemoryDescIter<'a> {
type Item = MemoryDescriptor;
fn next(&mut self) -> Option<Self::Item> {
if self.offset + size_of::<Self::Item>() > self.inner.size {
return None;
}
// 如果是空指针返回None
if unlikely(self.inner.vaddr.unwrap_or(VirtAddr::new(0)).is_null()) {
return None;
}
let vaddr = self.inner.vaddr? + self.offset;
self.offset += size_of::<Self::Item>();
let res = unsafe { *(vaddr.data() as *const Self::Item) };
return Some(res);
}
}
impl EFIManager {
@ -59,13 +104,14 @@ impl EFIManager {
fn do_efi_memmap_init(&self, data: &EFIFdtParams, early: bool) -> Result<(), SystemError> {
let paddr = data.mmap_base.expect("mmap_base is not set");
let paddr = PhysAddr::new(paddr as usize);
kdebug!("do_efi_memmap_init: paddr={paddr:?}");
let mut inner_guard = self.inner.write();
if early {
let offset = paddr.data() - page_align_down(paddr.data());
let map_size = data.mmap_size.unwrap() as usize + offset;
kdebug!("do_efi_memmap_init: map_size={map_size:#x}");
// kdebug!("do_efi_memmap_init: map_size={map_size:#x}");
// 映射内存
let mut vaddr = EarlyIoRemap::map(
PhysAddr::new(page_align_down(paddr.data())),
@ -77,7 +123,9 @@ impl EFIManager {
vaddr += offset;
inner_guard.mmap.vaddr = Some(vaddr);
inner_guard.mmap.late = false;
} else {
inner_guard.mmap.late = true;
unimplemented!("efi_memmap_init_late")
}
@ -97,4 +145,21 @@ impl EFIManager {
return Ok(());
}
/// 清除EFI Memory Table在内存中的映射
pub fn efi_memmap_unmap(&self) {
let mut inner_guard = self.inner.write_irqsave();
// 没有启用memmap
if !inner_guard.init_flags.contains(EFIInitFlags::MEMMAP) {
return;
}
if !inner_guard.mmap.late {
EarlyIoRemap::unmap(inner_guard.mmap.vaddr.take().unwrap()).unwrap();
} else {
unimplemented!("efi_memmap_unmap");
}
inner_guard.init_flags.set(EFIInitFlags::MEMMAP, false);
}
}

View File

@ -2,11 +2,13 @@ use system_error::SystemError;
use crate::{libs::rwlock::RwLock, mm::PhysAddr};
use self::memmap::EFIMemoryMapInfo;
use self::{guid::DragonStubPayloadEFI, memmap::EFIMemoryMapInfo};
mod fdt;
pub mod guid;
pub mod init;
pub mod memmap;
pub mod tables;
static EFI_MANAGER: EFIManager = EFIManager::new();
@ -32,6 +34,7 @@ struct InnerEFIManager {
pub runtime_paddr: Option<PhysAddr>,
/// runtime services的版本号
pub runtime_service_version: Option<uefi_raw::table::Revision>,
pub dragonstub_load_info: Option<DragonStubPayloadEFI>,
}
impl EFIManager {
@ -42,6 +45,7 @@ impl EFIManager {
init_flags: EFIInitFlags::empty(),
runtime_paddr: None,
runtime_service_version: None,
dragonstub_load_info: None,
}),
}
}

View File

@ -0,0 +1,167 @@
use core::{ffi::CStr, mem::size_of};
use hashbrown::Equivalent;
use system_error::SystemError;
use uefi_raw::table::{
boot::{MemoryAttribute, MemoryType},
configuration::ConfigurationTable,
};
use crate::{
driver::firmware::efi::{
efi_manager,
guid::{DragonStubPayloadEFI, DRAGONSTUB_EFI_PAYLOAD_EFI_GUID},
},
mm::{early_ioremap::EarlyIoRemap, PhysAddr},
};
use super::EFIManager;
impl EFIManager {
/// 显示EFI系统表头的信息
///
/// ## 参数
///
/// - header: system table表头
/// - firmware_vendor: firmware vendor字符串的物理地址
#[inline(never)]
pub fn report_systable_header(
&self,
header: &uefi_raw::table::Header,
firmware_vendor: PhysAddr,
) {
const TMPBUF_SIZE: usize = 100;
let mut tmp_buf = [0u8; TMPBUF_SIZE];
let fw_ptr =
EarlyIoRemap::map_not_aligned(firmware_vendor, TMPBUF_SIZE * size_of::<u16>(), true);
if let Ok(fw_ptr) = fw_ptr {
let slice =
unsafe { core::slice::from_raw_parts(fw_ptr.data() as *const u16, TMPBUF_SIZE) };
for i in 0..(TMPBUF_SIZE - 1) {
let val = slice[i];
if (val & 0xff) == 0 {
break;
}
tmp_buf[i] = (val & 0xff) as u8;
}
EarlyIoRemap::unmap(fw_ptr).map_err(|e|{
kerror!("report systable header: failed to unmap systable header, fw_ptr: {fw_ptr:?}, err: {e:?}");
e
}).ok();
} else {
kwarn!("report systable header: failed to map systable header, err: {fw_ptr:?}");
}
let s = CStr::from_bytes_with_nul(&tmp_buf)
.unwrap_or_else(|_| CStr::from_bytes_with_nul(b"Unknown\0").unwrap());
kinfo!("EFI version: {:?}, vendor: {:?}", header.revision, s);
}
/// 解析EFI config table
pub fn parse_config_tables(&self, tables: &[ConfigurationTable]) -> Result<(), SystemError> {
for table in tables {
if table
.vendor_guid
.equivalent(&DRAGONSTUB_EFI_PAYLOAD_EFI_GUID)
{
let table_paddr: PhysAddr = PhysAddr::new(table.vendor_table as usize);
let vaddr = EarlyIoRemap::map_not_aligned(
table_paddr,
size_of::<DragonStubPayloadEFI>(),
true,
)?;
let data = unsafe { *(vaddr.data() as *const DragonStubPayloadEFI) };
efi_manager().inner.write().dragonstub_load_info = Some(data);
EarlyIoRemap::unmap(vaddr).unwrap();
}
}
return Ok(());
}
}
/// A structure describing a region of memory.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct MemoryDescriptor {
/// Type of memory occupying this range.
pub ty: MemoryType,
/// Starting physical address.
pub phys_start: uefi_raw::PhysicalAddress,
/// Starting virtual address.
pub virt_start: uefi_raw::VirtualAddress,
/// Number of 4 KiB pages contained in this range.
pub page_count: u64,
/// The capability attributes of this memory range.
pub att: MemoryAttribute,
}
#[allow(dead_code)]
impl MemoryDescriptor {
/// Memory descriptor version number.
pub const VERSION: u32 = 1;
/// 当前内存描述符是否表示真实的内存
#[inline]
pub fn is_memory(&self) -> bool {
if self.att.contains(
MemoryAttribute::WRITE_BACK
| MemoryAttribute::WRITE_THROUGH
| MemoryAttribute::WRITE_COMBINE,
) {
return true;
}
return false;
}
/// 判断当前内存描述符所表示的区域是否能被作为系统内存使用
///
/// ## 返回
///
/// - `true` - 可以
/// - `false` - 不可以
pub fn is_usable_memory(&self) -> bool {
match self.ty {
MemoryType::LOADER_CODE
| MemoryType::LOADER_DATA
| MemoryType::ACPI_RECLAIM
| MemoryType::BOOT_SERVICES_CODE
| MemoryType::BOOT_SERVICES_DATA
| MemoryType::CONVENTIONAL
| MemoryType::PERSISTENT_MEMORY => {
// SPECIAL_PURPOSE的内存是“软保留”的这意味着它最初被留出
// 但在启动后可以通过热插拔再次使用或者分配给dax驱动程序。
if self.att.contains(MemoryAttribute::SPECIAL_PURPOSE) {
return false;
}
// 根据规范在调用ExitBootServices()之后,这些区域就不再被保留了。
// 然而只有当它们可以被映射为WRITE_BACK缓存时我们才能将它们用作系统内存
return self.att.contains(MemoryAttribute::WRITE_BACK);
}
_ => {
return false;
}
}
}
}
impl Default for MemoryDescriptor {
fn default() -> MemoryDescriptor {
MemoryDescriptor {
ty: MemoryType::RESERVED,
phys_start: 0,
virt_start: 0,
page_count: 0,
att: MemoryAttribute::empty(),
}
}
}

View File

@ -4,15 +4,7 @@ use fdt::{
};
use system_error::SystemError;
use crate::{
arch::MMArch,
init::boot_params,
libs::{align::page_align_down, rwlock::RwLock},
mm::{
memblock::{mem_block_manager, MemBlockManager},
MemoryManagementArch, PhysAddr,
},
};
use crate::{init::boot_params, libs::rwlock::RwLock};
#[inline(always)]
pub fn open_firmware_fdt_driver() -> &'static OpenFirmwareFdtDriver {
@ -45,6 +37,7 @@ impl FdtGlobalData {
pub struct OpenFirmwareFdtDriver;
impl OpenFirmwareFdtDriver {
#[allow(dead_code)]
pub fn early_scan_device_tree(&self) -> Result<(), SystemError> {
let fdt_vaddr = boot_params().read().fdt().unwrap();
let fdt = unsafe {
@ -207,7 +200,22 @@ impl OpenFirmwareFdtDriver {
return found_memory;
}
fn early_init_dt_add_memory(&self, base: u64, size: u64) {
#[cfg(target_arch = "x86_64")]
pub fn early_init_dt_add_memory(&self, _base: u64, _size: u64) {
kBUG!("x86_64 should not call early_init_dt_add_memory");
}
#[cfg(not(target_arch = "x86_64"))]
pub fn early_init_dt_add_memory(&self, base: u64, size: u64) {
use crate::{
arch::MMArch,
libs::align::page_align_down,
mm::{
memblock::{mem_block_manager, MemBlockManager},
MemoryManagementArch, PhysAddr,
},
};
let mut base = base as usize;
let mut size = size as usize;

View File

@ -1,2 +1,2 @@
#[cfg(target_arch = "riscv64")]
// #[cfg(target_arch = "riscv64")]
pub mod fdt;