Compare commits

..

17 Commits

@ -0,0 +1,25 @@
name: Build Rust Doc
on: [push]
env:
CARGO_TERM_COLOR: always
jobs:
build-doc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build doc
run: |
rustup target add riscv64gc-unknown-none-elf
rustup component add llvm-tools-preview
rustup component add rust-src
cd os
cargo doc --no-deps --verbose
- name: Deploy to Github Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./os/target/riscv64gc-unknown-none-elf/doc
destination_dir: ${{ github.ref_name }}

@ -1,66 +0,0 @@
name: Build Rust Doc And Run tests
on: [push]
env:
CARGO_TERM_COLOR: always
jobs:
build-doc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-04-11
components: rust-src, llvm-tools-preview
target: riscv64gc-unknown-none-elf
- name: Build doc
run: cd os && cargo doc --no-deps --verbose
- name: Deploy to Github Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./os/target/riscv64gc-unknown-none-elf/doc
destination_dir: ${{ github.ref_name }}
run-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2022-04-11
components: rust-src, llvm-tools-preview
target: riscv64gc-unknown-none-elf
- uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
use-tool-cache: true
- name: Cache QEMU
uses: actions/cache@v3
with:
path: qemu-7.0.0
key: qemu-7.0.0-x86_64-riscv64
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install ninja-build -y
if [ ! -d qemu-7.0.0 ]; then
wget https://download.qemu.org/qemu-7.0.0.tar.xz
tar -xf qemu-7.0.0.tar.xz
cd qemu-7.0.0
./configure --target-list=riscv64-softmmu
make -j
else
cd qemu-7.0.0
fi
sudo make install
qemu-system-riscv64 --version
- name: Run usertests
run: cd os && make run TEST=1
timeout-minutes: 10

18
.gitignore vendored

@ -1,13 +1,17 @@
.*/*
!.github/*
!.vscode/settings.json
**/target/
**/Cargo.lock
.idea/*
os/target/*
os/.idea/*
os/src/link_app.S
os/src/linker.ld
os/last-*
os/Cargo.lock
os/.gdb_history
user/target/*
user/.idea/*
user/Cargo.lock
easy-fs/Cargo.lock
easy-fs/target/*
easy-fs-fuse/Cargo.lock
easy-fs-fuse/target/*
tools/
pushall.sh

@ -1,10 +0,0 @@
{
// Prevent "can't find crate for `test`" error on no_std
// Ref: https://github.com/rust-lang/vscode-rust/issues/729
// For vscode-rust plugin users:
"rust.target": "riscv64gc-unknown-none-elf",
"rust.all_targets": false,
// For Rust Analyzer plugin users:
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
"rust-analyzer.checkOnSave.allTargets": false
}

@ -6,5 +6,3 @@ docker:
build_docker:
docker build -t ${DOCKER_NAME} .
fmt:
cd easy-fs; cargo fmt; cd ../easy-fs-fuse cargo fmt; cd ../os ; cargo fmt; cd ../user; cargo fmt; cd ..

@ -3,8 +3,6 @@ rCore-Tutorial version 3.5. See the [Documentation in Chinese](https://rcore-os.
rCore-Tutorial API Docs. See the [API Docs of Ten OSes ](#OS-API-DOCS)
If you don't know Rust Language and try to learn it, please visit [Rust Learning Resources](https://github.com/rcore-os/rCore/wiki/study-resource-of-system-programming-in-RUST)
Official QQ group number: 735045051
## news
@ -190,44 +188,6 @@ $ make run BOARD=k210
Type `Ctrl+]` to disconnect from K210.
## Show runtime debug info of OS kernel version
The branch of ch9-log contains a lot of debug info. You could try to run rcore tutorial
for understand the internal behavior of os kernel.
```sh
$ git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
$ cd rCore-Tutorial-v3/os
$ git checkout ch9-log
$ make run
......
[rustsbi] RustSBI version 0.2.0-alpha.10, adapting to RISC-V SBI v0.3
.______ __ __ _______.___________. _______..______ __
| _ \ | | | | / | | / || _ \ | |
| |_) | | | | | | (----`---| |----`| (----`| |_) || |
| / | | | | \ \ | | \ \ | _ < | |
| |\ \----.| `--' |.----) | | | .----) | | |_) || |
| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__|
[rustsbi] Implementation: RustSBI-QEMU Version 0.0.2
[rustsbi-dtb] Hart count: cluster0 with 1 cores
[rustsbi] misa: RV64ACDFIMSU
[rustsbi] mideleg: ssoft, stimer, sext (0x222)
[rustsbi] medeleg: ima, ia, bkpt, la, sa, uecall, ipage, lpage, spage (0xb1ab)
[rustsbi] pmp0: 0x10000000 ..= 0x10001fff (rw-)
[rustsbi] pmp1: 0x2000000 ..= 0x200ffff (rw-)
[rustsbi] pmp2: 0xc000000 ..= 0xc3fffff (rw-)
[rustsbi] pmp3: 0x80000000 ..= 0x8fffffff (rwx)
[rustsbi] enter supervisor 0x80200000
[KERN] rust_main() begin
[KERN] clear_bss() begin
[KERN] clear_bss() end
[KERN] mm::init() begin
[KERN] mm::init_heap() begin
[KERN] mm::init_heap() end
[KERN] mm::init_frame_allocator() begin
[KERN] mm::frame_allocator::lazy_static!FRAME_ALLOCATOR begin
......
```
## Rustdoc
Currently it can only help you view the code since only a tiny part of the code has been documented.

Binary file not shown.

@ -23,8 +23,6 @@ impl BlockDevice for BlockFile {
.expect("Error when seeking!");
assert_eq!(file.write(buf).unwrap(), BLOCK_SZ, "Not a complete block!");
}
fn handle_irq(&self) { unimplemented!(); }
}
fn main() {

@ -3,5 +3,4 @@ use core::any::Any;
pub trait BlockDevice: Send + Sync + Any {
fn read_block(&self, block_id: usize, buf: &mut [u8]);
fn write_block(&self, block_id: usize, buf: &[u8]);
fn handle_irq(&self);
}

@ -12,7 +12,6 @@ lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
buddy_system_allocator = "0.6"
bitflags = "1.2.1"
xmas-elf = "0.7.0"
volatile = "0.3"
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers" }
k210-pac = { git = "https://github.com/wyfcyx/k210-pac" }
k210-hal = { git = "https://github.com/wyfcyx/k210-hal" }

@ -37,9 +37,6 @@ OBJCOPY := rust-objcopy --binary-architecture=riscv64
# Disassembly
DISASM ?= -x
# Run usertests or usershell
TEST ?=
build: env switch-check $(KERNEL_BIN) fs-img
switch-check:
@ -64,7 +61,7 @@ $(KERNEL_BIN): kernel
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
fs-img: $(APPS)
@cd ../user && make build TEST=$(TEST)
@cd ../user && make build
@rm -f $(FS_IMG)
@cd ../easy-fs-fuse && cargo run --release -- -s ../user/src/bin/ -t ../user/target/riscv64gc-unknown-none-elf/release/
@ -84,7 +81,7 @@ disasm: kernel
disasm-vim: kernel
@$(OBJDUMP) $(DISASM) $(KERNEL_ELF) > $(DISASM_TMP)
@nvim $(DISASM_TMP)
@vim $(DISASM_TMP)
@rm $(DISASM_TMP)
run: run-inner

@ -21,10 +21,3 @@ pub const MMIO: &[(usize, usize)] = &[
pub type BlockDeviceImpl = crate::drivers::block::SDCardWrapper;
pub fn device_init() {
unimplemented!();
}
pub fn irq_handler() {
unimplemented!();
}

@ -1,126 +1,6 @@
pub const CLOCK_FREQ: usize = 12500000;
pub const MMIO: &[(usize, usize)] = &[
(0x1000_0000, 0x1000), // VIRT_UART0 in virt machine
(0x1000_1000, 0x1000), // VIRT_VIRTIO in virt machine
(0x0C00_0000, 0x40_0000), // VIRT_PLIC in virt machine
(0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine
];
pub const MMIO: &[(usize, usize)] = &[(0x10001000, 0x1000)];
pub type BlockDeviceImpl = crate::drivers::block::VirtIOBlock;
pub type CharDeviceImpl = crate::drivers::chardev::NS16550a<VIRT_UART>;
pub const VIRT_PLIC: usize = 0xC00_0000;
pub const VIRT_UART: usize = 0x1000_0000;
use crate::drivers::block::BLOCK_DEVICE;
use crate::drivers::chardev::{CharDevice, UART};
use crate::drivers::plic::{IntrTargetPriority, PLIC};
pub fn device_init() {
use riscv::register::sie;
let mut plic = unsafe { PLIC::new(VIRT_PLIC) };
let hart_id: usize = 0;
let supervisor = IntrTargetPriority::Supervisor;
let machine = IntrTargetPriority::Machine;
plic.set_threshold(hart_id, supervisor, 0);
plic.set_threshold(hart_id, machine, 1);
for intr_src_id in [1usize, 10] {
plic.enable(hart_id, supervisor, intr_src_id);
plic.set_priority(intr_src_id, 1);
}
unsafe {
sie::set_sext();
}
}
pub fn irq_handler() {
let mut plic = unsafe { PLIC::new(VIRT_PLIC) };
let intr_src_id = plic.claim(0, IntrTargetPriority::Supervisor);
match intr_src_id {
1 => BLOCK_DEVICE.handle_irq(),
10 => UART.handle_irq(),
_ => panic!("unsupported IRQ {}", intr_src_id),
}
plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id);
}
//ref:: https://github.com/andre-richter/qemu-exit
use core::arch::asm;
const EXIT_SUCCESS: u32 = 0x5555; // Equals `exit(0)`. qemu successful exit
const EXIT_FAILURE_FLAG: u32 = 0x3333;
const EXIT_FAILURE: u32 = exit_code_encode(1); // Equals `exit(1)`. qemu failed exit
const EXIT_RESET: u32 = 0x7777; // qemu reset
pub trait QEMUExit {
/// Exit with specified return code.
///
/// Note: For `X86`, code is binary-OR'ed with `0x1` inside QEMU.
fn exit(&self, code: u32) -> !;
/// Exit QEMU using `EXIT_SUCCESS`, aka `0`, if possible.
///
/// Note: Not possible for `X86`.
fn exit_success(&self) -> !;
/// Exit QEMU using `EXIT_FAILURE`, aka `1`.
fn exit_failure(&self) -> !;
}
/// RISCV64 configuration
pub struct RISCV64 {
/// Address of the sifive_test mapped device.
addr: u64,
}
/// Encode the exit code using EXIT_FAILURE_FLAG.
const fn exit_code_encode(code: u32) -> u32 {
(code << 16) | EXIT_FAILURE_FLAG
}
impl RISCV64 {
/// Create an instance.
pub const fn new(addr: u64) -> Self {
RISCV64 { addr }
}
}
impl QEMUExit for RISCV64 {
/// Exit qemu with specified exit code.
fn exit(&self, code: u32) -> ! {
// If code is not a special value, we need to encode it with EXIT_FAILURE_FLAG.
let code_new = match code {
EXIT_SUCCESS | EXIT_FAILURE | EXIT_RESET => code,
_ => exit_code_encode(code),
};
unsafe {
asm!(
"sw {0}, 0({1})",
in(reg)code_new, in(reg)self.addr
);
// For the case that the QEMU exit attempt did not work, transition into an infinite
// loop. Calling `panic!()` here is unfeasible, since there is a good chance
// this function here is the last expression in the `panic!()` handler
// itself. This prevents a possible infinite loop.
loop {
asm!("wfi", options(nomem, nostack));
}
}
}
fn exit_success(&self) -> ! {
self.exit(EXIT_SUCCESS);
}
fn exit_failure(&self) -> ! {
self.exit(EXIT_FAILURE);
}
}
const VIRT_TEST: u64 = 0x100000;
pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(VIRT_TEST);

@ -11,3 +11,4 @@ pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1;
pub const TRAP_CONTEXT_BASE: usize = TRAMPOLINE - PAGE_SIZE;
pub use crate::board::{CLOCK_FREQ, MMIO};

@ -1,4 +1,4 @@
use crate::drivers::chardev::{CharDevice, UART};
use crate::sbi::console_putchar;
use core::fmt::{self, Write};
struct Stdout;
@ -6,7 +6,7 @@ struct Stdout;
impl Write for Stdout {
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
UART.write(c as u8);
console_putchar(c as usize);
}
Ok(())
}

@ -1,13 +1,13 @@
mod sdcard;
mod virtio_blk;
pub use sdcard::SDCardWrapper;
pub use virtio_blk::VirtIOBlock;
pub use sdcard::SDCardWrapper;
use crate::board::BlockDeviceImpl;
use alloc::sync::Arc;
use easy_fs::BlockDevice;
use lazy_static::*;
use crate::board::BlockDeviceImpl;
lazy_static! {
pub static ref BLOCK_DEVICE: Arc<dyn BlockDevice> = Arc::new(BlockDeviceImpl::new());

@ -3,7 +3,7 @@
#![allow(unused)]
use super::BlockDevice;
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use core::convert::TryInto;
use k210_hal::prelude::*;
use k210_pac::{Peripherals, SPI0};
@ -321,14 +321,14 @@ impl</*'a,*/ X: SPI> SDCard</*'a,*/ X> {
* Get SD card data response.
* @param None
* @retval The SD status: Read data response xxx0<status>1
* - status 010: Data accepted
* - status 010: Data accecpted
* - status 101: Data rejected due to a crc error
* - status 110: Data rejected due to a Write error.
* - status 111: Data rejected due to other error.
*/
fn get_dataresponse(&self) -> u8 {
let response = &mut [0u8];
/* Read response */
/* Read resonse */
self.read_data(response);
/* Mask unused bits */
response[0] &= 0x1F;
@ -423,7 +423,7 @@ impl</*'a,*/ X: SPI> SDCard</*'a,*/ X> {
/* Byte 15 */
CSD_CRC: (csd_tab[15] & 0xFE) >> 1,
Reserved4: 1,
/* Return the response */
/* Return the reponse */
})
}
@ -715,8 +715,8 @@ fn io_init() {
}
lazy_static! {
static ref PERIPHERALS: UPIntrFreeCell<Peripherals> =
unsafe { UPIntrFreeCell::new(Peripherals::take().unwrap()) };
static ref PERIPHERALS: UPSafeCell<Peripherals> =
unsafe { UPSafeCell::new(Peripherals::take().unwrap()) };
}
fn init_sdcard() -> SDCard<SPIImpl<SPI0>> {
@ -740,11 +740,11 @@ fn init_sdcard() -> SDCard<SPIImpl<SPI0>> {
sd
}
pub struct SDCardWrapper(UPIntrFreeCell<SDCard<SPIImpl<SPI0>>>);
pub struct SDCardWrapper(UPSafeCell<SDCard<SPIImpl<SPI0>>>);
impl SDCardWrapper {
pub fn new() -> Self {
unsafe { Self(UPIntrFreeCell::new(init_sdcard())) }
unsafe { Self(UPSafeCell::new(init_sdcard())) }
}
}
@ -761,7 +761,4 @@ impl BlockDevice for SDCardWrapper {
.write_sector(buf, block_id as u32)
.unwrap();
}
fn handle_irq(&self) {
unimplemented!();
}
}

@ -3,93 +3,42 @@ use crate::mm::{
frame_alloc, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum,
StepByOne, VirtAddr,
};
use crate::sync::{Condvar, UPIntrFreeCell};
use crate::task::schedule;
use crate::DEV_NON_BLOCKING_ACCESS;
use alloc::collections::BTreeMap;
use crate::sync::UPSafeCell;
use alloc::vec::Vec;
use lazy_static::*;
use virtio_drivers::{BlkResp, RespStatus, VirtIOBlk, VirtIOHeader};
use virtio_drivers::{VirtIOBlk, VirtIOHeader};
#[allow(unused)]
const VIRTIO0: usize = 0x10001000;
pub struct VirtIOBlock {
virtio_blk: UPIntrFreeCell<VirtIOBlk<'static>>,
condvars: BTreeMap<u16, Condvar>,
}
pub struct VirtIOBlock(UPSafeCell<VirtIOBlk<'static>>);
lazy_static! {
static ref QUEUE_FRAMES: UPIntrFreeCell<Vec<FrameTracker>> =
unsafe { UPIntrFreeCell::new(Vec::new()) };
static ref QUEUE_FRAMES: UPSafeCell<Vec<FrameTracker>> = unsafe { UPSafeCell::new(Vec::new()) };
}
impl BlockDevice for VirtIOBlock {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access();
if nb {
let mut resp = BlkResp::default();
let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| {
let token = unsafe { blk.read_block_nb(block_id, buf, &mut resp).unwrap() };
self.condvars.get(&token).unwrap().wait_no_sched()
});
schedule(task_cx_ptr);
assert_eq!(
resp.status(),
RespStatus::Ok,
"Error when reading VirtIOBlk"
);
} else {
self.virtio_blk
.exclusive_access()
.read_block(block_id, buf)
.expect("Error when reading VirtIOBlk");
}
self.0
.exclusive_access()
.read_block(block_id, buf)
.expect("Error when reading VirtIOBlk");
}
fn write_block(&self, block_id: usize, buf: &[u8]) {
let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access();
if nb {
let mut resp = BlkResp::default();
let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| {
let token = unsafe { blk.write_block_nb(block_id, buf, &mut resp).unwrap() };
self.condvars.get(&token).unwrap().wait_no_sched()
});
schedule(task_cx_ptr);
assert_eq!(
resp.status(),
RespStatus::Ok,
"Error when writing VirtIOBlk"
);
} else {
self.virtio_blk
.exclusive_access()
.write_block(block_id, buf)
.expect("Error when writing VirtIOBlk");
}
}
fn handle_irq(&self) {
self.virtio_blk.exclusive_session(|blk| {
while let Ok(token) = blk.pop_used() {
self.condvars.get(&token).unwrap().signal();
}
});
self.0
.exclusive_access()
.write_block(block_id, buf)
.expect("Error when writing VirtIOBlk");
}
}
impl VirtIOBlock {
#[allow(unused)]
pub fn new() -> Self {
let virtio_blk = unsafe {
UPIntrFreeCell::new(VirtIOBlk::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap())
};
let mut condvars = BTreeMap::new();
let channels = virtio_blk.exclusive_access().virt_queue_size();
for i in 0..channels {
let condvar = Condvar::new();
condvars.insert(i, condvar);
}
Self {
virtio_blk,
condvars,
unsafe {
Self(UPSafeCell::new(
VirtIOBlk::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(),
))
}
}
}

@ -1,17 +0,0 @@
mod ns16550a;
pub use ns16550a::NS16550a;
use crate::board::CharDeviceImpl;
use alloc::sync::Arc;
use lazy_static::*;
pub trait CharDevice {
fn read(&self) -> u8;
fn write(&self, ch: u8);
fn handle_irq(&self);
}
lazy_static! {
pub static ref UART: Arc<CharDeviceImpl> = Arc::new(CharDeviceImpl::new());
}

@ -1,175 +0,0 @@
///! Ref: https://www.lammertbies.nl/comm/info/serial-uart
///! Ref: ns16550a datasheet: https://datasheetspdf.com/pdf-file/605590/NationalSemiconductor/NS16550A/1
///! Ref: ns16450 datasheet: https://datasheetspdf.com/pdf-file/1311818/NationalSemiconductor/NS16450/1
use super::CharDevice;
use crate::sync::{Condvar, UPIntrFreeCell};
use crate::task::schedule;
use alloc::collections::VecDeque;
use bitflags::*;
use volatile::{ReadOnly, Volatile, WriteOnly};
bitflags! {
/// InterruptEnableRegister
pub struct IER: u8 {
const RX_AVAILABLE = 1 << 0;
const TX_EMPTY = 1 << 1;
}
/// LineStatusRegister
pub struct LSR: u8 {
const DATA_AVAILABLE = 1 << 0;
const THR_EMPTY = 1 << 5;
}
/// Model Control Register
pub struct MCR: u8 {
const DATA_TERMINAL_READY = 1 << 0;
const REQUEST_TO_SEND = 1 << 1;
const AUX_OUTPUT1 = 1 << 2;
const AUX_OUTPUT2 = 1 << 3;
}
}
#[repr(C)]
#[allow(dead_code)]
struct ReadWithoutDLAB {
/// receiver buffer register
pub rbr: ReadOnly<u8>,
/// interrupt enable register
pub ier: Volatile<IER>,
/// interrupt identification register
pub iir: ReadOnly<u8>,
/// line control register
pub lcr: Volatile<u8>,
/// model control register
pub mcr: Volatile<MCR>,
/// line status register
pub lsr: ReadOnly<LSR>,
/// ignore MSR
_padding1: ReadOnly<u8>,
/// ignore SCR
_padding2: ReadOnly<u8>,
}
#[repr(C)]
#[allow(dead_code)]
struct WriteWithoutDLAB {
/// transmitter holding register
pub thr: WriteOnly<u8>,
/// interrupt enable register
pub ier: Volatile<IER>,
/// ignore FCR
_padding0: ReadOnly<u8>,
/// line control register
pub lcr: Volatile<u8>,
/// modem control register
pub mcr: Volatile<MCR>,
/// line status register
pub lsr: ReadOnly<LSR>,
/// ignore other registers
_padding1: ReadOnly<u16>,
}
pub struct NS16550aRaw {
base_addr: usize,
}
impl NS16550aRaw {
fn read_end(&mut self) -> &mut ReadWithoutDLAB {
unsafe { &mut *(self.base_addr as *mut ReadWithoutDLAB) }
}
fn write_end(&mut self) -> &mut WriteWithoutDLAB {
unsafe { &mut *(self.base_addr as *mut WriteWithoutDLAB) }
}
pub fn new(base_addr: usize) -> Self {
Self { base_addr }
}
pub fn init(&mut self) {
let read_end = self.read_end();
let mut mcr = MCR::empty();
mcr |= MCR::DATA_TERMINAL_READY;
mcr |= MCR::REQUEST_TO_SEND;
mcr |= MCR::AUX_OUTPUT2;
read_end.mcr.write(mcr);
let ier = IER::RX_AVAILABLE;
read_end.ier.write(ier);
}
pub fn read(&mut self) -> Option<u8> {
let read_end = self.read_end();
let lsr = read_end.lsr.read();
if lsr.contains(LSR::DATA_AVAILABLE) {
Some(read_end.rbr.read())
} else {
None
}
}
pub fn write(&mut self, ch: u8) {
let write_end = self.write_end();
loop {
if write_end.lsr.read().contains(LSR::THR_EMPTY) {
write_end.thr.write(ch);
break;
}
}
}
}
struct NS16550aInner {
ns16550a: NS16550aRaw,
read_buffer: VecDeque<u8>,
}
pub struct NS16550a<const BASE_ADDR: usize> {
inner: UPIntrFreeCell<NS16550aInner>,
condvar: Condvar,
}
impl<const BASE_ADDR: usize> NS16550a<BASE_ADDR> {
pub fn new() -> Self {
let mut inner = NS16550aInner {
ns16550a: NS16550aRaw::new(BASE_ADDR),
read_buffer: VecDeque::new(),
};
inner.ns16550a.init();
Self {
inner: unsafe { UPIntrFreeCell::new(inner) },
condvar: Condvar::new(),
}
}
}
impl<const BASE_ADDR: usize> CharDevice for NS16550a<BASE_ADDR> {
fn read(&self) -> u8 {
loop {
let mut inner = self.inner.exclusive_access();
if let Some(ch) = inner.read_buffer.pop_front() {
return ch;
} else {
let task_cx_ptr = self.condvar.wait_no_sched();
drop(inner);
schedule(task_cx_ptr);
}
}
}
fn write(&self, ch: u8) {
let mut inner = self.inner.exclusive_access();
inner.ns16550a.write(ch);
}
fn handle_irq(&self) {
let mut count = 0;
self.inner.exclusive_session(|inner| {
while let Some(ch) = inner.ns16550a.read() {
count += 1;
inner.read_buffer.push_back(ch);
}
});
if count > 0 {
self.condvar.signal();
}
}
}

@ -1,6 +1,3 @@
pub mod block;
pub mod chardev;
pub mod plic;
pub use block::BLOCK_DEVICE;
pub use chardev::UART;

@ -1,124 +0,0 @@
#[allow(clippy::upper_case_acronyms)]
pub struct PLIC {
base_addr: usize,
}
#[derive(Copy, Clone)]
pub enum IntrTargetPriority {
Machine = 0,
Supervisor = 1,
}
impl IntrTargetPriority {
pub fn supported_number() -> usize {
2
}
}
impl PLIC {
fn priority_ptr(&self, intr_source_id: usize) -> *mut u32 {
assert!(intr_source_id > 0 && intr_source_id <= 132);
(self.base_addr + intr_source_id * 4) as *mut u32
}
fn hart_id_with_priority(hart_id: usize, target_priority: IntrTargetPriority) -> usize {
let priority_num = IntrTargetPriority::supported_number();
hart_id * priority_num + target_priority as usize
}
fn enable_ptr(
&self,
hart_id: usize,
target_priority: IntrTargetPriority,
intr_source_id: usize,
) -> (*mut u32, usize) {
let id = Self::hart_id_with_priority(hart_id, target_priority);
let (reg_id, reg_shift) = (intr_source_id / 32, intr_source_id % 32);
(
(self.base_addr + 0x2000 + 0x80 * id + 0x4 * reg_id) as *mut u32,
reg_shift,
)
}
fn threshold_ptr_of_hart_with_priority(
&self,
hart_id: usize,
target_priority: IntrTargetPriority,
) -> *mut u32 {
let id = Self::hart_id_with_priority(hart_id, target_priority);
(self.base_addr + 0x20_0000 + 0x1000 * id) as *mut u32
}
fn claim_comp_ptr_of_hart_with_priority(
&self,
hart_id: usize,
target_priority: IntrTargetPriority,
) -> *mut u32 {
let id = Self::hart_id_with_priority(hart_id, target_priority);
(self.base_addr + 0x20_0004 + 0x1000 * id) as *mut u32
}
pub unsafe fn new(base_addr: usize) -> Self {
Self { base_addr }
}
pub fn set_priority(&mut self, intr_source_id: usize, priority: u32) {
assert!(priority < 8);
unsafe {
self.priority_ptr(intr_source_id).write_volatile(priority);
}
}
#[allow(unused)]
pub fn get_priority(&mut self, intr_source_id: usize) -> u32 {
unsafe { self.priority_ptr(intr_source_id).read_volatile() & 7 }
}
pub fn enable(
&mut self,
hart_id: usize,
target_priority: IntrTargetPriority,
intr_source_id: usize,
) {
let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id);
unsafe {
reg_ptr.write_volatile(reg_ptr.read_volatile() | 1 << shift);
}
}
#[allow(unused)]
pub fn disable(
&mut self,
hart_id: usize,
target_priority: IntrTargetPriority,
intr_source_id: usize,
) {
let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id);
unsafe {
reg_ptr.write_volatile(reg_ptr.read_volatile() & (!(1u32 << shift)));
}
}
pub fn set_threshold(
&mut self,
hart_id: usize,
target_priority: IntrTargetPriority,
threshold: u32,
) {
assert!(threshold < 8);
let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority);
unsafe {
threshold_ptr.write_volatile(threshold);
}
}
#[allow(unused)]
pub fn get_threshold(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 {
let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority);
unsafe { threshold_ptr.read_volatile() & 7 }
}
pub fn claim(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 {
let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority);
unsafe { claim_comp_ptr.read_volatile() }
}
pub fn complete(
&mut self,
hart_id: usize,
target_priority: IntrTargetPriority,
completion: u32,
) {
let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority);
unsafe {
claim_comp_ptr.write_volatile(completion);
}
}
}

@ -1,7 +1,7 @@
use super::File;
use crate::drivers::BLOCK_DEVICE;
use crate::mm::UserBuffer;
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use alloc::sync::Arc;
use alloc::vec::Vec;
use bitflags::*;
@ -11,7 +11,7 @@ use lazy_static::*;
pub struct OSInode {
readable: bool,
writable: bool,
inner: UPIntrFreeCell<OSInodeInner>,
inner: UPSafeCell<OSInodeInner>,
}
pub struct OSInodeInner {
@ -24,7 +24,7 @@ impl OSInode {
Self {
readable,
writable,
inner: unsafe { UPIntrFreeCell::new(OSInodeInner { offset: 0, inode }) },
inner: unsafe { UPSafeCell::new(OSInodeInner { offset: 0, inode }) },
}
}
pub fn read_all(&self) -> Vec<u8> {

@ -1,6 +1,6 @@
use super::File;
use crate::mm::UserBuffer;
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use alloc::sync::{Arc, Weak};
use crate::task::suspend_current_and_run_next;
@ -8,18 +8,18 @@ use crate::task::suspend_current_and_run_next;
pub struct Pipe {
readable: bool,
writable: bool,
buffer: Arc<UPIntrFreeCell<PipeRingBuffer>>,
buffer: Arc<UPSafeCell<PipeRingBuffer>>,
}
impl Pipe {
pub fn read_end_with_buffer(buffer: Arc<UPIntrFreeCell<PipeRingBuffer>>) -> Self {
pub fn read_end_with_buffer(buffer: Arc<UPSafeCell<PipeRingBuffer>>) -> Self {
Self {
readable: true,
writable: false,
buffer,
}
}
pub fn write_end_with_buffer(buffer: Arc<UPIntrFreeCell<PipeRingBuffer>>) -> Self {
pub fn write_end_with_buffer(buffer: Arc<UPSafeCell<PipeRingBuffer>>) -> Self {
Self {
readable: false,
writable: true,
@ -98,7 +98,7 @@ impl PipeRingBuffer {
/// Return (read_end, write_end)
pub fn make_pipe() -> (Arc<Pipe>, Arc<Pipe>) {
let buffer = Arc::new(unsafe { UPIntrFreeCell::new(PipeRingBuffer::new()) });
let buffer = Arc::new(unsafe { UPSafeCell::new(PipeRingBuffer::new()) });
let read_end = Arc::new(Pipe::read_end_with_buffer(buffer.clone()));
let write_end = Arc::new(Pipe::write_end_with_buffer(buffer.clone()));
buffer.exclusive_access().set_write_end(&write_end);

@ -1,8 +1,10 @@
use super::File;
use crate::drivers::chardev::{CharDevice, UART};
use crate::mm::UserBuffer;
use crate::sbi::console_getchar;
use crate::task::suspend_current_and_run_next;
pub struct Stdin;
pub struct Stdout;
impl File for Stdin {
@ -14,8 +16,18 @@ impl File for Stdin {
}
fn read(&self, mut user_buf: UserBuffer) -> usize {
assert_eq!(user_buf.len(), 1);
//println!("before UART.read() in Stdin::read()");
let ch = UART.read();
// busy loop
let mut c: usize;
loop {
c = console_getchar();
if c == 0 {
suspend_current_and_run_next();
continue;
} else {
break;
}
}
let ch = c as u8;
unsafe {
user_buf.buffers[0].as_mut_ptr().write_volatile(ch);
}

@ -18,7 +18,7 @@ fn panic(info: &PanicInfo) -> ! {
unsafe {
backtrace();
}
shutdown(255)
shutdown()
}
unsafe fn backtrace() {

@ -29,7 +29,9 @@ mod task;
mod timer;
mod trap;
core::arch::global_asm!(include_str!("entry.asm"));
use core::arch::global_asm;
global_asm!(include_str!("entry.asm"));
fn clear_bss() {
extern "C" {
@ -42,25 +44,19 @@ fn clear_bss() {
}
}
use lazy_static::*;
use sync::UPIntrFreeCell;
lazy_static! {
pub static ref DEV_NON_BLOCKING_ACCESS: UPIntrFreeCell<bool> =
unsafe { UPIntrFreeCell::new(false) };
}
#[no_mangle]
pub fn rust_main() -> ! {
clear_bss();
println!("[kernel] Hello, world!");
mm::init();
mm::remap_test();
trap::init();
trap::enable_timer_interrupt();
timer::set_next_trigger();
board::device_init();
task::stackless_coroutine::kernel_stackless_coroutine_test();
fs::list_apps();
task::kernel_stackful_coroutine_test();
task::add_initproc();
*DEV_NON_BLOCKING_ACCESS.exclusive_access() = true;
task::run_tasks();
panic!("Unreachable in rust_main!");
}

@ -83,11 +83,7 @@ impl From<PhysPageNum> for usize {
}
impl From<VirtAddr> for usize {
fn from(v: VirtAddr) -> Self {
if v.0 >= (1 << (VA_WIDTH_SV39 - 1)) {
v.0 | (!((1 << VA_WIDTH_SV39) - 1))
} else {
v.0
}
v.0
}
}
impl From<VirtPageNum> for usize {

@ -1,10 +1,11 @@
use super::{PhysAddr, PhysPageNum};
use crate::config::MEMORY_END;
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use alloc::vec::Vec;
use core::fmt::{self, Debug, Formatter};
use lazy_static::*;
#[derive(Clone)]
pub struct FrameTracker {
pub ppn: PhysPageNum,
}
@ -83,8 +84,8 @@ impl FrameAllocator for StackFrameAllocator {
type FrameAllocatorImpl = StackFrameAllocator;
lazy_static! {
pub static ref FRAME_ALLOCATOR: UPIntrFreeCell<FrameAllocatorImpl> =
unsafe { UPIntrFreeCell::new(FrameAllocatorImpl::new()) };
pub static ref FRAME_ALLOCATOR: UPSafeCell<FrameAllocatorImpl> =
unsafe { UPSafeCell::new(FrameAllocatorImpl::new()) };
}
pub fn init_frame_allocator() {

@ -3,7 +3,7 @@ use super::{PTEFlags, PageTable, PageTableEntry};
use super::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum};
use super::{StepByOne, VPNRange};
use crate::config::{MEMORY_END, MMIO, PAGE_SIZE, TRAMPOLINE};
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use alloc::collections::BTreeMap;
use alloc::sync::Arc;
use alloc::vec::Vec;
@ -25,8 +25,8 @@ extern "C" {
}
lazy_static! {
pub static ref KERNEL_SPACE: Arc<UPIntrFreeCell<MemorySet>> =
Arc::new(unsafe { UPIntrFreeCell::new(MemorySet::new_kernel()) });
pub static ref KERNEL_SPACE: Arc<UPSafeCell<MemorySet>> =
Arc::new(unsafe { UPSafeCell::new(MemorySet::new_kernel()) });
}
pub fn kernel_token() -> usize {
@ -242,8 +242,17 @@ impl MemorySet {
//*self = Self::new_bare();
self.areas.clear();
}
pub fn kernel_copy() -> Self {
let areas = KERNEL_SPACE.exclusive_access().areas.clone();
Self {
page_table: PageTable::from_token(kernel_token()),
areas: areas,
}
}
}
#[derive(Clone)]
pub struct MapArea {
vpn_range: VPNRange,
data_frames: BTreeMap<VirtPageNum, FrameTracker>,
@ -351,20 +360,26 @@ pub fn remap_test() {
let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into();
let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into();
let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into();
assert!(!kernel_space
.page_table
.translate(mid_text.floor())
.unwrap()
.writable(),);
assert!(!kernel_space
.page_table
.translate(mid_rodata.floor())
.unwrap()
.writable(),);
assert!(!kernel_space
.page_table
.translate(mid_data.floor())
.unwrap()
.executable(),);
assert!(
!kernel_space
.page_table
.translate(mid_text.floor())
.unwrap()
.writable(),
);
assert!(
!kernel_space
.page_table
.translate(mid_rodata.floor())
.unwrap()
.writable(),
);
assert!(
!kernel_space
.page_table
.translate(mid_data.floor())
.unwrap()
.executable(),
);
println!("remap_test passed!");
}

@ -16,7 +16,7 @@ const SBI_SHUTDOWN: usize = 8;
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let mut ret;
unsafe {
core::arch::asm!(
asm!(
"ecall",
inlateout("x10") arg0 => ret,
in("x11") arg1,
@ -39,9 +39,7 @@ pub fn console_getchar() -> usize {
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
}
use crate::board::QEMUExit;
pub fn shutdown(exit_code: usize) -> ! {
//sbi_call(SBI_SHUTDOWN, exit_code, 0, 0);
crate::board::QEMU_EXIT_HANDLE.exit_failure();
pub fn shutdown() -> ! {
sbi_call(SBI_SHUTDOWN, 0, 0, 0);
panic!("It should shutdown!");
}

@ -1,12 +1,9 @@
use crate::sync::{Mutex, UPIntrFreeCell};
use crate::task::{
add_task, block_current_and_run_next, block_current_task, current_task, TaskContext,
TaskControlBlock,
};
use crate::sync::{Mutex, UPSafeCell};
use crate::task::{add_task, block_current_and_run_next, current_task, TaskControlBlock};
use alloc::{collections::VecDeque, sync::Arc};
pub struct Condvar {
pub inner: UPIntrFreeCell<CondvarInner>,
pub inner: UPSafeCell<CondvarInner>,
}
pub struct CondvarInner {
@ -17,7 +14,7 @@ impl Condvar {
pub fn new() -> Self {
Self {
inner: unsafe {
UPIntrFreeCell::new(CondvarInner {
UPSafeCell::new(CondvarInner {
wait_queue: VecDeque::new(),
})
},
@ -31,28 +28,12 @@ impl Condvar {
}
}
/*
pub fn wait(&self) {
pub fn wait(&self, mutex: Arc<dyn Mutex>) {
mutex.unlock();
let mut inner = self.inner.exclusive_access();
inner.wait_queue.push_back(current_task().unwrap());
drop(inner);
block_current_and_run_next();
}
*/
pub fn wait_no_sched(&self) -> *mut TaskContext {
self.inner.exclusive_session(|inner| {
inner.wait_queue.push_back(current_task().unwrap());
});
block_current_task()
}
pub fn wait_with_mutex(&self, mutex: Arc<dyn Mutex>) {
mutex.unlock();
self.inner.exclusive_session(|inner| {
inner.wait_queue.push_back(current_task().unwrap());
});
block_current_and_run_next();
mutex.lock();
}
}

@ -6,4 +6,4 @@ mod up;
pub use condvar::Condvar;
pub use mutex::{Mutex, MutexBlocking, MutexSpin};
pub use semaphore::Semaphore;
pub use up::{UPIntrFreeCell, UPIntrRefMut};
pub use up::UPSafeCell;

@ -1,4 +1,4 @@
use super::UPIntrFreeCell;
use super::UPSafeCell;
use crate::task::TaskControlBlock;
use crate::task::{add_task, current_task};
use crate::task::{block_current_and_run_next, suspend_current_and_run_next};
@ -10,13 +10,13 @@ pub trait Mutex: Sync + Send {
}
pub struct MutexSpin {
locked: UPIntrFreeCell<bool>,
locked: UPSafeCell<bool>,
}
impl MutexSpin {
pub fn new() -> Self {
Self {
locked: unsafe { UPIntrFreeCell::new(false) },
locked: unsafe { UPSafeCell::new(false) },
}
}
}
@ -43,7 +43,7 @@ impl Mutex for MutexSpin {
}
pub struct MutexBlocking {
inner: UPIntrFreeCell<MutexBlockingInner>,
inner: UPSafeCell<MutexBlockingInner>,
}
pub struct MutexBlockingInner {
@ -55,7 +55,7 @@ impl MutexBlocking {
pub fn new() -> Self {
Self {
inner: unsafe {
UPIntrFreeCell::new(MutexBlockingInner {
UPSafeCell::new(MutexBlockingInner {
locked: false,
wait_queue: VecDeque::new(),
})

@ -1,9 +1,9 @@
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use crate::task::{add_task, block_current_and_run_next, current_task, TaskControlBlock};
use alloc::{collections::VecDeque, sync::Arc};
pub struct Semaphore {
pub inner: UPIntrFreeCell<SemaphoreInner>,
pub inner: UPSafeCell<SemaphoreInner>,
}
pub struct SemaphoreInner {
@ -15,7 +15,7 @@ impl Semaphore {
pub fn new(res_count: usize) -> Self {
Self {
inner: unsafe {
UPIntrFreeCell::new(SemaphoreInner {
UPSafeCell::new(SemaphoreInner {
count: res_count as isize,
wait_queue: VecDeque::new(),
})

@ -1,9 +1,5 @@
use core::cell::{RefCell, RefMut, UnsafeCell};
use core::ops::{Deref, DerefMut};
use lazy_static::*;
use riscv::register::sstatus;
use core::cell::{RefCell, RefMut};
/*
/// Wrap a static data structure inside it so that we are
/// able to access it without any `unsafe`.
///
@ -31,110 +27,3 @@ impl<T> UPSafeCell<T> {
self.inner.borrow_mut()
}
}
*/
pub struct UPSafeCellRaw<T> {
inner: UnsafeCell<T>,
}
unsafe impl<T> Sync for UPSafeCellRaw<T> {}
impl<T> UPSafeCellRaw<T> {
pub unsafe fn new(value: T) -> Self {
Self {
inner: UnsafeCell::new(value),
}
}
pub fn get_mut(&self) -> &mut T {
unsafe { &mut (*self.inner.get()) }
}
}
pub struct IntrMaskingInfo {
nested_level: usize,
sie_before_masking: bool,
}
lazy_static! {
static ref INTR_MASKING_INFO: UPSafeCellRaw<IntrMaskingInfo> =
unsafe { UPSafeCellRaw::new(IntrMaskingInfo::new()) };
}
impl IntrMaskingInfo {
pub fn new() -> Self {
Self {
nested_level: 0,
sie_before_masking: false,
}
}
pub fn enter(&mut self) {
let sie = sstatus::read().sie();
unsafe {
sstatus::clear_sie();
}
if self.nested_level == 0 {
self.sie_before_masking = sie;
}
self.nested_level += 1;
}
pub fn exit(&mut self) {
self.nested_level -= 1;
if self.nested_level == 0 && self.sie_before_masking {
unsafe {
sstatus::set_sie();
}
}
}
}
pub struct UPIntrFreeCell<T> {
/// inner data
inner: RefCell<T>,
}
unsafe impl<T> Sync for UPIntrFreeCell<T> {}
pub struct UPIntrRefMut<'a, T>(Option<RefMut<'a, T>>);
impl<T> UPIntrFreeCell<T> {
pub unsafe fn new(value: T) -> Self {
Self {
inner: RefCell::new(value),
}
}
/// Panic if the data has been borrowed.
pub fn exclusive_access(&self) -> UPIntrRefMut<'_, T> {
INTR_MASKING_INFO.get_mut().enter();
UPIntrRefMut(Some(self.inner.borrow_mut()))
}
pub fn exclusive_session<F, V>(&self, f: F) -> V
where
F: FnOnce(&mut T) -> V,
{
let mut inner = self.exclusive_access();
f(inner.deref_mut())
}
}
impl<'a, T> Drop for UPIntrRefMut<'a, T> {
fn drop(&mut self) {
self.0 = None;
INTR_MASKING_INFO.get_mut().exit();
}
}
impl<'a, T> Deref for UPIntrRefMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.as_ref().unwrap().deref()
}
}
impl<'a, T> DerefMut for UPIntrRefMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut().unwrap().deref_mut()
}
}

@ -129,6 +129,6 @@ pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize {
let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap());
let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap());
drop(process_inner);
condvar.wait_with_mutex(mutex);
condvar.wait(mutex);
0
}

@ -2,9 +2,9 @@ use crate::trap::trap_return;
#[repr(C)]
pub struct TaskContext {
ra: usize,
sp: usize,
s: [usize; 12],
pub ra: usize,
pub sp: usize,
pub s: [usize; 12],
}
impl TaskContext {
@ -15,6 +15,7 @@ impl TaskContext {
s: [0; 12],
}
}
pub fn goto_trap_return(kstack_ptr: usize) -> Self {
Self {
ra: trap_return as usize,

@ -1,7 +1,7 @@
use super::ProcessControlBlock;
use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT_BASE, USER_STACK_SIZE};
use crate::mm::{MapPermission, PhysPageNum, VirtAddr, KERNEL_SPACE};
use crate::sync::UPIntrFreeCell;
use crate::mm::{MapPermission, PhysPageNum, VirtAddr, KERNEL_SPACE, PhysAddr};
use crate::sync::UPSafeCell;
use alloc::{
sync::{Arc, Weak},
vec::Vec,
@ -40,14 +40,12 @@ impl RecycleAllocator {
}
lazy_static! {
static ref PID_ALLOCATOR: UPIntrFreeCell<RecycleAllocator> =
unsafe { UPIntrFreeCell::new(RecycleAllocator::new()) };
static ref KSTACK_ALLOCATOR: UPIntrFreeCell<RecycleAllocator> =
unsafe { UPIntrFreeCell::new(RecycleAllocator::new()) };
static ref PID_ALLOCATOR: UPSafeCell<RecycleAllocator> =
unsafe { UPSafeCell::new(RecycleAllocator::new()) };
static ref KSTACK_ALLOCATOR: UPSafeCell<RecycleAllocator> =
unsafe { UPSafeCell::new(RecycleAllocator::new()) };
}
pub const IDLE_PID: usize = 0;
pub struct PidHandle(pub usize);
pub fn pid_alloc() -> PidHandle {
@ -72,6 +70,7 @@ pub struct KernelStack(pub usize);
pub fn kstack_alloc() -> KernelStack {
let kstack_id = KSTACK_ALLOCATOR.exclusive_access().alloc();
let (kstack_bottom, kstack_top) = kernel_stack_position(kstack_id);
//println!("kstack_alloc kstack_bottom: {:#x?}, kstack_top: {:#x?}", kstack_bottom, kstack_top);
KERNEL_SPACE.exclusive_access().insert_framed_area(
kstack_bottom.into(),
kstack_top.into(),
@ -84,6 +83,8 @@ impl Drop for KernelStack {
fn drop(&mut self) {
let (kernel_stack_bottom, _) = kernel_stack_position(self.0);
let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into();
// let kernel_stack_bottom_pa: PhysAddr = kernel_stack_bottom.into();
// println!("kstack_drop kstack_bottom: va: {:#x?}, pa: {:#x?}", kernel_stack_bottom_va, kernel_stack_bottom_pa);
KERNEL_SPACE
.exclusive_access()
.remove_area_with_start_vpn(kernel_stack_bottom_va.into());
@ -223,3 +224,41 @@ impl Drop for TaskUserRes {
self.dealloc_user_res();
}
}
use alloc::alloc::{alloc, dealloc, Layout};
#[derive(Clone)]
pub struct KStack(usize);
const STACK_SIZE: usize = 0x8000;
impl KStack {
pub fn new() -> KStack {
let bottom =
unsafe {
alloc(Layout::from_size_align(STACK_SIZE, STACK_SIZE).unwrap())
} as usize;
KStack(bottom)
}
pub fn top(&self) -> usize {
self.0 + STACK_SIZE
}
}
use core::fmt::{self, Debug, Formatter};
impl Debug for KStack {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("KStack:{:#x}", self.0))
}
}
impl Drop for KStack {
fn drop(&mut self) {
unsafe {
dealloc(
self.0 as _,
Layout::from_size_align(STACK_SIZE, STACK_SIZE).unwrap()
);
}
}
}

@ -1,5 +1,5 @@
use super::{ProcessControlBlock, TaskControlBlock};
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use alloc::collections::{BTreeMap, VecDeque};
use alloc::sync::Arc;
use lazy_static::*;
@ -24,10 +24,10 @@ impl TaskManager {
}
lazy_static! {
pub static ref TASK_MANAGER: UPIntrFreeCell<TaskManager> =
unsafe { UPIntrFreeCell::new(TaskManager::new()) };
pub static ref PID2PCB: UPIntrFreeCell<BTreeMap<usize, Arc<ProcessControlBlock>>> =
unsafe { UPIntrFreeCell::new(BTreeMap::new()) };
pub static ref TASK_MANAGER: UPSafeCell<TaskManager> =
unsafe { UPSafeCell::new(TaskManager::new()) };
pub static ref PID2PCB: UPSafeCell<BTreeMap<usize, Arc<ProcessControlBlock>>> =
unsafe { UPSafeCell::new(BTreeMap::new()) };
}
pub fn add_task(task: Arc<TaskControlBlock>) {

@ -5,19 +5,19 @@ mod process;
mod processor;
mod signal;
mod switch;
pub mod stackless_coroutine;
#[allow(clippy::module_inception)]
mod task;
use self::id::TaskUserRes;
use crate::fs::{open_file, OpenFlags};
use alloc::{sync::Arc, vec::Vec};
use alloc::sync::Arc;
use lazy_static::*;
use manager::fetch_task;
use process::ProcessControlBlock;
use switch::__switch;
pub use context::TaskContext;
pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle, IDLE_PID};
pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle};
pub use manager::{add_task, pid2process, remove_from_pid2process};
pub use processor::{
current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va,
@ -26,6 +26,9 @@ pub use processor::{
pub use signal::SignalFlags;
pub use task::{TaskControlBlock, TaskStatus};
pub use stackless_coroutine::kernel_stackless_coroutine_test;
pub fn suspend_current_and_run_next() {
// There must be an application running.
let task = take_current_task().unwrap();
@ -44,21 +47,15 @@ pub fn suspend_current_and_run_next() {
schedule(task_cx_ptr);
}
/// This function must be followed by a schedule
pub fn block_current_task() -> *mut TaskContext {
pub fn block_current_and_run_next() {
let task = take_current_task().unwrap();
let mut task_inner = task.inner_exclusive_access();
let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
task_inner.task_status = TaskStatus::Blocking;
&mut task_inner.task_cx as *mut TaskContext
}
pub fn block_current_and_run_next() {
let task_cx_ptr = block_current_task();
drop(task_inner);
schedule(task_cx_ptr);
}
use crate::board::QEMUExit;
pub fn exit_current_and_run_next(exit_code: i32) {
let task = take_current_task().unwrap();
let mut task_inner = task.inner_exclusive_access();
@ -74,21 +71,7 @@ pub fn exit_current_and_run_next(exit_code: i32) {
// however, if this is the main thread of current process
// the process should terminate at once
if tid == 0 {
let pid = process.getpid();
if pid == IDLE_PID {
println!(
"[kernel] Idle process exit with exit_code {} ...",
exit_code
);
if exit_code != 0 {
//crate::sbi::shutdown(255); //255 == -1 for err hint
crate::board::QEMU_EXIT_HANDLE.exit_failure();
} else {
//crate::sbi::shutdown(0); //0 for success hint
crate::board::QEMU_EXIT_HANDLE.exit_success();
}
}
remove_from_pid2process(pid);
remove_from_pid2process(process.getpid());
let mut process_inner = process.inner_exclusive_access();
// mark this process as a zombie process
process_inner.is_zombie = true;
@ -107,21 +90,12 @@ pub fn exit_current_and_run_next(exit_code: i32) {
// deallocate user res (including tid/trap_cx/ustack) of all threads
// it has to be done before we dealloc the whole memory_set
// otherwise they will be deallocated twice
let mut recycle_res = Vec::<TaskUserRes>::new();
for task in process_inner.tasks.iter().filter(|t| t.is_some()) {
let task = task.as_ref().unwrap();
let mut task_inner = task.inner_exclusive_access();
if let Some(res) = task_inner.res.take() {
recycle_res.push(res);
}
task_inner.res = None;
}
// dealloc_tid and dealloc_user_res require access to PCB inner, so we
// need to collect those user res first, then release process_inner
// for now to avoid deadlock/double borrow problem.
drop(process_inner);
recycle_res.clear();
let mut process_inner = process.inner_exclusive_access();
process_inner.children.clear();
// deallocate other data in user space i.e. program code/data section
process_inner.memory_set.recycle_data_pages();
@ -157,3 +131,82 @@ pub fn current_add_signal(signal: SignalFlags) {
let mut process_inner = process.inner_exclusive_access();
process_inner.signals |= signal;
}
#[no_mangle]
pub fn kthread_create(f: fn()) {
println!("kthread_create");
// create kernel thread
let new_tcb = TaskControlBlock::create_kthread(f);
// let kernel_stack = new_tcb.get_kernel_stack();
let new_task = Arc::new(new_tcb);
// add kernel thread into TASK_MANAGER
// println!("add task");
add_task(Arc::clone(&new_task));
}
#[no_mangle]
pub fn kernel_stackful_coroutine_test() {
println!("kernel_stackful_coroutine_test");
kthread_create( ||
{
let id = 1;
println!("kernel thread {:?} STARTING", id);
for i in 0..10 {
println!("kernel thread: {} counter: {}", id, i);
}
println!("kernel thread {:?} FINISHED", id);
kthread_stop();
}
);
kthread_create( ||
{
let id = 2;
println!("kernel thread {:?} STARTING", 2);
for i in 0..10 {
println!("kernel thread: {} counter: {}", 2, i);
kthread_yield();
}
println!("kernel thread {:?} FINISHED", 2);
kthread_stop();
}
);
kthread_create( ||
{
let id = 3;
println!("kernel thread {:?} STARTING", 3);
for i in 0..10 {
println!("kernel thread: {} counter: {}", 3, i);
kthread_yield();
}
println!("kernel thread {:?} FINISHED", 3);
kthread_stop();
}
);
}
pub fn kthread_stop(){
do_exit();
}
#[no_mangle]
pub fn do_exit(){
println!("kthread do exit");
exit_kthread_and_run_next(0);
panic!("Unreachable in sys_exit!");
}
pub fn kthread_yield(){
suspend_current_and_run_next();
}
#[no_mangle]
pub fn exit_kthread_and_run_next(exit_code: i32) {
println!("exit_kthread_and_run_next");
// we do not have to save task context
let mut _unused = TaskContext::zero_init();
schedule(&mut _unused as *mut _);
}

@ -5,18 +5,19 @@ use super::{add_task, SignalFlags};
use super::{pid_alloc, PidHandle};
use crate::fs::{File, Stdin, Stdout};
use crate::mm::{translated_refmut, MemorySet, KERNEL_SPACE};
use crate::sync::{Condvar, Mutex, Semaphore, UPIntrFreeCell, UPIntrRefMut};
use crate::sync::{Condvar, Mutex, Semaphore, UPSafeCell};
use crate::trap::{trap_handler, TrapContext};
use alloc::string::String;
use alloc::sync::{Arc, Weak};
use alloc::vec;
use alloc::vec::Vec;
use core::cell::RefMut;
pub struct ProcessControlBlock {
// immutable
pub pid: PidHandle,
// mutable
inner: UPIntrFreeCell<ProcessControlBlockInner>,
inner: UPSafeCell<ProcessControlBlockInner>,
}
pub struct ProcessControlBlockInner {
@ -67,7 +68,7 @@ impl ProcessControlBlockInner {
}
impl ProcessControlBlock {
pub fn inner_exclusive_access(&self) -> UPIntrRefMut<'_, ProcessControlBlockInner> {
pub fn inner_exclusive_access(&self) -> RefMut<'_, ProcessControlBlockInner> {
self.inner.exclusive_access()
}
@ -79,7 +80,7 @@ impl ProcessControlBlock {
let process = Arc::new(Self {
pid: pid_handle,
inner: unsafe {
UPIntrFreeCell::new(ProcessControlBlockInner {
UPSafeCell::new(ProcessControlBlockInner {
is_zombie: false,
memory_set,
parent: None,
@ -205,7 +206,7 @@ impl ProcessControlBlock {
let child = Arc::new(Self {
pid,
inner: unsafe {
UPIntrFreeCell::new(ProcessControlBlockInner {
UPSafeCell::new(ProcessControlBlockInner {
is_zombie: false,
memory_set,
parent: Some(Arc::downgrade(self)),
@ -255,4 +256,30 @@ impl ProcessControlBlock {
pub fn getpid(&self) -> usize {
self.pid.0
}
pub fn kernel_process() -> Arc<Self>{
let memory_set = MemorySet::kernel_copy();
let process = Arc::new(
ProcessControlBlock {
pid: super::pid_alloc(),
inner: unsafe {
UPSafeCell::new(
ProcessControlBlockInner {
is_zombie: false,
memory_set: memory_set,
parent: None,
children: Vec::new(),
exit_code: 0,
fd_table: Vec::new(),
signals: SignalFlags::empty(),
tasks: Vec::new(),
task_res_allocator: RecycleAllocator::new(),
mutex_list: Vec::new(),
semaphore_list: Vec::new(),
condvar_list: Vec::new(),
})
},
});
process
}
}

@ -1,7 +1,7 @@
use super::__switch;
use super::{fetch_task, TaskStatus};
use super::{ProcessControlBlock, TaskContext, TaskControlBlock};
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use crate::trap::TrapContext;
use alloc::sync::Arc;
use lazy_static::*;
@ -30,8 +30,7 @@ impl Processor {
}
lazy_static! {
pub static ref PROCESSOR: UPIntrFreeCell<Processor> =
unsafe { UPIntrFreeCell::new(Processor::new()) };
pub static ref PROCESSOR: UPSafeCell<Processor> = unsafe { UPSafeCell::new(Processor::new()) };
}
pub fn run_tasks() {
@ -40,10 +39,11 @@ pub fn run_tasks() {
if let Some(task) = fetch_task() {
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
// access coming task TCB exclusively
let next_task_cx_ptr = task.inner.exclusive_session(|task_inner| {
task_inner.task_status = TaskStatus::Running;
&task_inner.task_cx as *const TaskContext
});
let mut task_inner = task.inner_exclusive_access();
let next_task_cx_ptr = &task_inner.task_cx as *const TaskContext;
task_inner.task_status = TaskStatus::Running;
drop(task_inner);
// release coming task TCB manually
processor.current = Some(task);
// release processor manually
drop(processor);
@ -95,8 +95,9 @@ pub fn current_kstack_top() -> usize {
}
pub fn schedule(switched_task_cx_ptr: *mut TaskContext) {
let idle_task_cx_ptr =
PROCESSOR.exclusive_session(|processor| processor.get_idle_task_cx_ptr());
let mut processor = PROCESSOR.exclusive_access();
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
drop(processor);
unsafe {
__switch(switched_task_cx_ptr, idle_task_cx_ptr);
}

@ -0,0 +1,121 @@
// https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/
// https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::task::{RawWaker, RawWakerVTable, Waker};
extern crate alloc;
use alloc::collections::VecDeque;
use alloc::boxed::Box;
enum State {
Halted,
Running,
}
struct Task {
state: State,
}
impl Task {
fn waiter<'a>(&'a mut self) -> Waiter<'a> {
Waiter { task: self }
}
}
struct Waiter<'a> {
task: &'a mut Task,
}
impl<'a> Future for Waiter<'a> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
match self.task.state {
State::Halted => {
self.task.state = State::Running;
Poll::Ready(())
}
State::Running => {
self.task.state = State::Halted;
Poll::Pending
}
}
}
}
struct Executor {
tasks: VecDeque<Pin<Box<dyn Future<Output = ()>>>>,
}
impl Executor {
fn new() -> Self {
Executor {
tasks: VecDeque::new(),
}
}
fn push<C, F>(&mut self, closure: C)
where
F: Future<Output = ()> + 'static,
C: FnOnce(Task) -> F,
{
let task = Task {
state: State::Running,
};
self.tasks.push_back(Box::pin(closure(task)));
}
fn run(&mut self) {
let waker = create_waker();
let mut context = Context::from_waker(&waker);
while let Some(mut task) = self.tasks.pop_front() {
match task.as_mut().poll(&mut context) {
Poll::Pending => {
self.tasks.push_back(task);
}
Poll::Ready(()) => {}
}
}
}
}
pub fn create_waker() -> Waker {
// Safety: The waker points to a vtable with functions that do nothing. Doing
// nothing is memory-safe.
unsafe { Waker::from_raw(RAW_WAKER) }
}
const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE);
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
unsafe fn clone(_: *const ()) -> RawWaker {
RAW_WAKER
}
unsafe fn wake(_: *const ()) {}
unsafe fn wake_by_ref(_: *const ()) {}
unsafe fn drop(_: *const ()) {}
#[no_mangle]
pub fn kernel_stackless_coroutine_test() {
println!("kernel stackless coroutine Begin..");
let mut exec = Executor::new();
println!(" Create futures");
for instance in 1..=3 {
exec.push(move |mut task| async move {
println!(" Kernel Task {}: begin state", instance);
task.waiter().await;
println!(" Kernel Task {}: next state", instance);
task.waiter().await;
println!(" Kernel Task {}: end state", instance);
});
}
println!(" Running");
exec.run();
println!(" Done");
println!("kernel stackless coroutine PASSED");
}

@ -1,22 +1,20 @@
use super::id::TaskUserRes;
use super::{kstack_alloc, KernelStack, ProcessControlBlock, TaskContext};
use crate::trap::TrapContext;
use crate::{
mm::PhysPageNum,
sync::{UPIntrFreeCell, UPIntrRefMut},
};
use crate::{mm::PhysPageNum, sync::UPSafeCell};
use alloc::sync::{Arc, Weak};
use core::cell::RefMut;
pub struct TaskControlBlock {
// immutable
pub process: Weak<ProcessControlBlock>,
pub kstack: KernelStack,
// mutable
pub inner: UPIntrFreeCell<TaskControlBlockInner>,
inner: UPSafeCell<TaskControlBlockInner>,
}
impl TaskControlBlock {
pub fn inner_exclusive_access(&self) -> UPIntrRefMut<'_, TaskControlBlockInner> {
pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> {
self.inner.exclusive_access()
}
@ -60,7 +58,7 @@ impl TaskControlBlock {
process: Arc::downgrade(&process),
kstack,
inner: unsafe {
UPIntrFreeCell::new(TaskControlBlockInner {
UPSafeCell::new(TaskControlBlockInner {
res: Some(res),
trap_cx_ppn,
task_cx: TaskContext::goto_trap_return(kstack_top),
@ -70,6 +68,42 @@ impl TaskControlBlock {
},
}
}
pub fn create_kthread(f: fn()) -> Self{
use crate::mm::{KERNEL_SPACE, PhysPageNum, VirtAddr, PhysAddr};
let process = ProcessControlBlock::kernel_process();
let process = Arc::downgrade(&process);
let kstack = kstack_alloc();
let kernelstack = crate::task::id::KStack::new();
let kstack_top = kernelstack.top();
let mut context = TaskContext::zero_init();
let context_addr = &context as *const TaskContext as usize;
let pa = PhysAddr::from(context_addr);
let context_ppn = pa.floor();
context.ra = f as usize;
context.sp = kstack_top;
//println!("context ppn :{:#x?}", context_ppn);
Self {
process,
kstack:KernelStack(kstack_top),
//kstack,
inner: unsafe {
UPSafeCell::new(TaskControlBlockInner {
res: None,
trap_cx_ppn: context_ppn,
task_cx: context,
task_status: TaskStatus::Ready,
exit_code: None,
})
},
}
}
}
#[derive(Copy, Clone, PartialEq)]

@ -2,7 +2,7 @@ use core::cmp::Ordering;
use crate::config::CLOCK_FREQ;
use crate::sbi::set_timer;
use crate::sync::UPIntrFreeCell;
use crate::sync::UPSafeCell;
use crate::task::{add_task, TaskControlBlock};
use alloc::collections::BinaryHeap;
use alloc::sync::Arc;
@ -50,8 +50,8 @@ impl Ord for TimerCondVar {
}
lazy_static! {
static ref TIMERS: UPIntrFreeCell<BinaryHeap<TimerCondVar>> =
unsafe { UPIntrFreeCell::new(BinaryHeap::<TimerCondVar>::new()) };
static ref TIMERS: UPSafeCell<BinaryHeap<TimerCondVar>> =
unsafe { UPSafeCell::new(BinaryHeap::<TimerCondVar>::new()) };
}
pub fn add_timer(expire_ms: usize, task: Arc<TaskControlBlock>) {

@ -11,7 +11,7 @@ use core::arch::{asm, global_asm};
use riscv::register::{
mtvec::TrapMode,
scause::{self, Exception, Interrupt, Trap},
sie, sscratch, sstatus, stval, stvec,
sie, stval, stvec,
};
global_asm!(include_str!("trap.S"));
@ -21,14 +21,8 @@ pub fn init() {
}
fn set_kernel_trap_entry() {
extern "C" {
fn __alltraps();
fn __alltraps_k();
}
let __alltraps_k_va = __alltraps_k as usize - __alltraps as usize + TRAMPOLINE;
unsafe {
stvec::write(__alltraps_k_va, TrapMode::Direct);
sscratch::write(trap_from_kernel as usize);
stvec::write(trap_from_kernel as usize, TrapMode::Direct);
}
}
@ -44,32 +38,16 @@ pub fn enable_timer_interrupt() {
}
}
fn enable_supervisor_interrupt() {
unsafe {
sstatus::set_sie();
}
}
fn disable_supervisor_interrupt() {
unsafe {
sstatus::clear_sie();
}
}
#[no_mangle]
pub fn trap_handler() -> ! {
set_kernel_trap_entry();
let scause = scause::read();
let stval = stval::read();
//println!("into {:?}", scause.cause());
match scause.cause() {
Trap::Exception(Exception::UserEnvCall) => {
// jump to next instruction anyway
let mut cx = current_trap_cx();
cx.sepc += 4;
enable_supervisor_interrupt();
// get system call return value
let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]);
// cx is changed during sys_exec, so we have to call it again
@ -100,9 +78,6 @@ pub fn trap_handler() -> ! {
check_timer();
suspend_current_and_run_next();
}
Trap::Interrupt(Interrupt::SupervisorExternal) => {
crate::board::irq_handler();
}
_ => {
panic!(
"Unsupported trap {:?}, stval = {:#x}!",
@ -121,7 +96,6 @@ pub fn trap_handler() -> ! {
#[no_mangle]
pub fn trap_return() -> ! {
disable_supervisor_interrupt();
set_user_trap_entry();
let trap_cx_user_va = current_trap_cx_user_va();
let user_satp = current_user_token();
@ -130,7 +104,6 @@ pub fn trap_return() -> ! {
fn __restore();
}
let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
//println!("before return");
unsafe {
asm!(
"fence.i",
@ -144,26 +117,10 @@ pub fn trap_return() -> ! {
}
#[no_mangle]
pub fn trap_from_kernel(_trap_cx: &TrapContext) {
let scause = scause::read();
let stval = stval::read();
match scause.cause() {
Trap::Interrupt(Interrupt::SupervisorExternal) => {
crate::board::irq_handler();
}
Trap::Interrupt(Interrupt::SupervisorTimer) => {
set_next_trigger();
check_timer();
// do not schedule now
}
_ => {
panic!(
"Unsupported trap from kernel: {:?}, stval = {:#x}!",
scause.cause(),
stval
);
}
}
pub fn trap_from_kernel() -> ! {
use riscv::register::sepc;
println!("stval = {:#x}, sepc = {:#x}", stval::read(), sepc::read());
panic!("a trap {:?} from kernel!", scause::read().cause());
}
pub use context::TrapContext;

@ -8,8 +8,6 @@
.section .text.trampoline
.globl __alltraps
.globl __restore
.globl __alltraps_k
.globl __restore_k
.align 2
__alltraps:
csrrw sp, sscratch, sp
@ -69,36 +67,3 @@ __restore:
# back to user stack
ld sp, 2*8(sp)
sret
.align 2
__alltraps_k:
addi sp, sp, -34*8
sd x1, 1*8(sp)
sd x3, 3*8(sp)
.set n, 5
.rept 27
SAVE_GP %n
.set n, n+1
.endr
csrr t0, sstatus
csrr t1, sepc
sd t0, 32*8(sp)
sd t1, 33*8(sp)
mv a0, sp
csrr t2, sscratch
jalr t2
__restore_k:
ld t0, 32*8(sp)
ld t1, 33*8(sp)
csrw sstatus, t0
csrw sepc, t1
ld x1, 1*8(sp)
ld x3, 3*8(sp)
.set n, 5
.rept 27
LOAD_GP %n
.set n, n+1
.endr
addi sp, sp, 34*8
sret

@ -1,2 +0,0 @@
export PATH=$(rustc --print sysroot)/bin:$PATH
export RUST_SRC_PATH=$(rustc --print sysroot)/lib/rustlib/src/rust/library/

@ -8,15 +8,9 @@ BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
OBJDUMP := rust-objdump --arch-name=riscv64
OBJCOPY := rust-objcopy --binary-architecture=riscv64
CP := cp
TEST ?=
elf: $(APPS)
@cargo build --release
ifeq ($(TEST), 1)
@$(CP) $(TARGET_DIR)/usertests $(TARGET_DIR)/initproc
endif
binary: elf
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)

@ -9,14 +9,10 @@ use user_lib::{close, open, read, OpenFlags};
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
println!("argc = {}", argc);
for (i, arg) in argv.iter().enumerate() {
println!("argv[{}] = {}", i, arg);
}
assert!(argc == 2);
let fd = open(argv[1], OpenFlags::RDONLY);
if fd == -1 {
panic!("Error occurred when opening file");
panic!("Error occured when opening file");
}
let fd = fd as usize;
let mut buf = [0u8; 256];

@ -0,0 +1,22 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use user_lib::{thread_create, exit};
use alloc::vec::Vec;
pub fn thread_a() -> ! {
for i in 0..1000 { print!("{}", i); }
exit(1)
}
#[no_mangle]
pub fn main() -> i32 {
thread_create(thread_a as usize, 0);
println!("main thread exited.");
exit(0)
}

@ -1,138 +0,0 @@
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#[macro_use]
extern crate user_lib;
extern crate alloc;
extern crate core;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicUsize, Ordering};
use user_lib::{exit, sleep, thread_create, waittid};
const N: usize = 2;
const THREAD_NUM: usize = 10;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FlagState {
Out,
Want,
In,
}
static mut TURN: usize = 0;
static mut FLAG: [FlagState; THREAD_NUM] = [FlagState::Out; THREAD_NUM];
static GUARD: AtomicUsize = AtomicUsize::new(0);
fn critical_test_enter() {
assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0);
}
fn critical_test_claim() {
assert_eq!(GUARD.load(Ordering::SeqCst), 1);
}
fn critical_test_exit() {
assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1);
}
fn eisenberg_enter_critical(id: usize) {
/* announce that we want to enter */
loop {
println!("Thread[{}] try enter", id);
vstore!(&FLAG[id], FlagState::Want);
loop {
/* check if any with higher priority is `Want` or `In` */
let mut prior_thread: Option<usize> = None;
let turn = vload!(&TURN);
let ring_id = if id < turn { id + THREAD_NUM } else { id };
// FLAG.iter() may lead to some errors, use for-loop instead
for i in turn..ring_id {
if vload!(&FLAG[i % THREAD_NUM]) != FlagState::Out {
prior_thread = Some(i % THREAD_NUM);
break;
}
}
if prior_thread.is_none() {
break;
}
println!(
"Thread[{}]: prior thread {} exist, sleep and retry",
id,
prior_thread.unwrap()
);
sleep(1);
}
/* now tentatively claim the resource */
vstore!(&FLAG[id], FlagState::In);
/* enforce the order of `claim` and `conflict check`*/
memory_fence!();
/* check if anthor thread is also `In`, which imply a conflict*/
let mut conflict = false;
for i in 0..THREAD_NUM {
if i != id && vload!(&FLAG[i]) == FlagState::In {
conflict = true;
}
}
if !conflict {
break;
}
println!("Thread[{}]: CONFLECT!", id);
/* no need to sleep */
}
/* clain the trun */
vstore!(&TURN, id);
println!("Thread[{}] enter", id);
}
fn eisenberg_exit_critical(id: usize) {
/* find next one who wants to enter and give the turn to it*/
let mut next = id;
let ring_id = id + THREAD_NUM;
for i in (id + 1)..ring_id {
let idx = i % THREAD_NUM;
if vload!(&FLAG[idx]) == FlagState::Want {
next = idx;
break;
}
}
vstore!(&TURN, next);
/* All done */
vstore!(&FLAG[id], FlagState::Out);
println!("Thread[{}] exit, give turn to {}", id, next);
}
pub fn thread_fn(id: usize) -> ! {
println!("Thread[{}] init.", id);
for _ in 0..N {
eisenberg_enter_critical(id);
critical_test_enter();
for _ in 0..3 {
critical_test_claim();
sleep(2);
}
critical_test_exit();
eisenberg_exit_critical(id);
}
exit(0)
}
#[no_mangle]
pub fn main() -> i32 {
let mut v = Vec::new();
// TODO: really shuffle
assert_eq!(THREAD_NUM, 10);
let shuffle: [usize; 10] = [0, 7, 4, 6, 2, 9, 8, 1, 3, 5];
for i in 0..THREAD_NUM {
v.push(thread_create(thread_fn as usize, shuffle[i]));
}
for tid in v.iter() {
let exit_code = waittid(*tid as usize);
assert_eq!(exit_code, 0, "thread conflict happened!");
println!("thread#{} exited with code {}", tid, exit_code);
}
println!("main thread exited.");
0
}

@ -4,7 +4,7 @@
#[macro_use]
extern crate user_lib;
use user_lib::{exit, fork, getpid, sleep, wait, yield_};
use user_lib::{exit, fork, getpid, sleep, yield_};
const DEPTH: usize = 4;
@ -27,19 +27,11 @@ fn fork_tree(cur: &str) {
println!("pid{}: {}", getpid(), cur);
fork_child(cur, '0');
fork_child(cur, '1');
let mut exit_code: i32 = 0;
for _ in 0..2 {
wait(&mut exit_code);
}
}
#[no_mangle]
pub fn main() -> i32 {
fork_tree("");
let mut exit_code: i32 = 0;
for _ in 0..2 {
wait(&mut exit_code);
}
sleep(3000);
0
}

@ -24,7 +24,7 @@ pub fn main() -> i32 {
}
close(f);
let time_ms = (get_time() - start) as usize;
let speed_kbs = (size_mb << 20) / time_ms;
let speed_kbs = size_mb * 1000000 / time_ms;
println!(
"{}MiB written, time cost = {}ms, write speed = {}KiB/s",
size_mb, time_ms, speed_kbs

@ -1,56 +0,0 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::{fmt::format, string::String, vec::Vec};
use user_lib::{close, get_time, gettid, open, write, OpenFlags};
use user_lib::{exit, thread_create, waittid};
fn worker(size_kib: usize) {
let mut buffer = [0u8; 1024]; // 1KiB
for (i, ch) in buffer.iter_mut().enumerate() {
*ch = i as u8;
}
let filename = format(format_args!("testf{}\0", gettid()));
let f = open(filename.as_str(), OpenFlags::CREATE | OpenFlags::WRONLY);
if f < 0 {
panic!("Open test file failed!");
}
let f = f as usize;
for _ in 0..size_kib {
write(f, &buffer);
}
close(f);
exit(0)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
assert_eq!(argc, 2, "wrong argument");
let size_mb = 1usize;
let size_kb = size_mb << 10;
let workers = argv[1].parse::<usize>().expect("wrong argument");
assert!(workers >= 1 && size_kb % workers == 0, "wrong argument");
let start = get_time();
let mut v = Vec::new();
let size_mb = 1usize;
for _ in 0..workers {
v.push(thread_create(worker as usize, size_kb / workers));
}
for tid in v.iter() {
assert_eq!(0, waittid(*tid as usize));
}
let time_ms = (get_time() - start) as usize;
let speed_kbs = size_kb * 1000 / time_ms;
println!(
"{}MiB written by {} threads, time cost = {}ms, write speed = {}KiB/s",
size_mb, workers, time_ms, speed_kbs
);
0
}

@ -1,78 +0,0 @@
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#![feature(asm)]
#[macro_use]
extern crate user_lib;
extern crate alloc;
extern crate core;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicUsize, Ordering};
use user_lib::{exit, sleep, thread_create, waittid};
const N: usize = 3;
static mut TURN: usize = 0;
static mut FLAG: [bool; 2] = [false; 2];
static GUARD: AtomicUsize = AtomicUsize::new(0);
fn critical_test_enter() {
assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0);
}
fn critical_test_claim() {
assert_eq!(GUARD.load(Ordering::SeqCst), 1);
}
fn critical_test_exit() {
assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1);
}
fn peterson_enter_critical(id: usize, peer_id: usize) {
println!("Thread[{}] try enter", id);
vstore!(&FLAG[id], true);
vstore!(&TURN, peer_id);
memory_fence!();
while vload!(&FLAG[peer_id]) && vload!(&TURN) == peer_id {
println!("Thread[{}] enter fail", id);
sleep(1);
println!("Thread[{}] retry enter", id);
}
println!("Thread[{}] enter", id);
}
fn peterson_exit_critical(id: usize) {
vstore!(&FLAG[id], false);
println!("Thread[{}] exit", id);
}
pub fn thread_fn(id: usize) -> ! {
println!("Thread[{}] init.", id);
let peer_id: usize = id ^ 1;
for _ in 0..N {
peterson_enter_critical(id, peer_id);
critical_test_enter();
for _ in 0..3 {
critical_test_claim();
sleep(2);
}
critical_test_exit();
peterson_exit_critical(id);
}
exit(0)
}
#[no_mangle]
pub fn main() -> i32 {
let mut v = Vec::new();
v.push(thread_create(thread_fn as usize, 0));
// v.push(thread_create(thread_fn as usize, 1));
for tid in v.iter() {
let exit_code = waittid(*tid as usize);
assert_eq!(exit_code, 0, "thread conflict happened!");
println!("thread#{} exited with code {}", tid, exit_code);
}
println!("main thread exited.");
0
}

@ -34,10 +34,7 @@ pub fn main(argc: usize, argv: &[&str]) -> i32 {
} else if argc == 2 {
count = argv[1].to_string().parse::<usize>().unwrap();
} else {
println!(
"ERROR in argv, argc is {}, argv[0] {} , argv[1] {} , argv[2] {}",
argc, argv[0], argv[1], argv[2]
);
println!("ERROR in argv");
exit(-1);
}

@ -8,7 +8,7 @@ use user_lib::{exec, fork, wait};
#[no_mangle]
pub fn main() -> i32 {
for i in 0..50 {
for i in 0..1000 {
if fork() == 0 {
exec("pipe_large_test\0", &[core::ptr::null::<u8>()]);
} else {

@ -0,0 +1,352 @@
// we porting below codes to Rcore Tutorial v3
// https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/
// https://github.com/cfsamson/example-greenthreads
#![no_std]
#![no_main]
#![feature(naked_functions)]
#![feature(asm)]
extern crate alloc;
#[macro_use]
extern crate user_lib;
use core::arch::asm;
#[macro_use]
use alloc::vec;
use alloc::vec::Vec;
use user_lib::{exit};
// In our simple example we set most constraints here.
const DEFAULT_STACK_SIZE: usize = 4096; //128 got SEGFAULT, 256(1024, 4096) got right results.
const MAX_TASKS: usize = 5;
static mut RUNTIME: usize = 0;
pub struct Runtime {
tasks: Vec<Task>,
current: usize,
}
#[derive(PartialEq, Eq, Debug)]
enum State {
Available,
Running,
Ready,
}
struct Task {
id: usize,
stack: Vec<u8>,
ctx: TaskContext,
state: State,
}
#[derive(Debug, Default)]
#[repr(C)] // not strictly needed but Rust ABI is not guaranteed to be stable
pub struct TaskContext {
// 15 u64
x1: u64, //ra: return addres
x2: u64, //sp
x8: u64, //s0,fp
x9: u64, //s1
x18: u64, //x18-27: s2-11
x19: u64,
x20: u64,
x21: u64,
x22: u64,
x23: u64,
x24: u64,
x25: u64,
x26: u64,
x27: u64,
nx1: u64, //new return addres
}
impl Task {
fn new(id: usize) -> Self {
// We initialize each task here and allocate the stack. This is not neccesary,
// we can allocate memory for it later, but it keeps complexity down and lets us focus on more interesting parts
// to do it here. The important part is that once allocated it MUST NOT move in memory.
Task {
id,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: TaskContext::default(),
state: State::Available,
}
}
}
impl Runtime {
pub fn new() -> Self {
// This will be our base task, which will be initialized in the `running` state
let base_task = Task {
id: 0,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: TaskContext::default(),
state: State::Running,
};
// We initialize the rest of our tasks.
let mut tasks = vec![base_task];
let mut available_tasks: Vec<Task> = (1..MAX_TASKS).map(|i| Task::new(i)).collect();
tasks.append(&mut available_tasks);
Runtime {
tasks,
current: 0,
}
}
/// This is cheating a bit, but we need a pointer to our Runtime stored so we can call yield on it even if
/// we don't have a reference to it.
pub fn init(&self) {
unsafe {
let r_ptr: *const Runtime = self;
RUNTIME = r_ptr as usize;
}
}
/// This is where we start running our runtime. If it is our base task, we call yield until
/// it returns false (which means that there are no tasks scheduled) and we are done.
pub fn run(&mut self){
while self.t_yield() {}
println!("All tasks finished!");
}
/// This is our return function. The only place we use this is in our `guard` function.
/// If the current task is not our base task we set its state to Available. It means
/// we're finished with it. Then we yield which will schedule a new task to be run.
fn t_return(&mut self) {
if self.current != 0 {
self.tasks[self.current].state = State::Available;
self.t_yield();
}
}
/// This is the heart of our runtime. Here we go through all tasks and see if anyone is in the `Ready` state.
/// If no task is `Ready` we're all done. This is an extremely simple scheduler using only a round-robin algorithm.
///
/// If we find a task that's ready to be run we change the state of the current task from `Running` to `Ready`.
/// Then we call switch which will save the current context (the old context) and load the new context
/// into the CPU which then resumes based on the context it was just passed.
///
/// NOITCE: if we comment below `#[inline(never)]`, we can not get the corrent running result
#[inline(never)]
fn t_yield(&mut self) -> bool {
let mut pos = self.current;
while self.tasks[pos].state != State::Ready {
pos += 1;
if pos == self.tasks.len() {
pos = 0;
}
if pos == self.current {
return false;
}
}
if self.tasks[self.current].state != State::Available {
self.tasks[self.current].state = State::Ready;
}
self.tasks[pos].state = State::Running;
let old_pos = self.current;
self.current = pos;
unsafe {
switch(&mut self.tasks[old_pos].ctx, &self.tasks[pos].ctx);
}
// NOTE: this might look strange and it is. Normally we would just mark this as `unreachable!()` but our compiler
// is too smart for it's own good so it optimized our code away on release builds. Curiously this happens on windows
// and not on linux. This is a common problem in tests so Rust has a `black_box` function in the `test` crate that
// will "pretend" to use a value we give it to prevent the compiler from eliminating code. I'll just do this instead,
// this code will never be run anyways and if it did it would always be `true`.
self.tasks.len() > 0
}
/// While `yield` is the logically interesting function I think this the technically most interesting.
///
/// When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state).
/// If we run out of tasks we panic in this scenario but there are several (better) ways to handle that.
/// We keep things simple for now.
///
/// When we find an available task we get the stack length and a pointer to our u8 bytearray.
///
/// The next part we have to use some unsafe functions. First we write an address to our `guard` function
/// that will be called if the function we provide returns. Then we set the address to the function we
/// pass inn.
///
/// Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start
/// executing that first when we are scheuled to run.
///
/// Lastly we set the state as `Ready` which means we have work to do and is ready to do it.
pub fn spawn(&mut self, f: fn()) {
let available = self
.tasks
.iter_mut()
.find(|t| t.state == State::Available)
.expect("no available task.");
let size = available.stack.len();
unsafe {
let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
// make sure our stack itself is 8 byte aligned - it will always
// offset to a lower memory address. Since we know we're at the "high"
// memory address of our allocated space, we know that offsetting to
// a lower one will be a valid address (given that we actually allocated)
// enough space to actually get an aligned pointer in the first place).
let s_ptr = (s_ptr as usize & !7) as *mut u8;
available.ctx.x1 = guard as u64; //ctx.x1 is old return address
available.ctx.nx1 = f as u64; //ctx.nx2 is new return address
available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp
}
available.state = State::Ready;
}
}
/// This is our guard function that we place on top of the stack. All this function does is set the
/// state of our current task and then `yield` which will then schedule a new task to be run.
fn guard() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_return();
};
}
/// We know that Runtime is alive the length of the program and that we only access from one core
/// (so no datarace). We yield execution of the current task by dereferencing a pointer to our
/// Runtime and then calling `t_yield`
pub fn yield_task() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_yield();
};
}
/// So here is our inline Assembly. As you remember from our first example this is just a bit more elaborate where we first
/// read out the values of all the registers we need and then sets all the register values to the register values we
/// saved when we suspended exceution on the "new" task.
///
/// This is essentially all we need to do to save and resume execution.
///
/// Some details about inline assembly.
///
/// The assembly commands in the string literal is called the assemblt template. It is preceeded by
/// zero or up to four segments indicated by ":":
///
/// - First ":" we have our output parameters, this parameters that this function will return.
/// - Second ":" we have the input parameters which is our contexts. We only read from the "new" context
/// but we modify the "old" context saving our registers there (see volatile option below)
/// - Third ":" This our clobber list, this is information to the compiler that these registers can't be used freely
/// - Fourth ":" This is options we can pass inn, Rust has 3: "alignstack", "volatile" and "intel"
///
/// For this to work on windows we need to use "alignstack" where the compiler adds the neccesary padding to
/// make sure our stack is aligned. Since we modify one of our inputs, our assembly has "side effects"
/// therefore we should use the `volatile` option. I **think** this is actually set for us by default
/// when there are no output parameters given (my own assumption after going through the source code)
/// for the `asm` macro, but we should make it explicit anyway.
///
/// One last important part (it will not work without this) is the #[naked] attribute. Basically this lets us have full
/// control over the stack layout since normal functions has a prologue-and epilogue added by the
/// compiler that will cause trouble for us. We avoid this by marking the funtion as "Naked".
/// For this to work on `release` builds we also need to use the `#[inline(never)] attribute or else
/// the compiler decides to inline this function (curiously this currently only happens on Windows).
/// If the function is inlined we get a curious runtime error where it fails when switching back
/// to as saved context and in general our assembly will not work as expected.
///
/// see: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
/// see: https://doc.rust-lang.org/nightly/reference/inline-assembly.html
/// see: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
#[naked]
#[no_mangle]
unsafe fn switch(old: *mut TaskContext, new: *const TaskContext) {
// a0: _old, a1: _new
asm!("
sd x1, 0x00(a0)
sd x2, 0x08(a0)
sd x8, 0x10(a0)
sd x9, 0x18(a0)
sd x18, 0x20(a0)
sd x19, 0x28(a0)
sd x20, 0x30(a0)
sd x21, 0x38(a0)
sd x22, 0x40(a0)
sd x23, 0x48(a0)
sd x24, 0x50(a0)
sd x25, 0x58(a0)
sd x26, 0x60(a0)
sd x27, 0x68(a0)
sd x1, 0x70(a0)
ld x1, 0x00(a1)
ld x2, 0x08(a1)
ld x8, 0x10(a1)
ld x9, 0x18(a1)
ld x18, 0x20(a1)
ld x19, 0x28(a1)
ld x20, 0x30(a1)
ld x21, 0x38(a1)
ld x22, 0x40(a1)
ld x23, 0x48(a1)
ld x24, 0x50(a1)
ld x25, 0x58(a1)
ld x26, 0x60(a1)
ld x27, 0x68(a1)
ld t0, 0x70(a1)
jr t0
", options( noreturn)
);
}
#[no_mangle]
pub fn main() {
println!("stackful_coroutine begin...");
println!("TASK 0(Runtime) STARTING");
let mut runtime = Runtime::new();
runtime.init();
runtime.spawn(|| {
println!("TASK 1 STARTING");
let id = 1;
for i in 0..4 {
println!("task: {} counter: {}", id, i);
yield_task();
}
println!("TASK 1 FINISHED");
});
runtime.spawn(|| {
println!("TASK 2 STARTING");
let id = 2;
for i in 0..8 {
println!("task: {} counter: {}", id, i);
yield_task();
}
println!("TASK 2 FINISHED");
});
runtime.spawn(|| {
println!("TASK 3 STARTING");
let id = 3;
for i in 0..12 {
println!("task: {} counter: {}", id, i);
yield_task();
}
println!("TASK 3 FINISHED");
});
runtime.spawn(|| {
println!("TASK 4 STARTING");
let id = 4;
for i in 0..16 {
println!("task: {} counter: {}", id, i);
yield_task();
}
println!("TASK 4 FINISHED");
});
runtime.run();
println!("stackful_coroutine PASSED");
exit(0);
}

@ -0,0 +1,129 @@
// https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/
// https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86
#![no_std]
#![no_main]
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::task::{RawWaker, RawWakerVTable, Waker};
extern crate alloc;
use alloc::collections::VecDeque;
use alloc::boxed::Box;
#[macro_use]
extern crate user_lib;
enum State {
Halted,
Running,
}
struct Task {
state: State,
}
impl Task {
fn waiter<'a>(&'a mut self) -> Waiter<'a> {
Waiter { task: self }
}
}
struct Waiter<'a> {
task: &'a mut Task,
}
impl<'a> Future for Waiter<'a> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
match self.task.state {
State::Halted => {
self.task.state = State::Running;
Poll::Ready(())
}
State::Running => {
self.task.state = State::Halted;
Poll::Pending
}
}
}
}
struct Executor {
tasks: VecDeque<Pin<Box<dyn Future<Output = ()>>>>,
}
impl Executor {
fn new() -> Self {
Executor {
tasks: VecDeque::new(),
}
}
fn push<C, F>(&mut self, closure: C)
where
F: Future<Output = ()> + 'static,
C: FnOnce(Task) -> F,
{
let task = Task {
state: State::Running,
};
self.tasks.push_back(Box::pin(closure(task)));
}
fn run(&mut self) {
let waker = create_waker();
let mut context = Context::from_waker(&waker);
while let Some(mut task) = self.tasks.pop_front() {
match task.as_mut().poll(&mut context) {
Poll::Pending => {
self.tasks.push_back(task);
}
Poll::Ready(()) => {}
}
}
}
}
pub fn create_waker() -> Waker {
// Safety: The waker points to a vtable with functions that do nothing. Doing
// nothing is memory-safe.
unsafe { Waker::from_raw(RAW_WAKER) }
}
const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE);
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
unsafe fn clone(_: *const ()) -> RawWaker {
RAW_WAKER
}
unsafe fn wake(_: *const ()) {}
unsafe fn wake_by_ref(_: *const ()) {}
unsafe fn drop(_: *const ()) {}
#[no_mangle]
pub fn main() -> i32 {
println!("stackless coroutine Begin..");
let mut exec = Executor::new();
println!(" Create futures");
for instance in 1..=3 {
exec.push(move |mut task| async move {
println!(" Task {}: begin state", instance);
task.waiter().await;
println!(" Task {}: next state", instance);
task.waiter().await;
println!(" Task {}: end state", instance);
});
}
println!(" Running");
exec.run();
println!(" Done");
println!("stackless coroutine PASSED");
0
}

@ -4,138 +4,40 @@
#[macro_use]
extern crate user_lib;
// not in SUCC_TESTS & FAIL_TESTS
// count_lines, infloop, user_shell, usertests
// item of TESTS : app_name(argv_0), argv_1, argv_2, argv_3, exit_code
static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[
("filetest_simple\0", "\0", "\0", "\0", 0),
("cat\0", "filea\0", "\0", "\0", 0),
("cmdline_args\0", "1\0", "2\0", "3\0", 0),
("eisenberg\0", "\0", "\0", "\0", 0),
("exit\0", "\0", "\0", "\0", 0),
("fantastic_text\0", "\0", "\0", "\0", 0),
("forktest_simple\0", "\0", "\0", "\0", 0),
("forktest\0", "\0", "\0", "\0", 0),
("forktest2\0", "\0", "\0", "\0", 0),
("forktree\0", "\0", "\0", "\0", 0),
("hello_world\0", "\0", "\0", "\0", 0),
("huge_write\0", "\0", "\0", "\0", 0),
("matrix\0", "\0", "\0", "\0", 0),
("mpsc_sem\0", "\0", "\0", "\0", 0),
("peterson\0", "\0", "\0", "\0", 0),
("phil_din_mutex\0", "\0", "\0", "\0", 0),
("pipe_large_test\0", "\0", "\0", "\0", 0),
("pipetest\0", "\0", "\0", "\0", 0),
("race_adder_arg\0", "3\0", "\0", "\0", 0),
("race_adder_atomic\0", "\0", "\0", "\0", 0),
("race_adder_mutex_blocking\0", "\0", "\0", "\0", 0),
("race_adder_mutex_spin\0", "\0", "\0", "\0", 0),
("run_pipe_test\0", "\0", "\0", "\0", 0),
("sleep_simple\0", "\0", "\0", "\0", 0),
("sleep\0", "\0", "\0", "\0", 0),
("sleep_simple\0", "\0", "\0", "\0", 0),
("sync_sem\0", "\0", "\0", "\0", 0),
("test_condvar\0", "\0", "\0", "\0", 0),
("threads_arg\0", "\0", "\0", "\0", 0),
("threads\0", "\0", "\0", "\0", 0),
("yield\0", "\0", "\0", "\0", 0),
];
static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[
("stack_overflow\0", "\0", "\0", "\0", -11),
("race_adder_loop\0", "\0", "\0", "\0", -6),
("priv_csr\0", "\0", "\0", "\0", -4),
("priv_inst\0", "\0", "\0", "\0", -4),
("store_fault\0", "\0", "\0", "\0", -11),
("until_timeout\0", "\0", "\0", "\0", -6),
("race_adder\0", "\0", "\0", "\0", -6),
("huge_write_mt\0", "\0", "\0", "\0", -6),
static TESTS: &[&str] = &[
"exit\0",
"fantastic_text\0",
"forktest\0",
"forktest2\0",
"forktest_simple\0",
"hello_world\0",
"matrix\0",
"sleep\0",
"sleep_simple\0",
"stack_overflow\0",
"yield\0",
];
use user_lib::{exec, fork, waitpid};
fn run_tests(tests: &[(&str, &str, &str, &str, i32)]) -> i32 {
let mut pass_num = 0;
let mut arr: [*const u8; 4] = [
core::ptr::null::<u8>(),
core::ptr::null::<u8>(),
core::ptr::null::<u8>(),
core::ptr::null::<u8>(),
];
for test in tests {
println!("Usertests: Running {}", test.0);
arr[0] = test.0.as_ptr();
if test.1 != "\0" {
arr[1] = test.1.as_ptr();
arr[2] = core::ptr::null::<u8>();
arr[3] = core::ptr::null::<u8>();
if test.2 != "\0" {
arr[2] = test.2.as_ptr();
arr[3] = core::ptr::null::<u8>();
if test.3 != "\0" {
arr[3] = test.3.as_ptr();
} else {
arr[3] = core::ptr::null::<u8>();
}
} else {
arr[2] = core::ptr::null::<u8>();
arr[3] = core::ptr::null::<u8>();
}
} else {
arr[1] = core::ptr::null::<u8>();
arr[2] = core::ptr::null::<u8>();
arr[3] = core::ptr::null::<u8>();
}
#[no_mangle]
pub fn main() -> i32 {
for test in TESTS {
println!("Usertests: Running {}", test);
let pid = fork();
if pid == 0 {
exec(test.0, &arr[..]);
exec(*test, &[core::ptr::null::<u8>()]);
panic!("unreachable!");
} else {
let mut exit_code: i32 = Default::default();
let wait_pid = waitpid(pid as usize, &mut exit_code);
assert_eq!(pid, wait_pid);
if exit_code == test.4 {
// summary apps with exit_code
pass_num = pass_num + 1;
}
println!(
"\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m",
test.0, pid, exit_code
test, pid, exit_code
);
}
}
pass_num
}
#[no_mangle]
pub fn main() -> i32 {
let succ_num = run_tests(SUCC_TESTS);
let err_num = run_tests(FAIL_TESTS);
if succ_num == SUCC_TESTS.len() as i32 && err_num == FAIL_TESTS.len() as i32 {
println!(
"{} of sueecssed apps, {} of failed apps run correctly. \nUsertests passed!",
SUCC_TESTS.len(),
FAIL_TESTS.len()
);
return 0;
}
if succ_num != SUCC_TESTS.len() as i32 {
println!(
"all successed app_num is {} , but only passed {}",
SUCC_TESTS.len(),
succ_num
);
}
if err_num != FAIL_TESTS.len() as i32 {
println!(
"all failed app_num is {} , but only passed {}",
FAIL_TESTS.len(),
err_num
);
}
println!(" Usertests failed!");
return -1;
println!("Usertests passed!");
0
}

@ -2,7 +2,6 @@
#![feature(linkage)]
#![feature(panic_info_message)]
#![feature(alloc_error_handler)]
#![feature(core_intrinsics)]
#[macro_use]
pub mod console;
@ -198,24 +197,3 @@ pub fn condvar_signal(condvar_id: usize) {
pub fn condvar_wait(condvar_id: usize, mutex_id: usize) {
sys_condvar_wait(condvar_id, mutex_id);
}
#[macro_export]
macro_rules! vstore {
($var_ref: expr, $value: expr) => {
unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) }
};
}
#[macro_export]
macro_rules! vload {
($var_ref: expr) => {
unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) }
};
}
#[macro_export]
macro_rules! memory_fence {
() => {
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst)
};
}

@ -1,3 +1,5 @@
use core::arch::asm;
const SYSCALL_DUP: usize = 24;
const SYSCALL_OPEN: usize = 56;
const SYSCALL_CLOSE: usize = 57;
@ -29,7 +31,7 @@ const SYSCALL_CONDVAR_WAIT: usize = 1032;
fn syscall(id: usize, args: [usize; 3]) -> isize {
let mut ret: isize;
unsafe {
core::arch::asm!(
asm!(
"ecall",
inlateout("x10") args[0] => ret,
in("x11") args[1],

Loading…
Cancel
Save