Add the boot section of LoongArch in OSTD
This commit is contained in:
parent
b0b242edbc
commit
ce22374b50
|
|
@ -0,0 +1,174 @@
|
|||
/* SPDX-License-Identifier: MPL-2.0 */
|
||||
|
||||
.equ LOONGARCH_CSR_CRMD, 0x0 /* Current mode */
|
||||
.equ LOONGARCH_CSR_PRMD, 0x1 /* Previous mode */
|
||||
.equ LOONGARCH_CSR_EUEN, 0x2 /* Extended unit enable */
|
||||
.equ LOONGARCH_CSR_PGDL, 0x19 /* Page table base address when VA[47] = 0 */
|
||||
.equ LOONGARCH_CSR_PGDH, 0x1a /* Page table base address when VA[47] = 1 */
|
||||
.equ LOONGARCH_CSR_PGD, 0x1b /* Page table base */
|
||||
.equ LOONGARCH_CSR_PWCL, 0x1c /* Page table walk control low */
|
||||
.equ LOONGARCH_CSR_PWCH, 0x1d /* Page table walk control high */
|
||||
.equ LOONGARCH_CSR_STLBPS, 0x1e /* STLB page size */
|
||||
.equ LOONGARCH_CSR_CPUID, 0x20 /* CPUID */
|
||||
.equ LOONGARCH_CSR_TLBRENTRY, 0x88 /* TLB refill exception entry */
|
||||
.equ LOONGARCH_CSR_TLBRBADV, 0x89 /* TLB refill badvaddr */
|
||||
.equ LOONGARCH_CSR_TLBRERA, 0x8a /* TLB refill ERA */
|
||||
.equ LOONGARCH_CSR_TLBRSAVE, 0x8b /* KScratch for TLB refill exception */
|
||||
.equ LOONGARCH_CSR_TLBRELO0, 0x8c /* TLB refill entrylo0 */
|
||||
.equ LOONGARCH_CSR_TLBRELO1, 0x8d /* TLB refill entrylo1 */
|
||||
.equ LOONGARCH_CSR_TLBREHI, 0x8e /* TLB refill entryhi */
|
||||
.equ LOONGARCH_CSR_DMW0, 0x180 /* Direct mapping window 0 */
|
||||
.equ LOONGARCH_CSR_DMW1, 0x181 /* Direct mapping window 1 */
|
||||
.equ LOONGARCH_CSR_DMW2, 0x182 /* Direct mapping window 2 */
|
||||
.equ LOONGARCH_CSR_DMW3, 0x183 /* Direct mapping window 3 */
|
||||
.equ SAVE_TLBREFILL_T1, 0x30 /* Save 0 */
|
||||
.equ LOONGARCH_CSR_SAVE1, 0x31 /* Save 1 */
|
||||
.equ LOONGARCH_CSR_SAVE2, 0x32 /* Save 2 */
|
||||
.equ LOONGARCH_CSR_SAVE3, 0x33 /* Save 3 */
|
||||
.equ LOONGARCH_CSR_SAVE4, 0x34 /* Save 4 */
|
||||
.equ LOONGARCH_CSR_SAVE5, 0x35 /* Save 5 */
|
||||
.equ LOONGARCH_CSR_SAVE6, 0x36 /* Save 6 */
|
||||
.equ LOONGARCH_CSR_SAVE7, 0x37 /* Save 7 */
|
||||
|
||||
.section ".boot", "awx", @progbits
|
||||
.globl _start
|
||||
_start:
|
||||
# Set DMW0 (kernel)
|
||||
li.d $t0, 0x9000000000000011 # CA, PLV0, 0x9000_xxxx_xxxx_xxxx
|
||||
csrwr $t0, LOONGARCH_CSR_DMW0
|
||||
|
||||
# Set DMW1 (kernel), temporary use
|
||||
li.d $t0, 0x0000000000000011 # CA, PLV0, 0x0000_xxxx_xxxx_xxxx
|
||||
csrwr $t0, LOONGARCH_CSR_DMW1
|
||||
|
||||
# Set DMW2 (device)
|
||||
li.d $t0, 0x8000000000000001 # UA, PLV0, 0x8000_xxxx_xxxx_xxxx
|
||||
csrwr $t0, LOONGARCH_CSR_DMW2
|
||||
|
||||
# Disable floating point unit etc.
|
||||
li.w $t0, 0x00000000 # FPE=0, SXE=0, ASXE=0, BTE=0
|
||||
csrwr $t0, LOONGARCH_CSR_EUEN
|
||||
|
||||
# Set the first level (root level) page table size
|
||||
li.w $t0, 0x0000000c # 4KB
|
||||
csrwr $t0, LOONGARCH_CSR_STLBPS
|
||||
|
||||
# Set the page size of the TLB refill
|
||||
# Now we only support the 4KB page size
|
||||
li.w $t0, 0x0000000c # 4KB
|
||||
csrwr $t0, LOONGARCH_CSR_TLBREHI
|
||||
|
||||
# Set the structure of page table
|
||||
# PTbase=12, PTwidth=9, Dir1_base=12 + 9, Dir1_width=9, Dir2_base=12 + 9 + 9, Dir2_width=9, PTEWidth=0 (64 bits)
|
||||
li.w $t0, 12 | 9 << 5 | 21 << 10 | 9 << 15 | 30 << 20 | 9 << 25
|
||||
csrwr $t0, LOONGARCH_CSR_PWCL
|
||||
# Dir3_base=12 + 9 + 9 + 9, Dir3_width=9
|
||||
li.w $t0, 39 | 9 << 6
|
||||
csrwr $t0, LOONGARCH_CSR_PWCH
|
||||
|
||||
# Set the boot page table
|
||||
la $t0, boot_l4pt
|
||||
csrwr $t0, LOONGARCH_CSR_PGDL
|
||||
la $t0, boot_l4pt
|
||||
csrwr $t0, LOONGARCH_CSR_PGDH
|
||||
|
||||
# Initialize TLB
|
||||
invtlb 0, $zero, $zero
|
||||
|
||||
# Enable address translation and disable interrupts
|
||||
li.w $t0, 0x00000010 # PLV=0, IE=0, PG=1
|
||||
csrwr $t0, LOONGARCH_CSR_CRMD
|
||||
li.w $t0, 0x00000000 # PPLV=0, PIE=0, PWE=0
|
||||
csrwr $t0, LOONGARCH_CSR_PRMD
|
||||
|
||||
# Set the entry of TLB refill exception
|
||||
la.global $t0, _handle_tlb_refill
|
||||
# Convert the address to physical address
|
||||
.extern KERNEL_VMA_OFFSET
|
||||
la.global $t1, KERNEL_VMA_OFFSET
|
||||
sub.d $t0, $t0, $t1
|
||||
csrwr $t0, LOONGARCH_CSR_TLBRENTRY
|
||||
|
||||
# Update SP/PC to use the virtual address
|
||||
la $sp, boot_stack_top
|
||||
add.d $sp, $sp, $t1
|
||||
la.global $t0, _start_virt
|
||||
jr $t0
|
||||
|
||||
.balign 4096
|
||||
.globl boot_l4pt
|
||||
boot_l4pt:
|
||||
.zero 8 * 512
|
||||
|
||||
.section ".boot.stack", "aw", @nobits
|
||||
.globl boot_stack_bottom
|
||||
boot_stack_bottom:
|
||||
.balign 4096
|
||||
.skip 0x40000 # 256 KiB
|
||||
.globl boot_stack_top
|
||||
boot_stack_top:
|
||||
|
||||
# From here, we're in the .text section: we no longer use physical address.
|
||||
.text
|
||||
.globl _start_virt
|
||||
_start_virt:
|
||||
# Unset DMW1 (kernel)
|
||||
csrwr $zero, LOONGARCH_CSR_DMW1
|
||||
|
||||
# Initialize r21 to the CPU-local start address.
|
||||
.extern __cpu_local_start
|
||||
la.global $r21, __cpu_local_start
|
||||
|
||||
# Jump to rust loongarch_boot
|
||||
la.global $t0, loongarch_boot
|
||||
jr $t0
|
||||
|
||||
.balign 4096
|
||||
.text
|
||||
.globl _handle_tlb_refill
|
||||
_handle_tlb_refill:
|
||||
# Save $t0, $t1
|
||||
csrwr $t0, LOONGARCH_CSR_TLBRSAVE
|
||||
csrwr $t1, SAVE_TLBREFILL_T1
|
||||
|
||||
# Read PGD
|
||||
csrrd $t0, LOONGARCH_CSR_PGD
|
||||
|
||||
# Walk page table
|
||||
lddir $t0, $t0, 3
|
||||
andi $t1, $t0, 0x1
|
||||
beqz $t1, _invalid_pte
|
||||
li.d $t1, 0x1
|
||||
andn $t0, $t0, $t1
|
||||
|
||||
lddir $t0, $t0, 2
|
||||
andi $t1, $t0, 0x1
|
||||
beqz $t1, _invalid_pte
|
||||
li.d $t1, 0x1
|
||||
andn $t0, $t0, $t1
|
||||
|
||||
lddir $t0, $t0, 1
|
||||
andi $t1, $t0, 0x1
|
||||
beqz $t1, _invalid_pte
|
||||
li.d $t1, 0x1
|
||||
andn $t0, $t0, $t1
|
||||
|
||||
# Load PTEs
|
||||
ldpte $t0, 0
|
||||
ldpte $t0, 1
|
||||
b _fill_tlb
|
||||
|
||||
_invalid_pte:
|
||||
# For invalid PTE, set the PTE to 0, which is valid.
|
||||
csrwr $zero, LOONGARCH_CSR_TLBRELO0
|
||||
csrwr $zero, LOONGARCH_CSR_TLBRELO1
|
||||
|
||||
_fill_tlb:
|
||||
# Fill TLB
|
||||
tlbfill
|
||||
|
||||
# Restore $t0, $t1
|
||||
csrrd $t0, LOONGARCH_CSR_TLBRSAVE
|
||||
csrrd $t1, SAVE_TLBREFILL_T1
|
||||
|
||||
ertn
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::mm::{paddr_to_vaddr, Paddr};
|
||||
|
||||
macro_rules! efi_guid {
|
||||
($a:expr, $b:expr, $c:expr, $d:expr) => {{
|
||||
let a = ($a as u32).to_le_bytes(); // u32 -> [u8; 4]
|
||||
let b = ($b as u16).to_le_bytes(); // u16 -> [u8; 2]
|
||||
let c = ($c as u16).to_le_bytes(); // u16 -> [u8; 2]
|
||||
let d = $d;
|
||||
EfiGuid {
|
||||
b: [
|
||||
a[0], a[1], a[2], a[3], b[0], b[1], c[0], c[1], d[0], d[1], d[2], d[3], d[4], d[5],
|
||||
d[6], d[7],
|
||||
],
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Reference: <https://github.com/torvalds/linux/blob/master/include/linux/efi.h#L417>
|
||||
const LINUX_EFI_INITRD_MEDIA_GUID: EfiGuid = efi_guid!(
|
||||
0x5568e427,
|
||||
0x68fc,
|
||||
0x4f3d,
|
||||
[0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68]
|
||||
);
|
||||
|
||||
/// Reference: <https://uefi.org/specs/UEFI/2.10/04_EFI_System_Table.html#devicetree-tables>
|
||||
const DEVICE_TREE_GUID: EfiGuid = efi_guid!(
|
||||
0xb1b621d5,
|
||||
0xf19c,
|
||||
0x41a5,
|
||||
[0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0]
|
||||
);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct EfiGuid {
|
||||
b: [u8; 16],
|
||||
}
|
||||
|
||||
/// Reference: <https://uefi.org/specs/UEFI/2.10/04_EFI_System_Table.html#id4>
|
||||
#[repr(C)]
|
||||
struct EfiTableHeader {
|
||||
signature: u64,
|
||||
revision: u32,
|
||||
headersize: u32,
|
||||
crc32: u32,
|
||||
reserved: u32,
|
||||
}
|
||||
|
||||
/// Reference: <https://uefi.org/specs/UEFI/2.10/04_EFI_System_Table.html#efi-configuration-table>
|
||||
#[repr(C)]
|
||||
struct EfiConfigurationTable {
|
||||
guid: EfiGuid,
|
||||
table: *const core::ffi::c_void,
|
||||
}
|
||||
|
||||
/// Reference: <https://uefi.org/specs/UEFI/2.10/04_EFI_System_Table.html#id6>
|
||||
#[repr(C)]
|
||||
pub(super) struct EfiSystemTable {
|
||||
hdr: EfiTableHeader,
|
||||
fw_vendor: u64, // physical addr of CHAR16*
|
||||
fw_revision: u32,
|
||||
con_in_handle: u64,
|
||||
con_in: *const u64,
|
||||
con_out_handle: u64,
|
||||
con_out: *const u64,
|
||||
stderr_handle: u64,
|
||||
stderr_placeholder: u64,
|
||||
runtime: *const u64,
|
||||
boottime: *const u64,
|
||||
nr_tables: u64,
|
||||
tables: *const EfiConfigurationTable,
|
||||
}
|
||||
|
||||
// SAFETY: The `EfiSystemTable` structure is only accessed in a read-only manner
|
||||
// during early EFI initialization. The raw pointers it contains are not written
|
||||
// to across threads, so it is safe to mark this type as thread-safe.
|
||||
unsafe impl Sync for EfiSystemTable {}
|
||||
|
||||
impl EfiSystemTable {
|
||||
fn table(&self, guid: &EfiGuid) -> Option<&EfiConfigurationTable> {
|
||||
for i in 0..self.nr_tables as usize {
|
||||
let table = unsafe {
|
||||
&*(paddr_to_vaddr(self.tables.add(i) as _) as *const EfiConfigurationTable)
|
||||
};
|
||||
if table.guid == *guid {
|
||||
return Some(table);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn initrd(&self) -> Option<&EfiInitrd> {
|
||||
let table = self.table(&LINUX_EFI_INITRD_MEDIA_GUID)?;
|
||||
Some(unsafe { &*(paddr_to_vaddr(table.table as _) as *const EfiInitrd) })
|
||||
}
|
||||
|
||||
pub(super) fn device_tree(&self) -> Option<Paddr> {
|
||||
let table = self.table(&DEVICE_TREE_GUID)?;
|
||||
Some(table.table as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference: <https://github.com/torvalds/linux/blob/master/include/linux/efi.h#L1327>
|
||||
#[repr(C)]
|
||||
pub(super) struct EfiInitrd {
|
||||
base: u64,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
impl EfiInitrd {
|
||||
pub(super) fn range(&self) -> Option<(usize, usize)> {
|
||||
if self.size == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((self.base as _, (self.base + self.size) as _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! The LoongArch boot module defines the entrypoints of Asterinas.
|
||||
|
||||
mod efi;
|
||||
pub mod smp;
|
||||
use core::{arch::global_asm, ffi::CStr};
|
||||
|
||||
use fdt::Fdt;
|
||||
use spin::Once;
|
||||
|
||||
use crate::{
|
||||
arch::boot::efi::EfiSystemTable,
|
||||
boot::{
|
||||
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
|
||||
BootloaderAcpiArg, BootloaderFramebufferArg,
|
||||
},
|
||||
mm::paddr_to_vaddr,
|
||||
};
|
||||
|
||||
global_asm!(include_str!("boot.S"));
|
||||
|
||||
static EFI_SYSTEM_TABLE: Once<&'static EfiSystemTable> = Once::new();
|
||||
|
||||
/// The Flattened Device Tree of the platform.
|
||||
pub static DEVICE_TREE: Once<Fdt> = Once::new();
|
||||
|
||||
fn parse_bootloader_name() -> &'static str {
|
||||
"Unknown"
|
||||
}
|
||||
|
||||
fn parse_initramfs() -> Option<&'static [u8]> {
|
||||
let Some((start, end)) = parse_initramfs_range() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let base_va = paddr_to_vaddr(start);
|
||||
let length = end - start;
|
||||
Some(unsafe { core::slice::from_raw_parts(base_va as *const u8, length) })
|
||||
}
|
||||
|
||||
fn parse_acpi_arg() -> BootloaderAcpiArg {
|
||||
BootloaderAcpiArg::NotProvided
|
||||
}
|
||||
|
||||
fn parse_framebuffer_info() -> Option<BootloaderFramebufferArg> {
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_memory_regions() -> MemoryRegionArray {
|
||||
let mut regions = MemoryRegionArray::new();
|
||||
|
||||
for region in DEVICE_TREE.get().unwrap().memory().regions() {
|
||||
if region.size.unwrap_or(0) > 0 {
|
||||
regions
|
||||
.push(MemoryRegion::new(
|
||||
region.starting_address as usize,
|
||||
region.size.unwrap(),
|
||||
MemoryRegionType::Usable,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Add the kernel region.
|
||||
regions.push(MemoryRegion::kernel()).unwrap();
|
||||
|
||||
// Add the initramfs region.
|
||||
if let Some((start, end)) = parse_initramfs_range() {
|
||||
regions
|
||||
.push(MemoryRegion::new(
|
||||
start,
|
||||
end - start,
|
||||
MemoryRegionType::Module,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
regions.into_non_overlapping()
|
||||
}
|
||||
|
||||
fn parse_initramfs_range() -> Option<(usize, usize)> {
|
||||
EFI_SYSTEM_TABLE.get().unwrap().initrd()?.range()
|
||||
}
|
||||
|
||||
/// Checks the LoongArch CPU configuration using `cpucfg` instruction.
|
||||
fn check_cpu_config() {
|
||||
let palen = loongArch64::cpu::get_palen();
|
||||
let valen = loongArch64::cpu::get_valen();
|
||||
let support_iocsr = loongArch64::cpu::get_support_iocsr();
|
||||
|
||||
// Now we only support the 48 bits PA width.
|
||||
assert_eq!(palen, 48);
|
||||
// Now we only support the 48 bits VA width.
|
||||
assert_eq!(valen, 48);
|
||||
// Now we require IOCSR support be present.
|
||||
assert!(support_iocsr);
|
||||
}
|
||||
|
||||
/// The entry point of the Rust code portion of Asterinas.
|
||||
///
|
||||
/// Reference: <https://docs.kernel.org/arch/loongarch/booting.html#information-passed-from-bootloader-to-kernel>
|
||||
#[no_mangle]
|
||||
pub extern "C" fn loongarch_boot(_efi_boot: usize, cmdline_paddr: usize, systab_paddr: usize) -> ! {
|
||||
check_cpu_config();
|
||||
|
||||
let systab_ptr = paddr_to_vaddr(systab_paddr) as *const EfiSystemTable;
|
||||
let systab = unsafe { &*(systab_ptr) };
|
||||
EFI_SYSTEM_TABLE.call_once(|| systab);
|
||||
|
||||
let device_tree_ptr =
|
||||
paddr_to_vaddr(systab.device_tree().expect("device tree not found")) as *const u8;
|
||||
let fdt = unsafe { fdt::Fdt::from_ptr(device_tree_ptr).unwrap() };
|
||||
DEVICE_TREE.call_once(|| fdt);
|
||||
|
||||
let cmdline_ptr = paddr_to_vaddr(cmdline_paddr) as *const i8;
|
||||
let cmdline = unsafe { CStr::from_ptr(cmdline_ptr) }.to_str();
|
||||
|
||||
use crate::boot::{call_ostd_main, EarlyBootInfo, EARLY_INFO};
|
||||
|
||||
EARLY_INFO.call_once(|| EarlyBootInfo {
|
||||
bootloader_name: parse_bootloader_name(),
|
||||
kernel_cmdline: cmdline.unwrap_or(""),
|
||||
initramfs: parse_initramfs(),
|
||||
acpi_arg: parse_acpi_arg(),
|
||||
framebuffer_arg: parse_framebuffer_info(),
|
||||
memory_regions: parse_memory_regions(),
|
||||
});
|
||||
|
||||
call_ostd_main();
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Multiprocessor Boot Support
|
||||
|
||||
use crate::{boot::smp::PerApRawInfo, mm::Paddr};
|
||||
|
||||
pub(crate) fn count_processors() -> Option<u32> {
|
||||
Some(1)
|
||||
}
|
||||
|
||||
pub(crate) fn bringup_all_aps(_info_ptr: *const PerApRawInfo, _pr_ptr: Paddr, _num_cpus: u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
|
@ -32,6 +32,9 @@ pub mod arch;
|
|||
#[cfg(target_arch = "riscv64")]
|
||||
#[path = "arch/riscv/mod.rs"]
|
||||
pub mod arch;
|
||||
#[cfg(target_arch = "loongarch64")]
|
||||
#[path = "arch/loongarch/mod.rs"]
|
||||
pub mod arch;
|
||||
pub mod boot;
|
||||
pub mod bus;
|
||||
pub mod console;
|
||||
|
|
|
|||
Loading…
Reference in New Issue