mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 12:56:48 +00:00
Add kxos-frame crate
This commit is contained in:
parent
a1f1b728cc
commit
92d29e2030
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
|
9
src/kxos-frame/Cargo.toml
Normal file
9
src/kxos-frame/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "kxos-frame"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.3"
|
127
src/kxos-frame/src/cpu.rs
Normal file
127
src/kxos-frame/src/cpu.rs
Normal file
@ -0,0 +1,127 @@
|
||||
//! CPU.
|
||||
|
||||
/// Defines a CPU-local variable.
|
||||
#[macro_export]
|
||||
macro_rules! cpu_local {
|
||||
() => {
|
||||
todo!()
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the number of CPUs.
|
||||
pub fn num_cpus() -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the ID of this CPU.
|
||||
pub fn this_cpu() -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Cpu context, including both general-purpose registers and floating-point registers.
|
||||
#[derive(Clone, Default)]
|
||||
#[repr(C)]
|
||||
pub struct CpuContext {
|
||||
pub gp_regs: GpRegs,
|
||||
pub fs_base: u64,
|
||||
pub fp_regs: FpRegs,
|
||||
}
|
||||
|
||||
/// The general-purpose registers of CPU.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct GpRegs {
|
||||
pub r8: u64,
|
||||
pub r9: u64,
|
||||
pub r10: u64,
|
||||
pub r11: u64,
|
||||
pub r12: u64,
|
||||
pub r13: u64,
|
||||
pub r14: u64,
|
||||
pub r15: u64,
|
||||
pub rdi: u64,
|
||||
pub rsi: u64,
|
||||
pub rbp: u64,
|
||||
pub rbx: u64,
|
||||
pub rdx: u64,
|
||||
pub rax: u64,
|
||||
pub rcx: u64,
|
||||
pub rsp: u64,
|
||||
pub rip: u64,
|
||||
pub rflag: u64,
|
||||
}
|
||||
|
||||
/// The floating-point state of CPU.
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
pub struct FpRegs {
|
||||
//buf: Aligned<A16, [u8; 512]>,
|
||||
is_valid: bool,
|
||||
}
|
||||
|
||||
impl FpRegs {
|
||||
/// Create a new instance.
|
||||
///
|
||||
/// Note that a newly-created instance's floating point state is not
|
||||
/// initialized, thus considered invalid (i.e., `self.is_valid() == false`).
|
||||
pub fn new() -> Self {
|
||||
//let buf = Aligned(unsafe { MaybeUninit::uninit().assume_init() });
|
||||
//let is_valid = false;
|
||||
//Self { buf, is_valid }
|
||||
todo!("import aligned")
|
||||
}
|
||||
|
||||
/// Save CPU's current floating pointer states into this instance.
|
||||
pub fn save(&mut self) {
|
||||
// unsafe {
|
||||
// _fxsave(self.buf.as_mut_ptr() as *mut u8);
|
||||
// }
|
||||
self.is_valid = true;
|
||||
}
|
||||
|
||||
/// Save the floating state given by a slice of u8.
|
||||
///
|
||||
/// It is the caller's responsibility to ensure that the source slice contains
|
||||
/// data that is in xsave/xrstor format. The slice must have a length of 512 bytes.
|
||||
///
|
||||
/// After calling this method, the state of the instance will be considered valid.
|
||||
pub unsafe fn save_from_slice(&mut self, src: &[u8]) {
|
||||
//(&mut self.buf).copy_from_slice(src);
|
||||
//self.is_valid = true;
|
||||
}
|
||||
|
||||
/// Returns whether the instance can contains data in valid xsave/xrstor format.
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.is_valid
|
||||
}
|
||||
|
||||
/// Clear the state of the instance.
|
||||
///
|
||||
/// This method does not reset the underlying buffer that contains the floating
|
||||
/// point state; it only marks the buffer __invalid__.
|
||||
pub fn clear(&mut self) {
|
||||
self.is_valid = false;
|
||||
}
|
||||
|
||||
/// Restore CPU's CPU floating pointer states from this instance.
|
||||
///
|
||||
/// Panic. If the current state is invalid, the method will panic.
|
||||
pub fn restore(&self) {
|
||||
assert!(self.is_valid);
|
||||
//unsafe { _fxrstor(self.buf.as_ptr()) };
|
||||
}
|
||||
|
||||
/// Returns the floating point state as a slice.
|
||||
///
|
||||
/// Note that the slice may contain garbage if `self.is_valid() == false`.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
//&*self.buf
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FpRegs {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
38
src/kxos-frame/src/device/io_port.rs
Normal file
38
src/kxos-frame/src/device/io_port.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// An I/O port, representing a specific address in the I/O address of x86.
|
||||
pub struct IoPort<T> {
|
||||
addr: u32,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> IoPort<T> {
|
||||
/// Create an I/O port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is marked unsafe as creating an I/O port is considered
|
||||
/// a privileged operation.
|
||||
pub unsafe fn new(addr: u32) -> Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IoPort<u32> {
|
||||
/// Get the address of this I/O port.
|
||||
pub fn addr(&self) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Read a value of `u32`.
|
||||
pub fn read_u32(&self) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Write a value of `u32`.
|
||||
pub fn write_u32(&self, val: u32) {
|
||||
todo!()
|
||||
}
|
||||
}
|
46
src/kxos-frame/src/device/irq.rs
Normal file
46
src/kxos-frame/src/device/irq.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
/// An interupt request (IRQ) line.
|
||||
pub struct IrqLine {}
|
||||
|
||||
impl IrqLine {
|
||||
/// Acquire an interrupt request line.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is marked unsafe as manipulating interrupt lines is
|
||||
/// considered a dangerous operation.
|
||||
pub unsafe fn acquire(irq_num: u32) -> Arc<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get the IRQ number.
|
||||
pub fn num(&self) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Register a callback that will be invoked when the IRQ is active.
|
||||
///
|
||||
/// A handle to the callback is returned. Dropping the handle
|
||||
/// automatically unregisters the callback.
|
||||
///
|
||||
/// For each IRQ line, multiple callbacks may be registered.
|
||||
pub fn on_active<F>(&self, callback: F) -> IrqCallbackHandle
|
||||
where
|
||||
F: Fn(&Self),
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// The handle to a registered callback for a IRQ line.
|
||||
///
|
||||
/// When the handle is dropped, the callback will be unregistered automatically.
|
||||
#[must_use]
|
||||
pub struct IrqCallbackHandle {}
|
||||
|
||||
impl Drop for IrqCallbackHandle {
|
||||
fn drop(&mut self) {
|
||||
todo!("unregister the callback")
|
||||
}
|
||||
}
|
7
src/kxos-frame/src/device/mod.rs
Normal file
7
src/kxos-frame/src/device/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! Device-related APIs.
|
||||
|
||||
mod io_port;
|
||||
mod irq;
|
||||
|
||||
pub use self::io_port::IoPort;
|
||||
pub use self::irq::{IrqCallbackHandle, IrqLine};
|
8
src/kxos-frame/src/error.rs
Normal file
8
src/kxos-frame/src/error.rs
Normal file
@ -0,0 +1,8 @@
|
||||
/// The error type which is returned from the APIs of this crate.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Error {
|
||||
InvalidArgs,
|
||||
NoMemory,
|
||||
PageFault,
|
||||
AccessDenied,
|
||||
}
|
19
src/kxos-frame/src/lib.rs
Normal file
19
src/kxos-frame/src/lib.rs
Normal file
@ -0,0 +1,19 @@
|
||||
//! The framework part of KxOS.
|
||||
#![no_std]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
#![feature(negative_impls)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod cpu;
|
||||
pub mod device;
|
||||
mod error;
|
||||
pub mod prelude;
|
||||
pub mod task;
|
||||
pub mod timer;
|
||||
pub mod user;
|
||||
mod util;
|
||||
pub mod vm;
|
||||
|
||||
pub use self::error::Error;
|
10
src/kxos-frame/src/prelude.rs
Normal file
10
src/kxos-frame/src/prelude.rs
Normal file
@ -0,0 +1,10 @@
|
||||
//! The prelude.
|
||||
|
||||
pub type Result<T> = core::result::Result<T, crate::error::Error>;
|
||||
|
||||
pub(crate) use alloc::boxed::Box;
|
||||
pub(crate) use alloc::sync::Arc;
|
||||
pub(crate) use alloc::vec::Vec;
|
||||
pub(crate) use core::any::Any;
|
||||
|
||||
pub use crate::vm::{Paddr, Vaddr};
|
5
src/kxos-frame/src/sync/mod.rs
Normal file
5
src/kxos-frame/src/sync/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod spin;
|
||||
mod wait;
|
||||
|
||||
pub use self::spin::{SpinLock, SpinLockGuard};
|
||||
pub use self::wait::{WaitQueue};
|
39
src/kxos-frame/src/sync/spin.rs
Normal file
39
src/kxos-frame/src/sync/spin.rs
Normal file
@ -0,0 +1,39 @@
|
||||
/// A spin lock.
|
||||
pub struct SpinLock<T: ?Sized> {
|
||||
val: T,
|
||||
}
|
||||
|
||||
impl<T> SpinLock<T> {
|
||||
/// Creates a new spin lock.
|
||||
pub fn new(val: T) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Acquire the spin lock.
|
||||
///
|
||||
/// This method runs in a busy loop until the lock can be acquired.
|
||||
/// After acquiring the spin lock, all interrupts are disabled.
|
||||
pub fn lock(&self) -> SpinLockGuard<'a> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Send> Send for SpinLock<T> {}
|
||||
unsafe impl<T: ?Sized + Send> Sync for SpinLock<T> {}
|
||||
|
||||
/// The guard of a spin lock.
|
||||
pub struct SpinLockGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a SpinLock<T>
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for SpinLockGuard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> !Send for SpinLockGuard<'a, T> {}
|
||||
|
||||
unsafe impl<T: ?Sized + Sync> Sync for SpinLockGuard<'_, T> {}
|
69
src/kxos-frame/src/sync/wait.rs
Normal file
69
src/kxos-frame/src/sync/wait.rs
Normal file
@ -0,0 +1,69 @@
|
||||
/// A wait queue.
|
||||
///
|
||||
/// One may wait on a wait queue to put its executing thread to sleep.
|
||||
/// Multiple threads may be the waiters of a wait queue.
|
||||
/// Other threads may invoke the `wake`-family methods of a wait queue to
|
||||
/// wake up one or many waiter threads.
|
||||
pub struct WaitQueue {}
|
||||
|
||||
impl WaitQueue {
|
||||
/// Creates a new instance.
|
||||
pub fn new() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Wait until some condition becomes true.
|
||||
///
|
||||
/// This method takes a closure that tests a user-given condition.
|
||||
/// The method only returns if the condition becomes true.
|
||||
/// A waker thread should first make the condition true, then invoke the
|
||||
/// `wake`-family method. This ordering is important to ensure that waiter
|
||||
/// threads do not lose any wakeup notifiations.
|
||||
///
|
||||
/// By taking a condition closure, this wait-wakeup mechanism becomes
|
||||
/// more efficient and robust.
|
||||
pub fn wait_until<F>(&self, mut cond: F)
|
||||
where
|
||||
F: FnMut() -> bool,
|
||||
{
|
||||
let waiter = Waiter::new();
|
||||
self.enqueue(&waiter);
|
||||
loop {
|
||||
if (cond)() {
|
||||
self.dequeue(&waiter);
|
||||
return;
|
||||
}
|
||||
waiter.wait();
|
||||
}
|
||||
self.dequeue(&waiter);
|
||||
}
|
||||
|
||||
/// Wake one waiter thread, if there is one.
|
||||
pub fn wake_one(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Wake all waiter threads.
|
||||
pub fn wake_all(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn enqueue(&self, waiter: &Waiter) {
|
||||
todo!()
|
||||
}
|
||||
fn dequeue(&self, waiter: &Waiter) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct Waiter {}
|
||||
|
||||
impl Waiter {
|
||||
pub fn new() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn wait(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
7
src/kxos-frame/src/task/mod.rs
Normal file
7
src/kxos-frame/src/task/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
//! Tasks are the unit of code execution.
|
||||
|
||||
mod scheduler;
|
||||
mod task;
|
||||
|
||||
pub use self::scheduler::{set_scheduler, Scheduler};
|
||||
pub use self::task::{Task, TaskStatus};
|
19
src/kxos-frame/src/task/scheduler.rs
Normal file
19
src/kxos-frame/src/task/scheduler.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use crate::prelude::*;
|
||||
use crate::task::Task;
|
||||
|
||||
/// A scheduler for tasks.
|
||||
///
|
||||
/// An implementation of scheduler can attach scheduler-related information
|
||||
/// with the `TypeMap` returned from `task.data()`.
|
||||
pub trait Scheduler {
|
||||
fn enqueue(&self, task: Arc<Task>);
|
||||
|
||||
fn dequeue(&self) -> Option<Arc<Task>>;
|
||||
}
|
||||
|
||||
/// Set the global task scheduler.
|
||||
///
|
||||
/// This must be called before invoking `Task::spawn`.
|
||||
pub fn set_scheduler(scheduler: &'static dyn Scheduler) {
|
||||
todo!()
|
||||
}
|
66
src/kxos-frame/src/task/task.rs
Normal file
66
src/kxos-frame/src/task/task.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use crate::prelude::*;
|
||||
use crate::user::UserSpace;
|
||||
|
||||
/// A task that executes a function to the end.
|
||||
pub struct Task {
|
||||
func: Box<dyn FnOnce()>,
|
||||
data: Box<dyn Any + Send + Sync>,
|
||||
user_space: Option<UserSpace>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
/// Gets the current task.
|
||||
pub fn current() -> Arc<Task> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Yields execution so that another task may be scheduled.
|
||||
///
|
||||
/// Note that this method cannot be simply named "yield" as the name is
|
||||
/// a Rust keyword.
|
||||
pub fn yield_now() {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Spawns a task that executes a function.
|
||||
///
|
||||
/// Each task is associated with a per-task data and an optional user space.
|
||||
/// If having a user space, then the task can switch to the user space to
|
||||
/// execute user code. Multiple tasks can share a single user space.
|
||||
pub fn spawn<F, T>(
|
||||
task_fn: F,
|
||||
task_data: T,
|
||||
user_space: Option<Arc<UserSpace>>,
|
||||
) -> Result<Arc<Self>>
|
||||
where
|
||||
F: FnOnce(),
|
||||
T: Any + Send + Sync,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the task status.
|
||||
pub fn status(&self) -> TaskStatus {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the task data.
|
||||
pub fn data(&self) -> &dyn Any {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the user space of this task, if it has.
|
||||
pub fn user_space(&self) -> Option<&Arc<UserSpace>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// The status of a task.
|
||||
pub enum TaskStatus {
|
||||
/// The task is runnable.
|
||||
Runnable,
|
||||
/// The task is sleeping.
|
||||
Sleeping,
|
||||
/// The task has exited.
|
||||
Exited,
|
||||
}
|
43
src/kxos-frame/src/timer.rs
Normal file
43
src/kxos-frame/src/timer.rs
Normal file
@ -0,0 +1,43 @@
|
||||
//! Timer.
|
||||
|
||||
use crate::prelude::*;
|
||||
use core::time::Duration;
|
||||
|
||||
/// A timer invokes a callback function after a specified span of time elapsed.
|
||||
///
|
||||
/// A new timer is initially inactive. Only after a timeout value is set with
|
||||
/// the `set` method can the timer become active and the callback function
|
||||
/// be triggered.
|
||||
///
|
||||
/// Timers are one-shot. If the time is out, one has to set the timer again
|
||||
/// in order to trigger the callback again.
|
||||
pub struct Timer {}
|
||||
|
||||
impl Timer {
|
||||
/// Creates a new instance, given a callback function.
|
||||
pub fn new<F>(f: F) -> Result<Self>
|
||||
where
|
||||
F: FnMut(&Self),
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Set a timeout value.
|
||||
///
|
||||
/// If a timeout value is already set, the timeout value will be refreshed.
|
||||
pub fn set(&self, timeout: Duration) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the remaining timeout value.
|
||||
///
|
||||
/// If the timer is not set, then the remaining timeout value is zero.
|
||||
pub fn remain(&self) -> Duration {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Clear the timeout value.
|
||||
pub fn clear(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
105
src/kxos-frame/src/user.rs
Normal file
105
src/kxos-frame/src/user.rs
Normal file
@ -0,0 +1,105 @@
|
||||
//! User space.
|
||||
|
||||
use crate::cpu::CpuContext;
|
||||
use crate::prelude::*;
|
||||
use crate::task::Task;
|
||||
use crate::vm::VmSpace;
|
||||
|
||||
/// A user space.
|
||||
///
|
||||
/// Each user space has a VM address space and allows a task to execute in
|
||||
/// user mode.
|
||||
pub struct UserSpace {}
|
||||
|
||||
impl UserSpace {
|
||||
/// Creates a new instance.
|
||||
///
|
||||
/// Each instance maintains a VM address space and the CPU state to enable
|
||||
/// execution in the user space.
|
||||
pub fn new(vm_space: VmSpace, cpu_ctx: CpuContext) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the VM address space.
|
||||
pub fn vm_space(&self) -> &VmSpace {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the user mode that is bound to the current task and user space.
|
||||
///
|
||||
/// See `UserMode` on how to use it to execute user code.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// This method is intended to only allow each task to have at most one
|
||||
/// instance of `UserMode` initiated. If this method is called again before
|
||||
/// the first instance for the current task is dropped, then the method
|
||||
/// panics.
|
||||
pub fn user_mode(&self) -> UserMode<'_> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Code execution in the user mode.
|
||||
///
|
||||
/// This type enables executing the code in user space from a task in the kernel
|
||||
/// space safely.
|
||||
///
|
||||
/// Here is a sample code on how to use `UserMode`.
|
||||
///
|
||||
/// ```
|
||||
/// let current = Task::current();
|
||||
/// let user_space = current.user_space()
|
||||
/// .expect("the current task is associated with a user space");
|
||||
/// let mut user_mode = user_space.user_mode();
|
||||
/// loop {
|
||||
/// // Execute in the user space until some interesting user event occurs
|
||||
/// let user_event = user_mode.execute();
|
||||
/// todo!("handle the user event, e.g., syscall");
|
||||
/// }
|
||||
/// ```
|
||||
pub struct UserMode<'a> {
|
||||
current: Arc<Task>,
|
||||
user_space: &'a Arc<UserSpace>,
|
||||
}
|
||||
|
||||
// An instance of `UserMode` is bound to the current task. So it cannot be
|
||||
impl<'a> !Send for UserMode<'a> {}
|
||||
|
||||
impl<'a> UserMode<'a> {
|
||||
/// Starts executing in the user mode.
|
||||
///
|
||||
/// The method returns for one of three possible reasons indicated by `UserEvent`.
|
||||
/// 1. The user invokes a system call;
|
||||
/// 2. The user triggers an exception;
|
||||
/// 3. The user triggers a fault.
|
||||
///
|
||||
/// After handling the user event and updating the user-mode CPU context,
|
||||
/// this method can be invoked again to go back to the user space.
|
||||
pub fn execute(&mut self) -> UserEvent {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns an immutable reference the user-mode CPU context.
|
||||
pub fn context(&self) -> &CpuContext {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference the user-mode CPU context.
|
||||
pub fn context_mut(&mut self) -> &mut CpuContext {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// A user event is what brings back the control of the CPU back from
|
||||
/// the user space to the kernel space.
|
||||
///
|
||||
/// Note that hardware interrupts are not considered user events as they
|
||||
/// are triggered by devices and not visible to user programs.
|
||||
/// To handle interrupts, one should register callback funtions for
|
||||
/// IRQ lines (`IrqLine`).
|
||||
pub enum UserEvent {
|
||||
Syscall,
|
||||
Exception,
|
||||
Fault,
|
||||
}
|
3
src/kxos-frame/src/util/mod.rs
Normal file
3
src/kxos-frame/src/util/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod type_map;
|
||||
|
||||
pub use self::type_map::TypeMap;
|
26
src/kxos-frame/src/util/type_map.rs
Normal file
26
src/kxos-frame/src/util/type_map.rs
Normal file
@ -0,0 +1,26 @@
|
||||
/// A type map is a collection whose keys are types, rather than values.
|
||||
pub struct TypeMap {}
|
||||
|
||||
pub trait Any: core::any::Any + Send + Sync {}
|
||||
|
||||
impl TypeMap {
|
||||
/// Creates an empty typed map.
|
||||
pub fn new() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Inserts a new item of type `T`.
|
||||
pub fn insert<T: Any>(&mut self, val: T) -> Option<T> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Gets an item of type `T`.
|
||||
pub fn get<T: Any>(&self) -> Option<&T> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Gets an item of type `T`.
|
||||
pub fn remove<T: Any>(&self) -> Option<T> {
|
||||
todo!()
|
||||
}
|
||||
}
|
172
src/kxos-frame/src/vm/frame.rs
Normal file
172
src/kxos-frame/src/vm/frame.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use core::iter::Iterator;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A collection of page frames (physical memory pages).
|
||||
///
|
||||
/// For the most parts, `VmFrameVec` is like `Vec<VmFrame>`. But the
|
||||
/// implementation may or may not be based on `Vec`. Having a dedicated
|
||||
/// type to represent a series of page frames is convenient because,
|
||||
/// more often than not, one needs to operate on a batch of frames rather
|
||||
/// a single frame.
|
||||
pub struct VmFrameVec(Vec<VmFrame>);
|
||||
|
||||
impl VmFrameVec {
|
||||
/// Allocate a collection of free frames according to the given options.
|
||||
///
|
||||
/// All returned frames are safe to use in the sense that they are
|
||||
/// not _typed memory_. We define typed memory as the memory that
|
||||
/// may store Rust objects or affect Rust memory safety, e.g.,
|
||||
/// the code and data segments of the OS kernel, the stack and heap
|
||||
/// allocated for the OS kernel.
|
||||
///
|
||||
/// For more information, see `VmAllocOptions`.
|
||||
pub fn allocate(options: &VmAllocOptions) -> Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Pushs a new frame to the collection.
|
||||
pub fn push(&mut self, new_frame: VmFrame) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Pop a frame from the collection.
|
||||
pub fn pop(&mut self) -> Option<VmFrame> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Removes a frame at a position.
|
||||
pub fn remove(&mut self, at: usize) -> VmFrame {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Append some frames.
|
||||
pub fn append(&mut self, more: VmFrameVec) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Truncate some frames.
|
||||
///
|
||||
/// If `new_len >= self.len()`, then this method has no effect.
|
||||
pub fn truncate(&mut self, new_len: usize) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns an iterator
|
||||
pub fn iter(&self) -> VmFrameVecIter<'_> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the number of frames.
|
||||
pub fn len(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the number of bytes.
|
||||
///
|
||||
/// This method is equivalent to `self.len() * PAGE_SIZE`.
|
||||
pub fn nbytes(&self) -> usize {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator for frames.
|
||||
pub struct VmFrameVecIter<'a> {
|
||||
frames: &'a VmFrameVec,
|
||||
// more...
|
||||
}
|
||||
|
||||
impl<'a> Iterator for VmFrameVecIter<'a> {
|
||||
type Item = &'a VmFrame;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for allocating physical memory pages (or frames).
|
||||
/// See `VmFrameVec::alloc`.
|
||||
pub struct VmAllocOptions {}
|
||||
|
||||
impl VmAllocOptions {
|
||||
/// Creates new options for allocating the specified number of frames.
|
||||
pub fn new(len: usize) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets the physical address of the first frame.
|
||||
///
|
||||
/// If the physical address is given, then the allocated frames will be
|
||||
/// contiguous.
|
||||
///
|
||||
/// The default value is `None`.
|
||||
pub fn paddr(&mut self, paddr: Option<Paddr>) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets whether the allocated frames should be contiguous.
|
||||
///
|
||||
/// If the physical address is set, then the frames must be contiguous.
|
||||
///
|
||||
/// The default value is `false`.
|
||||
pub fn is_contiguous(&mut self, is_contiguous: bool) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets whether the pages can be accessed by devices through
|
||||
/// Direct Memory Access (DMA).
|
||||
///
|
||||
/// In a TEE environment, DMAable pages are untrusted pages shared with
|
||||
/// the VMM.
|
||||
pub fn can_dma(&mut self, can_dma: bool) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to a page frame.
|
||||
///
|
||||
/// An instance of `VmFrame` is a handle to a page frame (a physical memory
|
||||
/// page). A cloned `VmFrame` refers to the same page frame as the original.
|
||||
/// As the original and cloned instances point to the same physical address,
|
||||
/// they are treated as equal to each other. Behind the scene,
|
||||
/// a reference counter is maintained for each page frame so that
|
||||
/// when all instances of `VmFrame` that refer to the
|
||||
/// same page frame are dropped, the page frame will be freed.
|
||||
/// Free page frames are allocated in bulk by `VmFrameVec::allocate`.
|
||||
pub struct VmFrame {}
|
||||
|
||||
impl VmFrame {
|
||||
/// Creates a new VmFrame.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The given physical address must be valid for use.
|
||||
pub(crate) unsafe fn new(paddr: Paddr) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns the physical address of the page frame.
|
||||
pub fn paddr(&self) -> Paddr {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns whether the page frame is accessible by DMA.
|
||||
///
|
||||
/// In a TEE environment, DMAable pages are untrusted pages shared with
|
||||
/// the VMM.
|
||||
pub fn can_dma(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for VmFrame {
|
||||
fn clone(&self) -> Self {
|
||||
todo!("inc ref cnt")
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VmFrame {
|
||||
fn drop(&mut self) {
|
||||
todo!("dec ref cnt and if zero, free the page frame")
|
||||
}
|
||||
}
|
64
src/kxos-frame/src/vm/io.rs
Normal file
64
src/kxos-frame/src/vm/io.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use crate::prelude::*;
|
||||
use crate::vm::Pod;
|
||||
|
||||
/// A trait that enables reading/writing data from/to a VM object,
|
||||
/// e.g., `VmSpace`, `VmFrameVec`, and `VmFrame`.
|
||||
///
|
||||
/// # Concurrency
|
||||
///
|
||||
/// The methods may be executed by multiple concurrent reader and writer
|
||||
/// threads. In this case, if the results of concurrent reads or writes
|
||||
/// desire predictability or atomicity, the users should add extra mechanism
|
||||
/// for such properties.
|
||||
pub trait VmIo: Send + Sync {
|
||||
/// Read a specified number of bytes at a specified offset into a given buffer.
|
||||
///
|
||||
/// # No short reads
|
||||
///
|
||||
/// On success, the output `buf` must be filled with the requested data
|
||||
/// completely. If, for any reason, the requested data is only partially
|
||||
/// available, then the method shall return an error.
|
||||
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()>;
|
||||
|
||||
/// Read a value of a specified type at a specified offset.
|
||||
fn read_val<T: Pod>(&self, offset: usize) -> Result<T> {
|
||||
let mut val = T::new_uninit();
|
||||
self.read_bytes(offset, val.as_bytes_mut())?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Read a slice of a specified type at a specified offset.
|
||||
///
|
||||
/// # No short reads
|
||||
///
|
||||
/// Similar to `read_bytes`.
|
||||
fn read_slice<T: Pod>(&self, offset: usize, slice: &mut [T]) -> Result<()> {
|
||||
let buf = unsafe { core::mem::transmute(slice) };
|
||||
self.read_bytes(offset, buf)
|
||||
}
|
||||
|
||||
/// Write a specified number of bytes from a given buffer at a specified offset.
|
||||
///
|
||||
/// # No short writes
|
||||
///
|
||||
/// On success, the input `buf` must be written to the VM object entirely.
|
||||
/// If, for any reason, the input data can only be written partially,
|
||||
/// then the method shall return an error.
|
||||
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()>;
|
||||
|
||||
/// Write a value of a specified type at a specified offset.
|
||||
fn write_val<T: Pod>(&self, offset: usize, new_val: &T) -> Result<()> {
|
||||
self.write_bytes(offset, new_val.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write a slice of a specified type at a specified offset.
|
||||
///
|
||||
/// # No short write
|
||||
///
|
||||
/// Similar to `write_bytes`.
|
||||
fn write_slice<T: Pod>(&self, offset: usize, slice: &[T]) -> Result<()> {
|
||||
let buf = unsafe { core::mem::transmute(slice) };
|
||||
self.write_bytes(offset, buf)
|
||||
}
|
||||
}
|
20
src/kxos-frame/src/vm/mod.rs
Normal file
20
src/kxos-frame/src/vm/mod.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Virtual memory (VM).
|
||||
|
||||
/// Virtual addresses.
|
||||
pub type Vaddr = usize;
|
||||
|
||||
/// Physical addresses.
|
||||
pub type Paddr = usize;
|
||||
|
||||
/// The size of a VM page or a page frame.
|
||||
pub const PAGE_SIZE: usize = 0x1000; // 4KB
|
||||
|
||||
mod frame;
|
||||
mod io;
|
||||
mod pod;
|
||||
mod space;
|
||||
|
||||
pub use self::frame::{VmAllocOptions, VmFrame, VmFrameVec, VmFrameVecIter};
|
||||
pub use self::io::VmIo;
|
||||
pub use self::pod::Pod;
|
||||
pub use self::space::{VmMapOptions, VmPerm, VmSpace};
|
60
src/kxos-frame/src/vm/pod.rs
Normal file
60
src/kxos-frame/src/vm/pod.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
/// A marker trait for plain old data (POD).
|
||||
///
|
||||
/// A POD type `T:Pod` supports converting to and from arbitrary
|
||||
/// `mem::size_of::<T>()` bytes _safely_.
|
||||
/// For example, simple primitive types like `u8` and `i16`
|
||||
/// are POD types. But perhaps surprisingly, `bool` is not POD
|
||||
/// because Rust compiler makes implicit assumption that
|
||||
/// a byte of `bool` has a value of either `0` or `1`.
|
||||
/// Interpreting a byte of value `3` has a `bool` value has
|
||||
/// undefined behavior.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Marking a non-POD type as POD may cause undefined behaviors.
|
||||
pub unsafe trait Pod: Copy + Sized {
|
||||
/// Creates a new instance of Pod type that is filled with zeroes.
|
||||
fn new_zeroed() -> Self {
|
||||
// SAFETY. An all-zero value of `T: Pod` is always valid.
|
||||
unsafe { core::mem::zeroed() }
|
||||
}
|
||||
|
||||
/// Creates a new instance of Pod type with uninitialized content.
|
||||
fn new_uninit() -> Self {
|
||||
// SAFETY. A value of `T: Pod` can have arbitrary bits.
|
||||
unsafe { MaybeUninit::uninit().assume_init() }
|
||||
}
|
||||
|
||||
/// Creates a new instance from the given bytes.
|
||||
fn from_bytes(bytes: &[u8]) -> Self {
|
||||
let mut new_self = Self::new_uninit();
|
||||
new_self.as_bytes_mut().copy_from_slice(bytes);
|
||||
new_self
|
||||
}
|
||||
|
||||
/// As a slice of bytes.
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
let ptr = self as *const Self as *const u8;
|
||||
let len = core::mem::size_of::<Self>();
|
||||
unsafe { core::slice::from_raw_parts(ptr, len) }
|
||||
}
|
||||
|
||||
/// As a mutable slice of bytes.
|
||||
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||
let ptr = self as *mut Self as *mut u8;
|
||||
let len = core::mem::size_of::<Self>();
|
||||
unsafe { core::slice::from_raw_parts_mut(ptr, len) }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_pod_for {
|
||||
($($token:tt)*/* define the input */) => {
|
||||
/* define the expansion */
|
||||
};
|
||||
}
|
||||
|
||||
impl_pod_for!(u8, u16, u32, u64, i8, i16, i32, i64,);
|
||||
|
||||
//unsafe impl<T: Pod, const N> [T; N] for Pod {}
|
112
src/kxos-frame/src/vm/space.rs
Normal file
112
src/kxos-frame/src/vm/space.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use bitflags::bitflags;
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::vm::VmFrameVec;
|
||||
|
||||
/// Virtual memory space.
|
||||
///
|
||||
/// A virtual memory space (`VmSpace`) can be created and assigned to a user space so that
|
||||
/// the virtual memory of the user space can be manipulated safely. For example,
|
||||
/// given an arbitrary user-space pointer, one can read and write the memory
|
||||
/// location refered to by the user-space pointer without the risk of breaking the
|
||||
/// memory safety of the kernel space.
|
||||
///
|
||||
/// A newly-created `VmSpace` is not backed by any physical memory pages.
|
||||
/// To provide memory pages for a `VmSpace`, one can allocate and map
|
||||
/// physical memory (`VmFrames`) to the `VmSpace`.
|
||||
pub struct VmSpace {}
|
||||
|
||||
impl VmSpace {
|
||||
/// Creates a new VM address space.
|
||||
pub fn new() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Maps some physical memory pages into the VM space according to the given
|
||||
/// options, returning the address where the mapping is created.
|
||||
///
|
||||
/// For more information, see `VmMapOptions`.
|
||||
pub fn map(&self, frames: VmFrameVec, options: &VmMapOptions) -> Result<Vaddr> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Unmaps the physical memory pages within the VM address range.
|
||||
///
|
||||
/// The range is allowed to contain gaps, where no physical memory pages
|
||||
/// are mapped.
|
||||
pub fn unmap(&self, range: &Range<Vaddr>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update the VM protection permissions within the VM address range.
|
||||
///
|
||||
/// The entire specified VM range must have been mapped with physical
|
||||
/// memory pages.
|
||||
pub fn protect(&self, range: &Range<Vaddr>, perm: VmPerm) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for mapping physical memory pages into a VM address space.
|
||||
/// See `VmSpace::map`.
|
||||
pub struct VmMapOptions {}
|
||||
|
||||
impl VmMapOptions {
|
||||
/// Creates the default options.
|
||||
pub fn new() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets the alignment of the address of the mapping.
|
||||
///
|
||||
/// The alignment must be a power-of-2 and greater than or equal to the
|
||||
/// page size.
|
||||
///
|
||||
/// The default value of this option is the page size.
|
||||
pub fn align(&mut self, align: usize) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets the permissions of the mapping, which affects whether
|
||||
/// the mapping can be read, written, or executed.
|
||||
///
|
||||
/// The default value of this option is read-only.
|
||||
pub fn perm(&mut self, perm: VmPerm) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets the address of the new mapping.
|
||||
///
|
||||
/// The default value of this option is `None`.
|
||||
pub fn addr(&mut self, addr: Option<Vaddr>) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sets whether the mapping can overwrite any existing mappings.
|
||||
///
|
||||
/// If this option is `true`, then the address option must be `Some(_)`.
|
||||
///
|
||||
/// The default value of this option is `false`.
|
||||
pub fn can_overwrite(&mut self, can_overwrite: bool) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Virtual memory protection permissions.
|
||||
pub struct VmPerm: u8 {
|
||||
/// Readable.
|
||||
const R = 0b00000001;
|
||||
/// Writable.
|
||||
const W = 0b00000010;
|
||||
/// Executable.
|
||||
const X = 0b00000100;
|
||||
/// Readable + writable.
|
||||
const RW = Self::R.bits | Self::W.bits;
|
||||
/// Readable + execuable.
|
||||
const RX = Self::R.bits | Self::X.bits;
|
||||
/// Readable + writable + executable.
|
||||
const RWX = Self::R.bits | Self::W.bits | Self::X.bits;
|
||||
}
|
||||
}
|
2
src/rust-toolchain.toml
Normal file
2
src/rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2022-08-01"
|
Loading…
x
Reference in New Issue
Block a user