Merge branch 'main' into fix/kstack-leak

This commit is contained in:
Yifan Wu 2023-02-08 11:08:27 +08:00 committed by GitHub
commit 2dc89534a4
106 changed files with 3974 additions and 2260 deletions

View file

@ -3,5 +3,5 @@ target = "riscv64gc-unknown-none-elf"
[target.riscv64gc-unknown-none-elf]
rustflags = [
"-Clink-args=-Tsrc/linker.ld",
"-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
]

View file

@ -10,10 +10,10 @@ edition = "2018"
buddy_system_allocator = "0.6"
bitflags = "1.2.1"
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
embedded-graphics = "0.7.1"
oorandom ="11"
virtio-input-decoder = "0.1.4"
[profile.release]
debug = true
[features]
board_qemu = []
board_k210 = []

View file

@ -19,7 +19,7 @@ ifeq ($(TEST), 1)
endif
binary: elf
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
@$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
build: binary

55
user/src/bin/adder.rs Normal file
View file

@ -0,0 +1,55 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
static mut A: usize = 0;
const PER_THREAD_DEFAULT: usize = 10000;
const THREAD_COUNT_DEFAULT: usize = 16;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
critical_section(&mut t);
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
let start = get_time();
let mut v = Vec::new();
for _ in 0..thread_count {
v.push(thread_create(f as usize, 0) as usize);
}
for tid in v.into_iter() {
waittid(tid);
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,72 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicBool, Ordering};
use user_lib::{exit, get_time, thread_create, waittid, yield_};
static mut A: usize = 0;
static OCCUPIED: AtomicBool = AtomicBool::new(false);
const PER_THREAD_DEFAULT: usize = 10000;
const THREAD_COUNT_DEFAULT: usize = 16;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
fn lock() {
while OCCUPIED
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
yield_();
}
}
fn unlock() {
OCCUPIED.store(false, Ordering::Relaxed);
}
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
lock();
critical_section(&mut t);
unlock();
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
let start = get_time();
let mut v = Vec::new();
for _ in 0..thread_count {
v.push(thread_create(f as usize, 0) as usize);
}
for tid in v.into_iter() {
waittid(tid);
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,59 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock};
static mut A: usize = 0;
const PER_THREAD_DEFAULT: usize = 10000;
const THREAD_COUNT_DEFAULT: usize = 16;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
mutex_lock(0);
critical_section(&mut t);
mutex_unlock(0);
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
let start = get_time();
assert_eq!(mutex_blocking_create(), 0);
let mut v = Vec::new();
for _ in 0..thread_count {
v.push(thread_create(f as usize, 0) as usize);
}
for tid in v.into_iter() {
waittid(tid);
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,60 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
use user_lib::{mutex_create, mutex_lock, mutex_unlock};
static mut A: usize = 0;
const PER_THREAD_DEFAULT: usize = 10000;
const THREAD_COUNT_DEFAULT: usize = 16;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
mutex_lock(0);
critical_section(&mut t);
mutex_unlock(0);
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
let start = get_time();
assert_eq!(mutex_create(), 0);
let mut v = Vec::new();
for _ in 0..thread_count {
v.push(thread_create(f as usize, 0) as usize);
}
for tid in v.into_iter() {
waittid(tid);
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,90 @@
//! It only works on a single CPU!
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
use core::sync::atomic::{compiler_fence, Ordering};
static mut A: usize = 0;
static mut FLAG: [bool; 2] = [false; 2];
static mut TURN: usize = 0;
const PER_THREAD_DEFAULT: usize = 2000;
const THREAD_COUNT_DEFAULT: usize = 2;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
unsafe fn lock(id: usize) {
FLAG[id] = true;
let j = 1 - id;
TURN = j;
// Tell the compiler not to reorder memory operations
// across this fence.
compiler_fence(Ordering::SeqCst);
// Why do we need to use volatile_read here?
// Otherwise the compiler will assume that they will never
// be changed on this thread. Thus, they will be accessed
// only once!
while vload!(&FLAG[j]) && vload!(&TURN) == j {}
}
unsafe fn unlock(id: usize) {
FLAG[id] = false;
}
unsafe fn f(id: usize) -> ! {
let mut t = 2usize;
for _iter in 0..PER_THREAD {
lock(id);
critical_section(&mut t);
unlock(id);
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
// uncomment this if you want to check the assembly
// println!(
// "addr: lock={:#x}, unlock={:#x}",
// lock as usize,
// unlock as usize
// );
let start = get_time();
let mut v = Vec::new();
assert_eq!(thread_count, 2, "Peterson works when there are only 2 threads.");
for id in 0..thread_count {
v.push(thread_create(f as usize, id) as usize);
}
let mut time_cost = Vec::new();
for tid in v.iter() {
time_cost.push(waittid(*tid));
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,89 @@
//! It only works on a single CPU!
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid, yield_};
use core::sync::atomic::{compiler_fence, Ordering};
static mut A: usize = 0;
static mut FLAG: [bool; 2] = [false; 2];
static mut TURN: usize = 0;
const PER_THREAD_DEFAULT: usize = 2000;
const THREAD_COUNT_DEFAULT: usize = 2;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
unsafe fn lock(id: usize) {
FLAG[id] = true;
let j = 1 - id;
TURN = j;
// Tell the compiler not to reorder memory operations
// across this fence.
compiler_fence(Ordering::SeqCst);
while FLAG[j] && TURN == j {
yield_();
}
}
unsafe fn unlock(id: usize) {
FLAG[id] = false;
}
unsafe fn f(id: usize) -> ! {
let mut t = 2usize;
for _iter in 0..PER_THREAD {
lock(id);
critical_section(&mut t);
unlock(id);
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
// uncomment this if you want to check the assembly
// println!(
// "addr: lock={:#x}, unlock={:#x}",
// lock as usize,
// unlock as usize
// );
let start = get_time();
let mut v = Vec::new();
assert_eq!(thread_count, 2, "Peterson works when there are only 2 threads.");
for id in 0..thread_count {
v.push(thread_create(f as usize, id) as usize);
}
let mut time_cost = Vec::new();
for tid in v.iter() {
time_cost.push(waittid(*tid));
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,68 @@
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
static mut A: usize = 0;
static mut OCCUPIED: bool = false;
const PER_THREAD_DEFAULT: usize = 10000;
const THREAD_COUNT_DEFAULT: usize = 16;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
unsafe fn lock() {
while vload!(&OCCUPIED) {}
OCCUPIED = true;
}
unsafe fn unlock() {
OCCUPIED = false;
}
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
lock();
critical_section(&mut t);
unlock();
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
let start = get_time();
let mut v = Vec::new();
for _ in 0..thread_count {
v.push(thread_create(f as usize, 0) as usize);
}
for tid in v.into_iter() {
waittid(tid);
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,70 @@
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid, yield_};
static mut A: usize = 0;
static mut OCCUPIED: bool = false;
const PER_THREAD_DEFAULT: usize = 10000;
const THREAD_COUNT_DEFAULT: usize = 16;
static mut PER_THREAD: usize = 0;
unsafe fn critical_section(t: &mut usize) {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
*t = (*t) * (*t) % 10007;
}
a.write_volatile(cur + 1);
}
unsafe fn lock() {
while OCCUPIED {
yield_();
}
OCCUPIED = true;
}
unsafe fn unlock() {
OCCUPIED = false;
}
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
lock();
critical_section(&mut t);
unlock();
}
exit(t as i32)
}
#[no_mangle]
pub fn main(argc: usize, argv: &[&str]) -> i32 {
let mut thread_count = THREAD_COUNT_DEFAULT;
let mut per_thread = PER_THREAD_DEFAULT;
if argc >= 2 {
thread_count = argv[1].parse().unwrap();
if argc >= 3 {
per_thread = argv[2].parse().unwrap();
}
}
unsafe { PER_THREAD = per_thread; }
let start = get_time();
let mut v = Vec::new();
for _ in 0..thread_count {
v.push(thread_create(f as usize, 0) as usize);
}
for tid in v.into_iter() {
waittid(tid);
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
0
}

View file

@ -0,0 +1,72 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use user_lib::{thread_create, exit, waittid, mutex_create, mutex_lock, mutex_unlock, condvar_create, condvar_signal, condvar_wait};
use alloc::vec::Vec;
use core::cell::UnsafeCell;
use lazy_static::*;
const THREAD_NUM: usize = 3;
struct Barrier {
mutex_id: usize,
condvar_id: usize,
count: UnsafeCell<usize>,
}
impl Barrier {
pub fn new() -> Self {
Self {
mutex_id: mutex_create() as usize,
condvar_id: condvar_create() as usize,
count: UnsafeCell::new(0),
}
}
pub fn block(&self) {
mutex_lock(self.mutex_id);
let count = self.count.get();
// SAFETY: Here, the accesses of the count is in the
// critical section protected by the mutex.
unsafe { *count = *count + 1; }
if unsafe { *count } == THREAD_NUM {
condvar_signal(self.condvar_id);
} else {
condvar_wait(self.condvar_id, self.mutex_id);
condvar_signal(self.condvar_id);
}
mutex_unlock(self.mutex_id);
}
}
unsafe impl Sync for Barrier {}
lazy_static! {
static ref BARRIER_AB: Barrier = Barrier::new();
static ref BARRIER_BC: Barrier = Barrier::new();
}
fn thread_fn() {
for _ in 0..300 { print!("a"); }
BARRIER_AB.block();
for _ in 0..300 { print!("b"); }
BARRIER_BC.block();
for _ in 0..300 { print!("c"); }
exit(0)
}
#[no_mangle]
pub fn main() -> i32 {
let mut v: Vec<isize> = Vec::new();
for _ in 0..THREAD_NUM {
v.push(thread_create(thread_fn as usize, 0));
}
for tid in v.into_iter() {
waittid(tid as usize);
}
println!("\nOK!");
0
}

View file

@ -0,0 +1,33 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use user_lib::{thread_create, exit, waittid};
use alloc::vec::Vec;
const THREAD_NUM: usize = 3;
fn thread_fn() {
for ch in 'a'..='c' {
for _ in 0..300 {
print!("{}", ch);
}
}
exit(0)
}
#[no_mangle]
pub fn main() -> i32 {
let mut v: Vec<isize> = Vec::new();
for _ in 0..THREAD_NUM {
v.push(thread_create(thread_fn as usize, 0));
}
for tid in v.into_iter() {
waittid(tid as usize);
}
println!("\nOK!");
0
}

View file

@ -1,59 +1,59 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec;
use user_lib::exit;
use user_lib::{
condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock,
};
use user_lib::{sleep, thread_create, waittid};
static mut A: usize = 0;
const CONDVAR_ID: usize = 0;
const MUTEX_ID: usize = 0;
unsafe fn first() -> ! {
sleep(10);
println!("First work, Change A --> 1 and wakeup Second");
mutex_lock(MUTEX_ID);
A = 1;
condvar_signal(CONDVAR_ID);
mutex_unlock(MUTEX_ID);
exit(0)
}
unsafe fn second() -> ! {
println!("Second want to continue,but need to wait A=1");
mutex_lock(MUTEX_ID);
while A == 0 {
println!("Second: A is {}", A);
condvar_wait(CONDVAR_ID, MUTEX_ID);
}
mutex_unlock(MUTEX_ID);
println!("A is {}, Second can work now", A);
exit(0)
}
#[no_mangle]
pub fn main() -> i32 {
// create condvar & mutex
assert_eq!(condvar_create() as usize, CONDVAR_ID);
assert_eq!(mutex_blocking_create() as usize, MUTEX_ID);
// create threads
let threads = vec![
thread_create(first as usize, 0),
thread_create(second as usize, 0),
];
// wait for all threads to complete
for thread in threads.iter() {
waittid(*thread as usize);
}
println!("test_condvar passed!");
0
}
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec;
use user_lib::exit;
use user_lib::{
condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock,
};
use user_lib::{sleep, thread_create, waittid};
static mut A: usize = 0;
const CONDVAR_ID: usize = 0;
const MUTEX_ID: usize = 0;
unsafe fn first() -> ! {
sleep(10);
println!("First work, Change A --> 1 and wakeup Second");
mutex_lock(MUTEX_ID);
A = 1;
condvar_signal(CONDVAR_ID);
mutex_unlock(MUTEX_ID);
exit(0)
}
unsafe fn second() -> ! {
println!("Second want to continue,but need to wait A=1");
mutex_lock(MUTEX_ID);
while A == 0 {
println!("Second: A is {}", A);
condvar_wait(CONDVAR_ID, MUTEX_ID);
}
println!("A is {}, Second can work now", A);
mutex_unlock(MUTEX_ID);
exit(0)
}
#[no_mangle]
pub fn main() -> i32 {
// create condvar & mutex
assert_eq!(condvar_create() as usize, CONDVAR_ID);
assert_eq!(mutex_blocking_create() as usize, MUTEX_ID);
// create threads
let threads = vec![
thread_create(first as usize, 0),
thread_create(second as usize, 0),
];
// wait for all threads to complete
for thread in threads.iter() {
waittid(*thread as usize);
}
println!("test_condvar passed!");
0
}

View file

@ -0,0 +1,64 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec;
use user_lib::exit;
use user_lib::{
semaphore_create, semaphore_down, semaphore_up, mutex_blocking_create, mutex_lock, mutex_unlock,
};
use user_lib::{sleep, thread_create, waittid};
static mut A: usize = 0;
const SEM_ID: usize = 0;
const MUTEX_ID: usize = 0;
unsafe fn first() -> ! {
sleep(10);
println!("First work, Change A --> 1 and wakeup Second");
mutex_lock(MUTEX_ID);
A = 1;
semaphore_up(SEM_ID);
mutex_unlock(MUTEX_ID);
exit(0)
}
unsafe fn second() -> ! {
println!("Second want to continue,but need to wait A=1");
loop {
mutex_lock(MUTEX_ID);
if A == 0 {
println!("Second: A is {}", A);
mutex_unlock(MUTEX_ID);
semaphore_down(SEM_ID);
} else {
mutex_unlock(MUTEX_ID);
break;
}
}
println!("A is {}, Second can work now", A);
exit(0)
}
#[no_mangle]
pub fn main() -> i32 {
// create semaphore & mutex
assert_eq!(semaphore_create(0) as usize, SEM_ID);
assert_eq!(mutex_blocking_create() as usize, MUTEX_ID);
// create threads
let threads = vec![
thread_create(first as usize, 0),
thread_create(second as usize, 0),
];
// wait for all threads to complete
for thread in threads.iter() {
waittid(*thread as usize);
}
println!("test_condvar passed!");
0
}

View file

@ -1,19 +0,0 @@
#![no_std]
#![no_main]
use user_lib::create_desktop;
#[macro_use]
extern crate user_lib;
#[no_mangle]
pub fn main() -> i32 {
println!("gui");
create_desktop();
println!("exit pass.");
loop{}
0
}

67
user/src/bin/gui_rect.rs Normal file
View file

@ -0,0 +1,67 @@
#![no_std]
#![no_main]
extern crate alloc;
extern crate user_lib;
use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES};
use embedded_graphics::pixelcolor::Rgb888;
use embedded_graphics::prelude::{DrawTarget, Drawable, Point, RgbColor, Size};
use embedded_graphics::primitives::{Circle, Primitive, PrimitiveStyle, Rectangle,Triangle};
const INIT_X: i32 = 80;
const INIT_Y: i32 = 400;
const RECT_SIZE: u32 = 150;
pub struct DrawingBoard {
disp: Display,
latest_pos: Point,
}
impl DrawingBoard {
pub fn new() -> Self {
Self {
disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)),
latest_pos: Point::new(INIT_X, INIT_Y),
}
}
fn paint(&mut self) {
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
.into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 10))
.draw(&mut self.disp)
.ok();
Circle::new(self.latest_pos + Point::new(-70, -300), 150)
.into_styled(PrimitiveStyle::with_fill(Rgb888::BLUE))
.draw(&mut self.disp)
.ok();
Triangle::new(self.latest_pos + Point::new(0, 150), self.latest_pos + Point::new(80, 200), self.latest_pos + Point::new(-120, 300))
.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 10))
.draw(&mut self.disp)
.ok();
}
fn unpaint(&mut self) {
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
.into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 10))
.draw(&mut self.disp)
.ok();
}
pub fn move_rect(&mut self, dx: i32, dy: i32) {
self.unpaint();
self.latest_pos.x += dx;
self.latest_pos.y += dy;
self.paint();
}
}
#[no_mangle]
pub fn main() -> i32 {
let mut board = DrawingBoard::new();
let _ = board.disp.clear(Rgb888::BLACK).unwrap();
for i in 0..5 {
board.latest_pos.x += (RECT_SIZE as i32 + 20);
//board.latest_pos.y += i;
board.paint();
}
0
}

View file

@ -0,0 +1,23 @@
#![no_std]
#![no_main]
extern crate user_lib;
use user_lib::{VIRTGPU_XRES, VIRTGPU_YRES, Display};
use embedded_graphics::prelude::Size;
#[no_mangle]
pub fn main() -> i32 {
let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES));
disp.paint_on_framebuffer(|fb| {
for y in 0..VIRTGPU_YRES as usize {
for x in 0..VIRTGPU_XRES as usize {
let idx = (y * VIRTGPU_XRES as usize + x) * 4;
fb[idx] = x as u8;
fb[idx + 1] = y as u8;
fb[idx + 2] = (x + y) as u8;
}
}
});
0
}

351
user/src/bin/gui_snake.rs Normal file
View file

@ -0,0 +1,351 @@
#![no_std]
#![no_main]
extern crate user_lib;
extern crate alloc;
use user_lib::console::getchar;
use user_lib::{Display, key_pressed, sleep, VIRTGPU_XRES, VIRTGPU_YRES};
use embedded_graphics::pixelcolor::*;
use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size};
use embedded_graphics::primitives::Primitive;
use embedded_graphics::primitives::{PrimitiveStyle, Rectangle};
use embedded_graphics::Pixel;
use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions};
use oorandom; //random generator
struct Snake<T: PixelColor, const MAX_SIZE: usize> {
parts: [Pixel<T>; MAX_SIZE],
len: usize,
direction: Direction,
size_x: u32,
size_y: u32,
}
struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> {
snake: &'a Snake<T, MAX_SIZE>,
index: usize,
}
impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake<T, MAX_SIZE> {
type Item = Pixel<T>;
type IntoIter = SnakeIntoIterator<'a, T, MAX_SIZE>;
fn into_iter(self) -> Self::IntoIter {
SnakeIntoIterator {
snake: self,
index: 0,
}
}
}
impl<'a, T: PixelColor, const MAX_SIZE: usize> Iterator for SnakeIntoIterator<'a, T, MAX_SIZE> {
type Item = Pixel<T>;
fn next(&mut self) -> Option<Self::Item> {
let cur = self.snake.parts[self.index];
if self.index < self.snake.len {
self.index += 1;
return Some(cur);
}
None
}
}
impl<T: PixelColor, const MAX_SIZE: usize> Snake<T, MAX_SIZE> {
fn new(color: T, size_x: u32, size_y: u32) -> Snake<T, MAX_SIZE> {
Snake {
parts: [Pixel::<T>(Point { x: 0, y: 0 }, color); MAX_SIZE],
len: 1,
direction: Direction::None,
size_x,
size_y,
}
}
fn set_direction(&mut self, direction: Direction) {
self.direction = direction;
}
fn contains(&self, this: Point) -> bool {
for part in self.into_iter() {
if part.0 == this {
return true;
};
}
false
}
fn grow(&mut self) {
if self.len < MAX_SIZE - 1 {
self.len += 1;
}
}
fn make_step(&mut self) {
let mut i = self.len;
while i > 0 {
self.parts[i] = self.parts[i - 1];
i -= 1;
}
match self.direction {
Direction::Left => {
if self.parts[0].0.x == 0 {
self.parts[0].0.x = (self.size_x - 1) as i32;
} else {
self.parts[0].0.x -= 1;
}
}
Direction::Right => {
if self.parts[0].0.x == (self.size_x - 1) as i32 {
self.parts[0].0.x = 0;
} else {
self.parts[0].0.x += 1;
}
}
Direction::Up => {
if self.parts[0].0.y == 0 {
self.parts[0].0.y = (self.size_y - 1) as i32;
} else {
self.parts[0].0.y -= 1;
}
}
Direction::Down => {
if self.parts[0].0.y == (self.size_y - 1) as i32 {
self.parts[0].0.y = 0;
} else {
self.parts[0].0.y += 1;
}
}
Direction::None => {}
}
}
}
struct Food<T: PixelColor> {
size_x: u32,
size_y: u32,
place: Pixel<T>,
rng: oorandom::Rand32,
}
impl<T: PixelColor> Food<T> {
pub fn new(color: T, size_x: u32, size_y: u32) -> Self {
let seed = 4;
let rng = oorandom::Rand32::new(seed);
Food {
size_x,
size_y,
place: Pixel(Point { x: 0, y: 0 }, color),
rng,
}
}
fn replace<'a, const MAX_SIZE: usize>(&mut self, iter_source: &Snake<T, MAX_SIZE>) {
let mut p: Point;
'outer: loop {
let random_number = self.rng.rand_u32();
let blocked_positions = iter_source.into_iter();
p = Point {
x: ((random_number >> 24) as u16 % self.size_x as u16).into(),
y: ((random_number >> 16) as u16 % self.size_y as u16).into(),
};
for blocked_position in blocked_positions {
if p == blocked_position.0 {
continue 'outer;
}
}
break;
}
self.place = Pixel::<T> {
0: p,
1: self.place.1,
}
}
fn get_pixel(&self) -> Pixel<T> {
self.place
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Direction {
Left,
Right,
Up,
Down,
None,
}
pub struct SnakeGame<const MAX_SNAKE_SIZE: usize, T: PixelColor> {
snake: Snake<T, MAX_SNAKE_SIZE>,
food: Food<T>,
food_age: u32,
food_lifetime: u32,
size_x: u32,
size_y: u32,
scale_x: u32,
scale_y: u32,
}
impl<const MAX_SIZE: usize, T: PixelColor> SnakeGame<MAX_SIZE, T> {
pub fn new(
size_x: u32,
size_y: u32,
scale_x: u32,
scale_y: u32,
snake_color: T,
food_color: T,
food_lifetime: u32,
) -> Self {
let snake = Snake::<T, MAX_SIZE>::new(snake_color, size_x / scale_x, size_y / scale_y);
let mut food = Food::<T>::new(food_color, size_x / scale_x, size_y / scale_y);
food.replace(&snake);
SnakeGame {
snake,
food,
food_age: 0,
food_lifetime,
size_x,
size_y,
scale_x,
scale_y,
}
}
pub fn set_direction(&mut self, direction: Direction) {
self.snake.set_direction(direction);
}
pub fn draw<D>(&mut self, target: &mut D) -> ()
where
D: DrawTarget<Color = T>,
{
self.snake.make_step();
let hit = self.snake.contains(self.food.get_pixel().0);
if hit {
self.snake.grow();
}
self.food_age += 1;
if self.food_age >= self.food_lifetime || hit {
self.food.replace(&self.snake);
self.food_age = 0;
}
let mut scaled_display = ScaledDisplay::<D> {
real_display: target,
size_x: self.size_x / self.scale_x,
size_y: self.size_y / self.scale_y,
scale_x: self.scale_x,
scale_y: self.scale_y,
};
for part in self.snake.into_iter() {
_ = part.draw(&mut scaled_display);
}
_ = self.food.get_pixel().draw(&mut scaled_display);
}
}
/// A dummy DrawTarget implementation that can magnify each pixel so the user code does not need to adapt for scaling things
struct ScaledDisplay<'a, T: DrawTarget> {
real_display: &'a mut T,
size_x: u32,
size_y: u32,
scale_x: u32,
scale_y: u32,
}
impl<'a, T: DrawTarget> DrawTarget for ScaledDisplay<'a, T> {
type Color = T::Color;
type Error = T::Error;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
for pixel in pixels {
let style = PrimitiveStyle::with_fill(pixel.1);
Rectangle::new(
Point::new(
pixel.0.x * self.scale_x as i32,
pixel.0.y * self.scale_y as i32,
),
Size::new(self.scale_x as u32, self.scale_y as u32),
)
.into_styled(style)
.draw(self.real_display)?;
}
Ok(())
}
}
impl<'a, T: DrawTarget> OriginDimensions for ScaledDisplay<'a, T> {
fn size(&self) -> Size {
Size::new(self.size_x as u32, self.size_y as u32)
}
}
#[cfg(test)]
mod tests {
use crate::Snake;
use embedded_graphics::pixelcolor::*;
use embedded_graphics::prelude::*;
#[test]
fn snake_basic() {
let mut snake = Snake::<Rgb888, 20>::new(Rgb888::RED, 8, 8);
snake.set_direction(crate::Direction::Right);
assert_eq!(
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
snake.into_iter().next().unwrap()
);
snake.make_step();
assert_eq!(
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
snake.into_iter().nth(0).unwrap()
);
assert_eq!(
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
snake.into_iter().nth(1).unwrap()
);
snake.set_direction(crate::Direction::Down);
snake.make_step();
assert_eq!(
Pixel::<Rgb888>(Point { x: 1, y: 1 }, Rgb888::RED),
snake.into_iter().nth(0).unwrap()
);
assert_eq!(
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
snake.into_iter().nth(1).unwrap()
);
assert_eq!(
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
snake.into_iter().nth(2).unwrap()
);
assert_eq!(true, snake.contains(Point { x: 0, y: 0 }));
assert_eq!(true, snake.contains(Point { x: 1, y: 0 }));
assert_eq!(true, snake.contains(Point { x: 1, y: 1 }));
}
}
const LF: u8 = 0x0au8;
const CR: u8 = 0x0du8;
#[no_mangle]
pub fn main() -> i32 {
let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES));
let mut game = SnakeGame::<20, Rgb888>::new(1280, 800, 20, 20, Rgb888::RED, Rgb888::YELLOW, 50);
let _ = disp.clear(Rgb888::BLACK).unwrap();
loop {
if key_pressed() {
let c = getchar();
match c {
LF => break,
CR => break,
b'w' => game.set_direction(Direction::Up),
b's' => game.set_direction(Direction::Down),
b'a' => game.set_direction(Direction::Left),
b'd' => game.set_direction(Direction::Right),
_ => (),
}
}
let _ = disp.clear(Rgb888::BLACK).unwrap();
game.draw(&mut disp);
sleep(10);
}
0
}

125
user/src/bin/gui_uart.rs Normal file
View file

@ -0,0 +1,125 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use user_lib::console::getchar;
use user_lib::{framebuffer, framebuffer_flush};
use embedded_graphics::pixelcolor::Rgb888;
use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size};
use embedded_graphics::primitives::Primitive;
use embedded_graphics::primitives::{PrimitiveStyle, Rectangle};
use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions};
pub const VIRTGPU_XRES: usize = 1280;
pub const VIRTGPU_YRES: usize = 800;
pub const VIRTGPU_LEN: usize = VIRTGPU_XRES * VIRTGPU_YRES * 4;
const INIT_X: i32 = 640;
const INIT_Y: i32 = 400;
const RECT_SIZE: u32 = 40;
pub struct Display {
pub size: Size,
pub point: Point,
//pub fb: Arc<&'static mut [u8]>,
pub fb: &'static mut [u8],
}
impl Display {
pub fn new(size: Size, point: Point) -> Self {
let fb_ptr = framebuffer() as *mut u8;
println!(
"Hello world from user mode program! 0x{:X} , len {}",
fb_ptr as usize, VIRTGPU_LEN
);
let fb =
unsafe { core::slice::from_raw_parts_mut(fb_ptr as *mut u8, VIRTGPU_LEN as usize) };
Self { size, point, fb }
}
}
impl OriginDimensions for Display {
fn size(&self) -> Size {
self.size
}
}
impl DrawTarget for Display {
type Color = Rgb888;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
{
pixels.into_iter().for_each(|px| {
let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x)
as usize
* 4;
if idx + 2 >= self.fb.len() {
return;
}
self.fb[idx] = px.1.b();
self.fb[idx + 1] = px.1.g();
self.fb[idx + 2] = px.1.r();
});
framebuffer_flush();
Ok(())
}
}
pub struct DrawingBoard {
disp: Display,
latest_pos: Point,
}
impl DrawingBoard {
pub fn new() -> Self {
Self {
disp: Display::new(Size::new(1280, 800), Point::new(0, 0)),
latest_pos: Point::new(INIT_X, INIT_Y),
}
}
fn paint(&mut self) {
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
.into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1))
.draw(&mut self.disp)
.ok();
}
fn unpaint(&mut self) {
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
.into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 1))
.draw(&mut self.disp)
.ok();
}
pub fn move_rect(&mut self, dx: i32, dy: i32) {
self.unpaint();
self.latest_pos.x += dx;
self.latest_pos.y += dy;
self.paint();
}
}
const LF: u8 = 0x0au8;
const CR: u8 = 0x0du8;
#[no_mangle]
pub fn main() -> i32 {
// let fb_ptr = framebuffer() as *mut u8;
let mut board = DrawingBoard::new();
let _ = board.disp.clear(Rgb888::BLACK).unwrap();
for i in 0..20 {
let c=getchar();
if c == LF || c == CR {
break;
}
board.latest_pos.x += i;
board.latest_pos.y += i;
board.paint();
}
0
}

View file

@ -5,7 +5,7 @@
extern crate user_lib;
extern crate alloc;
use alloc::{fmt::format, string::String, vec::Vec};
use alloc::{fmt::format, vec::Vec};
use user_lib::{close, get_time, gettid, open, write, OpenFlags};
use user_lib::{exit, thread_create, waittid};

View file

@ -0,0 +1,25 @@
#![no_std]
#![no_main]
use user_lib::{event_get, DecodeType, Key, KeyType};
#[macro_use]
extern crate user_lib;
#[no_mangle]
pub fn main() -> i32 {
println!("Input device event test");
loop {
if let Some(event) = event_get() {
if let Some(decoder_type) = event.decode() {
println!("{:?}", decoder_type);
if let DecodeType::Key(key, keytype) = decoder_type {
if key == Key::Enter && keytype == KeyType::Press {
break;
}
}
}
}
}
0
}

View file

@ -14,7 +14,7 @@ use user_lib::{thread_create, waittid};
const SEM_MUTEX: usize = 0;
const SEM_EMPTY: usize = 1;
const SEM_EXISTED: usize = 2;
const SEM_AVAIL: usize = 2;
const BUFFER_SIZE: usize = 8;
static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE];
static mut FRONT: usize = 0;
@ -27,20 +27,20 @@ unsafe fn producer(id: *const usize) -> ! {
for _ in 0..NUMBER_PER_PRODUCER {
semaphore_down(SEM_EMPTY);
semaphore_down(SEM_MUTEX);
BUFFER[FRONT] = id;
FRONT = (FRONT + 1) % BUFFER_SIZE;
BUFFER[TAIL] = id;
TAIL = (TAIL + 1) % BUFFER_SIZE;
semaphore_up(SEM_MUTEX);
semaphore_up(SEM_EXISTED);
semaphore_up(SEM_AVAIL);
}
exit(0)
}
unsafe fn consumer() -> ! {
for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER {
semaphore_down(SEM_EXISTED);
semaphore_down(SEM_AVAIL);
semaphore_down(SEM_MUTEX);
print!("{} ", BUFFER[TAIL]);
TAIL = (TAIL + 1) % BUFFER_SIZE;
print!("{} ", BUFFER[FRONT]);
FRONT = (FRONT + 1) % BUFFER_SIZE;
semaphore_up(SEM_MUTEX);
semaphore_up(SEM_EMPTY);
}
@ -53,7 +53,7 @@ pub fn main() -> i32 {
// create semaphores
assert_eq!(semaphore_create(1) as usize, SEM_MUTEX);
assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY);
assert_eq!(semaphore_create(0) as usize, SEM_EXISTED);
assert_eq!(semaphore_create(0) as usize, SEM_AVAIL);
// create threads
let ids: Vec<_> = (0..PRODUCER_COUNT).collect();
let mut threads = Vec::new();

View file

@ -1,7 +1,6 @@
#![no_std]
#![no_main]
#![feature(core_intrinsics)]
#![feature(asm)]
#[macro_use]
extern crate user_lib;
@ -11,7 +10,7 @@ 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;
const N: usize = 1000;
static mut TURN: usize = 0;
static mut FLAG: [bool; 2] = [false; 2];
@ -30,27 +29,30 @@ fn critical_test_exit() {
}
fn peterson_enter_critical(id: usize, peer_id: usize) {
println!("Thread[{}] try enter", id);
// 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);
// println!("Thread[{}] enter fail", id);
sleep(1);
println!("Thread[{}] retry enter", id);
// println!("Thread[{}] retry enter", id);
}
println!("Thread[{}] enter", id);
// println!("Thread[{}] enter", id);
}
fn peterson_exit_critical(id: usize) {
vstore!(&FLAG[id], false);
println!("Thread[{}] exit", id);
// println!("Thread[{}] exit", id);
}
pub fn thread_fn(id: usize) -> ! {
println!("Thread[{}] init.", id);
// println!("Thread[{}] init.", id);
let peer_id: usize = id ^ 1;
for _ in 0..N {
for iter in 0..N {
if iter % 10 == 0 {
println!("[{}] it={}", id, iter);
}
peterson_enter_critical(id, peer_id);
critical_test_enter();
for _ in 0..3 {
@ -75,4 +77,4 @@ pub fn main() -> i32 {
}
println!("main thread exited.");
0
}
}

View file

@ -1,42 +0,0 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
static mut A: usize = 0;
const PER_THREAD: usize = 1000;
const THREAD_COUNT: usize = 16;
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
t = t * t % 10007;
}
a.write_volatile(cur + 1);
}
exit(t as i32)
}
#[no_mangle]
pub fn main() -> i32 {
let start = get_time();
let mut v = Vec::new();
for _ in 0..THREAD_COUNT {
v.push(thread_create(f as usize, 0) as usize);
}
let mut time_cost = Vec::new();
for tid in v.iter() {
time_cost.push(waittid(*tid));
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
0
}

View file

@ -1,51 +0,0 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicBool, Ordering};
use user_lib::{exit, get_time, thread_create, waittid, yield_};
static mut A: usize = 0;
static OCCUPIED: AtomicBool = AtomicBool::new(false);
const PER_THREAD: usize = 1000;
const THREAD_COUNT: usize = 16;
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
while OCCUPIED
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
yield_();
}
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
t = t * t % 10007;
}
a.write_volatile(cur + 1);
OCCUPIED.store(false, Ordering::Relaxed);
}
exit(t as i32)
}
#[no_mangle]
pub fn main() -> i32 {
let start = get_time();
let mut v = Vec::new();
for _ in 0..THREAD_COUNT {
v.push(thread_create(f as usize, 0) as usize);
}
let mut time_cost = Vec::new();
for tid in v.iter() {
time_cost.push(waittid(*tid));
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
0
}

View file

@ -1,51 +0,0 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid, yield_};
static mut A: usize = 0;
static mut OCCUPIED: bool = false;
const PER_THREAD: usize = 1000;
const THREAD_COUNT: usize = 16;
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
while OCCUPIED {
yield_();
}
OCCUPIED = true;
// enter critical section
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
t = t * t % 10007;
}
a.write_volatile(cur + 1);
// exit critical section
OCCUPIED = false;
}
exit(t as i32)
}
#[no_mangle]
pub fn main() -> i32 {
let start = get_time();
let mut v = Vec::new();
for _ in 0..THREAD_COUNT {
v.push(thread_create(f as usize, 0) as usize);
}
let mut time_cost = Vec::new();
for tid in v.iter() {
time_cost.push(waittid(*tid));
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
0
}

View file

@ -1,46 +0,0 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock};
static mut A: usize = 0;
const PER_THREAD: usize = 1000;
const THREAD_COUNT: usize = 16;
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
mutex_lock(0);
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
t = t * t % 10007;
}
a.write_volatile(cur + 1);
mutex_unlock(0);
}
exit(t as i32)
}
#[no_mangle]
pub fn main() -> i32 {
let start = get_time();
assert_eq!(mutex_blocking_create(), 0);
let mut v = Vec::new();
for _ in 0..THREAD_COUNT {
v.push(thread_create(f as usize, 0) as usize);
}
let mut time_cost = Vec::new();
for tid in v.iter() {
time_cost.push(waittid(*tid));
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
0
}

View file

@ -1,46 +0,0 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
extern crate alloc;
use alloc::vec::Vec;
use user_lib::{exit, get_time, thread_create, waittid};
use user_lib::{mutex_create, mutex_lock, mutex_unlock};
static mut A: usize = 0;
const PER_THREAD: usize = 1000;
const THREAD_COUNT: usize = 16;
unsafe fn f() -> ! {
let mut t = 2usize;
for _ in 0..PER_THREAD {
mutex_lock(0);
let a = &mut A as *mut usize;
let cur = a.read_volatile();
for _ in 0..500 {
t = t * t % 10007;
}
a.write_volatile(cur + 1);
mutex_unlock(0);
}
exit(t as i32)
}
#[no_mangle]
pub fn main() -> i32 {
let start = get_time();
assert_eq!(mutex_create(), 0);
let mut v = Vec::new();
for _ in 0..THREAD_COUNT {
v.push(thread_create(f as usize, 0) as usize);
}
let mut time_cost = Vec::new();
for tid in v.iter() {
time_cost.push(waittid(*tid));
}
println!("time cost is {}ms", get_time() - start);
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
0
}

View file

@ -0,0 +1,16 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate user_lib;
use oorandom;
#[no_mangle]
pub fn main() -> i32 {
println!("random num program!");
let seed = 4;
let mut rng = oorandom::Rand32::new(seed);
println!("OORandom: Random number 32bit: {}", rng.rand_i32());
println!("OORandom: Random number range: {}", rng.rand_range(1..100));
0
}

View file

@ -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..5 {
if fork() == 0 {
exec("pipe_large_test\0", &[core::ptr::null::<u8>()]);
} else {

View file

@ -4,9 +4,12 @@
#[macro_use]
extern crate user_lib;
fn f(d: usize) {
println!("d = {}", d);
f(d + 1);
#[allow(unconditional_recursion)]
fn f(depth: usize) {
if depth % 10 == 0 {
println!("depth = {}", depth);
}
f(depth + 1);
}
#[no_mangle]

View file

@ -0,0 +1,350 @@
// 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: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.");
println!("RUNTIME: spawning task {}\n", available.id);
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 extern "C" 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);
}

View file

@ -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
}

46
user/src/bin/udp.rs Normal file
View file

@ -0,0 +1,46 @@
#![no_std]
#![no_main]
use alloc::string::String;
#[macro_use]
extern crate user_lib;
#[macro_use]
extern crate alloc;
use user_lib::{connect, write, read};
#[no_mangle]
pub fn main() -> i32 {
println!("udp test open!");
let udp_fd = connect(10 << 24 | 0 << 16 | 2 << 8 | 2, 2001, 26099);
if udp_fd < 0 {
println!("failed to create udp connection.");
return -1;
}
let buf = "Hello rCoreOS user program!";
println!("send <{}>", buf);
write(udp_fd as usize, buf.as_bytes());
println!("udp send done, waiting for reply.");
let mut buf = vec![0u8; 1024];
let len = read(udp_fd as usize, &mut buf);
if len < 0 {
println!("can't receive udp packet");
return -1;
}
let recv_str = String::from_utf8_lossy(&buf[..len as usize]);
println!("receive reply <{}>", recv_str);
0
}

View file

@ -27,19 +27,22 @@ static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[
("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),
("adder_peterson_spin\0", "\0", "\0", "\0", 0),
("adder_peterson_yield\0", "\0", "\0", "\0", 0),
("adder_mutex_blocking\0", "\0", "\0", "\0", 0),
("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),
("condsync_sem\0", "\0", "\0", "\0", 0),
("condsync_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),
("barrier_fail\0", "\0", "\0", "\0", 0),
("barrier_condvar\0", "\0", "\0", "\0", 0),
];
static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[
@ -49,8 +52,9 @@ static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[
("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),
("adder\0", "\0", "\0", "\0", -6),
("adder_simple_spin\0", "\0", "\0", "\0", -6),
("adder_simple_yield\0", "\0", "\0", "\0", -6),
];
use user_lib::{exec, fork, waitpid};

30
user/src/file.rs Normal file
View file

@ -0,0 +1,30 @@
use super::*;
bitflags! {
pub struct OpenFlags: u32 {
const RDONLY = 0;
const WRONLY = 1 << 0;
const RDWR = 1 << 1;
const CREATE = 1 << 9;
const TRUNC = 1 << 10;
}
}
pub fn dup(fd: usize) -> isize {
sys_dup(fd)
}
pub fn open(path: &str, flags: OpenFlags) -> isize {
sys_open(path, flags.bits)
}
pub fn close(fd: usize) -> isize {
sys_close(fd)
}
pub fn pipe(pipe_fd: &mut [usize]) -> isize {
sys_pipe(pipe_fd)
}
pub fn read(fd: usize, buf: &mut [u8]) -> isize {
sys_read(fd, buf)
}
pub fn write(fd: usize, buf: &[u8]) -> isize {
sys_write(fd, buf)
}

118
user/src/io.rs Normal file
View file

@ -0,0 +1,118 @@
use super::*;
use embedded_graphics::prelude::{RgbColor, Size};
use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions};
use embedded_graphics::pixelcolor::Rgb888;
use virtio_input_decoder::Decoder;
pub use virtio_input_decoder::{DecodeType, Key, KeyType, Mouse};
pub const VIRTGPU_XRES: u32 = 1280;
pub const VIRTGPU_YRES: u32 = 800;
pub const VIRTGPU_LEN: usize = (VIRTGPU_XRES * VIRTGPU_YRES * 4) as usize;
pub fn framebuffer() -> isize {
sys_framebuffer()
}
pub fn framebuffer_flush() -> isize {
sys_framebuffer_flush()
}
pub struct Display {
pub size: Size,
pub fb: &'static mut [u8],
}
impl Display {
pub fn new(size: Size) -> Self {
let fb_ptr = framebuffer() as *mut u8;
let fb =
unsafe { core::slice::from_raw_parts_mut(fb_ptr, VIRTGPU_LEN as usize) };
Self { size, fb}
}
pub fn framebuffer(&mut self) -> &mut [u8] {
self.fb
}
pub fn paint_on_framebuffer(&mut self, p: impl FnOnce(&mut [u8]) -> ()) {
p(self.framebuffer());
framebuffer_flush();
}
}
impl OriginDimensions for Display {
fn size(&self) -> Size {
self.size
}
}
impl DrawTarget for Display {
type Color = Rgb888;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
{
pixels.into_iter().for_each(|px| {
let idx = (px.0.y * VIRTGPU_XRES as i32 + px.0.x)
as usize
* 4;
if idx + 2 >= self.fb.len() {
return;
}
self.fb[idx] = px.1.b();
self.fb[idx + 1] = px.1.g();
self.fb[idx + 2] = px.1.r();
});
framebuffer_flush();
Ok(())
}
}
pub fn event_get() -> Option<InputEvent> {
let raw_value = sys_event_get();
if raw_value == 0 {
None
} else {
Some((raw_value as u64).into())
}
}
pub fn key_pressed() -> bool {
if sys_key_pressed() == 1 {
true
} else {
false
}
}
#[repr(C)]
pub struct InputEvent {
pub event_type: u16,
pub code: u16,
pub value: u32,
}
impl From<u64> for InputEvent {
fn from(mut v: u64) -> Self {
let value = v as u32;
v >>= 32;
let code = v as u16;
v >>= 16;
let event_type = v as u16;
Self {
event_type,
code,
value,
}
}
}
impl InputEvent {
pub fn decode(&self) -> Option<DecodeType> {
Decoder::decode(
self.event_type as usize,
self.code as usize,
self.value as usize,
).ok()
}
}

View file

@ -8,6 +8,11 @@
pub mod console;
mod lang_items;
mod syscall;
mod file;
mod task;
mod sync;
mod io;
mod net;
extern crate alloc;
#[macro_use]
@ -16,6 +21,11 @@ extern crate bitflags;
use alloc::vec::Vec;
use buddy_system_allocator::LockedHeap;
use syscall::*;
pub use file::*;
pub use task::*;
pub use sync::*;
pub use io::*;
pub use net::*;
const USER_HEAP_SIZE: usize = 32768;
@ -59,148 +69,6 @@ fn main(_argc: usize, _argv: &[&str]) -> i32 {
panic!("Cannot find main!");
}
bitflags! {
pub struct OpenFlags: u32 {
const RDONLY = 0;
const WRONLY = 1 << 0;
const RDWR = 1 << 1;
const CREATE = 1 << 9;
const TRUNC = 1 << 10;
}
}
pub fn dup(fd: usize) -> isize {
sys_dup(fd)
}
pub fn open(path: &str, flags: OpenFlags) -> isize {
sys_open(path, flags.bits)
}
pub fn close(fd: usize) -> isize {
sys_close(fd)
}
pub fn pipe(pipe_fd: &mut [usize]) -> isize {
sys_pipe(pipe_fd)
}
pub fn read(fd: usize, buf: &mut [u8]) -> isize {
sys_read(fd, buf)
}
pub fn write(fd: usize, buf: &[u8]) -> isize {
sys_write(fd, buf)
}
pub fn exit(exit_code: i32) -> ! {
sys_exit(exit_code);
}
pub fn yield_() -> isize {
sys_yield()
}
pub fn get_time() -> isize {
sys_get_time()
}
pub fn getpid() -> isize {
sys_getpid()
}
pub fn fork() -> isize {
sys_fork()
}
pub fn exec(path: &str, args: &[*const u8]) -> isize {
sys_exec(path, args)
}
pub fn wait(exit_code: &mut i32) -> isize {
loop {
match sys_waitpid(-1, exit_code as *mut _) {
-2 => {
yield_();
}
// -1 or a real pid
exit_pid => return exit_pid,
}
}
}
pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
loop {
match sys_waitpid(pid as isize, exit_code as *mut _) {
-2 => {
yield_();
}
// -1 or a real pid
exit_pid => return exit_pid,
}
}
}
pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize {
sys_waitpid(pid as isize, exit_code as *mut _)
}
bitflags! {
pub struct SignalFlags: i32 {
const SIGINT = 1 << 2;
const SIGILL = 1 << 4;
const SIGABRT = 1 << 6;
const SIGFPE = 1 << 8;
const SIGSEGV = 1 << 11;
}
}
pub fn kill(pid: usize, signal: i32) -> isize {
sys_kill(pid, signal)
}
pub fn sleep(sleep_ms: usize) {
sys_sleep(sleep_ms);
}
pub fn thread_create(entry: usize, arg: usize) -> isize {
sys_thread_create(entry, arg)
}
pub fn gettid() -> isize {
sys_gettid()
}
pub fn waittid(tid: usize) -> isize {
loop {
match sys_waittid(tid) {
-2 => {
yield_();
}
exit_code => return exit_code,
}
}
}
pub fn mutex_create() -> isize {
sys_mutex_create(false)
}
pub fn mutex_blocking_create() -> isize {
sys_mutex_create(true)
}
pub fn mutex_lock(mutex_id: usize) {
sys_mutex_lock(mutex_id);
}
pub fn mutex_unlock(mutex_id: usize) {
sys_mutex_unlock(mutex_id);
}
pub fn semaphore_create(res_count: usize) -> isize {
sys_semaphore_create(res_count)
}
pub fn semaphore_up(sem_id: usize) {
sys_semaphore_up(sem_id);
}
pub fn semaphore_down(sem_id: usize) {
sys_semaphore_down(sem_id);
}
pub fn condvar_create() -> isize {
sys_condvar_create(0)
}
pub fn condvar_signal(condvar_id: usize) {
sys_condvar_signal(condvar_id);
}
pub fn condvar_wait(condvar_id: usize, mutex_id: usize) {
sys_condvar_wait(condvar_id, mutex_id);
}
pub fn create_desktop() {
sys_create_desktop();
}
#[macro_export]
macro_rules! vstore {
($var_ref: expr, $value: expr) => {

5
user/src/net.rs Normal file
View file

@ -0,0 +1,5 @@
use super::*;
pub fn connect(ip: u32, sport: u16, dport: u16) -> isize {
sys_connect(ip, sport, dport)
}

32
user/src/sync.rs Normal file
View file

@ -0,0 +1,32 @@
use super::*;
pub fn mutex_create() -> isize {
sys_mutex_create(false)
}
pub fn mutex_blocking_create() -> isize {
sys_mutex_create(true)
}
pub fn mutex_lock(mutex_id: usize) {
sys_mutex_lock(mutex_id);
}
pub fn mutex_unlock(mutex_id: usize) {
sys_mutex_unlock(mutex_id);
}
pub fn semaphore_create(res_count: usize) -> isize {
sys_semaphore_create(res_count)
}
pub fn semaphore_up(sem_id: usize) {
sys_semaphore_up(sem_id);
}
pub fn semaphore_down(sem_id: usize) {
sys_semaphore_down(sem_id);
}
pub fn condvar_create() -> isize {
sys_condvar_create()
}
pub fn condvar_signal(condvar_id: usize) {
sys_condvar_signal(condvar_id);
}
pub fn condvar_wait(condvar_id: usize, mutex_id: usize) {
sys_condvar_wait(condvar_id, mutex_id);
}

View file

@ -1,4 +1,5 @@
const SYSCALL_DUP: usize = 24;
const SYSCALL_CONNECT: usize = 29;
const SYSCALL_OPEN: usize = 56;
const SYSCALL_CLOSE: usize = 57;
const SYSCALL_PIPE: usize = 59;
@ -25,6 +26,10 @@ const SYSCALL_SEMAPHORE_DOWN: usize = 1022;
const SYSCALL_CONDVAR_CREATE: usize = 1030;
const SYSCALL_CONDVAR_SIGNAL: usize = 1031;
const SYSCALL_CONDVAR_WAIT: usize = 1032;
const SYSCALL_FRAMEBUFFER: usize = 2000;
const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001;
const SYSCALL_EVENT_GET: usize = 3000;
const SYSCALL_KEY_PRESSED: usize = 3001;
fn syscall(id: usize, args: [usize; 3]) -> isize {
let mut ret: isize;
@ -44,6 +49,10 @@ pub fn sys_dup(fd: usize) -> isize {
syscall(SYSCALL_DUP, [fd, 0, 0])
}
pub fn sys_connect(dest: u32, sport: u16, dport: u16) -> isize {
syscall(SYSCALL_CONNECT, [dest as usize, sport as usize, dport as usize])
}
pub fn sys_open(path: &str, flags: u32) -> isize {
syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0])
}
@ -143,8 +152,8 @@ pub fn sys_semaphore_down(sem_id: usize) -> isize {
syscall(SYSCALL_SEMAPHORE_DOWN, [sem_id, 0, 0])
}
pub fn sys_condvar_create(_arg: usize) -> isize {
syscall(SYSCALL_CONDVAR_CREATE, [_arg, 0, 0])
pub fn sys_condvar_create() -> isize {
syscall(SYSCALL_CONDVAR_CREATE, [0, 0, 0])
}
pub fn sys_condvar_signal(condvar_id: usize) -> isize {
@ -154,6 +163,20 @@ pub fn sys_condvar_signal(condvar_id: usize) -> isize {
pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize {
syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0])
}
pub fn sys_create_desktop() -> isize {
syscall(2000, [0, 0, 0])
pub fn sys_framebuffer() -> isize {
syscall(SYSCALL_FRAMEBUFFER, [0, 0, 0])
}
pub fn sys_framebuffer_flush() -> isize {
syscall(SYSCALL_FRAMEBUFFER_FLUSH, [0, 0, 0])
}
pub fn sys_event_get() -> isize {
syscall(SYSCALL_EVENT_GET, [0, 0, 0])
}
pub fn sys_key_pressed() -> isize {
syscall(SYSCALL_KEY_PRESSED, [0, 0, 0])
}

83
user/src/task.rs Normal file
View file

@ -0,0 +1,83 @@
use super::*;
pub fn exit(exit_code: i32) -> ! {
sys_exit(exit_code);
}
pub fn yield_() -> isize {
sys_yield()
}
pub fn get_time() -> isize {
sys_get_time()
}
pub fn getpid() -> isize {
sys_getpid()
}
pub fn fork() -> isize {
sys_fork()
}
pub fn exec(path: &str, args: &[*const u8]) -> isize {
sys_exec(path, args)
}
pub fn wait(exit_code: &mut i32) -> isize {
loop {
match sys_waitpid(-1, exit_code as *mut _) {
-2 => {
yield_();
}
// -1 or a real pid
exit_pid => return exit_pid,
}
}
}
pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
loop {
match sys_waitpid(pid as isize, exit_code as *mut _) {
-2 => {
yield_();
}
// -1 or a real pid
exit_pid => return exit_pid,
}
}
}
pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize {
sys_waitpid(pid as isize, exit_code as *mut _)
}
bitflags! {
pub struct SignalFlags: i32 {
const SIGINT = 1 << 2;
const SIGILL = 1 << 4;
const SIGABRT = 1 << 6;
const SIGFPE = 1 << 8;
const SIGSEGV = 1 << 11;
}
}
pub fn kill(pid: usize, signal: i32) -> isize {
sys_kill(pid, signal)
}
pub fn sleep(sleep_ms: usize) {
sys_sleep(sleep_ms);
}
pub fn thread_create(entry: usize, arg: usize) -> isize {
sys_thread_create(entry, arg)
}
pub fn gettid() -> isize {
sys_gettid()
}
pub fn waittid(tid: usize) -> isize {
loop {
match sys_waittid(tid) {
-2 => {
yield_();
}
exit_code => return exit_code,
}
}
}