在riscv输出hello world (#466)

增加了以下内容:
- SBI驱动
- 把内核的rust工具链升级到2023-08-15版本
- 输出riscv的helloworld
- 设置内核是PIC的
This commit is contained in:
LoGin
2023-12-07 02:13:22 +08:00
committed by GitHub
parent fca83acef4
commit 1a72a751b1
41 changed files with 722 additions and 122 deletions

View File

@ -0,0 +1,34 @@
#include "common/asm.h"
.section .bootstrap
#define CSR_SIE 0x104
#define CSR_SIP 0x144
#define CSR_IE CSR_SIE
#define CSR_IP CSR_SIP
// DragonStub
//
// a0: hartid ID
// a1: fdt
.global _start
.type _start, @function
ENTRY(_start)
/* Mask all interrupts */
csrw CSR_IE, zero
csrw CSR_IP, zero
/* Load the global pointer */
.option push
.option norelax
la sp, BSP_IDLE_STACK_SPACE
li t0, 32768
add sp, sp, t0
.option pop
/* Call the kernel */
call kernel_main
nop
_loop:
j _loop

View File

@ -1,9 +0,0 @@
#include "common/asm.h"
.section .bootstrap
.global _start
.type _start, @function
ENTRY(_start)
loop:
j loop

View File

@ -0,0 +1 @@
pub mod sbi;

View File

@ -0,0 +1,220 @@
#![allow(dead_code)]
use super::SbiError;
/// 使用给定的扩展和函数 ID 进行零参数的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 不接受任何参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall0(extension_id: usize, function_id: usize) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
in("a6") function_id,
in("a7") extension_id,
lateout("a0") error,
lateout("a1") value,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行单参数的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall1(
arg: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg => error,
in("a6") function_id,
in("a7") extension_id,
lateout("a1") value,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 一个带有给定扩展和函数ID的两参数`ecall`。
///
/// # 安全性
/// 只有在给定的函数ID接受两个参数时才安全调用此函数。否则行为将是未定义的
/// 因为将额外的中断寄存器传递给SBI实现时其内容将是未定义的。
#[inline]
pub unsafe fn ecall2(
arg0: usize,
arg1: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 3参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall3(
arg0: usize,
arg1: usize,
arg2: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 4参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall4(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 5参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
pub unsafe fn ecall5(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a4") arg4,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}
/// 使用给定的扩展和函数 ID 进行 6参数 的 `ecall`。
///
/// # 安全性
/// 只有在给定的函数 ID 接受一个参数时,调用此函数才是安全的,否则行为是未定义的,
/// 因为当传递给 SBI 实现时,额外的参数寄存器将具有未定义的内容。
#[inline]
#[allow(clippy::too_many_arguments)]
pub unsafe fn ecall6(
arg0: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize,
arg5: usize,
extension_id: usize,
function_id: usize,
) -> Result<usize, SbiError> {
let error: isize;
let value: usize;
core::arch::asm!(
"ecall",
inlateout("a0") arg0 => error,
inlateout("a1") arg1 => value,
in("a2") arg2,
in("a3") arg3,
in("a4") arg4,
in("a5") arg5,
in("a6") function_id,
in("a7") extension_id,
);
match error {
0 => Result::Ok(value),
e => Result::Err(SbiError::new(e)),
}
}

View File

@ -0,0 +1,194 @@
use crate::{
arch::driver::sbi::ecall::{ecall0, ecall1},
mm::VirtAddr,
};
use core::arch::asm;
/// `sbi_set_timer` extension ID
pub const SET_TIMER_EID: usize = 0x00;
/// `sbi_console_putchar` extension ID
pub const CONSOLE_PUTCHAR_EID: usize = 0x01;
/// `sbi_console_getchar` extension ID
pub const CONSOLE_GETCHAR_EID: usize = 0x02;
/// `sbi_clear_ipi` extension ID
pub const CLEAR_IPI_EID: usize = 0x03;
/// `sbi_send_ipi` extension ID
pub const SEND_IPI_EID: usize = 0x04;
/// `sbi_remote_fence_i` extension ID
pub const REMOTE_FENCE_I_EID: usize = 0x05;
/// `sbi_remote_sfence_vma` extension ID
pub const REMOTE_SFENCE_VMA_EID: usize = 0x06;
/// `sbi_remote_sfence_vma_asid` extension ID
pub const REMOTE_SFENCE_VMA_ASID_EID: usize = 0x07;
/// `sbi_shutdown` extension ID
pub const SHUTDOWN_EID: usize = 0x08;
/// 计划在未来的某个时间触发中断。
///
/// ## 参数
///
/// - `stime`:要触发中断的绝对时间,以滴答为单位。如果`stime`小于当前时间,则不会触发中断。
///
/// ## 详情
///
/// 要清除计时器中断而不预约另一个计时器事件,可以将时间设置为无限远(`u64::MAX`)或
/// mask `sie` CSR的`STIE` 位。此函数将清除待处理计时器中断位。
///
/// 注意:`time` 是一个绝对时间,不是从调用时刻开始的偏移量。这意味着如果您想要设置一个未来`n`和tick之后
/// 触发的时钟,您需要首先读取 `time` CSR然后将滴答数添加到该值。关于如何确定每个滴答的时间
/// 这是平台依赖的,而时钟频率应在 CPU 节点的 `timebase-frequency` 属性中表达,如果可用的话。
#[inline]
pub unsafe fn set_timer(stime: u64) {
#[cfg(target_arch = "riscv64")]
unsafe {
ecall1(stime as usize, SET_TIMER_EID, 0).ok();
}
#[cfg(target_arch = "riscv32")]
unsafe {
asm!(
"ecall",
inout ("a0") stime as usize => _,
inout ("a1") (stime >> 32) as usize => _,
in("a7") SET_TIMER_EID,
);
}
}
/// 将字符写入调试控制台。如果仍有待处理的控制台输出,此调用将阻塞。如果不存在控制台,则不会执行任何操作。
#[inline]
pub unsafe fn console_putchar(c: u8) {
unsafe {
ecall1(c.into(), CONSOLE_PUTCHAR_EID, 0).ok();
}
}
/// 尝试从调试控制台获取一个字符。
/// 如果没有任何字符等待阅读,或者没有调试控制台设备,则此函数将返回[`None`]。
#[inline]
pub unsafe fn console_getchar() -> Option<u8> {
let mut ret: i8;
unsafe {
asm!(
"ecall",
lateout("a0") ret,
in("a7") CONSOLE_GETCHAR_EID,
);
}
match ret {
-1 => None,
_ => Some(ret as u8),
}
}
/// 清除current核心的待处理中断IPIs
#[inline]
#[deprecated = "S模式可以直接清除`sip.SSIP` CSR位因此无需调用此函数。"]
pub unsafe fn clear_ipi() {
unsafe {
asm!(
"ecall",
in("a7") CLEAR_IPI_EID,
lateout("a0") _,
);
}
}
/// 向所有由`hart_mask`位掩码指定的核心发送中断IPI。接收到的中断表示为监视器软件中断。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制位向量,向上取整到下一个`usize`。
#[inline]
pub unsafe fn send_ipi(hart_mask: &[usize]) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a7") SEND_IPI_EID,
);
}
}
/// 对指定的心脏hart执行 `FENCE.I` 指令
///
/// ## 参数
/// - `hart_mask`: 一个长度为 `n_harts / size_of::<usize>()` 的位矢量,
/// 向上取整到下一个 `usize」。
#[inline]
pub unsafe fn remote_fence_i(hart_mask: &[usize]) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a7") REMOTE_FENCE_I_EID,
);
}
}
/// 在指定的hart上执行`SFENCE.VMA`指令
/// 为指定的虚拟内存范围(由`start`和`size`指定)执行。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制向量,
/// 向上取整到下一个`usize`。
/// - `start`: 要执行`SFENCE.VMA`的起始虚拟地址。
/// - `size`: 要对`start`执行的`SFENCE.VMA`的字节大小。例如,要失效一个
/// 包含2个4-KiB页面的区域您会为`size`传递`8192`。
///
/// 如果`start`和`size`都为`0`,或者如果`size`为[`usize::MAX`],则将执行完整的
/// `SFENCE.VMA`,而不仅仅是一个或多个页面大小的`SFENCE.VMA`。
#[inline]
pub unsafe fn remote_sfence_vma(hart_mask: &[usize], start: VirtAddr, size: usize) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a1") start.data(),
in("a2") size,
in("a7") REMOTE_SFENCE_VMA_EID,
);
}
}
/// 在指定的hart上执行SFENCE.VMA指令
///
/// 仅针对指定的地址空间IDASID执行虚拟内存范围指定的
/// start和size的hart_mask位掩码。
///
/// ## 参数
/// - `hart_mask`: 一个长度为`n_harts / size_of::<usize>()`的二进制向量,
/// 向上取整到下一个`usize`。
/// - `start`: 要执行`SFENCE.VMA`的起始虚拟地址。
/// - `size`: 要对`start`执行的`SFENCE.VMA`的字节大小。例如,要失效一个
/// 包含2个4-KiB页面的区域您会为`size`传递`8192`。
/// - `asid`: 要执行`SFENCE.VMA`的地址空间ID。
///
/// 如果start和size都为0或者如果size为[usize::MAX],则将执行全
/// 部SFENCE.VMA而不是多个页面大小的SFENCE.VMA`。
#[inline]
pub unsafe fn remote_sfence_vma_asid(hart_mask: &[usize], start: usize, size: usize, asid: usize) {
unsafe {
asm!(
"ecall",
inlateout("a0") hart_mask.as_ptr() => _,
in("a1") start,
in("a2") size,
in("a3") asid,
in("a7") REMOTE_SFENCE_VMA_ASID_EID,
);
}
}
/// 将所有核心置于关闭状态,此时处理器的执行模式比当前监督模式具有更高的特权。此调用不会返回。
#[inline]
pub unsafe fn shutdown() -> ! {
unsafe {
asm!(
"ecall",
in("a7") SHUTDOWN_EID,
options(noreturn)
);
}
}

View File

@ -0,0 +1,93 @@
use self::legacy::console_putchar;
/// The SBI S-mode driver.
///
/// Some code takes from `https://github.com/repnop/sbi.git`
mod ecall;
pub mod legacy;
/// Error codes returned by SBI calls
///
/// note: `SBI_SUCCESS` is not represented here since this is to be used as the
/// error type in a `Result`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SbiError {
/// The SBI call failed
Failed,
/// The SBI call is not implemented or the functionality is not available
NotSupported,
/// An invalid parameter was passed
InvalidParameter,
/// The SBI implementation has denied execution of the call functionality
Denied,
/// An invalid address was passed
InvalidAddress,
/// The resource is already available
AlreadyAvailable,
/// The resource was previously started
AlreadyStarted,
/// The resource was previously stopped
AlreadyStopped,
}
impl SbiError {
#[inline]
fn new(n: isize) -> Self {
match n {
-1 => SbiError::Failed,
-2 => SbiError::NotSupported,
-3 => SbiError::InvalidParameter,
-4 => SbiError::Denied,
-5 => SbiError::InvalidAddress,
-6 => SbiError::AlreadyAvailable,
-7 => SbiError::AlreadyStarted,
-8 => SbiError::AlreadyStopped,
n => unreachable!("bad SBI error return value: {}", n),
}
}
}
impl core::fmt::Display for SbiError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{}",
match self {
SbiError::AlreadyAvailable => "resource is already available",
SbiError::Denied => "SBI implementation denied execution",
SbiError::Failed => "call to SBI failed",
SbiError::InvalidAddress => "invalid address passed",
SbiError::InvalidParameter => "invalid parameter passed",
SbiError::NotSupported => "SBI call not implemented or functionality not available",
SbiError::AlreadyStarted => "resource was already started",
SbiError::AlreadyStopped => "resource was already stopped",
}
)
}
}
/// 向控制台打印字符串。
///
/// 该函数接受一个字节切片 `s` 作为输入,并迭代切片中的每个字节 `c`。
/// 然后调用 `console_putchar` 函数,将 `c` 的值作为参数传递给它。
///
/// # 安全性
/// 该函数被标记为 `unsafe`,因为它调用了 `console_putchar` 函数,
/// 而假设该函数执行可能有副作用或违反内存安全的底层操作。
/// 调用者有责任确保 `s` 切片是有效的并且正确终止的。
///
/// # 参数
///
/// * `s` - 表示要打印的字符串的字节切片。
///
/// # 示例
///
/// ```
/// let message = b"Hello, World!";
/// console_putstr(message);
/// ```
pub unsafe fn console_putstr(s: &[u8]) {
for c in s {
unsafe { console_putchar(*c) };
}
}

View File

@ -0,0 +1,14 @@
use core::intrinsics::unreachable;
use crate::{
driver::tty::serial::serial8250::send_to_default_serial8250_port, init::init_before_mem_init,
kdebug,
};
#[no_mangle]
unsafe extern "C" fn kernel_main(hartid: usize, fdt_addr: usize) -> ! {
init_before_mem_init();
send_to_default_serial8250_port(&b"Hello, world! RISC-V!\n"[..]);
loop {}
unreachable()
}

View File

@ -10,8 +10,8 @@ ENTRY(_start)
SECTIONS
{
KERNEL_VMA = 0xffffffc000000000;
//KERNEL_VMA = 0;
//KERNEL_VMA = 0xffffffc000000000;
KERNEL_VMA = 0;
. = 0x1000000;
.boot.text :
@ -44,7 +44,8 @@ SECTIONS
_data = .;
*(.data)
*(.data.*)
*(.got.plt)
*(.got)
_edata = .;
}

View File

@ -1,5 +1,7 @@
pub mod asm;
pub mod cpu;
pub mod driver;
mod init;
pub mod interrupt;
pub mod ipc;
mod kvm;

View File

@ -10,6 +10,19 @@ use super::interrupt::TrapFrame;
pub mod kthread;
pub mod syscall;
#[allow(dead_code)]
#[repr(align(32768))]
union InitProcUnion {
/// 用于存放idle进程的内核栈
idle_stack: [u8; 32768],
}
#[link_section = ".data.init_proc_union"]
#[no_mangle]
static BSP_IDLE_STACK_SPACE: InitProcUnion = InitProcUnion {
idle_stack: [0; 32768],
};
pub unsafe fn arch_switch_to_user(path: String, argv: Vec<String>, envp: Vec<String>) -> ! {
unimplemented!("RiscV64 arch_switch_to_user")
}

View File

@ -0,0 +1,18 @@
{
"arch": "riscv64",
"code-model": "medium",
"cpu": "generic-rv64",
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
"eh-frame-header": false,
"emit-debug-gdb-scripts": false,
"features": "+m,+a,+c",
"is-builtin": false,
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"llvm-target": "riscv64",
"max-atomic-width": 64,
"panic-strategy": "abort",
"relocation-model": "pic",
"position-independent-executables": true,
"target-pointer-width": "64"
}

View File

@ -67,7 +67,7 @@ impl Hpet {
let tm_num = hpet.timers_num();
kinfo!("HPET has {} timers", tm_num);
hpet_info.hpet_number = tm_num as u8;
drop(hpet);
drop(mmio);
if tm_num == 0 {
return Err(SystemError::ENODEV);
@ -118,7 +118,6 @@ impl Hpet {
unsafe { regs.write_main_counter_value(0) };
drop(regs);
drop(inner_guard);
let (inner_guard, timer_reg) = unsafe { self.timer_mut(0).ok_or(SystemError::ENODEV) }?;
@ -130,7 +129,6 @@ impl Hpet {
volwrite!(timer_reg, config, 0x004c);
volwrite!(timer_reg, comparator_value, ticks);
}
drop(timer_reg);
drop(inner_guard);
// todo!("register irq in C");
@ -142,7 +140,6 @@ impl Hpet {
// 置位旧设备中断路由兼容标志位、定时器组使能标志位
unsafe { regs.write_general_config(3) };
drop(regs);
drop(inner_guard);
kinfo!("HPET enabled");
@ -208,7 +205,7 @@ impl Hpet {
pub fn main_counter_value(&self) -> u64 {
let (inner_guard, regs) = unsafe { self.hpet_regs() };
let value = regs.main_counter_value();
drop(regs);
drop(inner_guard);
return value;
}
@ -217,7 +214,7 @@ impl Hpet {
let (inner_guard, regs) = unsafe { self.hpet_regs() };
let period = regs.counter_clock_period();
kdebug!("HPET period: {}", period);
drop(regs);
drop(inner_guard);
return period;
}