rv64: boot into upper half
This commit is contained in:
parent
07458f33e4
commit
86eb2d3252
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -967,6 +967,7 @@ dependencies = [
|
||||
"kernel-arch-hosted",
|
||||
"kernel-arch-i686",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-riscv64",
|
||||
"kernel-arch-x86_64",
|
||||
]
|
||||
|
||||
@ -1018,6 +1019,20 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel-arch-riscv64"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
"memtables",
|
||||
"static_assertions",
|
||||
"tock-registers 0.9.0",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel-arch-x86"
|
||||
version = "0.1.0"
|
||||
@ -2658,6 +2673,7 @@ dependencies = [
|
||||
"kernel-arch-aarch64",
|
||||
"kernel-arch-i686",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-riscv64",
|
||||
"kernel-arch-x86",
|
||||
"kernel-arch-x86_64",
|
||||
"libk",
|
||||
|
@ -16,8 +16,9 @@ members = [
|
||||
"lib/abi",
|
||||
"lib/libyalloc",
|
||||
"lib/runtime",
|
||||
"lib/qemu"
|
||||
, "lib/abi-serde"]
|
||||
"lib/qemu",
|
||||
"lib/abi-serde"
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
chrono = { version = "0.4.38", default-features = false, features = ["alloc"] }
|
||||
@ -53,6 +54,7 @@ abi-generator.path = "tool/abi-generator"
|
||||
# Kernel parts
|
||||
kernel-arch-interface.path = "kernel/arch/interface"
|
||||
kernel-arch-aarch64.path = "kernel/arch/aarch64"
|
||||
kernel-arch-riscv64.path = "kernel/arch/riscv64"
|
||||
kernel-arch-x86_64.path = "kernel/arch/x86_64"
|
||||
kernel-arch-i686.path = "kernel/arch/i686"
|
||||
kernel-arch-x86.path = "kernel/arch/x86"
|
||||
|
57
etc/ld/riscv/riscv64-unknown-qemu.ld
Normal file
57
etc/ld/riscv/riscv64-unknown-qemu.ld
Normal file
@ -0,0 +1,57 @@
|
||||
ENTRY(__rv64_entry);
|
||||
|
||||
KERNEL_PHYS_BASE = 0x80000000;
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
|
||||
|
||||
SECTIONS {
|
||||
. = KERNEL_PHYS_BASE;
|
||||
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
|
||||
|
||||
.text.entry : {
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET;
|
||||
|
||||
.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.tables))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET);
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
|
||||
.bss : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
|
||||
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
25
etc/riscv64-unknown-none.json
Normal file
25
etc/riscv64-unknown-none.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"arch": "riscv64",
|
||||
"os": "none",
|
||||
"abi": "softfloat",
|
||||
"cpu": "generic-rv64",
|
||||
"llvm-target": "riscv64",
|
||||
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
|
||||
"max-atomic-width": 64,
|
||||
"target-pointer-width": "64",
|
||||
"features": "+m,+a,+c",
|
||||
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "pic",
|
||||
"eh-frame-header": false,
|
||||
|
||||
"crt-objects-fallback": "false",
|
||||
"emit-debug-gdb-scripts": false,
|
||||
"llvm-abiname": "lp64",
|
||||
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld"
|
||||
}
|
@ -53,6 +53,9 @@ aarch64-cpu.workspace = true
|
||||
device-tree.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
|
||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
yboot-proto.workspace = true
|
||||
kernel-arch-x86_64.workspace = true
|
||||
@ -81,6 +84,7 @@ kernel-arch-x86_64.workspace = true
|
||||
kernel-arch-i686.workspace = true
|
||||
kernel-arch-x86.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["fb_console"]
|
||||
|
@ -3,21 +3,22 @@ name = "kernel-arch"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
|
||||
kernel-arch-x86_64 = { path = "x86_64" }
|
||||
kernel-arch-x86_64.path = "x86_64"
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies]
|
||||
kernel-arch-aarch64 = { path = "aarch64" }
|
||||
kernel-arch-aarch64.path = "aarch64"
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "x86"))'.dependencies]
|
||||
kernel-arch-i686 = { path = "i686" }
|
||||
kernel-arch-i686.path = "i686"
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies]
|
||||
kernel-arch-riscv64.path = "riscv64"
|
||||
|
||||
[target.'cfg(not(target_os = "none"))'.dependencies]
|
||||
kernel-arch-hosted = { path = "hosted" }
|
||||
kernel-arch-hosted.path = "hosted"
|
||||
|
||||
[dependencies]
|
||||
kernel-arch-interface = { path = "interface" }
|
||||
kernel-arch-interface.path = "interface"
|
||||
|
||||
cfg-if.workspace = true
|
||||
|
18
kernel/arch/riscv64/Cargo.toml
Normal file
18
kernel/arch/riscv64/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "kernel-arch-riscv64"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi.workspace = true
|
||||
kernel-arch-interface.workspace = true
|
||||
libk-mm-interface.workspace = true
|
||||
memtables.workspace = true
|
||||
device-api = { workspace = true, features = ["derive"] }
|
||||
|
||||
tock-registers.workspace = true
|
||||
bitflags.workspace = true
|
||||
static_assertions.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
54
kernel/arch/riscv64/src/context.rs
Normal file
54
kernel/arch/riscv64/src/context.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use kernel_arch_interface::{
|
||||
mem::{KernelTableManager, PhysicalMemoryAllocator},
|
||||
task::{TaskContext, UserContextInfo},
|
||||
};
|
||||
use libk_mm_interface::address::PhysicalAddress;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub struct TaskContextImpl<K, PA> {
|
||||
_pd: PhantomData<(K, PA)>,
|
||||
}
|
||||
|
||||
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
|
||||
TaskContext<K, PA> for TaskContextImpl<K, PA>
|
||||
{
|
||||
const USER_STACK_EXTRA_ALIGN: usize = 0;
|
||||
const SIGNAL_STACK_EXTRA_ALIGN: usize = 0;
|
||||
|
||||
fn user(context: UserContextInfo) -> Result<Self, Error> {
|
||||
let _ = context;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
|
||||
let _ = entry;
|
||||
let _ = arg;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_thread_pointer(&self, tp: usize) {
|
||||
let _ = tp;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn align_stack_for_entry(sp: usize) -> usize {
|
||||
let _ = sp;
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe fn enter(&self) -> ! {
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe fn switch(&self, from: &Self) {
|
||||
let _ = from;
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe fn switch_and_drop(&self, thread: *const ()) {
|
||||
let _ = thread;
|
||||
todo!()
|
||||
}
|
||||
}
|
109
kernel/arch/riscv64/src/lib.rs
Normal file
109
kernel/arch/riscv64/src/lib.rs
Normal file
@ -0,0 +1,109 @@
|
||||
#![feature(decl_macro)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
|
||||
use kernel_arch_interface::{
|
||||
cpu::{CpuImpl, IpiQueue},
|
||||
task::Scheduler,
|
||||
Architecture,
|
||||
};
|
||||
|
||||
pub mod mem;
|
||||
pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl};
|
||||
pub mod context;
|
||||
pub use context::TaskContextImpl;
|
||||
use registers::MSTATUS;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable};
|
||||
pub mod registers;
|
||||
|
||||
pub struct ArchitectureImpl;
|
||||
|
||||
impl Architecture for ArchitectureImpl {
|
||||
type PerCpuData = ();
|
||||
type CpuFeatures = ();
|
||||
type BreakpointType = u32;
|
||||
|
||||
const BREAKPOINT_VALUE: Self::BreakpointType = 0;
|
||||
|
||||
fn halt() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn set_local_cpu(cpu: *mut ()) {
|
||||
let _ = cpu;
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn local_cpu() -> *mut () {
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
|
||||
let _ = id;
|
||||
let _ = data;
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn init_ipi_queues(queues: Vec<IpiQueue<Self>>) {
|
||||
let _ = queues;
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn ipi_queue(cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
|
||||
let _ = cpu_id;
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_interrupt_mask(mask: bool) -> bool {
|
||||
let old = Self::interrupt_mask();
|
||||
if mask {
|
||||
MSTATUS.modify(MSTATUS::MIE::CLEAR);
|
||||
} else {
|
||||
MSTATUS.modify(MSTATUS::MIE::SET);
|
||||
}
|
||||
old
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn interrupt_mask() -> bool {
|
||||
MSTATUS.matches_all(MSTATUS::MIE::SET)
|
||||
}
|
||||
|
||||
fn wait_for_interrupt() {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn cpu_count() -> usize {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
let _ = cpu;
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
let _ = cpu;
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn idle_task() -> extern "C" fn(usize) -> ! {
|
||||
loop {}
|
||||
}
|
||||
}
|
120
kernel/arch/riscv64/src/mem/mod.rs
Normal file
120
kernel/arch/riscv64/src/mem/mod.rs
Normal file
@ -0,0 +1,120 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
split_spinlock,
|
||||
};
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
process::ProcessAddressSpaceManager,
|
||||
table::{page_index, MapAttributes, TableAllocator},
|
||||
};
|
||||
use static_assertions::const_assert_eq;
|
||||
use table::{L1, L2};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub use memtables::riscv64::FixedTables;
|
||||
|
||||
pub mod table;
|
||||
|
||||
split_spinlock! {
|
||||
use crate::ArchitectureImpl;
|
||||
use crate::mem::FixedTables;
|
||||
use libk_mm_interface::KernelImageObject;
|
||||
|
||||
#[link_section = ".data.tables"]
|
||||
#[used]
|
||||
static KERNEL_TABLES: KernelImageObject<FixedTables> =
|
||||
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
|
||||
}
|
||||
|
||||
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFFF0_00000000;
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80000000;
|
||||
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFFC0_00000000;
|
||||
|
||||
pub const KERNEL_START_L1I: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
pub const KERNEL_L2I: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
const_assert_eq!(KERNEL_START_L1I, 450);
|
||||
const_assert_eq!(KERNEL_L2I, 0);
|
||||
|
||||
/// Any VAs above this one are sign-extended
|
||||
pub const USER_BOUNDARY: usize = 0x40_00000000;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KernelTableManagerImpl;
|
||||
|
||||
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
|
||||
_pd: PhantomData<TA>,
|
||||
}
|
||||
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
fn virtualize(phys: u64) -> usize {
|
||||
let _ = phys;
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn physicalize(virt: usize) -> u64 {
|
||||
let _ = virt;
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn map_device_pages(
|
||||
base: u64,
|
||||
count: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
|
||||
let _ = base;
|
||||
let _ = count;
|
||||
let _ = attrs;
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
let _ = mapping;
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn unmap_physical_address(virt: usize) {
|
||||
let _ = virt;
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
|
||||
const LOWER_LIMIT_PFN: usize = 0;
|
||||
const UPPER_LIMIT_PFN: usize = 0;
|
||||
|
||||
fn new() -> Result<Self, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe fn map_page(
|
||||
&mut self,
|
||||
address: usize,
|
||||
physical: PhysicalAddress,
|
||||
flags: MapAttributes,
|
||||
) -> Result<(), Error> {
|
||||
let _ = address;
|
||||
let _ = physical;
|
||||
let _ = flags;
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
|
||||
let _ = address;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
|
||||
let _ = address;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn as_address_with_asid(&self) -> u64 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe fn clear(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
169
kernel/arch/riscv64/src/mem/table.rs
Normal file
169
kernel/arch/riscv64/src/mem/table.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
pointer::{PhysicalRef, PhysicalRefMut},
|
||||
table::{EntryLevel, NextPageTable, NonTerminalEntryLevel, TableAllocator},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use super::KernelTableManagerImpl;
|
||||
|
||||
bitflags! {
|
||||
pub struct PageAttributes: u64 {
|
||||
const N = 1 << 63;
|
||||
/// Dirty bit
|
||||
const D = 1 << 7;
|
||||
/// Access bit
|
||||
const A = 1 << 6;
|
||||
/// Global mapping bit, implies all lower levels are also global
|
||||
const G = 1 << 5;
|
||||
/// U-mode access permission
|
||||
const U = 1 << 4;
|
||||
/// Execute permission
|
||||
const X = 1 << 3;
|
||||
/// Write permission
|
||||
const W = 1 << 2;
|
||||
/// Read-permission
|
||||
const R = 1 << 1;
|
||||
/// Valid bit
|
||||
const V = 1 << 0;
|
||||
}
|
||||
|
||||
// X W R Meaning
|
||||
// 0 0 0 Pointer to next level of page table
|
||||
// 0 0 1 Read-only page
|
||||
// 0 1 0 ---
|
||||
// 0 1 1 Read-write page
|
||||
// 1 0 0 Execute only
|
||||
// 1 0 1 Read-execute page
|
||||
// 1 1 0 ---
|
||||
// 1 1 1 Read-write-execute page
|
||||
}
|
||||
|
||||
/// L3 - entry is 4KiB
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct L3;
|
||||
/// L2 - entry is 2MiB
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct L2;
|
||||
/// L1 - entry is 1GiB
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct L1;
|
||||
|
||||
impl EntryLevel for L3 {
|
||||
const SHIFT: usize = 12;
|
||||
}
|
||||
|
||||
impl EntryLevel for L2 {
|
||||
const SHIFT: usize = 21;
|
||||
}
|
||||
|
||||
impl EntryLevel for L1 {
|
||||
const SHIFT: usize = 30;
|
||||
}
|
||||
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct PageTable<L: EntryLevel> {
|
||||
entries: [PageEntry<L>; 512],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct PageEntry<L: EntryLevel>(u64, PhantomData<L>);
|
||||
|
||||
impl NonTerminalEntryLevel for L1 {
|
||||
type NextLevel = L2;
|
||||
}
|
||||
impl NonTerminalEntryLevel for L2 {
|
||||
type NextLevel = L3;
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> PageTable<L> {
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
entries: [PageEntry::INVALID; 512],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> PageEntry<L> {
|
||||
pub const INVALID: Self = Self(0, PhantomData);
|
||||
|
||||
pub const fn is_present(&self) -> bool {
|
||||
self.0 & PageAttributes::V.bits() != 0
|
||||
}
|
||||
|
||||
pub fn attributes(self) -> PageAttributes {
|
||||
PageAttributes::from_bits_retain(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
|
||||
type NextLevel = PageTable<L::NextLevel>;
|
||||
type TableRef = PhysicalRef<'static, PageTable<L::NextLevel>, KernelTableManagerImpl>;
|
||||
type TableRefMut = PhysicalRefMut<'static, PageTable<L::NextLevel>, KernelTableManagerImpl>;
|
||||
|
||||
fn get(&self, _index: usize) -> Option<Self::TableRef> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, _index: usize) -> Option<Self::TableRefMut> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn get_mut_or_alloc<TA: TableAllocator>(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
) -> Result<Self::TableRefMut, Error> {
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
// TODO validate address alignment
|
||||
Self(
|
||||
(address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn table(address: PhysicalAddress, mut attrs: PageAttributes) -> Self {
|
||||
attrs.remove(PageAttributes::R | PageAttributes::W | PageAttributes::X);
|
||||
Self(
|
||||
(address.into_u64() >> 2) | (PageAttributes::V | attrs).bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PageEntry<L3> {
|
||||
pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
(address.into_u64() >> 2) | (PageAttributes::R | PageAttributes::V | attrs).bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_page(&self) -> Option<PhysicalAddress> {
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> Index<usize> for PageTable<L> {
|
||||
type Output = PageEntry<L>;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.entries[index]
|
||||
}
|
||||
}
|
183
kernel/arch/riscv64/src/registers.rs
Normal file
183
kernel/arch/riscv64/src/registers.rs
Normal file
@ -0,0 +1,183 @@
|
||||
macro impl_csr_read($struct:ident, $repr:ty, $reg:ident, $register:ty) {
|
||||
impl tock_registers::interfaces::Readable for $struct {
|
||||
type T = $repr;
|
||||
type R = $register;
|
||||
|
||||
#[inline]
|
||||
fn get(&self) -> $repr {
|
||||
let mut value: $repr;
|
||||
unsafe {
|
||||
core::arch::asm!(concat!("csrr {0}, ", stringify!($reg)), out(reg) value);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
macro impl_csr_write($struct:ident, $repr:ty, $reg:ident, $register:ty) {
|
||||
impl tock_registers::interfaces::Writeable for $struct {
|
||||
type T = $repr;
|
||||
type R = $register;
|
||||
|
||||
#[inline]
|
||||
fn set(&self, value: $repr) {
|
||||
unsafe {
|
||||
core::arch::asm!(concat!("csrw ", stringify!($reg), ", {0}"), in(reg) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod misa {
|
||||
use tock_registers::{interfaces::Readable, register_bitfields};
|
||||
|
||||
use super::{impl_csr_read, impl_csr_write};
|
||||
|
||||
register_bitfields!(
|
||||
u64,
|
||||
pub MISA [
|
||||
A OFFSET(0) NUMBITS(1) [],
|
||||
C OFFSET(2) NUMBITS(1) [],
|
||||
D OFFSET(3) NUMBITS(1) [],
|
||||
E OFFSET(4) NUMBITS(1) [],
|
||||
F OFFSET(5) NUMBITS(1) [],
|
||||
H OFFSET(6) NUMBITS(1) [],
|
||||
I OFFSET(7) NUMBITS(1) [],
|
||||
M OFFSET(12) NUMBITS(1) [],
|
||||
Q OFFSET(16) NUMBITS(1) [],
|
||||
S OFFSET(17) NUMBITS(1) [],
|
||||
U OFFSET(18) NUMBITS(1) [],
|
||||
X OFFSET(23) NUMBITS(1) [],
|
||||
]
|
||||
);
|
||||
|
||||
pub struct Reg;
|
||||
|
||||
impl_csr_read!(Reg, u64, misa, MISA::Register);
|
||||
impl_csr_write!(Reg, u64, misa, MISA::Register);
|
||||
|
||||
impl Reg {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.get() != 0
|
||||
}
|
||||
}
|
||||
|
||||
pub const MISA: Reg = Reg;
|
||||
}
|
||||
|
||||
pub mod mstatus {
|
||||
use tock_registers::register_bitfields;
|
||||
|
||||
use super::{impl_csr_read, impl_csr_write};
|
||||
|
||||
register_bitfields!(
|
||||
u64,
|
||||
pub MSTATUS [
|
||||
/// Interrupt enable for S-mode
|
||||
SIE OFFSET(1) NUMBITS(1) [],
|
||||
/// Interrupt enable for M-mode
|
||||
MIE OFFSET(3) NUMBITS(1) [],
|
||||
/// Stored SIE state on S-mode trap delegation
|
||||
SPIE OFFSET(5) NUMBITS(1) [],
|
||||
/// U-mode big endian
|
||||
UBE OFFSET(6) NUMBITS(1) [],
|
||||
/// TODO: something written here on trap to M-mode
|
||||
MPIE OFFSET(7) NUMBITS(1) [],
|
||||
/// TODO: something for nested traps
|
||||
SPP OFFSET(8) NUMBITS(1) [],
|
||||
/// Vector register dirty status
|
||||
VS OFFSET(9) NUMBITS(2) [],
|
||||
/// Original mode before being trapped into M-mode
|
||||
MPP OFFSET(11) NUMBITS(2) [
|
||||
U = 0,
|
||||
S = 1,
|
||||
M = 3
|
||||
],
|
||||
/// Float register dirty status
|
||||
FS OFFSET(13) NUMBITS(2) [],
|
||||
/// U-mode extension dirty status
|
||||
XS OFFSET(15) NUMBITS(2) [],
|
||||
/// Effective privilege mode at which loads and stores execute.
|
||||
///
|
||||
/// When MPRV = 0, loads and stores behave as normal
|
||||
/// MPRV = 1, loads/stores are translated and protected
|
||||
MPRV OFFSET(17) NUMBITS(1) [],
|
||||
/// Permit supervisor user memory access
|
||||
///
|
||||
/// When SUM = 0, S-mode access to pages accessible by U-mode will fault
|
||||
SUM OFFSET(18) NUMBITS(1) [],
|
||||
MXR OFFSET(19) NUMBITS(1) [],
|
||||
/// Trap virtual memory
|
||||
///
|
||||
/// When TVM = 1, attempts to read/write satp CSR, execute sfence.vma or sinval.vma
|
||||
/// in S-mode will raise an illegal instruction exception
|
||||
TVM OFFSET(20) NUMBITS(1) [],
|
||||
/// Timeout wait
|
||||
///
|
||||
/// When TW = 1, wfi executed in lower privilege level which does not complete
|
||||
/// within some implementation-specific timeout, raises an illegal
|
||||
/// instruction exception
|
||||
TW OFFSET(21) NUMBITS(1) [],
|
||||
TSR OFFSET(22) NUMBITS(1) [],
|
||||
/// U-mode XLEN value
|
||||
UXL OFFSET(32) NUMBITS(2) [],
|
||||
/// S-mode XLEN value
|
||||
SXL OFFSET(34) NUMBITS(2) [],
|
||||
/// S-mode big endian
|
||||
SBE OFFSET(36) NUMBITS(1) [],
|
||||
/// M-mode big endian
|
||||
MBE OFFSET(37) NUMBITS(1) [],
|
||||
SD OFFSET(63) NUMBITS(1) [],
|
||||
]
|
||||
);
|
||||
|
||||
pub struct Reg;
|
||||
|
||||
impl_csr_read!(Reg, u64, mstatus, MSTATUS::Register);
|
||||
impl_csr_write!(Reg, u64, mstatus, MSTATUS::Register);
|
||||
|
||||
pub const MSTATUS: Reg = Reg;
|
||||
}
|
||||
|
||||
pub mod mepc {
|
||||
use super::{impl_csr_read, impl_csr_write};
|
||||
|
||||
pub struct Reg;
|
||||
|
||||
impl_csr_read!(Reg, u64, mepc, ());
|
||||
impl_csr_write!(Reg, u64, mepc, ());
|
||||
|
||||
pub const MEPC: Reg = Reg;
|
||||
}
|
||||
|
||||
pub mod satp {
|
||||
use tock_registers::register_bitfields;
|
||||
|
||||
use super::{impl_csr_read, impl_csr_write};
|
||||
|
||||
register_bitfields!(
|
||||
u64,
|
||||
pub SATP [
|
||||
PPN OFFSET(0) NUMBITS(44) [],
|
||||
ASID OFFSET(44) NUMBITS(16) [],
|
||||
MODE OFFSET(60) NUMBITS(4) [
|
||||
Bare = 0,
|
||||
Sv39 = 8,
|
||||
Sv48 = 9,
|
||||
Sv57 = 10,
|
||||
Sv64 = 11,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
pub struct Reg;
|
||||
|
||||
impl_csr_read!(Reg, u64, satp, SATP::Register);
|
||||
impl_csr_write!(Reg, u64, satp, SATP::Register);
|
||||
|
||||
pub const SATP: Reg = Reg;
|
||||
}
|
||||
|
||||
pub use mepc::MEPC;
|
||||
pub use misa::MISA;
|
||||
pub use mstatus::MSTATUS;
|
||||
pub use satp::SATP;
|
@ -28,6 +28,8 @@ cfg_if! {
|
||||
extern crate kernel_arch_x86_64 as imp;
|
||||
} else if #[cfg(target_arch = "x86")] {
|
||||
extern crate kernel_arch_i686 as imp;
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
extern crate kernel_arch_riscv64 as imp;
|
||||
} else {
|
||||
compile_error!("Unsupported architecture");
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ fn main() {
|
||||
"x86" => (),
|
||||
"x86_64" => build_x86_64(),
|
||||
"aarch64" => (),
|
||||
"riscv64" => (),
|
||||
_ => panic!("Unknown target arch: {:?}", arch),
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,10 @@ struct BusAddressAllocator {
|
||||
offset_32: u32,
|
||||
}
|
||||
|
||||
#[cfg_attr(any(target_arch = "x86_64", target_arch = "x86"), allow(dead_code))]
|
||||
#[cfg_attr(
|
||||
any(target_arch = "x86_64", target_arch = "x86", target_arch = "riscv64"),
|
||||
allow(dead_code)
|
||||
)]
|
||||
impl BusAddressAllocator {
|
||||
pub fn from_ranges(ranges: &[PciAddressRange]) -> Self {
|
||||
let mut range_32 = None;
|
||||
|
@ -14,6 +14,12 @@ pub mod x86_64;
|
||||
#[cfg(all(not(feature = "all"), target_arch = "x86_64"))]
|
||||
pub use x86_64::FixedTables;
|
||||
|
||||
// RISC-V 64-bit
|
||||
#[cfg(any(feature = "all", target_arch = "riscv64"))]
|
||||
pub mod riscv64;
|
||||
#[cfg(all(not(feature = "all"), target_arch = "riscv64"))]
|
||||
pub use riscv64::FixedTables;
|
||||
|
||||
#[cfg(feature = "all")]
|
||||
pub mod any;
|
||||
|
||||
|
19
kernel/lib/memtables/src/riscv64.rs
Normal file
19
kernel/lib/memtables/src/riscv64.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
use crate::RawTable;
|
||||
|
||||
pub const KERNEL_L3_COUNT: usize = 8;
|
||||
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct FixedTables {
|
||||
_dummy: RawTable,
|
||||
}
|
||||
|
||||
impl FixedTables {
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
_dummy: RawTable::zeroed(),
|
||||
}
|
||||
}
|
||||
}
|
@ -30,6 +30,8 @@ cfg_if! {
|
||||
const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_AARCH64;
|
||||
} else if #[cfg(target_arch = "x86")] {
|
||||
const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_386;
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
const EXPECTED_ELF_MACHINE: u16 = elf::abi::EM_RISCV;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,6 @@ use device_api::{
|
||||
interrupt::{IpiDeliveryTarget, IpiMessage},
|
||||
ResetDevice,
|
||||
};
|
||||
// use device_api::{
|
||||
// interrupt::{IpiDeliveryTarget, IpiMessage},
|
||||
// ResetDevice,
|
||||
// };
|
||||
use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
use libk_mm::table::EntryLevel;
|
||||
|
||||
@ -32,7 +28,17 @@ pub mod i686;
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
pub use i686::{I686 as PlatformImpl, PLATFORM};
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "x86")))]
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod riscv64;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub use riscv64::{Riscv64 as PlatformImpl, PLATFORM};
|
||||
|
||||
#[cfg(not(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "x86"
|
||||
)))]
|
||||
compile_error!("Unsupported architecture");
|
||||
|
||||
/// Architecture-specific lowest level of page mapping
|
||||
|
59
kernel/src/arch/riscv64/boot/entry.S
Normal file
59
kernel/src/arch/riscv64/boot/entry.S
Normal file
@ -0,0 +1,59 @@
|
||||
.macro LOAD_PCREL label, register, symbol
|
||||
\label: auipc \register, %pcrel_hi(\symbol)
|
||||
addi \register, \register, %pcrel_lo(\label)
|
||||
.endm
|
||||
|
||||
.section .text.entry
|
||||
.option norvc
|
||||
.type __rv64_entry, @function
|
||||
.global __rv64_entry
|
||||
__rv64_entry:
|
||||
// Jump to parking place if hard id is not zero
|
||||
csrr t0, mhartid
|
||||
bnez t0, .spin_loop
|
||||
|
||||
// Reset translation control
|
||||
csrw satp, zero
|
||||
|
||||
// Zero the .bss
|
||||
LOAD_PCREL .L00, t0, __bss_start_phys
|
||||
LOAD_PCREL .L01, t1, __bss_end_phys
|
||||
|
||||
1: bgeu t0, t1, 2f
|
||||
sd zero, (t0)
|
||||
addi t0, t0, 4
|
||||
j 1b
|
||||
2:
|
||||
|
||||
// Setup boot stack
|
||||
LOAD_PCREL .L02, sp, {boot_stack_bottom} + {boot_stack_size} - {kernel_virt_offset}
|
||||
|
||||
// Jump to entry
|
||||
LOAD_PCREL .L03, t0, {entry_mmode_lower} - {kernel_virt_offset}
|
||||
|
||||
jr t0
|
||||
|
||||
3: wfi
|
||||
j 3b
|
||||
|
||||
.spin_loop:
|
||||
wfi
|
||||
j .spin_loop
|
||||
|
||||
.size __rv64_entry, . - __rv64_entry
|
||||
|
||||
.section .text
|
||||
.global __rv64_smode_entry
|
||||
.type __rv64_smode_entry, @function
|
||||
.p2align 4
|
||||
__rv64_smode_entry:
|
||||
// Set up the stack again
|
||||
LOAD_PCREL .L04, sp, {boot_stack_bottom} + {boot_stack_size}
|
||||
// Enter kernel proper
|
||||
LOAD_PCREL .L05, t0, {entry_smode_lower}
|
||||
|
||||
jr t0
|
||||
|
||||
1: wfi
|
||||
j 1b
|
||||
.size __rv64_smode_entry, . - __rv64_smode_entry
|
125
kernel/src/arch/riscv64/boot/mod.rs
Normal file
125
kernel/src/arch/riscv64/boot/mod.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use core::arch::global_asm;
|
||||
|
||||
use kernel_arch_riscv64::{
|
||||
mem::{
|
||||
table::{PageAttributes, PageEntry, PageTable, L1},
|
||||
KERNEL_VIRT_OFFSET,
|
||||
},
|
||||
registers::{MEPC, MSTATUS, SATP},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, table::EntryLevel};
|
||||
use tock_registers::interfaces::{ReadWriteable, Writeable};
|
||||
|
||||
const BOOT_STACK_SIZE: usize = 65536;
|
||||
|
||||
#[repr(C, align(0x10))]
|
||||
struct BootStack<const N: usize>([u8; N]);
|
||||
|
||||
impl<const N: usize> BootStack<N> {
|
||||
pub const fn zeroed() -> Self {
|
||||
Self([0; N])
|
||||
}
|
||||
}
|
||||
|
||||
#[link_section = ".bss"]
|
||||
static mut BOOT_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
|
||||
static mut TABLE: PageTable<L1> = PageTable::zeroed();
|
||||
|
||||
unsafe fn long_jump(pc: usize, sp: usize) -> ! {
|
||||
core::arch::asm!(r#"
|
||||
mv sp, {sp}
|
||||
jr {pc}
|
||||
"#, pc = in(reg) pc, sp = in(reg) sp, options(noreturn));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn __rv64_bsp_smode_entry_lower() -> ! {
|
||||
// TODO move this to kernel-arch-riscv64, like in other archs
|
||||
for i in 0..4 {
|
||||
TABLE[i] = PageEntry::block(
|
||||
PhysicalAddress::from_usize(i << L1::SHIFT),
|
||||
PageAttributes::W | PageAttributes::X,
|
||||
);
|
||||
}
|
||||
// TODO magic numbers
|
||||
// Map kernel
|
||||
TABLE[450] = PageEntry::block(
|
||||
PhysicalAddress::from_usize(0x8000_0000),
|
||||
PageAttributes::W | PageAttributes::X,
|
||||
);
|
||||
|
||||
let address = (&raw const TABLE).addr();
|
||||
let address = if address >= KERNEL_VIRT_OFFSET {
|
||||
address - KERNEL_VIRT_OFFSET
|
||||
} else {
|
||||
address
|
||||
};
|
||||
|
||||
SATP.modify(SATP::PPN.val((address as u64) >> 12) + SATP::MODE::Sv39);
|
||||
|
||||
let stack = (&raw const BOOT_STACK).addr() + KERNEL_VIRT_OFFSET;
|
||||
let pc = __rv64_bsp_entry_upper as usize + KERNEL_VIRT_OFFSET;
|
||||
let sp = stack + BOOT_STACK_SIZE;
|
||||
|
||||
long_jump(pc, sp)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn __rv64_bsp_entry_upper() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
// Drop to S-mode
|
||||
unsafe extern "C" fn __rv64_bsp_mmode_entry_lower() -> ! {
|
||||
extern "C" {
|
||||
fn __rv64_smode_entry() -> !;
|
||||
}
|
||||
|
||||
MSTATUS.modify(
|
||||
// Mask S-mode interrupts
|
||||
MSTATUS::SIE::CLEAR
|
||||
+ MSTATUS::MPIE::CLEAR
|
||||
// UXLEN=SXLEN=64
|
||||
+ MSTATUS::UXL.val(2)
|
||||
+ MSTATUS::SXL.val(2)
|
||||
// Little endian
|
||||
+ MSTATUS::UBE::CLEAR
|
||||
+ MSTATUS::SBE::CLEAR
|
||||
// Don't trap S-mode VM insns, sret + U-mode wfi
|
||||
+ MSTATUS::TVM::CLEAR
|
||||
+ MSTATUS::TW::CLEAR
|
||||
+ MSTATUS::TSR::CLEAR
|
||||
// Disable effective privilege modification
|
||||
+ MSTATUS::MPRV::CLEAR
|
||||
// Enable S-mode access to U-mode pages
|
||||
+ MSTATUS::SUM::SET
|
||||
// Make mret return to S-mode
|
||||
+ MSTATUS::MPP::S,
|
||||
);
|
||||
|
||||
let entry = __rv64_smode_entry as usize - KERNEL_VIRT_OFFSET;
|
||||
MEPC.set(entry as u64);
|
||||
|
||||
// Modify pmpcfg/pmpaddr to allow lower-level execution
|
||||
unsafe {
|
||||
let mut pmpcfg0: u64;
|
||||
core::arch::asm!("csrr {0}, pmpcfg0", out(reg) pmpcfg0);
|
||||
|
||||
let pmpaddr0: u64 = 0xFFFFffffFFFFffff;
|
||||
|
||||
pmpcfg0 &= !0xFF;
|
||||
// A = 1, X, W, R
|
||||
pmpcfg0 |= 0xF;
|
||||
|
||||
core::arch::asm!("csrw pmpaddr0, {0}; csrw pmpcfg0, {1}", in(reg) pmpaddr0, in(reg) pmpcfg0);
|
||||
}
|
||||
|
||||
core::arch::asm!("mret", options(noreturn));
|
||||
}
|
||||
|
||||
global_asm!(
|
||||
include_str!("entry.S"),
|
||||
entry_mmode_lower = sym __rv64_bsp_mmode_entry_lower,
|
||||
entry_smode_lower = sym __rv64_bsp_smode_entry_lower,
|
||||
boot_stack_bottom = sym BOOT_STACK,
|
||||
kernel_virt_offset = const KERNEL_VIRT_OFFSET,
|
||||
boot_stack_size = const BOOT_STACK_SIZE,
|
||||
);
|
48
kernel/src/arch/riscv64/mod.rs
Normal file
48
kernel/src/arch/riscv64/mod.rs
Normal file
@ -0,0 +1,48 @@
|
||||
#![allow(missing_docs)]
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
interrupt::{IpiDeliveryTarget, IpiMessage},
|
||||
ResetDevice,
|
||||
};
|
||||
use kernel_arch_riscv64::mem::KERNEL_VIRT_OFFSET;
|
||||
use libk_mm::table::EntryLevel;
|
||||
|
||||
use super::Platform;
|
||||
|
||||
pub mod boot;
|
||||
|
||||
pub struct Riscv64;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct L3;
|
||||
impl EntryLevel for L3 {
|
||||
const SIZE: usize = 4096;
|
||||
const SHIFT: usize = 12;
|
||||
}
|
||||
|
||||
impl Platform for Riscv64 {
|
||||
const KERNEL_VIRT_OFFSET: usize = KERNEL_VIRT_OFFSET;
|
||||
type L3 = L3;
|
||||
|
||||
unsafe fn reset(&self) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<bool, Error> {
|
||||
let _ = target;
|
||||
let _ = msg;
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn start_application_processors(&self) {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
|
||||
let _ = reset;
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
pub static PLATFORM: Riscv64 = Riscv64;
|
@ -52,6 +52,10 @@ pub const fn arch_str() -> &'static str {
|
||||
{
|
||||
"x86_64"
|
||||
}
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
{
|
||||
"riscv64"
|
||||
}
|
||||
#[cfg(target_arch = "x86")]
|
||||
{
|
||||
"i686"
|
||||
|
@ -9,7 +9,7 @@ use std::{
|
||||
|
||||
use clap::Parser;
|
||||
use elf::{
|
||||
abi::{EM_386, EM_AARCH64, EM_X86_64, PT_LOAD},
|
||||
abi::{EM_386, EM_AARCH64, EM_RISCV, EM_X86_64, PT_LOAD},
|
||||
endian::AnyEndian,
|
||||
ElfStream,
|
||||
};
|
||||
@ -115,6 +115,7 @@ fn find_tables<F: Read + Seek>(elf: &mut ElfStream<AnyEndian, F>) -> Result<(u64
|
||||
let section_size = match elf.ehdr.e_machine {
|
||||
EM_AARCH64 => size_of::<memtables::aarch64::FixedTables>(),
|
||||
EM_X86_64 => size_of::<memtables::x86_64::FixedTables>(),
|
||||
EM_RISCV => size_of::<memtables::riscv64::FixedTables>(),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let (shdrs, strtab) = elf.section_headers_with_strtab()?;
|
||||
@ -237,6 +238,10 @@ fn build_tables<F: Read + Seek>(
|
||||
)?
|
||||
.build()
|
||||
.map(into_any),
|
||||
EM_RISCV => {
|
||||
// TODO
|
||||
std::process::exit(0);
|
||||
}
|
||||
_ => todo!(),
|
||||
}?;
|
||||
|
||||
|
@ -17,6 +17,11 @@ pub(crate) mod i686;
|
||||
#[cfg(target_arch = "x86")]
|
||||
use i686 as arch_impl;
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub(crate) mod riscv64;
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
use riscv64 as arch_impl;
|
||||
|
||||
pub use arch_impl::SavedFrame;
|
||||
|
||||
pub trait FrameOps {
|
||||
|
19
lib/abi/src/arch/riscv64.rs
Normal file
19
lib/abi/src/arch/riscv64.rs
Normal file
@ -0,0 +1,19 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use super::FrameOps;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct SavedFrame {}
|
||||
|
||||
impl FrameOps for SavedFrame {
|
||||
fn set_user_ip(&mut self, value: usize) {
|
||||
let _ = value;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn user_ip(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ use device::{QemuDevice, QemuSerialTarget};
|
||||
|
||||
pub mod aarch64;
|
||||
pub mod i386;
|
||||
pub mod riscv64;
|
||||
pub mod x86_64;
|
||||
|
||||
pub mod device;
|
||||
@ -82,6 +83,12 @@ impl Qemu<i386::QemuI386> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Qemu<riscv64::QemuRiscv64> {
|
||||
pub fn new_riscv64() -> Self {
|
||||
Qemu::new(riscv64::QemuRiscv64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Architecture> Qemu<A> {
|
||||
pub fn new(arch: A) -> Self {
|
||||
Self {
|
||||
|
60
lib/qemu/src/riscv64.rs
Normal file
60
lib/qemu/src/riscv64.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use std::{path::PathBuf, process::Command};
|
||||
|
||||
use crate::{Architecture, IntoArgs};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Cpu {
|
||||
Rv64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Machine {
|
||||
Virt,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QemuRiscv64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Image {
|
||||
pub kernel: PathBuf,
|
||||
}
|
||||
|
||||
impl IntoArgs for Machine {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
command.arg("-M");
|
||||
match self {
|
||||
Self::Virt => command.arg("virt"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for Cpu {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
command.arg("-cpu");
|
||||
match self {
|
||||
Self::Rv64 => command.arg("rv64,zicsr=true,zifencei=true"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoArgs for Image {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
command.arg("-kernel");
|
||||
command.arg(&self.kernel);
|
||||
command.args(["-bios", "none"]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Architecture for QemuRiscv64 {
|
||||
type CpuType = Cpu;
|
||||
type ImageType = Image;
|
||||
type MachineType = Machine;
|
||||
const DEFAULT_COMMAND: &'static str = "qemu-system-riscv64";
|
||||
}
|
||||
|
||||
impl IntoArgs for QemuRiscv64 {
|
||||
fn add_args(&self, command: &mut Command) {
|
||||
let _ = command;
|
||||
}
|
||||
}
|
@ -26,9 +26,14 @@ mod i686;
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
use i686 as imp;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
mod riscv64;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
use riscv64 as imp;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
mod variant1;
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
use variant1 as layout;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
|
12
lib/runtime/src/process/thread_local/riscv64.rs
Normal file
12
lib/runtime/src/process/thread_local/riscv64.rs
Normal file
@ -0,0 +1,12 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use abi::error::Error;
|
||||
|
||||
pub fn get_thread_pointer() -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> {
|
||||
let _ = value;
|
||||
todo!()
|
||||
}
|
@ -9,8 +9,11 @@ mod x86_64;
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
#[macro_use]
|
||||
mod i686;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
#[macro_use]
|
||||
mod riscv64;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[allow(missing_docs, unreachable_code)]
|
||||
mod generated {
|
||||
// Import all the necessary types from generated ABI
|
||||
use abi::{
|
||||
|
53
lib/runtime/src/sys/riscv64.rs
Normal file
53
lib/runtime/src/sys/riscv64.rs
Normal file
@ -0,0 +1,53 @@
|
||||
/// 64-bit RISC-V implementations of the syscall macro
|
||||
#[macro_export]
|
||||
macro_rules! syscall {
|
||||
($num:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
todo!()
|
||||
}};
|
||||
($num:expr, $a0:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
todo!()
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
todo!()
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
todo!()
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
let _ = $a3;
|
||||
todo!()
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
let _ = $a3;
|
||||
let _ = $a4;
|
||||
todo!()
|
||||
}};
|
||||
($num:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr $(,)?) => {{
|
||||
let _ = $num;
|
||||
let _ = $a0;
|
||||
let _ = $a1;
|
||||
let _ = $a2;
|
||||
let _ = $a3;
|
||||
let _ = $a4;
|
||||
let _ = $a5;
|
||||
todo!()
|
||||
}};
|
||||
}
|
@ -42,6 +42,7 @@ pub struct KernelProcessed(pub KernelBuilt);
|
||||
pub struct InitrdGenerated(pub PathBuf);
|
||||
pub struct ImageBuilt(pub PathBuf);
|
||||
pub enum AllBuilt {
|
||||
Riscv64(KernelProcessed),
|
||||
X86_64(ImageBuilt),
|
||||
AArch64(KernelProcessed, InitrdGenerated),
|
||||
I686(ImageBuilt),
|
||||
@ -98,12 +99,16 @@ pub fn build_all(env: &BuildEnv) -> Result<AllBuilt, Error> {
|
||||
// for module in modules {
|
||||
// install_extra.push((module.clone(), module.file_name().unwrap().into()));
|
||||
// }
|
||||
if env.arch == Arch::riscv64 {
|
||||
return Ok(AllBuilt::Riscv64(kernel));
|
||||
}
|
||||
|
||||
// Userspace stuff
|
||||
let initrd = userspace::build_initrd(env, install_extra, check)?;
|
||||
|
||||
// Build target-specific image
|
||||
let image = match env.arch {
|
||||
Arch::riscv64 => AllBuilt::Riscv64(kernel),
|
||||
Arch::aarch64 => AllBuilt::AArch64(kernel, initrd),
|
||||
Arch::x86_64 => AllBuilt::X86_64(x86_64::build_image(env, kernel, initrd)?),
|
||||
Arch::i686 => AllBuilt::I686(i686::build_image(env, kernel, initrd)?),
|
||||
|
@ -56,11 +56,16 @@ fn check_commands_aarch64() -> Result<CommandsOk, Error> {
|
||||
])
|
||||
}
|
||||
|
||||
fn check_commands_riscv64() -> Result<CommandsOk, Error> {
|
||||
check_command_list([("ld64.lld", "Install LLVM")])
|
||||
}
|
||||
|
||||
pub fn check_build_env(arch: Arch) -> Result<AllOk, Error> {
|
||||
let user_toolchain = check_user_toolchain()?;
|
||||
let commands = match arch {
|
||||
Arch::x86_64 => check_commands_x86_64()?,
|
||||
Arch::aarch64 => check_commands_aarch64()?,
|
||||
Arch::riscv64 => check_commands_riscv64()?,
|
||||
Arch::i686 => check_commands_i686()?,
|
||||
};
|
||||
Ok(AllOk(commands, user_toolchain))
|
||||
|
@ -62,6 +62,7 @@ pub enum Profile {
|
||||
pub enum Arch {
|
||||
#[default]
|
||||
aarch64,
|
||||
riscv64,
|
||||
x86_64,
|
||||
i686,
|
||||
}
|
||||
@ -127,6 +128,7 @@ impl BuildEnv {
|
||||
) -> Self {
|
||||
let kernel_triple = match (arch, board) {
|
||||
(Arch::aarch64, Board::virt | Board::default) => "aarch64-unknown-qemu",
|
||||
(Arch::riscv64, Board::virt | Board::default) => "riscv64-unknown-qemu",
|
||||
(Arch::aarch64, Board::raspi4b) => "aarch64-unknown-raspi4b",
|
||||
(Arch::x86_64, Board::default) => "x86_64-unknown-none",
|
||||
(Arch::i686, Board::default) => "i686-unknown-none",
|
||||
@ -137,6 +139,7 @@ impl BuildEnv {
|
||||
};
|
||||
let kernel_linker_script = match arch {
|
||||
Arch::aarch64 => format!("arm/{kernel_triple}.ld"),
|
||||
Arch::riscv64 => format!("riscv/{kernel_triple}.ld"),
|
||||
Arch::i686 | Arch::x86_64 => format!("x86/{kernel_triple}.ld"),
|
||||
};
|
||||
let kernel_output_dir =
|
||||
@ -190,6 +193,7 @@ impl BuildEnv {
|
||||
impl XTaskConfig {
|
||||
pub fn components(&self, env: &BuildEnv) -> &BuildComponents {
|
||||
match env.arch {
|
||||
Arch::riscv64 => todo!(),
|
||||
Arch::aarch64 => &self.target.aarch64.components,
|
||||
Arch::x86_64 => &self.target.x86_64.components,
|
||||
Arch::i686 => &self.target.i686.components,
|
||||
@ -217,6 +221,7 @@ impl Arch {
|
||||
|
||||
pub fn user_triple(&self) -> &str {
|
||||
match self {
|
||||
Self::riscv64 => "riscv64-unknown-yggdrasil",
|
||||
Self::aarch64 => "aarch64-unknown-yggdrasil",
|
||||
Self::x86_64 => "x86_64-unknown-yggdrasil",
|
||||
Self::i686 => "i686-unknown-yggdrasil",
|
||||
@ -225,6 +230,7 @@ impl Arch {
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::riscv64 => "riscv64",
|
||||
Self::aarch64 => "aarch64",
|
||||
Self::x86_64 => "x86_64",
|
||||
Self::i686 => "i686",
|
||||
|
@ -7,7 +7,7 @@ use std::{
|
||||
use qemu::{
|
||||
aarch64,
|
||||
device::{QemuDevice, QemuDrive, QemuNic, QemuSerialTarget},
|
||||
i386, x86_64, Qemu,
|
||||
i386, riscv64, x86_64, Qemu,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -254,6 +254,27 @@ fn run_i686(
|
||||
Ok(qemu.into_command())
|
||||
}
|
||||
|
||||
fn run_riscv64(
|
||||
config: &QemuConfig,
|
||||
qemu_bin: Option<PathBuf>,
|
||||
devices: Vec<QemuDevice>,
|
||||
kernel: PathBuf,
|
||||
) -> Result<Command, Error> {
|
||||
let _ = config;
|
||||
let _ = devices;
|
||||
|
||||
let mut qemu = Qemu::new_riscv64();
|
||||
if let Some(qemu_bin) = qemu_bin {
|
||||
qemu.override_qemu(qemu_bin);
|
||||
}
|
||||
qemu.with_serial(QemuSerialTarget::MonStdio)
|
||||
.with_machine(riscv64::Machine::Virt)
|
||||
.with_cpu(riscv64::Cpu::Rv64)
|
||||
.with_boot_image(riscv64::Image { kernel });
|
||||
|
||||
Ok(qemu.into_command())
|
||||
}
|
||||
|
||||
fn load_qemu_config<P: AsRef<Path>>(path: P) -> Result<QemuConfig, Error> {
|
||||
let path = path.as_ref();
|
||||
|
||||
@ -326,6 +347,9 @@ pub fn run(
|
||||
add_devices_from_config(&mut devices, disk.as_ref(), &config)?;
|
||||
|
||||
let mut command = match built {
|
||||
AllBuilt::Riscv64(KernelProcessed(KernelBuilt(kernel))) => {
|
||||
run_riscv64(&config, qemu, devices, kernel)?
|
||||
}
|
||||
AllBuilt::AArch64(KernelProcessed(KernelBuilt(kernel)), InitrdGenerated(initrd)) => {
|
||||
make_kernel_bin(kernel, &kernel_bin)?;
|
||||
run_aarch64(&config, &env, qemu, devices, kernel_bin, initrd)?
|
||||
|
Loading…
x
Reference in New Issue
Block a user