move processor from wrj

master
chenqiuhao 6 years ago
parent 66b18772c6
commit 2002ddd5fa

@ -1,13 +1,5 @@
//! Port from sbi.h
//!
//! This code is used for OS to use hardware outside with calling these implements
/*
** @brief translate implement calling message to RISCV asm
** @param which: usize ecall type
** arg0, arg1, arg2: usize ecall args
** @retval ret: usize the result of the asm
*/
#[inline(always)]
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let ret;
@ -21,38 +13,18 @@ fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
ret
}
/*
** @brief output char to console
** @param ch: usize the char to output to console
** @retval none
*/
pub fn console_putchar(ch: usize) {
sbi_call(SBI_CONSOLE_PUTCHAR, ch, 0, 0);
}
/*
** @brief input char from console
** @param none
** @retval ch: usize the char get from console
*/
pub fn console_getchar() -> usize {
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
}
/*
** @brief call this function to shutdown
** @param none
** @retval none
*/
pub fn shutdown() {
sbi_call(SBI_SHUTDOWN, 0, 0, 0);
}
/*
** @brief set a timer when running
** @param stime_value: u64 time to be set
** @retval none
*/
pub fn set_timer(stime_value: u64) {
#[cfg(target_pointer_width = "32")]
sbi_call(SBI_SET_TIMER, stime_value as usize, (stime_value >> 32) as usize, 0);
@ -60,49 +32,24 @@ pub fn set_timer(stime_value: u64) {
sbi_call(SBI_SET_TIMER, stime_value as usize, 0, 0);
}
/*
** @brief clear the ipi
** @param none
** @retval none
*/
pub fn clear_ipi() {
sbi_call(SBI_CLEAR_IPI, 0, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn send_ipi(hart_mask: *const usize) {
sbi_call(SBI_SEND_IPI, hart_mask as usize, 0, 0);
pub fn send_ipi(hart_mask: usize) {
sbi_call(SBI_SEND_IPI, &hart_mask as *const _ as usize, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn remote_fence_i(hart_mask: *const usize) {
sbi_call(SBI_REMOTE_FENCE_I, hart_mask as usize, 0, 0);
pub fn remote_fence_i(hart_mask: usize) {
sbi_call(SBI_REMOTE_FENCE_I, &hart_mask as *const _ as usize, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn remote_sfence_vma(hart_mask: *const usize, _start: usize, _size: usize) {
sbi_call(SBI_REMOTE_SFENCE_VMA, hart_mask as usize, 0, 0);
pub fn remote_sfence_vma(hart_mask: usize, _start: usize, _size: usize) {
sbi_call(SBI_REMOTE_SFENCE_VMA, &hart_mask as *const _ as usize, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn remote_sfence_vma_asid(hart_mask: *const usize, _start: usize, _size: usize, _asid: usize) {
sbi_call(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask as usize, 0, 0);
pub fn remote_sfence_vma_asid(hart_mask: usize, _start: usize, _size: usize, _asid: usize) {
sbi_call(SBI_REMOTE_SFENCE_VMA_ASID, &hart_mask as *const _ as usize, 0, 0);
}
const SBI_SET_TIMER: usize = 0;

@ -5,3 +5,4 @@ authors = ["WangRunji <wangrunji0408@163.com>"]
[dependencies]
log = "0.4"
spin = "0.4"

@ -1,18 +1,25 @@
#![no_std]
#![feature(alloc)]
#![feature(const_fn)]
#![feature(linkage)]
#![feature(nll)]
#![feature(vec_resize_default)]
extern crate alloc;
#[macro_use]
extern crate log;
extern crate spin;
// To use `println!` in test
#[cfg(test)]
#[macro_use]
extern crate std;
pub mod processor;
mod process_manager;
mod processor;
pub mod scheduler;
pub mod thread;
mod util;
mod event_hub;
pub use process_manager::*;
pub use processor::Processor;

@ -0,0 +1,196 @@
use alloc::boxed::Box;
use alloc::sync::Arc;
use spin::Mutex;
use scheduler::Scheduler;
use core::cell::UnsafeCell;
use alloc::vec::Vec;
use event_hub::EventHub;
struct Process {
id: Pid,
status: Status,
status_after_stop: Status,
context: Option<Box<Context>>,
}
pub type Pid = usize;
type ExitCode = usize;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Status {
Ready,
Running(usize),
Sleeping,
Waiting(Pid),
/// aka ZOMBIE. Its context was dropped.
Exited(ExitCode),
}
enum Event {
Wakeup(Pid),
}
pub trait Context {
unsafe fn switch_to(&mut self, target: &mut Context);
}
pub struct ProcessManager {
procs: Vec<Mutex<Option<Process>>>,
scheduler: Mutex<Box<Scheduler>>,
wait_queue: Vec<Mutex<Vec<Pid>>>,
event_hub: Mutex<EventHub<Event>>,
}
impl ProcessManager {
pub fn new(scheduler: Box<Scheduler>, max_proc_num: usize) -> Self {
ProcessManager {
procs: {
let mut vec = Vec::new();
vec.resize_default(max_proc_num);
vec
},
scheduler: Mutex::new(scheduler),
wait_queue: {
let mut vec = Vec::new();
vec.resize_default(max_proc_num);
vec
},
event_hub: Mutex::new(EventHub::new()),
}
}
fn alloc_pid(&self) -> Pid {
for (i, proc) in self.procs.iter().enumerate() {
if proc.lock().is_none() {
return i;
}
}
panic!("Process number exceeded");
}
/// Add a new process
pub fn add(&self, context: Box<Context>) -> Pid {
let pid = self.alloc_pid();
*(&self.procs[pid]).lock() = Some(Process {
id: pid,
status: Status::Ready,
status_after_stop: Status::Ready,
context: Some(context),
});
self.scheduler.lock().insert(pid);
pid
}
/// Make process `pid` time slice -= 1.
/// Return true if time slice == 0.
/// Called by timer interrupt handler.
pub fn tick(&self, pid: Pid) -> bool {
let mut event_hub = self.event_hub.lock();
event_hub.tick();
while let Some(event) = event_hub.pop() {
match event {
Event::Wakeup(pid) => self.set_status(pid, Status::Ready),
}
}
self.scheduler.lock().tick(pid)
}
/// Set the priority of process `pid`
pub fn set_priority(&self, pid: Pid, priority: u8) {
self.scheduler.lock().set_priority(pid, priority);
}
/// Called by Processor to get a process to run.
/// The manager first mark it `Running`,
/// then take out and return its Context.
pub fn run(&self, cpu_id: usize) -> (Pid, Box<Context>) {
let mut scheduler = self.scheduler.lock();
let pid = scheduler.select()
.expect("failed to select a runnable process");
scheduler.remove(pid);
let mut proc_lock = self.procs[pid].lock();
let mut proc = proc_lock.as_mut().unwrap();
proc.status = Status::Running(cpu_id);
(pid, proc.context.take().unwrap())
}
/// Called by Processor to finish running a process
/// and give its context back.
pub fn stop(&self, pid: Pid, context: Box<Context>) {
let mut proc_lock = self.procs[pid].lock();
let mut proc = proc_lock.as_mut().unwrap();
proc.status = proc.status_after_stop.clone();
proc.status_after_stop = Status::Ready;
proc.context = Some(context);
match proc.status {
Status::Ready => self.scheduler.lock().insert(pid),
Status::Exited(_) => proc.context = None,
_ => {}
}
}
/// Switch the status of a process.
/// Insert/Remove it to/from scheduler if necessary.
fn set_status(&self, pid: Pid, status: Status) {
let mut scheduler = self.scheduler.lock();
let mut proc_lock = self.procs[pid].lock();
let mut proc = proc_lock.as_mut().unwrap();
trace!("process {} {:?} -> {:?}", pid, proc.status, status);
match (&proc.status, &status) {
(Status::Ready, Status::Ready) => return,
(Status::Ready, _) => scheduler.remove(pid),
(Status::Running(_), _) => {},
(Status::Exited(_), _) => panic!("can not set status for a exited process"),
(Status::Waiting(target), Status::Exited(_)) =>
self.wait_queue[*target].lock().retain(|&i| i != pid),
// TODO: Sleep -> Exited Remove wakeup event.
(_, Status::Ready) => scheduler.insert(pid),
_ => {}
}
match proc.status {
Status::Running(_) => proc.status_after_stop = status,
_ => proc.status = status,
}
match proc.status {
Status::Exited(_) => proc.context = None,
_ => {}
}
}
pub fn get_status(&self, pid: Pid) -> Option<Status> {
self.procs[pid].lock().as_ref().map(|p| p.status.clone())
}
pub fn remove(&self, pid: Pid) {
let mut proc_lock = self.procs[pid].lock();
let proc = proc_lock.as_ref().unwrap();
match proc.status {
Status::Exited(_) => *proc_lock = None,
_ => panic!("can not remove non-exited process"),
}
}
pub fn sleep(&self, pid: Pid, time: usize) {
self.set_status(pid, Status::Sleeping);
if time != 0 {
self.event_hub.lock().push(time, Event::Wakeup(pid));
}
}
pub fn wakeup(&self, pid: Pid) {
self.set_status(pid, Status::Ready);
}
pub fn wait(&self, pid: Pid, target: Pid) {
self.set_status(pid, Status::Waiting(target));
self.wait_queue[target].lock().push(pid);
}
pub fn exit(&self, pid: Pid, code: ExitCode) {
self.set_status(pid, Status::Exited(code));
for waiter in self.wait_queue[pid].lock().drain(..) {
self.wakeup(waiter);
}
}
}

@ -1,396 +1,95 @@
use alloc::{boxed::Box, collections::BTreeMap};
use scheduler::*;
use event_hub::EventHub;
use util::GetMut2;
use core::fmt::Debug;
#[derive(Debug)]
pub struct Process<T> {
pid: Pid,
parent: Pid,
status: Status,
context: T,
}
pub type Pid = usize;
pub type ErrorCode = usize;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Status {
Ready,
Running,
Waiting(Pid),
Sleeping,
Exited(ErrorCode),
}
pub trait Context: Debug {
unsafe fn switch(&mut self, target: &mut Self);
fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self;
}
pub struct Processor_<T: Context, S: Scheduler> {
procs: BTreeMap<Pid, Process<T>>,
current_pid: Pid,
event_hub: EventHub<Event>,
/// Choose what on next schedule ?
next: Option<Pid>,
// WARNING: if MAX_PROCESS_NUM is too large, will cause stack overflow
scheduler: S,
}
impl<T> Process<T> {
fn exit_code(&self) -> Option<ErrorCode> {
match self.status {
Status::Exited(code) => Some(code),
_ => None,
}
}
}
// TODO: 除schedule()外的其它函数应该只设置进程状态不应调用schedule
impl<T: Context, S: Scheduler> Processor_<T, S> {
/*
** @brief create a new Processor
** @param init_context: T initiate context
** scheduler: S the scheduler to use
** @retval the Processor created
*/
pub fn new(init_context: T, scheduler: S) -> Self {
let init_proc = Process {
pid: 0,
parent: 0,
status: Status::Running,
context: init_context,
};
Processor_ {
procs: {
let mut map = BTreeMap::<Pid, Process<T>>::new();
map.insert(0, init_proc);
map
},
current_pid: 0,
event_hub: EventHub::new(),
next: None,
scheduler,
}
}
/*
** @brief set the priority of current process
** @param priority: u8 the priority to set
** @retval none
*/
pub fn set_priority(&mut self, priority: u8) {
self.scheduler.set_priority(self.current_pid, priority);
}
/*
** @brief mark the current process to reschedule
** @param none
** @retval none
*/
pub fn set_reschedule(&mut self) {
let pid = self.current_pid;
self.set_status(pid, Status::Ready);
}
/*
** @brief allocate the pid of the process
** @param none
** @retval the pid allocated
*/
fn alloc_pid(&self) -> Pid {
let mut next: Pid = 0;
for &i in self.procs.keys() {
if i != next {
return next;
} else {
next = i + 1;
}
}
return next;
}
/*
** @brief set the status of the process
** @param pid: Pid the pid of process which needs to be set
** status: Status the status to be set
** @retval none
*/
fn set_status(&mut self, pid: Pid, status: Status) {
let status0 = self.get(pid).status.clone();
match (&status0, &status) {
(&Status::Ready, &Status::Ready) => return,
(&Status::Ready, _) => self.scheduler.remove(pid),
(_, &Status::Ready) => self.scheduler.insert(pid),
_ => {}
}
trace!("process {} {:?} -> {:?}", pid, status0, status);
self.get_mut(pid).status = status;
}
/*
** @brief Called by timer.
** Handle events.
** @param none
** @retval none
*/
pub fn tick(&mut self) {
let current_pid = self.current_pid;
if self.scheduler.tick(current_pid) {
self.set_reschedule();
}
self.event_hub.tick();
while let Some(event) = self.event_hub.pop() {
debug!("event {:?}", event);
match event {
Event::Schedule => {
self.event_hub.push(10, Event::Schedule);
self.set_reschedule();
},
Event::Wakeup(pid) => {
self.set_status(pid, Status::Ready);
self.set_reschedule();
self.next = Some(pid);
},
}
}
}
/*
** @brief get now time
** @param none
** @retval the time got
*/
pub fn get_time(&self) -> usize {
self.event_hub.get_time()
}
/*
** @brief add a new process
** @param context: T the context fo the process
** @retval the pid of new process
*/
pub fn add(&mut self, context: T) -> Pid {
let pid = self.alloc_pid();
let process = Process {
pid,
parent: self.current_pid,
status: Status::Ready,
context,
};
self.scheduler.insert(pid);
self.procs.insert(pid, process);
pid
}
/*
** @brief Called every interrupt end
** Do schedule ONLY IF current status != Running
** @param none
** @retval none
*/
pub fn schedule(&mut self) {
if self.get(self.current_pid).status == Status::Running {
return;
}
let pid = self.next.take().unwrap_or_else(|| self.scheduler.select().unwrap());
self.switch_to(pid);
}
/*
** @brief Switch process to `pid`, switch page table if necessary.
Store `rsp` and point it to target kernel stack.
The current status must be set before, and not be `Running`.
** @param the pid of the process to switch
** @retval none
*/
fn switch_to(&mut self, pid: Pid) {
// for debug print
let pid0 = self.current_pid;
if pid == self.current_pid {
if self.get(self.current_pid).status != Status::Running {
self.set_status(pid, Status::Running);
}
return;
}
self.current_pid = pid;
let (from, to) = self.procs.get_mut2(pid0, pid);
assert_ne!(from.status, Status::Running);
assert_eq!(to.status, Status::Ready);
to.status = Status::Running;
self.scheduler.remove(pid);
//info!("switch from {} to {} {:x?}", pid0, pid, to.context);
unsafe { from.context.switch(&mut to.context); }
}
/*
** @brief get process by pid
** @param pid: Pid the pid of the process
** @retval the process struct
*/
fn get(&self, pid: Pid) -> &Process<T> {
self.procs.get(&pid).unwrap()
}
/*
** @brief get mut process struct by pid
** @param pid: Pid the pid of the process
** @retval the mut process struct
*/
fn get_mut(&mut self, pid: Pid) -> &mut Process<T> {
self.procs.get_mut(&pid).unwrap()
}
/*
** @brief get context of current process
** @param none
** @retval current context
*/
pub fn current_context(&self) -> &T {
&self.get(self.current_pid).context
}
pub fn current_context_mut(&mut self) -> &mut T {
let id = self.current_pid;
&mut self.get_mut(id).context
}
/*
** @brief get pid of current process
** @param none
** @retval current pid
*/
pub fn current_pid(&self) -> Pid {
self.current_pid
use alloc::boxed::Box;
use alloc::sync::Arc;
use spin::Mutex;
use core::cell::UnsafeCell;
use process_manager::*;
/// Process executor
///
/// Per-CPU struct. Defined at global.
/// Only accessed by associated CPU with interrupt disabled.
#[derive(Default)]
pub struct Processor {
inner: UnsafeCell<Option<ProcessorInner>>,
}
unsafe impl Sync for Processor {}
struct ProcessorInner {
id: usize,
proc: Option<(Pid, Box<Context>)>,
loop_context: Box<Context>,
manager: Arc<ProcessManager>,
}
impl Processor {
pub const fn new() -> Self {
Processor { inner: UnsafeCell::new(None) }
}
pub unsafe fn init(&self, id: usize, context: Box<Context>, manager: Arc<ProcessManager>) {
unsafe {
*self.inner.get() = Some(ProcessorInner {
id,
proc: None,
loop_context: context,
manager,
});
}
/*
** @brief kill a process by pid
** @param pid: Pid the pid of the process to kill
** @retval none
*/
pub fn kill(&mut self, pid: Pid) {
self.exit(pid, 0x1000); // TODO: error code for killed
}
/*
** @brief exit a process by pid
** @param pid: Pid the pid to exit
** error_code: ErrorCode the error code when exiting
** @retval none
*/
pub fn exit(&mut self, pid: Pid, error_code: ErrorCode) {
info!("{} exit, code: {}", pid, error_code);
self.set_status(pid, Status::Exited(error_code));
if let Some(waiter) = self.find_waiter(pid) {
info!(" then wakeup {}", waiter);
self.set_status(waiter, Status::Ready);
self.next = Some(waiter);
}
fn inner(&self) -> &mut ProcessorInner {
unsafe { &mut *self.inner.get() }.as_mut()
.expect("Processor is not initialized")
}
/*
** @brief let a process to sleep for a while
** @param pid: Pid the pid of the process to sleep
** time: usize the time to sleep
** @retval none
*/
pub fn sleep(&mut self, pid: Pid, time: usize) {
self.set_status(pid, Status::Sleeping);
self.event_hub.push(time, Event::Wakeup(pid));
/// Begin running processes after CPU setup.
///
/// This function never returns. It loops, doing:
/// - choose a process to run
/// - switch to start running that process
/// - eventually that process transfers control
/// via switch back to the scheduler.
pub fn run(&self) -> ! {
let inner = self.inner();
loop {
let proc = inner.manager.run(inner.id);
trace!("CPU{} begin running process {}", inner.id, proc.0);
inner.proc = Some(proc);
unsafe {
inner.loop_context.switch_to(&mut *inner.proc.as_mut().unwrap().1);
}
/*
** @brief let a process to sleep until wake up
** @param pid: Pid the pid of the process to sleep
** @retval none
*/
pub fn sleep_(&mut self, pid: Pid) {
self.set_status(pid, Status::Sleeping);
let (pid, context) = inner.proc.take().unwrap();
trace!("CPU{} stop running process {}", inner.id, pid);
inner.manager.stop(pid, context);
}
/*
** @brief wake up al sleeping process
** @param pid: Pid the pid of the process to wake up
** @retval none
*/
pub fn wakeup_(&mut self, pid: Pid) {
self.set_status(pid, Status::Ready);
}
/*
** @brief Let current process wait for another
** @param pid: Pid the pid of the process to wait for
** @retval the result of wait
*/
pub fn current_wait_for(&mut self, pid: Pid) -> WaitResult {
info!("current {} wait for {:?}", self.current_pid, pid);
if self.procs.values().filter(|&p| p.parent == self.current_pid).next().is_none() {
return WaitResult::NotExist;
/// Called by process running on this Processor.
/// Yield and reschedule.
pub fn yield_now(&self) {
let inner = self.inner();
unsafe {
inner.proc.as_mut().unwrap().1.switch_to(&mut *inner.loop_context);
}
let pid = self.try_wait(pid).unwrap_or_else(|| {
let current_pid = self.current_pid;
self.set_status(current_pid, Status::Waiting(pid));
self.schedule(); // yield
self.try_wait(pid).unwrap()
});
let exit_code = self.get(pid).exit_code().unwrap();
info!("{} wait end and remove {}", self.current_pid, pid);
self.procs.remove(&pid);
WaitResult::Ok(pid, exit_code)
}
/*
** @brief Try to find a exited wait target
** @param pid: Pid the pid of the process to wait for
** @retval the pid found or none
*/
fn try_wait(&mut self, pid: Pid) -> Option<Pid> {
match pid {
0 => self.procs.values()
.find(|&p| p.parent == self.current_pid && p.exit_code().is_some())
.map(|p| p.pid),
_ => self.get(pid).exit_code().map(|_| pid),
}
pub fn pid(&self) -> Pid {
self.inner().proc.as_ref().unwrap().0
}
/*
** @brief find one process which is waiting for the input process
** @param pid: Pid the pid of the target process
** @retval the pid of the waiting process or none
*/
fn find_waiter(&self, pid: Pid) -> Option<Pid> {
self.procs.values().find(|&p| {
p.status == Status::Waiting(pid) ||
(p.status == Status::Waiting(0) && self.get(pid).parent == p.pid)
}).map(|ref p| p.pid)
}
pub fn context(&self) -> &Context {
&*self.inner().proc.as_ref().unwrap().1
}
#[derive(Debug)]
pub enum WaitResult {
/// The target process is exited with `ErrorCode`.
Ok(Pid, ErrorCode),
/// The target process is not exist.
NotExist,
pub fn manager(&self) -> &ProcessManager {
&*self.inner().manager
}
#[derive(Debug)]
enum Event {
Schedule,
Wakeup(Pid),
pub fn tick(&self) {
let need_reschedule = self.manager().tick(self.pid());
if need_reschedule {
self.yield_now();
}
impl<T: Context> GetMut2<Pid> for BTreeMap<Pid, Process<T>> {
type Output = Process<T>;
fn get_mut(&mut self, id: Pid) -> &mut Process<T> {
self.get_mut(&id).unwrap()
}
}

@ -2,51 +2,19 @@ use alloc::{collections::BinaryHeap, vec::Vec};
type Pid = usize;
// implements of process scheduler
///
pub trait Scheduler {
/*
** @brief add a new process
** @param pid: Pid the pid of the process to add
** @retval none
*/
fn insert(&mut self, pid: Pid);
/*
** @brief remove a processs from the list
** @param pid: Pid the pid of the process to remove
** @retval none
*/
fn remove(&mut self, pid: Pid);
/*
** @brief choose a process to run next
** @param none
** @retval Option<Pid> the pid of the process to run or none
*/
fn select(&mut self) -> Option<Pid>;
/*
** @brief when a clock interrupt occurs, update the list and check whether need to reschedule
** @param current: Pid the pid of the process which is running now
** @retval bool if need to reschedule
*/
fn tick(&mut self, current: Pid) -> bool; // need reschedule?
/*
** @brief set the priority of the process
** @param pid: Pid the pid of the process to be set
** priority: u8 the priority to be set
** @retval none
*/
fn set_priority(&mut self, pid: Pid, priority: u8);
fn move_to_head(&mut self, pid: Pid);
}
pub use self::rr::RRScheduler;
pub use self::stride::StrideScheduler;
// use round-robin scheduling
mod rr {
use super::*;
@ -112,6 +80,14 @@ mod rr {
fn set_priority(&mut self, pid: usize, priority: u8) {
}
fn move_to_head(&mut self, pid: usize) {
let pid = pid + 1;
assert!(self.infos[pid].present);
self._list_remove(pid);
self._list_add_after(pid, 0);
trace!("rr move_to_head {}", pid - 1);
}
}
impl RRScheduler {
@ -128,6 +104,10 @@ mod rr {
self.infos[prev].next = i;
self.infos[at].prev = i;
}
fn _list_add_after(&mut self, i: Pid, at: Pid) {
let next = self.infos[at].next;
self._list_add_before(i, next);
}
fn _list_remove(&mut self, i: Pid) {
let next = self.infos[i].next;
let prev = self.infos[i].prev;
@ -139,7 +119,6 @@ mod rr {
}
}
// use stride scheduling
mod stride {
use super::*;
@ -190,6 +169,9 @@ mod stride {
let info = &mut self.infos[pid];
assert!(info.present);
info.present = false;
if self.queue.peek().is_some() && self.queue.peek().unwrap().1 == pid {
self.queue.pop();
} else {
// BinaryHeap only support pop the top.
// So in order to remove an arbitrary element,
// we have to take all elements into a Vec,
@ -197,6 +179,7 @@ mod stride {
let rest: Vec<_> = self.queue.drain().filter(|&p| p.1 != pid).collect();
use core::iter::FromIterator;
self.queue = BinaryHeap::from_iter(rest.into_iter());
}
trace!("stride remove {}", pid);
}
@ -229,6 +212,15 @@ mod stride {
self.infos[pid].priority = priority;
trace!("stride {} priority = {}", pid, priority);
}
fn move_to_head(&mut self, pid: Pid) {
if self.queue.peek().is_some() {
let stride = -self.queue.peek().unwrap().0;
self.remove(pid);
self.infos[pid].stride = stride;
self.insert(pid);
}
}
}
impl StrideScheduler {

@ -1,199 +1,165 @@
//! Thread std-like interface
//!
//! Based on Processor.
//! Used in the kernel.
//! Based on Processor. Used in kernel.
//!
//! # Example
//!
//! ```
//! // Define a support implementation struct
//! pub struct ThreadSupportImpl;
//!
//! // Impl `ThreadSupport` trait
//! impl ThreadSupport for ThreadSupportImpl { ... }
//!
//! // Export the full struct as `thread`.
//! #[allow(non_camel_case_types)]
//! pub type thread = ThreadMod<ThreadSupportImpl>;
//! ```
//!
//! ```
//! // Use it just like `std::thread`
//! use thread;
//! let t = thread::current();
//!
//! // But the other struct is not available ...
//! let t: thread::Thread; // ERROR!
//! ```
//! You need to implement the following functions before use:
//! - `processor`: Get a reference of the current `Processor`
//! - `new_kernel_context`: Construct a `Context` of the new kernel thread
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use core::any::Any;
use core::marker::PhantomData;
use core::ptr;
use core::time::Duration;
use core::ops::DerefMut;
use processor::*;
use process_manager::*;
use scheduler::Scheduler;
/// All dependencies for thread mod.
pub trait ThreadSupport {
type Context: Context;
type Scheduler: Scheduler;
type ProcessorGuard: DerefMut<Target=Processor_<Self::Context, Self::Scheduler>>;
fn processor() -> Self::ProcessorGuard;
#[linkage = "weak"]
#[no_mangle]
/// Get a reference of the current `Processor`
fn processor() -> &'static Processor {
unimplemented!("thread: Please implement and export `processor`")
}
/// Root structure served as thread mod
pub struct ThreadMod<S: ThreadSupport> {
mark: PhantomData<S>
#[linkage = "weak"]
#[no_mangle]
/// Construct a `Context` of the new kernel thread
fn new_kernel_context(entry: extern fn(usize) -> !, arg: usize) -> Box<Context> {
unimplemented!("thread: Please implement and export `new_kernel_context`")
}
impl<S: ThreadSupport> ThreadMod<S> {
/*
** @brief Gets a handle to the thread that invokes it.
** @param none
** @retval the thread to get
*/
pub fn current() -> Thread<S> {
/// Gets a handle to the thread that invokes it.
pub fn current() -> Thread {
Thread {
pid: S::processor().current_pid(),
mark: PhantomData,
pid: processor().pid(),
}
}
/*
** @brief Puts the current thread to sleep for the specified amount of time.
** @param dur: Duration the time to sleep
** @retval none
*/
/// Puts the current thread to sleep for the specified amount of time.
pub fn sleep(dur: Duration) {
let time = dur_to_ticks(dur);
info!("sleep: {:?} ticks", time);
let mut processor = S::processor();
let pid = processor.current_pid();
processor.sleep(pid, time);
processor.schedule();
processor().manager().sleep(current().id(), time);
park();
fn dur_to_ticks(dur: Duration) -> usize {
return dur.as_secs() as usize * 100 + dur.subsec_nanos() as usize / 10_000_000;
}
}
/*
** @brief Spawns a new thread, returning a JoinHandle for it.
** @param f: F the thread to start
** @retval JoinHandle the JoinHandle of the new thread
*/
pub fn spawn<F, T>(f: F) -> JoinHandle<S, T>
/// Spawns a new thread, returning a JoinHandle for it.
///
/// `F`: Type of the function `f`
/// `T`: Type of the return value of `f`
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: Send + 'static + FnOnce() -> T,
T: Send + 'static,
{
info!("spawn:");
// 注意到下面的问题:
// Processor只能从入口地址entry+参数arg创建新线程
// 而我们现在需要让它执行一个未知类型的闭包函数f
// 首先把函数本体(代码数据)置于堆空间中
let f = Box::into_raw(Box::new(f));
let pid = S::processor().add(Context::new_kernel(kernel_thread_entry::<S, F, T>, f as usize));
return JoinHandle {
thread: Thread { pid, mark: PhantomData },
mark: PhantomData,
};
extern fn kernel_thread_entry<S, F, T>(f: usize) -> !
// 定义一个静态函数作为新线程的入口点
// 其参数是函数f在堆上的指针
// 这样我们就把函数f传到了一个静态函数内部
//
// 注意到它具有泛型参数因此对每一次spawn调用
// 由于F类型是独特的因此都会生成一个新的kernel_thread_entry
extern fn kernel_thread_entry<F, T>(f: usize) -> !
where
S: ThreadSupport,
F: Send + 'static + FnOnce() -> T,
T: Send + 'static,
{
// 在静态函数内部:
// 根据传进来的指针恢复f
let f = unsafe { Box::from_raw(f as *mut F) };
// 调用f并将其返回值也放在堆上
let ret = Box::new(f());
// 清理本地线程存储
// unsafe { LocalKey::<usize>::get_map() }.clear();
let mut processor = S::processor();
let pid = processor.current_pid();
processor.exit(pid, Box::into_raw(ret) as usize);
processor.schedule();
// 让Processor退出当前线程
// 把f返回值在堆上的指针以线程返回码的形式传递出去
let exit_code = Box::into_raw(ret) as usize;
processor().manager().exit(current().id(), exit_code);
processor().yield_now();
// 再也不会被调度回来了
unreachable!()
}
// 在Processor中创建新的线程
let context = new_kernel_context(kernel_thread_entry::<F, T>, f as usize);
let pid = processor().manager().add(context);
// 接下来看看`JoinHandle::join()`的实现
// 了解是如何获取f返回值的
return JoinHandle {
thread: Thread { pid },
mark: PhantomData,
};
}
/*
** @brief Cooperatively gives up a timeslice to the OS scheduler.
** @param none
** @retval none
*/
/// Cooperatively gives up a timeslice to the OS scheduler.
pub fn yield_now() {
info!("yield:");
let mut processor = S::processor();
processor.set_reschedule();
processor.schedule();
processor().yield_now();
}
/*
** @brief Blocks unless or until the current thread's token is made available.
** @param none
** @retval none
*/
/// Blocks unless or until the current thread's token is made available.
pub fn park() {
info!("park:");
let mut processor = S::processor();
let pid = processor.current_pid();
processor.sleep_(pid);
processor.schedule();
}
processor().manager().sleep(current().id(), 0);
processor().yield_now();
}
/// A handle to a thread.
pub struct Thread<S: ThreadSupport> {
pub struct Thread {
pid: usize,
mark: PhantomData<S>,
}
impl<S: ThreadSupport> Thread<S> {
/*
** @brief Atomically makes the handle's token available if it is not already.
** @param none
** @retval none
*/
impl Thread {
/// Atomically makes the handle's token available if it is not already.
pub fn unpark(&self) {
let mut processor = S::processor();
processor.wakeup_(self.pid);
processor().manager().wakeup(self.pid);
}
/*
** @brief Gets the thread's unique identifier.
** @param none
** @retval usize the the thread's unique identifier
*/
/// Gets the thread's unique identifier.
pub fn id(&self) -> usize {
self.pid
}
}
/// An owned permission to join on a thread (block on its termination).
pub struct JoinHandle<S: ThreadSupport, T> {
thread: Thread<S>,
pub struct JoinHandle<T> {
thread: Thread,
mark: PhantomData<T>,
}
impl<S: ThreadSupport, T> JoinHandle<S, T> {
/*
** @brief Extracts a handle to the underlying thread.
** @param none
** @retval the thread of the handle
*/
pub fn thread(&self) -> &Thread<S> {
impl<T> JoinHandle<T> {
/// Extracts a handle to the underlying thread.
pub fn thread(&self) -> &Thread {
&self.thread
}
/*
** @brief Waits for the associated thread to finish.
** @param none
** @retval Result<T, ()> the result of the associated thread
*/
/// Waits for the associated thread to finish.
pub fn join(self) -> Result<T, ()> {
let mut processor = S::processor();
match processor.current_wait_for(self.thread.pid) {
WaitResult::Ok(_, exit_code) => unsafe {
Ok(*Box::from_raw(exit_code as *mut T))
}
WaitResult::NotExist => Err(()),
loop {
match processor().manager().get_status(self.thread.pid) {
Some(Status::Exited(exit_code)) => {
processor().manager().remove(self.thread.pid);
// Find return value on the heap from the exit code.
return Ok(unsafe { *Box::from_raw(exit_code as *mut T) });
}
None => return Err(()),
_ => {}
}
processor().manager().wait(current().id(), self.thread.pid);
processor().yield_now();
}
}
}

@ -1 +1 @@
Subproject commit a37a65fa13a00c5aa0068c3f2b5d55af6a37dd93
Subproject commit f358204af01f2374ab6ed6ea059f724cd5f2fe6f

1
kernel/Cargo.lock generated

@ -244,6 +244,7 @@ name = "ucore-process"
version = "0.1.0"
dependencies = [
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"spin 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]

@ -1,14 +1,19 @@
.section .text.entry
.globl _start
_start:
lui sp, %hi(bootstacktop)
addi sp, sp, %lo(bootstacktop)
add t0, a0, 1
slli t0, t0, 16
lui sp, %hi(bootstack)
addi sp, sp, %lo(bootstack)
add sp, sp, t0
call rust_main
.section .bss
.align 12 #PGSHIFT
.global bootstack
bootstack:
.space 4096 * 16 #KSTACKSIZE
.space 4096 * 16 * 8
.global bootstacktop
bootstacktop:

@ -13,7 +13,8 @@ _save_context:
# save x registers except x2 (sp)
sw x1, 1*4(sp)
sw x3, 3*4(sp)
sw x4, 4*4(sp)
# tp(x4) = hartid. DON'T change.
# sw x4, 4*4(sp)
sw x5, 5*4(sp)
sw x6, 6*4(sp)
sw x7, 7*4(sp)
@ -61,7 +62,7 @@ _save_context:
lw s1, 32*4(sp) # s1 = sstatus
lw s2, 33*4(sp) # s2 = sepc
andi s0, s1, 1 << 8
bnez s0, _restore_context # back to U-mode? (sstatus.SPP = 1)
bnez s0, _restore_context # back to S-mode? (sstatus.SPP = 1)
_save_kernel_sp:
addi s0, sp, 36*4
csrw 0x140, s0 # sscratch = kernel-sp
@ -73,7 +74,7 @@ _restore_context:
# restore x registers except x2 (sp)
lw x1, 1*4(sp)
lw x3, 3*4(sp)
lw x4, 4*4(sp)
# lw x4, 4*4(sp)
lw x5, 5*4(sp)
lw x6, 6*4(sp)
lw x7, 7*4(sp)

@ -0,0 +1,14 @@
// Physical address available on THINPAD:
// [0x80000000, 0x80800000]
const P2_SIZE: usize = 1 << 22;
const P2_MASK: usize = 0x3ff << 22;
pub const RECURSIVE_INDEX: usize = 0x3fe;
pub const KERNEL_OFFSET: usize = 0;
pub const KERNEL_P2_INDEX: usize = 0x8000_0000 >> 22;
pub const KERNEL_HEAP_OFFSET: usize = 0x8020_0000;
pub const KERNEL_HEAP_SIZE: usize = 0x0020_0000;
pub const MEMORY_OFFSET: usize = 0x8000_0000;
pub const MEMORY_END: usize = 0x8080_0000;
pub const USER_STACK_OFFSET: usize = 0x70000000;
pub const USER_STACK_SIZE: usize = 0x10000;
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;

@ -1,6 +1,6 @@
use super::super::riscv::register::*;
#[derive(Debug, Clone)]
#[derive(Clone)]
#[repr(C)]
pub struct TrapFrame {
pub x: [usize; 32], // general registers
@ -53,9 +53,8 @@ impl TrapFrame {
tf.x[2] = sp;
tf.sepc = entry_addr;
tf.sstatus = sstatus::read();
// Supervisor Previous Interrupt Disable ?
tf.sstatus.set_spie(false); // Enable interrupt
// Supervisor Previous Privilege Mode is User
tf.sstatus.set_spie(true);
tf.sstatus.set_sie(false);
tf.sstatus.set_spp(sstatus::SPP::User);
tf
}
@ -65,6 +64,29 @@ impl TrapFrame {
}
}
use core::fmt::{Debug, Formatter, Error};
impl Debug for TrapFrame {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
struct Regs<'a>(&'a [usize; 32]);
impl<'a> Debug for Regs<'a> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
const REG_NAME: [&str; 32] = [
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
"t3", "t4", "t5", "t6"];
f.debug_map().entries(REG_NAME.iter().zip(self.0)).finish()
}
}
f.debug_struct("TrapFrame")
.field("regs", &Regs(&self.x))
.field("sstatus", &self.sstatus)
.field("sepc", &self.sepc)
.field("sbadaddr", &self.sbadaddr)
.field("scause", &self.scause)
.finish()
}
}
/// kernel stack contents for a new thread
#[derive(Debug)]
#[repr(C)]

@ -0,0 +1,36 @@
use consts::MAX_CPU_NUM;
use core::ptr::{read_volatile, write_volatile};
use memory::*;
static mut STARTED: [bool; MAX_CPU_NUM] = [false; MAX_CPU_NUM];
pub unsafe fn set_cpu_id(cpu_id: usize) {
asm!("mv tp, $0" : : "r"(cpu_id));
}
pub fn id() -> usize {
let cpu_id;
unsafe { asm!("mv $0, tp" : "=r"(cpu_id)); }
cpu_id
}
pub fn send_ipi(cpu_id: usize) {
super::bbl::sbi::send_ipi(1 << cpu_id);
}
pub unsafe fn has_started(cpu_id: usize) -> bool {
read_volatile(&STARTED[cpu_id])
}
pub unsafe fn start_others(hart_mask: usize) {
for cpu_id in 0..MAX_CPU_NUM {
if (hart_mask >> cpu_id) & 1 != 0 {
write_volatile(&mut STARTED[cpu_id], true);
}
}
}
pub fn halt() {
use super::riscv::asm::wfi;
unsafe { wfi() }
}

@ -18,6 +18,8 @@ pub fn init() {
sscratch::write(0);
// Set the exception vector address
stvec::write(__alltraps as usize, stvec::TrapMode::Direct);
// Enable IPI
sie::set_ssoft();
}
info!("interrupt: init end");
}
@ -77,10 +79,13 @@ pub extern fn rust_trap(tf: &mut TrapFrame) {
Trap::Exception(E::InstructionPageFault) => page_fault(tf),
_ => ::trap::error(tf),
}
::trap::before_return();
trace!("Interrupt end");
}
fn ipi() {
debug!("IPI");
super::bbl::sbi::clear_ipi();
}
/*
* @brief:
* process timer interrupt

@ -7,19 +7,35 @@ pub mod timer;
pub mod paging;
pub mod memory;
pub mod compiler_rt;
pub mod consts;
pub mod cpu;
#[no_mangle]
pub extern fn rust_main() -> ! {
println!("Hello RISCV! {}", 123);
// First init log mod, so that we can print log info.
pub extern fn rust_main(hartid: usize, dtb: usize, hart_mask: usize) -> ! {
unsafe { cpu::set_cpu_id(hartid); }
println!("Hello RISCV! in hart {}, {}, {}", hartid, dtb, hart_mask);
if hartid != 0 {
while unsafe { !cpu::has_started(hartid) } { }
others_main();
unreachable!();
}
::logging::init();
// Init interrupt handling.
interrupt::init();
// Init physical memory management and heap
memory::init();
// Init timer interrupt
timer::init();
::process::init();
::thread::spawn(::fs::shell);
unsafe { cpu::start_others(hart_mask); }
::kmain();
}
fn others_main() -> ! {
interrupt::init();
timer::init();
::kmain();
}

@ -1,4 +1,4 @@
use consts::{KERNEL_PML4, RECURSIVE_PAGE_PML4};
use consts::{KERNEL_P2_INDEX, RECURSIVE_INDEX};
// Depends on kernel
use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame};
use super::riscv::addr::*;
@ -20,14 +20,14 @@ use ucore_memory::paging::*;
pub fn setup_page_table(frame: Frame) {
let p2 = unsafe { &mut *(frame.start_address().as_u32() as *mut RvPageTable) };
p2.zero();
p2.set_recursive(RECURSIVE_PAGE_PML4, frame.clone());
p2.set_recursive(RECURSIVE_INDEX, frame.clone());
// Set kernel identity map
// 0x10000000 ~ 1K area
p2.map_identity(0x40, EF::VALID | EF::READABLE | EF::WRITABLE);
// 0x80000000 ~ 8K area
p2.map_identity(KERNEL_PML4, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE);
p2.map_identity(KERNEL_PML4 + 1, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE);
p2.map_identity(KERNEL_P2_INDEX, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE);
p2.map_identity(KERNEL_P2_INDEX + 1, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE);
use super::riscv::register::satp;
unsafe { satp::set(satp::Mode::Sv32, 0, frame); }
@ -88,7 +88,7 @@ impl PageTable for ActivePageTable {
let page = Page::of_addr(VirtAddr::new(addr));
// ???
let _ = self.0.translate_page(page);
let entry_addr = ((addr >> 10) & 0x003ffffc) | (RECURSIVE_PAGE_PML4 << 22);
let entry_addr = ((addr >> 10) & ((1 << 22) - 4)) | (RECURSIVE_INDEX << 22);
unsafe { &mut *(entry_addr as *mut PageEntry) }
}
@ -130,7 +130,7 @@ impl PageTable for ActivePageTable {
// define the ROOT_PAGE_TABLE, and the virtual address of it?
const ROOT_PAGE_TABLE: *mut RvPageTable =
(((RECURSIVE_PAGE_PML4 << 10) | (RECURSIVE_PAGE_PML4 + 1)) << 12) as *mut RvPageTable;
(((RECURSIVE_INDEX << 10) | (RECURSIVE_INDEX + 1)) << 12) as *mut RvPageTable;
impl ActivePageTable {
pub unsafe fn new() -> Self {
@ -185,7 +185,6 @@ impl Entry for PageEntry {
flags.set(EF::RESERVED2, !writable);
}
fn clear_shared(&mut self) { self.as_flags().remove(EF::RESERVED1 | EF::RESERVED2); }
// valid property must be 0 used when swapped
fn swapped(&self) -> bool { self.0.flags().contains(EF::RESERVED1) }
fn set_swapped(&mut self, value: bool) { self.as_flags().set(EF::RESERVED1, value); }
fn user(&self) -> bool { self.0.flags().contains(EF::USER) }
@ -231,7 +230,7 @@ impl InactivePageTable for InactivePageTable0 {
.expect("failed to allocate frame");
active_table().with_temporary_map(&frame, |_, table: &mut RvPageTable| {
table.zero();
table.set_recursive(RECURSIVE_PAGE_PML4, frame.clone());
table.set_recursive(RECURSIVE_INDEX, frame.clone());
});
InactivePageTable0 { p2_frame: frame }
}
@ -244,17 +243,17 @@ impl InactivePageTable for InactivePageTable0 {
*/
fn edit(&mut self, f: impl FnOnce(&mut Self::Active)) {
active_table().with_temporary_map(&satp::read().frame(), |active_table, p2_table: &mut RvPageTable| {
let backup = p2_table[RECURSIVE_PAGE_PML4].clone();
let backup = p2_table[RECURSIVE_INDEX].clone();
// overwrite recursive mapping
p2_table[RECURSIVE_PAGE_PML4].set(self.p2_frame.clone(), EF::VALID);
p2_table[RECURSIVE_INDEX].set(self.p2_frame.clone(), EF::VALID);
sfence_vma_all();
// execute f in the new context
f(active_table);
// restore recursive mapping to original p4 table
p2_table[RECURSIVE_PAGE_PML4] = backup;
// restore recursive mapping to original p2 table
p2_table[RECURSIVE_INDEX] = backup;
sfence_vma_all();
});
}
@ -351,12 +350,12 @@ impl InactivePageTable0 {
fn map_kernel(&mut self) {
let table = unsafe { &mut *ROOT_PAGE_TABLE };
let e0 = table[0x40];
let e1 = table[KERNEL_PML4];
let e1 = table[KERNEL_P2_INDEX];
assert!(!e1.is_unused());
self.edit(|_| {
table[0x40] = e0;
table[KERNEL_PML4].set(e1.frame(), EF::VALID | EF::GLOBAL);
table[KERNEL_P2_INDEX].set(e1.frame(), EF::VALID | EF::GLOBAL);
});
}
}

@ -0,0 +1,97 @@
// Copy from Redox consts.rs:
// Because the memory map is so important to not be aliased, it is defined here, in one place
// The lower 256 PML4 entries are reserved for userspace
// Each PML4 entry references up to 512 GB of memory
// The top (511) PML4 is reserved for recursive mapping
// The second from the top (510) PML4 is reserved for the kernel
/// The size of a single PML4
pub const PML4_SIZE: usize = 0x0000_0080_0000_0000;
pub const PML4_MASK: usize = 0x0000_ff80_0000_0000;
/// Offset of recursive paging
pub const RECURSIVE_PAGE_OFFSET: usize = (-(PML4_SIZE as isize)) as usize;
pub const RECURSIVE_PAGE_PML4: usize = (RECURSIVE_PAGE_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset of kernel
pub const KERNEL_OFFSET: usize = RECURSIVE_PAGE_OFFSET - PML4_SIZE;
pub const KERNEL_PML4: usize = (KERNEL_OFFSET & PML4_MASK) / PML4_SIZE;
pub const KERNEL_SIZE: usize = PML4_SIZE;
/// Offset to kernel heap
pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE;
pub const KERNEL_HEAP_PML4: usize = (KERNEL_HEAP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Size of kernel heap
pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024; // 8 MB
pub const MEMORY_OFFSET: usize = 0;
/// Offset to kernel percpu variables
//TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;
pub const KERNEL_PERCPU_OFFSET: usize = 0xC000_0000;
/// Size of kernel percpu variables
pub const KERNEL_PERCPU_SIZE: usize = 64 * 1024; // 64 KB
/// Offset to user image
pub const USER_OFFSET: usize = 0;
pub const USER_PML4: usize = (USER_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user TCB
pub const USER_TCB_OFFSET: usize = 0xB000_0000;
/// Offset to user arguments
pub const USER_ARG_OFFSET: usize = USER_OFFSET + PML4_SIZE / 2;
/// Offset to user heap
pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE;
pub const USER_HEAP_PML4: usize = (USER_HEAP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user grants
pub const USER_GRANT_OFFSET: usize = USER_HEAP_OFFSET + PML4_SIZE;
pub const USER_GRANT_PML4: usize = (USER_GRANT_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user stack
pub const USER_STACK_OFFSET: usize = USER_GRANT_OFFSET + PML4_SIZE;
pub const USER32_STACK_OFFSET: usize = 0xB000_0000;
pub const USER_STACK_PML4: usize = (USER_STACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Size of user stack
pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB
/// Offset to user sigstack
pub const USER_SIGSTACK_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
pub const USER_SIGSTACK_PML4: usize = (USER_SIGSTACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Size of user sigstack
pub const USER_SIGSTACK_SIZE: usize = 256 * 1024; // 256 KB
/// Offset to user TLS
pub const USER_TLS_OFFSET: usize = USER_SIGSTACK_OFFSET + PML4_SIZE;
pub const USER_TLS_PML4: usize = (USER_TLS_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary image (used when cloning)
pub const USER_TMP_OFFSET: usize = USER_TLS_OFFSET + PML4_SIZE;
pub const USER_TMP_PML4: usize = (USER_TMP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary heap (used when cloning)
pub const USER_TMP_HEAP_OFFSET: usize = USER_TMP_OFFSET + PML4_SIZE;
pub const USER_TMP_HEAP_PML4: usize = (USER_TMP_HEAP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary page for grants
pub const USER_TMP_GRANT_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE;
pub const USER_TMP_GRANT_PML4: usize = (USER_TMP_GRANT_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary stack (used when cloning)
pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_GRANT_OFFSET + PML4_SIZE;
pub const USER_TMP_STACK_PML4: usize = (USER_TMP_STACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary sigstack (used when cloning)
pub const USER_TMP_SIGSTACK_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
pub const USER_TMP_SIGSTACK_PML4: usize = (USER_TMP_SIGSTACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary tls (used when cloning)
pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_SIGSTACK_OFFSET + PML4_SIZE;
pub const USER_TMP_TLS_PML4: usize = (USER_TMP_TLS_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset for usage in other temporary pages
pub const USER_TMP_MISC_OFFSET: usize = USER_TMP_TLS_OFFSET + PML4_SIZE;
pub const USER_TMP_MISC_PML4: usize = (USER_TMP_MISC_OFFSET & PML4_MASK) / PML4_SIZE;

@ -1,3 +1,6 @@
use super::apic::{LocalApic, XApic};
use super::raw_cpuid::CpuId;
/// Exit qemu
/// See: https://wiki.osdev.org/Shutdown
/// Must run qemu with `-device isa-debug-exit`
@ -8,3 +11,22 @@ pub unsafe fn exit_in_qemu(error_code: u8) -> ! {
Port::new(0x501).write((error_code - 1) / 2);
unreachable!()
}
pub fn id() -> usize {
CpuId::new().get_feature_info().unwrap().initial_local_apic_id() as usize
}
pub fn send_ipi(cpu_id: usize) {
let mut lapic = unsafe { XApic::new(0xffffff00_fee00000) };
unsafe { lapic.send_ipi(cpu_id as u8, 0x30); } // TODO: Find a IPI trap num
}
pub fn init() {
let mut lapic = unsafe { XApic::new(0xffffff00_fee00000) };
lapic.cpu_init();
}
pub fn halt() {
use x86_64::instructions::hlt;
hlt();
}

@ -5,267 +5,120 @@
use spin::Mutex;
lazy_static! {
pub static ref DISK0: LockedIde = LockedIde(Mutex::new(DmaController::new(0)));
pub static ref DISK1: LockedIde = LockedIde(Mutex::new(DmaController::new(1)));
pub static ref DISK0: LockedIde = LockedIde(Mutex::new(IDE::new(0)));
pub static ref DISK1: LockedIde = LockedIde(Mutex::new(IDE::new(1)));
}
pub const BLOCK_SIZE: usize = 512;
pub struct LockedIde(pub Mutex<DmaController>);
pub struct LockedIde(pub Mutex<IDE>);
pub struct DmaController {
pub struct IDE {
num: u8,
}
impl DmaController
{
/// Read ATA DMA. Block size = 512 bytes.
pub fn read(&self, blockidx: u64, count: usize, dst: &mut [u32]) -> Result<usize, ()> {
assert_eq!(dst.len(), count * SECTOR_SIZE);
let dst = if count > MAX_DMA_SECTORS { &mut dst[..MAX_DMA_SECTORS * SECTOR_SIZE] } else { dst };
//self.do_dma(blockidx, DMABuffer::new_mut(dst, 32), disk, false);
self.ide_read_secs(self.num, blockidx, dst, count as u8)
}
/// Write ATA DMA. Block size = 512 bytes.
pub fn write(&self, blockidx: u64, count: usize, dst: &[u32]) -> Result<usize, ()> {
assert_eq!(dst.len(), count * SECTOR_SIZE);
let dst = if count > MAX_DMA_SECTORS { &dst[..MAX_DMA_SECTORS * SECTOR_SIZE] } else { dst };
//println!("ide_write_secs: disk={},blockidx={},count={}",disk,blockidx,count);
self.ide_write_secs(self.num, blockidx, dst, count as u8)
}
/// Create structure and init
fn new(num: u8) -> Self {
assert!(num < MAX_IDE as u8);
let ide = DmaController { num };
ide.ide_init();
/// I/O Base
base: u16,
/// Control Base
ctrl: u16,
}
impl IDE {
pub fn new(num: u8) -> Self {
let ide = match num {
0 => IDE { num: 0, base: 0x1f0, ctrl: 0x3f4 },
1 => IDE { num: 1, base: 0x1f0, ctrl: 0x3f4 },
2 => IDE { num: 2, base: 0x170, ctrl: 0x374 },
3 => IDE { num: 3, base: 0x170, ctrl: 0x374 },
_ => panic!("ide number should be 0,1,2,3"),
};
ide.init();
ide
}
fn ide_wait_ready(&self, iobase: u16, check_error: usize) -> usize {
/// Read ATA DMA. Block size = 512 bytes.
pub fn read(&self, sector: u64, count: usize, data: &mut [u32]) -> Result<(), ()> {
assert_eq!(data.len(), count * SECTOR_SIZE);
self.wait();
unsafe {
let mut r = port::inb(iobase + ISA_STATUS);
//println!("iobase:{} ready:{}",iobase,r);
while (r & IDE_BSY) > 0 {
r = port::inb(iobase + ISA_STATUS);
//println!("busy");
self.select(sector, count as u8);
port::outb(self.base + ISA_COMMAND, IDE_CMD_READ);
for i in 0..count {
let ptr = &data[(i as usize) * SECTOR_SIZE];
if self.wait_error() {
return Err(());
}
/* nothing */
if check_error == 1 && (r & (IDE_DF | IDE_ERR)) != 0 {
return 1;
asm!("rep insl" :: "{dx}"(self.base), "{rdi}"(ptr), "{cx}"(SECTOR_SIZE) : "rdi" : "volatile");
}
}
return 0;
Ok(())
}
fn ide_init(&self) {
//static_assert((SECTSIZE % 4) == 0);
let ideno = self.num;
//println!("ideno:{}",ideno);
/* assume that no device here */
//ide_devices[ideno].valid = 0;
//let iobase = IO_BASE(ideno);
let iobase = CHANNELS[if ideno > 2 { 1 } else { 0 }].0;
/* wait device ready */
self.ide_wait_ready(iobase, 0);
//println!("ide_wait_ready");
/// Write ATA DMA. Block size = 512 bytes.
pub fn write(&self, sector: u64, count: usize, data: &[u32]) -> Result<(), ()> {
assert_eq!(data.len(), count * SECTOR_SIZE);
self.wait();
unsafe {
/* step1: select drive */
//println!("outb");
port::outb(iobase + ISA_SDH, (0xE0 | ((ideno & 1) << 4)) as u8);
self.ide_wait_ready(iobase, 0);
/* step2: send ATA identify command */
//println!("outb");
port::outb(iobase + ISA_COMMAND, IDE_CMD_IDENTIFY);
self.ide_wait_ready(iobase, 0);
/* step3: polling */
//println!("inb");
if port::inb(iobase + ISA_STATUS) == 0 || self.ide_wait_ready(iobase, 1) != 0 {
return;
}
//println!("insl");
let mut buffer: [u32; 128] = [0; 128];
for i in 0..buffer.len() {
buffer[i] = i as u32;
if i == 1 {
//println!("{:#x}",&buffer[i] as *const u32 as usize - ::consts::KERNEL_OFFSET)
}
self.select(sector, count as u8);
port::outb(self.base + ISA_COMMAND, IDE_CMD_WRITE);
for i in 0..count {
let ptr = &data[(i as usize) * SECTOR_SIZE];
if self.wait_error() {
return Err(());
}
//println!("insl {:#x}",&buffer as *const u32 as usize - ::consts::KERNEL_OFFSET);
//println!("insl {:#x}",buffer.as_ptr() as usize - ::consts::KERNEL_OFFSET);
//port::insl(iobase + ISA_DATA, &mut buffer);
let port = iobase + ISA_DATA;
//let buf=&mut buffer;
for i in 0..buffer.len() {
asm!("insl %dx, (%rdi)"
:: "{dx}"(port), "{rdi}"(&buffer[i])
: "rdi" : "volatile");
asm!("rep outsl" :: "{dx}"(self.base), "{rsi}"(ptr), "{cx}"(SECTOR_SIZE) : "rsi" : "volatile");
}
//println!("insl");
for i in 0..4 {
info!("ide init: {}", buffer[i]);
}
Ok(())
}
/* device is ok */
//ide_devices[ideno].valid = 1;
/* read identification space of the device */
/*let buffer[128];
insl(iobase + ISA_DATA, buffer, sizeof(buffer) / sizeof(unsigned int));
unsigned char *ident = (unsigned char *)buffer;
unsigned int sectors;
unsigned int cmdsets = *(unsigned int *)(ident + IDE_IDENT_CMDSETS);
/* device use 48-bits or 28-bits addressing */
if (cmdsets & (1 << 26)) {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA_EXT);
}
else {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA);
fn wait(&self) {
while unsafe { port::inb(self.base + ISA_STATUS) } & IDE_BUSY != 0 {}
}
ide_devices[ideno].sets = cmdsets;
ide_devices[ideno].size = sectors;
/* check if supports LBA */
assert((*(unsigned short *)(ident + IDE_IDENT_CAPABILITIES) & 0x200) != 0);
unsigned char *model = ide_devices[ideno].model, *data = ident + IDE_IDENT_MODEL;
unsigned int i, length = 40;
for (i = 0; i < length; i += 2) {
model[i] = data[i + 1], model[i + 1] = data[i];
fn wait_error(&self) -> bool {
self.wait();
let status = unsafe { port::inb(self.base + ISA_STATUS) };
status & (IDE_DF | IDE_ERR) != 0
}
do {
model[i] = '\0';
} while (i -- > 0 && model[i] == ' ');
cprintf("ide %d: %10u(sectors), '%s'.\n", ideno, ide_devices[ideno].size, ide_devices[ideno].model);*/
fn init(&self) {
self.wait();
unsafe {
// step1: select drive
port::outb(self.base + ISA_SDH, (0xE0 | ((self.num & 1) << 4)) as u8);
self.wait();
// enable ide interrupt
//pic_enable(IRQ_IDE1);
//pic_enable(IRQ_IDE2);
// step2: send ATA identify command
port::outb(self.base + ISA_COMMAND, IDE_CMD_IDENTIFY);
self.wait();
info!("ide {} init end", self.num);
// step3: polling
if port::inb(self.base + ISA_STATUS) == 0 || self.wait_error() {
return;
}
fn ide_read_secs<'a>(&'a self, ideno: u8, secno: u64, dst: &'a mut [u32], nsecs: u8) -> Result<usize, ()> {
//assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
//assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
let iobase = CHANNELS[if ideno > 2 { 1 } else { 0 }].0;
let ioctrl = CHANNELS[if ideno > 2 { 1 } else { 0 }].1;
//ide_wait_ready(iobase, 0);
self.ide_wait_ready(iobase, 0);
let ret = 0;
// generate interrupt
unsafe {
port::outb(ioctrl + ISA_CTRL, 0);
port::outb(iobase + ISA_SECCNT, nsecs);
port::outb(iobase + ISA_SECTOR, (secno & 0xFF) as u8);
port::outb(iobase + ISA_CYL_LO, ((secno >> 8) & 0xFF) as u8);
port::outb(iobase + ISA_CYL_HI, ((secno >> 16) & 0xFF) as u8);
port::outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | (((secno >> 24) & 0xF) as u8));
//port::outb(iobase + ISA_SDH, (0xE0 | ((ideno & 1) << 4)) as u8);
//self.ide_wait_ready(iobase, 0);
port::outb(iobase + ISA_COMMAND, IDE_CMD_READ);
//self.ide_wait_ready(iobase, 0);
// if port::inb(iobase + ISA_STATUS) == 0 || self.ide_wait_ready(iobase, 1) != 0 {
// println!("error?");
// }
for i in 0..nsecs {
//dst = dst + SECTSIZE;
let tmp = &mut dst[(i as usize) * SECTOR_SIZE..((i + 1) as usize) * SECTOR_SIZE];
if self.ide_wait_ready(iobase, 1) != 0 {
println!("wait ready error");
}
//self.ide_wait_ready(iobase, 1);
//port::insl(iobase, tmp);
let port = iobase;
//let buf=&mut buffer;
for i in 0..tmp.len() {
asm!("insl %dx, (%rdi)"
:: "{dx}"(port), "{rdi}"(&tmp[i])
: "rdi" : "volatile");
}
//println!("read :{}",i);
// ???
let mut data = [0; SECTOR_SIZE];
asm!("rep insl" :: "{dx}"(self.base + ISA_DATA), "{rdi}"(data.as_ptr()), "{cx}"(SECTOR_SIZE) : "rdi" : "volatile");
}
}
Ok(ret)
}
fn ide_write_secs<'a>(&'a self, ideno: u8, secno: u64, src: &'a [u32], nsecs: u8) -> Result<usize, ()> {
//assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
//assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
let iobase = CHANNELS[if ideno > 2 { 1 } else { 0 }].0;
let ioctrl = CHANNELS[if ideno > 2 { 1 } else { 0 }].1;
//ide_wait_ready(iobase, 0);
self.ide_wait_ready(iobase, 0);
let ret = 0;
// generate interrupt
fn select(&self, sector: u64, count: u8) {
assert_ne!(count, 0);
self.wait();
unsafe {
port::outb(ioctrl + ISA_CTRL, 0);
port::outb(iobase + ISA_SECCNT, nsecs);
port::outb(iobase + ISA_SECTOR, (secno & 0xFF) as u8);
port::outb(iobase + ISA_CYL_LO, ((secno >> 8) & 0xFF) as u8);
port::outb(iobase + ISA_CYL_HI, ((secno >> 16) & 0xFF) as u8);
port::outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | (((secno >> 24) & 0xF) as u8));
port::outb(iobase + ISA_COMMAND, IDE_CMD_WRITE);
//println!("{}",nsecs);
for i in 0..nsecs {
//dst = dst + SECTSIZE;
// if ((ret = ide_wait_ready(iobase, 1)) != 0) {
// goto out;
// }
//port::insb(iobase, dst);
//println!("i={}",i);
let tmp = &src[(i as usize) * SECTOR_SIZE..((i + 1) as usize) * SECTOR_SIZE];
if self.ide_wait_ready(iobase, 1) != 0 {
println!("wait ready error");
}
//println!("write {}:{}",i,src[i as usize]);
//println!("outsl");
//port::outsl(iobase, tmp);
let port = iobase;
//let buf=&mut buffer;
for i in 0..tmp.len() {
asm!("outsl (%rsi), %dx"
:: "{dx}"(port), "{rsi}"(&tmp[i])
: "rsi");
}
//println!("write :{}",i);
// for i in 0..4 {
// println!("{}",src[i as usize]);
// }
//port::outb(iobase, src[i as usize]);
}
// generate interrupt
port::outb(self.ctrl + ISA_CTRL, 0);
port::outb(self.base + ISA_SECCNT, count);
port::outb(self.base + ISA_SECTOR, (sector & 0xFF) as u8);
port::outb(self.base + ISA_CYL_LO, ((sector >> 8) & 0xFF) as u8);
port::outb(self.base + ISA_CYL_HI, ((sector >> 16) & 0xFF) as u8);
port::outb(self.base + ISA_SDH, 0xE0 | ((self.num & 1) << 4) | (((sector >> 24) & 0xF) as u8));
}
Ok(ret)
}
}
const SECTOR_SIZE: usize = 128;
//const MAX_DMA_SECTORS: usize = 0x2_0000 / SECTOR_SIZE; // Limited by sector count (and PRDT entries)
const MAX_DMA_SECTORS: usize = 0x1F_F000 / SECTOR_SIZE; // Limited by sector count (and PRDT entries)
// 512 PDRT entries, assume maximum fragmentation = 512 * 4K max = 2^21 = 2MB per transfer
const HDD_PIO_W28: u8 = 0x30;
const HDD_PIO_R28: u8 = 0x20;
const HDD_PIO_W48: u8 = 0x34;
const HDD_PIO_R48: u8 = 0x24;
const HDD_IDENTIFY: u8 = 0xEC;
const HDD_DMA_R28: u8 = 0xC8;
const HDD_DMA_W28: u8 = 0xCA;
const HDD_DMA_R48: u8 = 0x25;
const HDD_DMA_W48: u8 = 0x35;
const ISA_DATA: u16 = 0x00;
const ISA_ERROR: u16 = 0x01;
const ISA_PRECOMP: u16 = 0x01;
@ -278,7 +131,7 @@ const ISA_SDH: u16 = 0x06;
const ISA_COMMAND: u16 = 0x07;
const ISA_STATUS: u16 = 0x07;
const IDE_BSY: u8 = 0x80;
const IDE_BUSY: u8 = 0x80;
const IDE_DRDY: u8 = 0x40;
const IDE_DF: u8 = 0x20;
const IDE_DRQ: u8 = 0x08;
@ -288,33 +141,7 @@ const IDE_CMD_READ: u8 = 0x20;
const IDE_CMD_WRITE: u8 = 0x30;
const IDE_CMD_IDENTIFY: u8 = 0xEC;
const IDE_IDENT_SECTORS: usize = 20;
const IDE_IDENT_MODEL: usize = 54;
const IDE_IDENT_CAPABILITIES: usize = 98;
const IDE_IDENT_CMDSETS: usize = 164;
const IDE_IDENT_MAX_LBA: usize = 120;
const IDE_IDENT_MAX_LBA_EXT: usize = 200;
const IO_BASE0: u16 = 0x1F0;
const IO_BASE1: u16 = 0x170;
const IO_CTRL0: u16 = 0x3F4;
const IO_CTRL1: u16 = 0x374;
const MAX_IDE: usize = 4;
const MAX_NSECS: usize = 128;
//const MAX_DISK_NSECS 0x10000000U;
//const VALID_IDE(ideno) (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid))
struct Channels {
base: u16,
// I/O Base
ctrl: u16, // Control Base
}
const CHANNELS: [(u16, u16); 2] = [(IO_BASE0, IO_CTRL0), (IO_BASE1, IO_CTRL1)];
//const IO_BASE(ideno) (CHANNELS[(ideno) >> 1].base)
//const IO_CTRL(ideno) (CHANNELS[(ideno) >> 1].ctrl)
mod port {
use x86_64::instructions::port::Port;

@ -1,7 +1,6 @@
extern crate syscall as redox_syscall;
pub mod vga;
pub mod apic;
pub mod serial;
pub mod pic;
pub mod keyboard;
@ -11,13 +10,12 @@ pub mod ide;
pub fn init() {
assert_has_not_been_called!();
if cfg!(feature = "use_apic") {
// Use IOAPIC instead of PIC
pic::disable();
apic::init();
} else {
pic::init();
}
pit::init();
// Use APIC Timer instead of PIT
// pit::init();
serial::init();
keyboard::init();
}

@ -1,5 +1,4 @@
use alloc::boxed::Box;
use arch::driver::apic::lapic_id;
use consts::MAX_CPU_NUM;
use core::fmt;
use core::fmt::Debug;
@ -50,7 +49,7 @@ pub fn init() {
load_tss(TSS_SELECTOR);
}
CPUS[lapic_id() as usize].call_once(||
CPUS[super::cpu::id() as usize].call_once(||
Mutex::new(Cpu { gdt, tss: unsafe { &mut *tss } }));
}
@ -67,7 +66,7 @@ pub struct Cpu {
impl Cpu {
pub fn current() -> MutexGuard<'static, Cpu> {
CPUS[lapic_id() as usize].try().unwrap().lock()
CPUS[super::cpu::id()].try().unwrap().lock()
}
/// 设置从Ring3跳到Ring0时自动切换栈的地址

@ -72,7 +72,7 @@ global_asm!(include_str!("vector.asm"));
#[no_mangle]
pub extern fn rust_trap(tf: &mut TrapFrame) {
trace!("Interrupt: {:#x}", tf.trap_num);
trace!("Interrupt: {:#x} @ CPU{}", tf.trap_num, super::super::cpu::id());
// Dispatch
match tf.trap_num as u8 {
T_BRKPT => breakpoint(),
@ -88,11 +88,7 @@ pub extern fn rust_trap(tf: &mut TrapFrame) {
IRQ_IDE => ide(),
_ => panic!("Invalid IRQ number: {}", irq),
}
#[cfg(feature = "use_apic")]
use arch::driver::apic::ack;
#[cfg(not(feature = "use_apic"))]
use arch::driver::pic::ack;
ack(irq);
super::ack(irq);
}
T_SWITCH_TOK => to_kernel(tf),
T_SWITCH_TOU => to_user(tf),
@ -101,7 +97,6 @@ pub extern fn rust_trap(tf: &mut TrapFrame) {
T_DIVIDE | T_GPFLT | T_ILLOP => error(tf),
_ => panic!("Unhandled interrupt {:x}", tf.trap_num),
}
::trap::before_return();
}
fn breakpoint() {

@ -1,5 +1,4 @@
use x86_64;
use arch::driver::{apic::IOAPIC, pic};
pub mod consts;
mod handler;
@ -7,6 +6,8 @@ mod trapframe;
pub use self::trapframe::*;
pub use self::handler::*;
use super::apic::*;
use consts::KERNEL_OFFSET;
#[inline(always)]
pub unsafe fn enable() {
@ -39,9 +40,12 @@ pub fn no_interrupt(f: impl FnOnce()) {
#[inline(always)]
pub fn enable_irq(irq: u8) {
if cfg!(feature = "use_apic") {
IOAPIC.lock().enable(irq, 0);
} else {
pic::enable_irq(irq);
let mut ioapic = unsafe { IoApic::new(KERNEL_OFFSET + IOAPIC_ADDR as usize) };
ioapic.enable(irq, 0);
}
#[inline(always)]
pub fn ack(irq: u8) {
let mut lapic = unsafe { XApic::new(KERNEL_OFFSET + LAPIC_ADDR) };
lapic.eoi();
}

@ -1,14 +1,15 @@
use bit_allocator::BitAlloc;
use consts::KERNEL_OFFSET;
// Depends on kernel
use memory::{FRAME_ALLOCATOR, init_heap};
use memory::{FRAME_ALLOCATOR, init_heap, active_table};
use super::{BootInfo, MemoryRegionType};
use ucore_memory::PAGE_SIZE;
use ucore_memory::paging::PageTable;
use ucore_memory::paging::*;
pub fn init(boot_info: &BootInfo) {
assert_has_not_been_called!("memory::init must be called only once");
init_frame_allocator(boot_info);
init_device_vm_map();
init_heap();
info!("memory: init end");
}
@ -22,3 +23,11 @@ fn init_frame_allocator(boot_info: &BootInfo) {
}
}
}
fn init_device_vm_map() {
let mut page_table = active_table();
// IOAPIC
page_table.map(KERNEL_OFFSET + 0xfec00000, 0xfec00000).update();
// LocalAPIC
page_table.map(KERNEL_OFFSET + 0xfee00000, 0xfee00000).update();
}

@ -1,6 +1,10 @@
extern crate bootloader;
extern crate apic;
extern crate raw_cpuid;
use self::bootloader::bootinfo::{BootInfo, MemoryRegionType};
use core::sync::atomic::*;
use consts::KERNEL_OFFSET;
pub mod driver;
pub mod cpu;
@ -8,14 +12,23 @@ pub mod interrupt;
pub mod paging;
pub mod gdt;
pub mod idt;
// TODO: Move multi-core init to bootloader
//pub mod smp;
pub mod memory;
pub mod io;
pub mod consts;
static AP_CAN_INIT: AtomicBool = ATOMIC_BOOL_INIT;
/// The entry point of kernel
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! {
let cpu_id = cpu::id();
println!("Hello world! from CPU {}!", cpu_id);
if cpu_id != 0 {
while !AP_CAN_INIT.load(Ordering::Relaxed) {}
other_start();
}
// First init log mod, so that we can print log info.
::logging::init();
info!("Hello world!");
@ -30,20 +43,22 @@ pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! {
// Now heap is available
gdt::init();
cpu::init();
driver::init();
::process::init();
::thread::spawn(::fs::shell);
AP_CAN_INIT.store(true, Ordering::Relaxed);
::kmain();
}
/// The entry point for another processors
#[no_mangle]
pub extern "C" fn other_main() -> ! {
/// The entry point for other processors
fn other_start() -> ! {
idt::init();
gdt::init();
driver::apic::other_init();
let cpu_id = driver::apic::lapic_id();
// let ms = unsafe { smp::notify_started(cpu_id) };
println!("Hello world! from CPU {}!", cpu_id);
// unsafe{ let a = *(0xdeadbeaf as *const u8); } // Page fault
loop {}
cpu::init();
::kmain();
}

@ -1,6 +1,6 @@
use bit_allocator::{BitAlloc, BitAlloc64K};
// Depends on kernel
use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame};
use memory::{active_table, alloc_frame, dealloc_frame};
use spin::{Mutex, MutexGuard};
use ucore_memory::cow::CowExt;
use ucore_memory::memory_set::*;
@ -49,7 +49,7 @@ impl PageTable for ActivePageTable {
let flags = EF::PRESENT | EF::WRITABLE | EF::NO_EXECUTE;
self.0.map_to(Page::of_addr(addr), Frame::of_addr(target), flags, &mut FrameAllocatorForX86)
.unwrap().flush();
self.get_entry(addr)
unsafe { &mut *(get_entry_ptr(addr, 1)) }
}
fn unmap(&mut self, addr: usize) {
@ -57,9 +57,12 @@ impl PageTable for ActivePageTable {
flush.flush();
}
fn get_entry(&mut self, addr: usize) -> &mut PageEntry {
let entry_addr = ((addr >> 9) & 0o777_777_777_7770) | 0xffffff80_00000000;
unsafe { &mut *(entry_addr as *mut PageEntry) }
fn get_entry(&mut self, addr: usize) -> Option<&mut PageEntry> {
for level in 0..3 {
let entry = get_entry_ptr(addr, 4 - level);
if unsafe { !(*entry).present() } { return None; }
}
unsafe { Some(&mut *(get_entry_ptr(addr, 1))) }
}
fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: usize) -> &'b mut [u8] {
@ -140,6 +143,12 @@ impl Entry for PageEntry {
fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::NO_EXECUTE, !value); }
}
fn get_entry_ptr(addr: usize, level: u8) -> *mut PageEntry {
debug_assert!(level <= 4);
let entry_addr = ((addr >> (level * 9)) & !0x7) | !((1 << (48 - level * 9)) - 1);
entry_addr as *mut PageEntry
}
impl PageEntry {
fn as_flags(&mut self) -> &mut EF {
unsafe { &mut *(self as *mut _ as *mut EF) }
@ -222,10 +231,6 @@ impl InactivePageTable for InactivePageTable0 {
fn dealloc_frame(target: usize) {
dealloc_frame(target)
}
fn alloc_stack() -> Stack {
alloc_stack()
}
}
impl InactivePageTable0 {

@ -1,133 +1,6 @@
#![allow(dead_code)]
#[cfg(target_arch = "riscv32")]
pub use self::riscv::*;
#[cfg(target_arch = "x86_64")]
pub use self::x86_64::*;
pub use arch::consts::*;
pub const MAX_CPU_NUM: usize = 8;
pub const MAX_PROCESS_NUM: usize = 48;
// Memory address for riscv32
#[cfg(target_arch = "riscv32")]
mod riscv {
// Physical address available on THINPAD:
// [0x80000000, 0x80800000]
const P2_SIZE: usize = 1 << 22;
const P2_MASK: usize = 0x3ff << 22;
// RECURSIVE_PAGE_PML4 indicate the index of the self-maping entry in root pagetable
pub const RECURSIVE_PAGE_PML4: usize = 0x3fe;
// KERNEL_OFFSET indicate (virtual kernel address - physical kernel address) ???
pub const KERNEL_OFFSET: usize = 0;
// KERNEL_PML4 indicate the index of the kernel entry in root pagetable
pub const KERNEL_PML4: usize = 0x8000_0000 >> 22;
pub const KERNEL_HEAP_OFFSET: usize = 0x8020_0000;
pub const KERNEL_HEAP_SIZE: usize = 0x0020_0000;
pub const MEMORY_OFFSET: usize = 0x8000_0000;
pub const MEMORY_END: usize = 0x8080_0000;
pub const USER_STACK_OFFSET: usize = 0x70000000;
pub const USER_STACK_SIZE: usize = 0x10000;
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;
}
// Memory address for x86_64
#[cfg(target_arch = "x86_64")]
mod x86_64 {
// Copy from Redox consts.rs:
// Because the memory map is so important to not be aliased, it is defined here, in one place
// The lower 256 PML4 entries are reserved for userspace
// Each PML4 entry references up to 512 GB of memory
// The top (511) PML4 is reserved for recursive mapping
// The second from the top (510) PML4 is reserved for the kernel
/// The size of a single PML4
pub const PML4_SIZE: usize = 0x0000_0080_0000_0000;
pub const PML4_MASK: usize = 0x0000_ff80_0000_0000;
/// Offset of recursive paging
pub const RECURSIVE_PAGE_OFFSET: usize = (-(PML4_SIZE as isize)) as usize;
pub const RECURSIVE_PAGE_PML4: usize = (RECURSIVE_PAGE_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset of kernel
pub const KERNEL_OFFSET: usize = RECURSIVE_PAGE_OFFSET - PML4_SIZE;
pub const KERNEL_PML4: usize = (KERNEL_OFFSET & PML4_MASK) / PML4_SIZE;
pub const KERNEL_SIZE: usize = PML4_SIZE;
/// Offset to kernel heap
pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE;
pub const KERNEL_HEAP_PML4: usize = (KERNEL_HEAP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Size of kernel heap
pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024; // 8 MB
pub const MEMORY_OFFSET: usize = 0;
/// Offset to kernel percpu variables
//TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;
pub const KERNEL_PERCPU_OFFSET: usize = 0xC000_0000;
/// Size of kernel percpu variables
pub const KERNEL_PERCPU_SIZE: usize = 64 * 1024; // 64 KB
/// Offset to user image
pub const USER_OFFSET: usize = 0;
pub const USER_PML4: usize = (USER_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user TCB
pub const USER_TCB_OFFSET: usize = 0xB000_0000;
/// Offset to user arguments
pub const USER_ARG_OFFSET: usize = USER_OFFSET + PML4_SIZE / 2;
/// Offset to user heap
pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE;
pub const USER_HEAP_PML4: usize = (USER_HEAP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user grants
pub const USER_GRANT_OFFSET: usize = USER_HEAP_OFFSET + PML4_SIZE;
pub const USER_GRANT_PML4: usize = (USER_GRANT_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user stack
pub const USER_STACK_OFFSET: usize = USER_GRANT_OFFSET + PML4_SIZE;
pub const USER32_STACK_OFFSET: usize = 0xB000_0000;
pub const USER_STACK_PML4: usize = (USER_STACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Size of user stack
pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB
/// Offset to user sigstack
pub const USER_SIGSTACK_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
pub const USER_SIGSTACK_PML4: usize = (USER_SIGSTACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Size of user sigstack
pub const USER_SIGSTACK_SIZE: usize = 256 * 1024; // 256 KB
/// Offset to user TLS
pub const USER_TLS_OFFSET: usize = USER_SIGSTACK_OFFSET + PML4_SIZE;
pub const USER_TLS_PML4: usize = (USER_TLS_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary image (used when cloning)
pub const USER_TMP_OFFSET: usize = USER_TLS_OFFSET + PML4_SIZE;
pub const USER_TMP_PML4: usize = (USER_TMP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary heap (used when cloning)
pub const USER_TMP_HEAP_OFFSET: usize = USER_TMP_OFFSET + PML4_SIZE;
pub const USER_TMP_HEAP_PML4: usize = (USER_TMP_HEAP_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary page for grants
pub const USER_TMP_GRANT_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE;
pub const USER_TMP_GRANT_PML4: usize = (USER_TMP_GRANT_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary stack (used when cloning)
pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_GRANT_OFFSET + PML4_SIZE;
pub const USER_TMP_STACK_PML4: usize = (USER_TMP_STACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary sigstack (used when cloning)
pub const USER_TMP_SIGSTACK_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
pub const USER_TMP_SIGSTACK_PML4: usize = (USER_TMP_SIGSTACK_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset to user temporary tls (used when cloning)
pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_SIGSTACK_OFFSET + PML4_SIZE;
pub const USER_TMP_TLS_PML4: usize = (USER_TMP_TLS_OFFSET & PML4_MASK) / PML4_SIZE;
/// Offset for usage in other temporary pages
pub const USER_TMP_MISC_OFFSET: usize = USER_TMP_TLS_OFFSET + PML4_SIZE;
pub const USER_TMP_MISC_PML4: usize = (USER_TMP_MISC_OFFSET & PML4_MASK) / PML4_SIZE;
}

@ -48,8 +48,9 @@ pub fn shell() {
if let Ok(file) = root.borrow().lookup(name.as_str()) {
use process::*;
let len = file.borrow().read_at(0, &mut *buf).unwrap();
let pid = processor().add(Context::new_user(&buf[..len]));
processor().current_wait_for(pid);
let pid = processor().manager().add(ContextImpl::new_user(&buf[..len]));
processor().manager().wait(thread::current().id(), pid);
processor().yield_now();
} else {
println!("Program not exist");
}

@ -13,7 +13,8 @@ pub fn panic(info: &PanicInfo) -> ! {
let location = info.location().unwrap();
let message = info.message().unwrap();
error!("\n\nPANIC in {} at line {}\n {}", location.file(), location.line(), message);
loop { }
use arch::cpu::halt;
loop { halt() }
}
#[lang = "oom"]

@ -9,6 +9,8 @@
#![feature(panic_info_message)]
#![feature(global_asm)]
#![feature(compiler_builtins_lib)]
#![feature(raw)]
#![feature(vec_resize_default)]
#![no_std]
@ -34,6 +36,8 @@ extern crate volatile;
extern crate x86_64;
extern crate xmas_elf;
pub use process::{processor, new_kernel_context};
use ucore_process::thread;
use linked_list_allocator::LockedHeap;
#[macro_use] // print!
@ -45,8 +49,6 @@ mod consts;
mod process;
mod syscall;
mod fs;
use process::{thread, thread_};
mod sync;
mod trap;
mod console;
@ -61,22 +63,13 @@ pub mod arch;
pub mod arch;
pub fn kmain() -> ! {
// Init the first kernel process(idle proc)
process::init();
// enable the interrupt
unsafe { arch::interrupt::enable(); }
process::processor().run();
// the test is not supported in riscv32(maybe)
// thread::test::local_key();
// thread::test::unpack();
// sync::test::philosopher_using_mutex();
// sync::test::philosopher_using_monitor();
// sync::mpsc::test::test_all();
// come into shell
fs::shell();
loop {}
}
/// Global heap allocator

@ -7,7 +7,7 @@ use ucore_memory::{*, paging::PageTable};
use ucore_memory::cow::CowExt;
pub use ucore_memory::memory_set::{MemoryArea, MemoryAttr, MemorySet as MemorySet_, Stack};
use ucore_memory::swap::*;
use process::processor;
use process::{processor, process};
pub type MemorySet = MemorySet_<InactivePageTable0>;
@ -73,6 +73,26 @@ pub fn alloc_stack() -> Stack {
Stack { top, bottom }
}
pub struct KernelStack(usize);
const STACK_SIZE: usize = 0x8000;
impl KernelStack {
pub fn new() -> Self {
use alloc::alloc::{alloc, Layout};
let bottom = unsafe{ alloc(Layout::from_size_align(STACK_SIZE, STACK_SIZE).unwrap()) } as usize;
KernelStack(bottom)
}
pub fn top(&self) -> usize {
self.0 + STACK_SIZE
}
}
impl Drop for KernelStack {
fn drop(&mut self) {
use alloc::alloc::{dealloc, Layout};
unsafe{ dealloc(self.0 as _, Layout::from_size_align(STACK_SIZE, STACK_SIZE).unwrap()); }
}
}
/*
@ -92,8 +112,7 @@ pub fn page_fault_handler(addr: usize) -> bool {
// handle the swap in/out
info!("start handling swap in/out page fault");
unsafe { ACTIVE_TABLE_SWAP.force_unlock(); }
let mut temp_proc = processor();
let pt = temp_proc.current_context_mut().get_memory_set_mut().get_page_table_mut();
let pt = process().get_memory_set_mut().get_page_table_mut();
if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, || alloc_frame().unwrap()){
return true;
}

@ -1,66 +1,47 @@
use arch::interrupt::{TrapFrame, Context as ArchContext};
use memory::{MemoryArea, MemoryAttr, MemorySet, active_table_swap, alloc_frame};
use memory::{MemoryArea, MemoryAttr, MemorySet, KernelStack, active_table_swap, alloc_frame};
use xmas_elf::{ElfFile, header, program::{Flags, ProgramHeader, Type}};
use core::fmt::{Debug, Error, Formatter};
use ucore_process::Context;
use alloc::boxed::Box;
use ucore_memory::{Page};
use ::memory::{InactivePageTable0};
pub struct Context {
pub struct ContextImpl {
arch: ArchContext,
memory_set: MemorySet,
kstack: KernelStack,
}
impl ::ucore_process::processor::Context for Context {
/*
* @param:
* target: the target process context
* @brief:
* switch to the target process context
*/
unsafe fn switch(&mut self, target: &mut Self) {
super::PROCESSOR.try().unwrap().force_unlock();
impl Context for ContextImpl {
unsafe fn switch_to(&mut self, target: &mut Context) {
use core::mem::transmute;
let (target, _): (&mut ContextImpl, *const ()) = transmute(target);
self.arch.switch(&mut target.arch);
use core::mem::forget;
// don't run the distructor of processor()
forget(super::processor());
}
/*
* @param:
* entry: the program entry for the process
* arg: a0 (a parameter)
* @brief:
* new a kernel thread Context
* @retval:
* the new kernel thread Context
*/
fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self {
let ms = MemorySet::new();
Context {
arch: unsafe { ArchContext::new_kernel_thread(entry, arg, ms.kstack_top(), ms.token()) },
memory_set: ms,
}
}
}
impl Context {
pub unsafe fn new_init() -> Self {
Context {
impl ContextImpl {
pub unsafe fn new_init() -> Box<Context> {
Box::new(ContextImpl {
arch: ArchContext::null(),
memory_set: MemorySet::new(),
kstack: KernelStack::new(),
})
}
pub fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Box<Context> {
let memory_set = MemorySet::new();
let kstack = KernelStack::new();
Box::new(ContextImpl {
arch: unsafe { ArchContext::new_kernel_thread(entry, arg, kstack.top(), memory_set.token()) },
memory_set,
kstack,
})
}
/// Make a new user thread from ELF data
/*
* @param:
* data: the ELF data stream
* @brief:
* make a new thread from ELF data
* @retval:
* the new user thread Context
*/
pub fn new_user(data: &[u8]) -> Self {
pub fn new_user(data: &[u8]) -> Box<Context> {
// Parse elf
let elf = ElfFile::new(data).expect("failed to read elf");
let is32 = match elf.header.pt2 {
@ -106,22 +87,23 @@ impl Context {
}
});
}
// map the memory set swappable
//memory_set_map_swappable(&mut memory_set);
let kstack = KernelStack::new();
//set the user Memory pages in the memory set swappable
memory_set_map_swappable(&mut memory_set);
Context {
Box::new(ContextImpl {
arch: unsafe {
ArchContext::new_user_thread(
entry_addr, user_stack_top - 8, memory_set.kstack_top(), is32, memory_set.token())
entry_addr, user_stack_top - 8, kstack.top(), is32, memory_set.token())
},
memory_set,
}
kstack,
})
}
/// Fork
pub fn fork(&self, tf: &TrapFrame) -> Self {
pub fn fork(&self, tf: &TrapFrame) -> Box<Context> {
// Clone memory set, make a new page table
let mut memory_set = self.memory_set.clone();
@ -140,12 +122,15 @@ impl Context {
});
}
// map the memory set swappable
memory_set_map_swappable(&mut memory_set);
//memory_set_map_swappable(&mut memory_set);
Context {
arch: unsafe { ArchContext::new_fork(tf, memory_set.kstack_top(), memory_set.token()) },
let kstack = KernelStack::new();
Box::new(ContextImpl {
arch: unsafe { ArchContext::new_fork(tf, kstack.top(), memory_set.token()) },
memory_set,
}
kstack,
})
}
pub fn get_memory_set_mut(&mut self) -> &mut MemorySet {
@ -153,11 +138,11 @@ impl Context {
}
}
impl Drop for Context{
/*
impl Drop for ContextImpl{
fn drop(&mut self){
//set the user Memory pages in the memory set unswappable
let Self {ref mut arch, ref mut memory_set} = self;
let Self {ref mut arch, ref mut memory_set, ref mut kstack} = self;
let pt = {
memory_set.get_page_table_mut() as *mut InactivePageTable0
};
@ -173,8 +158,8 @@ impl Drop for Context{
}
}
impl Debug for Context {
*/
impl Debug for ContextImpl {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{:x?}", self.arch)
}
@ -196,7 +181,7 @@ fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> MemorySet {
}
let (virt_addr, mem_size, flags) = match ph {
ProgramHeader::Ph32(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags),
ProgramHeader::Ph64(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags),//???
ProgramHeader::Ph64(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags),
};
set.push(MemoryArea::new(virt_addr, virt_addr + mem_size, memory_attr_from(flags), ""));
@ -210,7 +195,7 @@ fn memory_attr_from(elf_flags: Flags) -> MemoryAttr {
if elf_flags.is_execute() { flags = flags.execute(); }
flags
}
/*
/*
* @param:
* memory_set: the target MemorySet to set swappable
@ -229,3 +214,4 @@ fn memory_set_map_swappable(memory_set: &mut MemorySet){
}
info!("Finishing setting pages swappable");
}
*/

@ -1,57 +1,94 @@
use spin::Once;
use sync::{SpinNoIrqLock, Mutex, MutexGuard, SpinNoIrq};
pub use self::context::Context;
pub use ucore_process::processor::{*, Context as _whatever};
pub use ucore_process::scheduler::*;
pub use ucore_process::thread::*;
use spin::Mutex;
pub use self::context::ContextImpl;
pub use ucore_process::*;
use consts::{MAX_CPU_NUM, MAX_PROCESS_NUM};
use arch::cpu;
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use sync::Condvar;
use core::sync::atomic::*;
mod context;
type Processor = Processor_<Context, StrideScheduler>;
/*
* @brief:
* initialize a new kernel process (idleproc)
*/
pub fn init() {
PROCESSOR.call_once(||
SpinNoIrqLock::new({
let mut processor = Processor::new(
unsafe { Context::new_init() },
// NOTE: max_time_slice <= 5 to ensure 'priority' test pass
StrideScheduler::new(5),
);
extern fn idle(arg: usize) -> ! {
loop {}
}
processor.add(Context::new_kernel(idle, 0));
processor
})
);
let scheduler = Box::new(scheduler::RRScheduler::new(5));
let manager = Arc::new(ProcessManager::new(scheduler, MAX_PROCESS_NUM));
extern fn idle(_arg: usize) -> ! {
loop { cpu::halt(); }
}
for i in 0..4 {
manager.add(ContextImpl::new_kernel(idle, i));
}
unsafe {
for cpu_id in 0..MAX_CPU_NUM {
PROCESSORS[cpu_id].init(cpu_id, ContextImpl::new_init(), manager.clone());
}
}
info!("process init end");
}
pub static PROCESSOR: Once<SpinNoIrqLock<Processor>> = Once::new();
static PROCESSORS: [Processor; MAX_CPU_NUM] = [Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new()];
pub fn processor() -> MutexGuard<'static, Processor, SpinNoIrq> {
PROCESSOR.try().unwrap().lock()
/// Ugly solution for sys_wait(0) (wait for any child)
#[derive(Default)]
pub struct Process {
parent: AtomicUsize,
children: Mutex<Vec<usize>>,
subproc_exit: Condvar, // Trigger parent's when exit
}
#[allow(non_camel_case_types)]
pub type thread = ThreadMod<ThreadSupportImpl>;
impl Process {
pub fn new_fork(pid: usize, parent: usize) {
PROCESS[pid].parent.store(parent, Ordering::Relaxed);
PROCESS[pid].subproc_exit._clear();
PROCESS[parent].children.lock().push(pid);
}
pub fn proc_exit(pid: usize) {
let parent = PROCESS[pid].parent.load(Ordering::Relaxed);
PROCESS[parent].subproc_exit.notify_all();
}
pub fn wait_child() {
Self::current().subproc_exit._wait();
}
pub fn get_children() -> Vec<usize> {
Self::current().children.lock().clone()
}
pub fn do_wait(pid: usize) {
Self::current().children.lock().retain(|&p| p != pid);
}
fn current() -> &'static Self {
&PROCESS[thread::current().id()]
}
}
lazy_static! {
pub static ref PROCESS: Vec<Process> = {
let mut vec = Vec::new();
vec.resize_default(MAX_PROCESS_NUM);
vec
};
}
pub mod thread_ {
pub type Thread = super::Thread<super::ThreadSupportImpl>;
/// Get current thread struct
pub fn process() -> &'static mut ContextImpl {
use core::mem::transmute;
let (process, _): (&mut ContextImpl, *const ()) = unsafe {
transmute(processor().context())
};
process
}
pub struct ThreadSupportImpl;
impl ThreadSupport for ThreadSupportImpl {
type Context = Context;
type Scheduler = StrideScheduler;
type ProcessorGuard = MutexGuard<'static, Processor, SpinNoIrq>;
// Implement dependencies for std::thread
fn processor() -> Self::ProcessorGuard {
processor()
#[no_mangle]
pub fn processor() -> &'static Processor {
&PROCESSORS[cpu::id()]
}
#[no_mangle]
pub fn new_kernel_context(entry: extern fn(usize) -> !, arg: usize) -> Box<Context> {
ContextImpl::new_kernel(entry, arg)
}

@ -0,0 +1,4 @@
pub struct cpu {
pub id: usize
}

@ -1,11 +1,10 @@
use alloc::collections::VecDeque;
use super::*;
use thread;
use thread_;
#[derive(Default)]
pub struct Condvar {
wait_queue: SpinNoIrqLock<VecDeque<thread_::Thread>>,
wait_queue: SpinNoIrqLock<VecDeque<thread::Thread>>,
}
impl Condvar {
@ -34,4 +33,7 @@ impl Condvar {
t.unpark();
}
}
pub fn _clear(&self) {
self.wait_queue.lock().clear();
}
}

@ -58,25 +58,45 @@ fn sys_close(fd: usize) -> i32 {
/// Fork the current process. Return the child's PID.
fn sys_fork(tf: &TrapFrame) -> i32 {
let mut processor = processor();
let context = processor.current_context().fork(tf);
let pid = processor.add(context);
info!("fork: {} -> {}", processor.current_pid(), pid);
let mut context = process().fork(tf);
let pid = processor().manager().add(context);
Process::new_fork(pid, thread::current().id());
info!("fork: {} -> {}", thread::current().id(), pid);
pid as i32
}
/// Wait the process exit.
/// Return the PID. Store exit code to `code` if it's not null.
fn sys_wait(pid: usize, code: *mut i32) -> i32 {
let mut processor = processor();
match processor.current_wait_for(pid) {
WaitResult::Ok(pid, error_code) => {
loop {
let wait_procs = match pid {
0 => Process::get_children(),
_ => vec![pid],
};
if wait_procs.is_empty() {
return -1;
}
for pid in wait_procs {
match processor().manager().get_status(pid) {
Some(Status::Exited(exit_code)) => {
if !code.is_null() {
unsafe { *code = error_code as i32 };
unsafe { code.write(exit_code as i32); }
}
processor().manager().remove(pid);
Process::do_wait(pid);
info!("wait: {} -> {}", thread::current().id(), pid);
return 0;
}
None => return -1,
_ => {}
}
0
}
WaitResult::NotExist => -1,
if pid == 0 {
Process::wait_child();
} else {
processor().manager().wait(thread::current().id(), pid);
processor().yield_now();
}
}
}
@ -87,7 +107,11 @@ fn sys_yield() -> i32 {
/// Kill the process
fn sys_kill(pid: usize) -> i32 {
processor().kill(pid);
processor().manager().exit(pid, 0x100);
Process::proc_exit(pid);
if pid == thread::current().id() {
processor().yield_now();
}
0
}
@ -97,11 +121,12 @@ fn sys_getpid() -> i32 {
}
/// Exit the current process
fn sys_exit(error_code: usize) -> i32 {
let mut processor = processor();
let pid = processor.current_pid();
processor.exit(pid, error_code);
0
fn sys_exit(exit_code: usize) -> i32 {
let pid = thread::current().id();
processor().manager().exit(pid, exit_code);
Process::proc_exit(pid);
processor().yield_now();
unreachable!();
}
fn sys_sleep(time: usize) -> i32 {
@ -111,13 +136,12 @@ fn sys_sleep(time: usize) -> i32 {
}
fn sys_get_time() -> i32 {
let processor = processor();
processor.get_time() as i32
unsafe { ::trap::TICK as i32 }
}
fn sys_lab6_set_priority(priority: usize) -> i32 {
let mut processor = processor();
processor.set_priority(priority as u8);
let pid = thread::current().id();
processor().manager().set_priority(pid, priority as u8);
0
}

@ -1,36 +1,22 @@
use process::*;
use arch::interrupt::TrapFrame;
use arch::cpu;
/*
* @brief:
* process timer interrupt
*/
pub fn timer() {
let mut processor = processor();
processor.tick();
}
pub static mut TICK: usize = 0;
pub fn before_return() {
if let Some(processor) = PROCESSOR.try() {
processor.lock().schedule();
pub fn timer() {
processor().tick();
if cpu::id() == 0 {
unsafe { TICK += 1; }
}
}
/*
* @param:
* TrapFrame: the error's trapframe
* @brief:
* process the error trap, if processor inited then exit else panic!
*/
pub fn error(tf: &TrapFrame) -> ! {
if let Some(processor) = PROCESSOR.try() {
let mut processor = processor.lock();
let pid = processor.current_pid();
error!("Process {} error:\n{:#x?}", pid, tf);
processor.exit(pid, 0x100); // TODO: Exit code for error
processor.schedule();
error!("{:#x?}", tf);
let pid = processor().pid();
error!("On CPU{} Process {}", cpu::id(), pid);
processor().manager().exit(pid, 0x100);
processor().yield_now();
unreachable!();
} else {
panic!("Exception when processor not inited\n{:#x?}", tf);
}
}
Loading…
Cancel
Save