diff --git a/ch1/README.md b/ch1/README.md
new file mode 100644
index 00000000..eef7a09b
--- /dev/null
+++ b/ch1/README.md
@@ -0,0 +1,3 @@
+# Tutorial 第一章测试用例
+
+第一章只需要在 RV64 裸机平台运行一个嵌入式应用,因此只要能够在 SBI 的帮助下在屏幕上输出一行字符串就算成功。
\ No newline at end of file
diff --git a/ch2/.cargo/config b/ch2/.cargo/config
new file mode 100644
index 00000000..e5ded8a1
--- /dev/null
+++ b/ch2/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+ "-Clink-args=-Tsrc/linker.ld",
+]
diff --git a/ch2/.gitignore b/ch2/.gitignore
new file mode 100644
index 00000000..fbd7c914
--- /dev/null
+++ b/ch2/.gitignore
@@ -0,0 +1,2 @@
+target/*
+Cargo.lock
diff --git a/ch2/.idea/misc.xml b/ch2/.idea/misc.xml
new file mode 100644
index 00000000..56189180
--- /dev/null
+++ b/ch2/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch2/.idea/user.iml b/ch2/.idea/user.iml
new file mode 100644
index 00000000..b44d0258
--- /dev/null
+++ b/ch2/.idea/user.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch2/.idea/vcs.xml b/ch2/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/ch2/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch2/.idea/workspace.xml b/ch2/.idea/workspace.xml
new file mode 100644
index 00000000..b4ed534b
--- /dev/null
+++ b/ch2/.idea/workspace.xml
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1605727203780
+
+
+ 1605727203780
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch2/Cargo.toml b/ch2/Cargo.toml
new file mode 100644
index 00000000..817ca525
--- /dev/null
+++ b/ch2/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "user_lib"
+version = "0.1.0"
+authors = ["Yifan Wu "]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/ch2/Makefile b/ch2/Makefile
new file mode 100644
index 00000000..ab508d9a
--- /dev/null
+++ b/ch2/Makefile
@@ -0,0 +1,21 @@
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+APP_DIR := src/bin
+TARGET_DIR := target/$(TARGET)/$(MODE)
+APPS := $(wildcard $(APP_DIR)/*.rs)
+ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
+BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
+
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+elf:
+ @cargo build --release
+ @echo $(APPS)
+ @echo $(ELFS)
+ @echo $(BINS)
+
+binary: elf
+ $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
+
+build: binary
diff --git a/ch2/README.md b/ch2/README.md
new file mode 100644
index 00000000..f70bc956
--- /dev/null
+++ b/ch2/README.md
@@ -0,0 +1,20 @@
+# Tutorial 第二章测试用例
+
+第二章我们需要实现一个批处理系统。在 `src/bin` 目录中,我们可以找到三个应用程序:
+
+* `00hello_world`
+* `01store_fault`
+* `02power`
+
+我们需要按照编号从小到大的顺序去加载并运行它们。
+
+应用被设计为运行在用户模式,批处理系统应运行在监督模式,它们都直接访问物理内存。
+
+三个应用被需要被加载到同一个物理地址。
+
+本章需要实现的系统调用:
+
+* `sys_write` 用于向屏幕输出字符串;
+* `sys_exit` 用于告知批处理系统当前应用退出,应切换到下一个应用。
+
+注意:应用 `01store_fault` 会访问非法的物理地址,批处理系统需要杀死它并能够正常运行序列中的下一个应用 `02power`。
\ No newline at end of file
diff --git a/ch2/src/bin/00hello_world.rs b/ch2/src/bin/00hello_world.rs
new file mode 100644
index 00000000..684396a2
--- /dev/null
+++ b/ch2/src/bin/00hello_world.rs
@@ -0,0 +1,11 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+#[no_mangle]
+fn main() -> i32 {
+ println!("Hello, world!");
+ 0
+}
\ No newline at end of file
diff --git a/ch2/src/bin/01store_fault.rs b/ch2/src/bin/01store_fault.rs
new file mode 100644
index 00000000..790196b2
--- /dev/null
+++ b/ch2/src/bin/01store_fault.rs
@@ -0,0 +1,14 @@
+#![no_std]
+#![no_main]
+#![feature(llvm_asm)]
+
+#[macro_use]
+extern crate user_lib;
+
+#[no_mangle]
+fn main() -> i32 {
+ println!("Into Test store_fault, we will insert an invalid store operation...");
+ println!("Kernel should kill this application!");
+ unsafe { (0x0 as *mut u8).write_volatile(0); }
+ 0
+}
\ No newline at end of file
diff --git a/tests/chapter2/power.rs b/ch2/src/bin/02power.rs
similarity index 72%
rename from tests/chapter2/power.rs
rename to ch2/src/bin/02power.rs
index 8826a900..86154c71 100644
--- a/tests/chapter2/power.rs
+++ b/ch2/src/bin/02power.rs
@@ -1,12 +1,16 @@
-/// rCore application without user_lib.
-/// Load it manually to 0x80040000.
-/// It should OK.
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
const SIZE: usize = 10;
const P: u32 = 3;
-const STEP: usize = 10000000;
+const STEP: usize = 100000;
const MOD: u32 = 10007;
-fn main() -> ! {
+
+#[no_mangle]
+fn main() -> i32 {
let mut pow = [0u32; SIZE];
let mut index: usize = 0;
pow[index] = 1;
@@ -19,5 +23,5 @@ fn main() -> ! {
}
}
println!("Test power OK!");
- loop {}
+ 0
}
\ No newline at end of file
diff --git a/ch2/src/console.rs b/ch2/src/console.rs
new file mode 100644
index 00000000..a826b8fe
--- /dev/null
+++ b/ch2/src/console.rs
@@ -0,0 +1,29 @@
+use core::fmt::{self, Write};
+use crate::syscall::{STDOUT, sys_write};
+
+struct Stdout;
+
+impl Write for Stdout {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ sys_write(STDOUT, s.as_bytes());
+ Ok(())
+ }
+}
+
+pub fn print(args: fmt::Arguments) {
+ Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+ }
+}
+
+#[macro_export]
+macro_rules! println {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+ }
+}
\ No newline at end of file
diff --git a/ch2/src/lang_items.rs b/ch2/src/lang_items.rs
new file mode 100644
index 00000000..c90d297f
--- /dev/null
+++ b/ch2/src/lang_items.rs
@@ -0,0 +1,10 @@
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+ let err = panic_info.message().unwrap();
+ if let Some(location) = panic_info.location() {
+ println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
+ } else {
+ println!("Panicked: {}", err);
+ }
+ loop {}
+}
\ No newline at end of file
diff --git a/ch2/src/lib.rs b/ch2/src/lib.rs
new file mode 100644
index 00000000..ed7ba84c
--- /dev/null
+++ b/ch2/src/lib.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![feature(llvm_asm)]
+#![feature(linkage)]
+#![feature(panic_info_message)]
+
+#[macro_use]
+pub mod console;
+mod syscall;
+mod lang_items;
+
+#[no_mangle]
+#[link_section = ".text.entry"]
+pub extern "C" fn _start() -> ! {
+ clear_bss();
+ syscall::sys_exit(main());
+ panic!("unreachable after sys_exit!");
+}
+
+#[linkage = "weak"]
+#[no_mangle]
+fn main() -> i32 {
+ panic!("Cannot find main!");
+}
+
+fn clear_bss() {
+ extern "C" {
+ fn start_bss();
+ fn end_bss();
+ }
+ (start_bss as usize..end_bss as usize).for_each(|addr| {
+ unsafe { (addr as *mut u8).write_volatile(0); }
+ });
+}
\ No newline at end of file
diff --git a/ch2/src/linker.ld b/ch2/src/linker.ld
new file mode 100644
index 00000000..cbf7486e
--- /dev/null
+++ b/ch2/src/linker.ld
@@ -0,0 +1,28 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x80040000;
+
+SECTIONS
+{
+ . = BASE_ADDRESS;
+ .text : {
+ *(.text.entry)
+ *(.text .text.*)
+ }
+ .rodata : {
+ *(.rodata .rodata.*)
+ }
+ .data : {
+ *(.data .data.*)
+ }
+ .bss : {
+ start_bss = .;
+ *(.bss .bss.*)
+ end_bss = .;
+ }
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.debug*)
+ }
+}
\ No newline at end of file
diff --git a/ch2/src/syscall.rs b/ch2/src/syscall.rs
new file mode 100644
index 00000000..edb26d77
--- /dev/null
+++ b/ch2/src/syscall.rs
@@ -0,0 +1,25 @@
+pub const STDOUT: usize = 1;
+
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+ let mut ret: isize;
+ unsafe {
+ llvm_asm!("ecall"
+ : "={x10}" (ret)
+ : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
+ : "memory"
+ : "volatile"
+ );
+ }
+ ret
+}
+
+pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
+ syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_exit(xstate: i32) -> isize {
+ syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
+}
diff --git a/ch3-coop/.cargo/config b/ch3-coop/.cargo/config
new file mode 100644
index 00000000..e5ded8a1
--- /dev/null
+++ b/ch3-coop/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+ "-Clink-args=-Tsrc/linker.ld",
+]
diff --git a/ch3-coop/.gitignore b/ch3-coop/.gitignore
new file mode 100644
index 00000000..e420ee4b
--- /dev/null
+++ b/ch3-coop/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/ch3-coop/.idea/misc.xml b/ch3-coop/.idea/misc.xml
new file mode 100644
index 00000000..56189180
--- /dev/null
+++ b/ch3-coop/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3-coop/.idea/user.iml b/ch3-coop/.idea/user.iml
new file mode 100644
index 00000000..b44d0258
--- /dev/null
+++ b/ch3-coop/.idea/user.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3-coop/.idea/vcs.xml b/ch3-coop/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/ch3-coop/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3-coop/.idea/workspace.xml b/ch3-coop/.idea/workspace.xml
new file mode 100644
index 00000000..b4ed534b
--- /dev/null
+++ b/ch3-coop/.idea/workspace.xml
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1605727203780
+
+
+ 1605727203780
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3-coop/Cargo.toml b/ch3-coop/Cargo.toml
new file mode 100644
index 00000000..817ca525
--- /dev/null
+++ b/ch3-coop/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "user_lib"
+version = "0.1.0"
+authors = ["Yifan Wu "]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/ch3-coop/Makefile b/ch3-coop/Makefile
new file mode 100644
index 00000000..5ca334ea
--- /dev/null
+++ b/ch3-coop/Makefile
@@ -0,0 +1,23 @@
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+APP_DIR := src/bin
+TARGET_DIR := target/$(TARGET)/$(MODE)
+APPS := $(wildcard $(APP_DIR)/*.rs)
+ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
+BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
+
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+elf: $(APPS)
+ @python3 build.py
+
+binary: elf
+ $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
+
+build: binary
+
+clean:
+ @cargo clean
+
+.PHONY: elf binary build clean
\ No newline at end of file
diff --git a/ch3-coop/README.md b/ch3-coop/README.md
new file mode 100644
index 00000000..b5bf1e13
--- /dev/null
+++ b/ch3-coop/README.md
@@ -0,0 +1,4 @@
+# Tutorial 第三章测试用例 part1
+
+在 Tutorial 第三章第一阶段中,只需实现一个非抢占式调度的分时多任务系统。
+
diff --git a/ch3-coop/build.py b/ch3-coop/build.py
new file mode 100644
index 00000000..b558177a
--- /dev/null
+++ b/ch3-coop/build.py
@@ -0,0 +1,25 @@
+import os
+
+base_address = 0x80100000
+step = 0x20000
+linker = 'src/linker.ld'
+
+app_id = 0
+apps = os.listdir('src/bin')
+apps.sort()
+for app in apps:
+ app = app[:app.find('.')]
+ lines = []
+ lines_before = []
+ with open(linker, 'r') as f:
+ for line in f.readlines():
+ lines_before.append(line)
+ line = line.replace(hex(base_address), hex(base_address+step*app_id))
+ lines.append(line)
+ with open(linker, 'w+') as f:
+ f.writelines(lines)
+ os.system('cargo build --bin %s --release' % app)
+ print('[build.py] application %s start with address %s' %(app, hex(base_address+step*app_id)))
+ with open(linker, 'w+') as f:
+ f.writelines(lines_before)
+ app_id = app_id + 1
diff --git a/ch3-coop/src/bin/00write_a.rs b/ch3-coop/src/bin/00write_a.rs
new file mode 100644
index 00000000..f7ada612
--- /dev/null
+++ b/ch3-coop/src/bin/00write_a.rs
@@ -0,0 +1,21 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::sys_yield;
+
+const WIDTH: usize = 10;
+const HEIGHT: usize = 5;
+
+#[no_mangle]
+fn main() -> i32 {
+ for i in 0..HEIGHT {
+ for _ in 0..WIDTH { print!("A"); }
+ println!(" [{}/{}]", i + 1, HEIGHT);
+ sys_yield();
+ }
+ println!("Test write_a OK!");
+ 0
+}
\ No newline at end of file
diff --git a/ch3-coop/src/bin/01write_b.rs b/ch3-coop/src/bin/01write_b.rs
new file mode 100644
index 00000000..e16b79da
--- /dev/null
+++ b/ch3-coop/src/bin/01write_b.rs
@@ -0,0 +1,21 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::sys_yield;
+
+const WIDTH: usize = 10;
+const HEIGHT: usize = 2;
+
+#[no_mangle]
+fn main() -> i32 {
+ for i in 0..HEIGHT {
+ for _ in 0..WIDTH { print!("B"); }
+ println!(" [{}/{}]", i + 1, HEIGHT);
+ sys_yield();
+ }
+ println!("Test write_b OK!");
+ 0
+}
diff --git a/ch3-coop/src/bin/02write_c.rs b/ch3-coop/src/bin/02write_c.rs
new file mode 100644
index 00000000..771f6447
--- /dev/null
+++ b/ch3-coop/src/bin/02write_c.rs
@@ -0,0 +1,21 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::sys_yield;
+
+const WIDTH: usize = 10;
+const HEIGHT: usize = 3;
+
+#[no_mangle]
+fn main() -> i32 {
+ for i in 0..HEIGHT {
+ for _ in 0..WIDTH { print!("C"); }
+ println!(" [{}/{}]", i + 1, HEIGHT);
+ sys_yield();
+ }
+ println!("Test write_c OK!");
+ 0
+}
diff --git a/ch3-coop/src/console.rs b/ch3-coop/src/console.rs
new file mode 100644
index 00000000..a826b8fe
--- /dev/null
+++ b/ch3-coop/src/console.rs
@@ -0,0 +1,29 @@
+use core::fmt::{self, Write};
+use crate::syscall::{STDOUT, sys_write};
+
+struct Stdout;
+
+impl Write for Stdout {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ sys_write(STDOUT, s.as_bytes());
+ Ok(())
+ }
+}
+
+pub fn print(args: fmt::Arguments) {
+ Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+ }
+}
+
+#[macro_export]
+macro_rules! println {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+ }
+}
\ No newline at end of file
diff --git a/ch3-coop/src/lang_items.rs b/ch3-coop/src/lang_items.rs
new file mode 100644
index 00000000..c90d297f
--- /dev/null
+++ b/ch3-coop/src/lang_items.rs
@@ -0,0 +1,10 @@
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+ let err = panic_info.message().unwrap();
+ if let Some(location) = panic_info.location() {
+ println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
+ } else {
+ println!("Panicked: {}", err);
+ }
+ loop {}
+}
\ No newline at end of file
diff --git a/ch3-coop/src/lib.rs b/ch3-coop/src/lib.rs
new file mode 100644
index 00000000..fdcb6d56
--- /dev/null
+++ b/ch3-coop/src/lib.rs
@@ -0,0 +1,35 @@
+#![no_std]
+#![feature(llvm_asm)]
+#![feature(linkage)]
+#![feature(panic_info_message)]
+
+#[macro_use]
+pub mod console;
+mod syscall;
+mod lang_items;
+
+#[no_mangle]
+#[link_section = ".text.entry"]
+pub extern "C" fn _start() -> ! {
+ clear_bss();
+ syscall::sys_exit(main());
+ panic!("unreachable after sys_exit!");
+}
+
+#[linkage = "weak"]
+#[no_mangle]
+fn main() -> i32 {
+ panic!("Cannot find main!");
+}
+
+fn clear_bss() {
+ extern "C" {
+ fn start_bss();
+ fn end_bss();
+ }
+ (start_bss as usize..end_bss as usize).for_each(|addr| {
+ unsafe { (addr as *mut u8).write_volatile(0); }
+ });
+}
+
+pub use syscall::*;
\ No newline at end of file
diff --git a/ch3-coop/src/linker.ld b/ch3-coop/src/linker.ld
new file mode 100644
index 00000000..1c64a19b
--- /dev/null
+++ b/ch3-coop/src/linker.ld
@@ -0,0 +1,29 @@
+
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x80100000;
+
+SECTIONS
+{
+ . = BASE_ADDRESS;
+ .text : {
+ *(.text.entry)
+ *(.text .text.*)
+ }
+ .rodata : {
+ *(.rodata .rodata.*)
+ }
+ .data : {
+ *(.data .data.*)
+ }
+ .bss : {
+ start_bss = .;
+ *(.bss .bss.*)
+ end_bss = .;
+ }
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.debug*)
+ }
+}
\ No newline at end of file
diff --git a/ch3-coop/src/syscall.rs b/ch3-coop/src/syscall.rs
new file mode 100644
index 00000000..8e4ff50b
--- /dev/null
+++ b/ch3-coop/src/syscall.rs
@@ -0,0 +1,35 @@
+pub const STDOUT: usize = 1;
+
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+const SYSCALL_YIELD: usize = 124;
+const SYSCALL_GET_TIME: usize = 169;
+
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+ let mut ret: isize;
+ unsafe {
+ llvm_asm!("ecall"
+ : "={x10}" (ret)
+ : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
+ : "memory"
+ : "volatile"
+ );
+ }
+ ret
+}
+
+pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
+ syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_exit(xstate: i32) -> isize {
+ syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
+}
+
+pub fn sys_yield() -> isize {
+ syscall(SYSCALL_YIELD, [0, 0, 0])
+}
+
+pub fn sys_get_time() -> isize {
+ syscall(SYSCALL_GET_TIME, [0, 0, 0])
+}
\ No newline at end of file
diff --git a/ch3/.cargo/config b/ch3/.cargo/config
new file mode 100644
index 00000000..e5ded8a1
--- /dev/null
+++ b/ch3/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+ "-Clink-args=-Tsrc/linker.ld",
+]
diff --git a/ch3/.gitignore b/ch3/.gitignore
new file mode 100644
index 00000000..e420ee4b
--- /dev/null
+++ b/ch3/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/ch3/.idea/misc.xml b/ch3/.idea/misc.xml
new file mode 100644
index 00000000..56189180
--- /dev/null
+++ b/ch3/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3/.idea/user.iml b/ch3/.idea/user.iml
new file mode 100644
index 00000000..b44d0258
--- /dev/null
+++ b/ch3/.idea/user.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3/.idea/vcs.xml b/ch3/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/ch3/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3/.idea/workspace.xml b/ch3/.idea/workspace.xml
new file mode 100644
index 00000000..b4ed534b
--- /dev/null
+++ b/ch3/.idea/workspace.xml
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1605727203780
+
+
+ 1605727203780
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch3/Cargo.toml b/ch3/Cargo.toml
new file mode 100644
index 00000000..817ca525
--- /dev/null
+++ b/ch3/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "user_lib"
+version = "0.1.0"
+authors = ["Yifan Wu "]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/ch3/Makefile b/ch3/Makefile
new file mode 100644
index 00000000..5ca334ea
--- /dev/null
+++ b/ch3/Makefile
@@ -0,0 +1,23 @@
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+APP_DIR := src/bin
+TARGET_DIR := target/$(TARGET)/$(MODE)
+APPS := $(wildcard $(APP_DIR)/*.rs)
+ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
+BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
+
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+elf: $(APPS)
+ @python3 build.py
+
+binary: elf
+ $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
+
+build: binary
+
+clean:
+ @cargo clean
+
+.PHONY: elf binary build clean
\ No newline at end of file
diff --git a/ch3/build.py b/ch3/build.py
new file mode 100644
index 00000000..b558177a
--- /dev/null
+++ b/ch3/build.py
@@ -0,0 +1,25 @@
+import os
+
+base_address = 0x80100000
+step = 0x20000
+linker = 'src/linker.ld'
+
+app_id = 0
+apps = os.listdir('src/bin')
+apps.sort()
+for app in apps:
+ app = app[:app.find('.')]
+ lines = []
+ lines_before = []
+ with open(linker, 'r') as f:
+ for line in f.readlines():
+ lines_before.append(line)
+ line = line.replace(hex(base_address), hex(base_address+step*app_id))
+ lines.append(line)
+ with open(linker, 'w+') as f:
+ f.writelines(lines)
+ os.system('cargo build --bin %s --release' % app)
+ print('[build.py] application %s start with address %s' %(app, hex(base_address+step*app_id)))
+ with open(linker, 'w+') as f:
+ f.writelines(lines_before)
+ app_id = app_id + 1
diff --git a/ch3/src/bin/00power_3.rs b/ch3/src/bin/00power_3.rs
new file mode 100644
index 00000000..064b4e07
--- /dev/null
+++ b/ch3/src/bin/00power_3.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+const LEN: usize = 100;
+
+#[no_mangle]
+fn main() -> i32 {
+ let p = 3u64;
+ let m = 998244353u64;
+ let iter: usize = 200000;
+ let mut s = [0u64; LEN];
+ let mut cur = 0usize;
+ s[cur] = 1;
+ for i in 1..=iter {
+ let next = if cur + 1 == LEN { 0 } else { cur + 1 };
+ s[next] = s[cur] * p % m;
+ cur = next;
+ if i % 10000 == 0 {
+ println!("power_3 [{}/{}]", i, iter);
+ }
+ }
+ println!("{}^{} = {}", p, iter, s[cur]);
+ println!("Test power_3 OK!");
+ 0
+}
\ No newline at end of file
diff --git a/ch3/src/bin/01power_5.rs b/ch3/src/bin/01power_5.rs
new file mode 100644
index 00000000..157af298
--- /dev/null
+++ b/ch3/src/bin/01power_5.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+const LEN: usize = 100;
+
+#[no_mangle]
+fn main() -> i32 {
+ let p = 5u64;
+ let m = 998244353u64;
+ let iter: usize = 140000;
+ let mut s = [0u64; LEN];
+ let mut cur = 0usize;
+ s[cur] = 1;
+ for i in 1..=iter {
+ let next = if cur + 1 == LEN { 0 } else { cur + 1 };
+ s[next] = s[cur] * p % m;
+ cur = next;
+ if i % 10000 == 0 {
+ println!("power_5 [{}/{}]", i, iter);
+ }
+ }
+ println!("{}^{} = {}", p, iter, s[cur]);
+ println!("Test power_5 OK!");
+ 0
+}
diff --git a/ch3/src/bin/02power_7.rs b/ch3/src/bin/02power_7.rs
new file mode 100644
index 00000000..a5c72dba
--- /dev/null
+++ b/ch3/src/bin/02power_7.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+const LEN: usize = 100;
+
+#[no_mangle]
+fn main() -> i32 {
+ let p = 7u64;
+ let m = 998244353u64;
+ let iter: usize = 160000;
+ let mut s = [0u64; LEN];
+ let mut cur = 0usize;
+ s[cur] = 1;
+ for i in 1..=iter {
+ let next = if cur + 1 == LEN { 0 } else { cur + 1 };
+ s[next] = s[cur] * p % m;
+ cur = next;
+ if i % 10000 == 0 {
+ println!("power_7 [{}/{}]", i, iter);
+ }
+ }
+ println!("{}^{} = {}", p, iter, s[cur]);
+ println!("Test power_7 OK!");
+ 0
+}
diff --git a/ch3/src/bin/03sleep.rs b/ch3/src/bin/03sleep.rs
new file mode 100644
index 00000000..f3d9f31b
--- /dev/null
+++ b/ch3/src/bin/03sleep.rs
@@ -0,0 +1,18 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sys_get_time, sys_yield};
+
+#[no_mangle]
+fn main() -> i32 {
+ let current_timer = sys_get_time();
+ let wait_for = current_timer + 10000000;
+ while sys_get_time() < wait_for {
+ sys_yield();
+ }
+ println!("Test sleep OK!");
+ 0
+}
\ No newline at end of file
diff --git a/ch3/src/console.rs b/ch3/src/console.rs
new file mode 100644
index 00000000..a826b8fe
--- /dev/null
+++ b/ch3/src/console.rs
@@ -0,0 +1,29 @@
+use core::fmt::{self, Write};
+use crate::syscall::{STDOUT, sys_write};
+
+struct Stdout;
+
+impl Write for Stdout {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ sys_write(STDOUT, s.as_bytes());
+ Ok(())
+ }
+}
+
+pub fn print(args: fmt::Arguments) {
+ Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+ }
+}
+
+#[macro_export]
+macro_rules! println {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+ }
+}
\ No newline at end of file
diff --git a/ch3/src/lang_items.rs b/ch3/src/lang_items.rs
new file mode 100644
index 00000000..c90d297f
--- /dev/null
+++ b/ch3/src/lang_items.rs
@@ -0,0 +1,10 @@
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+ let err = panic_info.message().unwrap();
+ if let Some(location) = panic_info.location() {
+ println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
+ } else {
+ println!("Panicked: {}", err);
+ }
+ loop {}
+}
\ No newline at end of file
diff --git a/ch3/src/lib.rs b/ch3/src/lib.rs
new file mode 100644
index 00000000..fdcb6d56
--- /dev/null
+++ b/ch3/src/lib.rs
@@ -0,0 +1,35 @@
+#![no_std]
+#![feature(llvm_asm)]
+#![feature(linkage)]
+#![feature(panic_info_message)]
+
+#[macro_use]
+pub mod console;
+mod syscall;
+mod lang_items;
+
+#[no_mangle]
+#[link_section = ".text.entry"]
+pub extern "C" fn _start() -> ! {
+ clear_bss();
+ syscall::sys_exit(main());
+ panic!("unreachable after sys_exit!");
+}
+
+#[linkage = "weak"]
+#[no_mangle]
+fn main() -> i32 {
+ panic!("Cannot find main!");
+}
+
+fn clear_bss() {
+ extern "C" {
+ fn start_bss();
+ fn end_bss();
+ }
+ (start_bss as usize..end_bss as usize).for_each(|addr| {
+ unsafe { (addr as *mut u8).write_volatile(0); }
+ });
+}
+
+pub use syscall::*;
\ No newline at end of file
diff --git a/ch3/src/linker.ld b/ch3/src/linker.ld
new file mode 100644
index 00000000..1c64a19b
--- /dev/null
+++ b/ch3/src/linker.ld
@@ -0,0 +1,29 @@
+
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x80100000;
+
+SECTIONS
+{
+ . = BASE_ADDRESS;
+ .text : {
+ *(.text.entry)
+ *(.text .text.*)
+ }
+ .rodata : {
+ *(.rodata .rodata.*)
+ }
+ .data : {
+ *(.data .data.*)
+ }
+ .bss : {
+ start_bss = .;
+ *(.bss .bss.*)
+ end_bss = .;
+ }
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.debug*)
+ }
+}
\ No newline at end of file
diff --git a/ch3/src/syscall.rs b/ch3/src/syscall.rs
new file mode 100644
index 00000000..8e4ff50b
--- /dev/null
+++ b/ch3/src/syscall.rs
@@ -0,0 +1,35 @@
+pub const STDOUT: usize = 1;
+
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+const SYSCALL_YIELD: usize = 124;
+const SYSCALL_GET_TIME: usize = 169;
+
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+ let mut ret: isize;
+ unsafe {
+ llvm_asm!("ecall"
+ : "={x10}" (ret)
+ : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
+ : "memory"
+ : "volatile"
+ );
+ }
+ ret
+}
+
+pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
+ syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_exit(xstate: i32) -> isize {
+ syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
+}
+
+pub fn sys_yield() -> isize {
+ syscall(SYSCALL_YIELD, [0, 0, 0])
+}
+
+pub fn sys_get_time() -> isize {
+ syscall(SYSCALL_GET_TIME, [0, 0, 0])
+}
\ No newline at end of file
diff --git a/ch4/.cargo/config b/ch4/.cargo/config
new file mode 100644
index 00000000..e5ded8a1
--- /dev/null
+++ b/ch4/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+ "-Clink-args=-Tsrc/linker.ld",
+]
diff --git a/ch4/.gitignore b/ch4/.gitignore
new file mode 100644
index 00000000..e420ee4b
--- /dev/null
+++ b/ch4/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/ch4/.idea/misc.xml b/ch4/.idea/misc.xml
new file mode 100644
index 00000000..56189180
--- /dev/null
+++ b/ch4/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch4/.idea/user.iml b/ch4/.idea/user.iml
new file mode 100644
index 00000000..b44d0258
--- /dev/null
+++ b/ch4/.idea/user.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch4/.idea/vcs.xml b/ch4/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/ch4/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch4/.idea/workspace.xml b/ch4/.idea/workspace.xml
new file mode 100644
index 00000000..b4ed534b
--- /dev/null
+++ b/ch4/.idea/workspace.xml
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1605727203780
+
+
+ 1605727203780
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch4/Cargo.toml b/ch4/Cargo.toml
new file mode 100644
index 00000000..817ca525
--- /dev/null
+++ b/ch4/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "user_lib"
+version = "0.1.0"
+authors = ["Yifan Wu "]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/ch4/Makefile b/ch4/Makefile
new file mode 100644
index 00000000..5e0f87c3
--- /dev/null
+++ b/ch4/Makefile
@@ -0,0 +1,23 @@
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+APP_DIR := src/bin
+TARGET_DIR := target/$(TARGET)/$(MODE)
+APPS := $(wildcard $(APP_DIR)/*.rs)
+ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
+BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
+
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+elf: $(APPS)
+ @cargo build --release
+
+binary: elf
+ $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
+
+build: binary
+
+clean:
+ @cargo clean
+
+.PHONY: elf binary build clean
\ No newline at end of file
diff --git a/ch4/src/bin/00power_3.rs b/ch4/src/bin/00power_3.rs
new file mode 100644
index 00000000..532829da
--- /dev/null
+++ b/ch4/src/bin/00power_3.rs
@@ -0,0 +1,29 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+const LEN: usize = 100;
+
+static mut S: [u64; LEN] = [0u64; LEN];
+
+#[no_mangle]
+unsafe fn main() -> i32 {
+ let p = 3u64;
+ let m = 998244353u64;
+ let iter: usize = 300000;
+ let mut cur = 0usize;
+ S[cur] = 1;
+ for i in 1..=iter {
+ let next = if cur + 1 == LEN { 0 } else { cur + 1 };
+ S[next] = S[cur] * p % m;
+ cur = next;
+ if i % 10000 == 0 {
+ println!("power_3 [{}/{}]", i, iter);
+ }
+ }
+ println!("{}^{} = {}(mod {})", p, iter, S[cur], m);
+ println!("Test power_3 OK!");
+ 0
+}
\ No newline at end of file
diff --git a/ch4/src/bin/01power_5.rs b/ch4/src/bin/01power_5.rs
new file mode 100644
index 00000000..f23e3b84
--- /dev/null
+++ b/ch4/src/bin/01power_5.rs
@@ -0,0 +1,29 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+const LEN: usize = 100;
+
+static mut S: [u64; LEN] = [0u64; LEN];
+
+#[no_mangle]
+unsafe fn main() -> i32 {
+ let p = 5u64;
+ let m = 998244353u64;
+ let iter: usize = 210000;
+ let mut cur = 0usize;
+ S[cur] = 1;
+ for i in 1..=iter {
+ let next = if cur + 1 == LEN { 0 } else { cur + 1 };
+ S[next] = S[cur] * p % m;
+ cur = next;
+ if i % 10000 == 0 {
+ println!("power_5 [{}/{}]", i, iter);
+ }
+ }
+ println!("{}^{} = {}(mod {})", p, iter, S[cur], m);
+ println!("Test power_5 OK!");
+ 0
+}
diff --git a/ch4/src/bin/02power_7.rs b/ch4/src/bin/02power_7.rs
new file mode 100644
index 00000000..35bada18
--- /dev/null
+++ b/ch4/src/bin/02power_7.rs
@@ -0,0 +1,29 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+const LEN: usize = 100;
+
+static mut S: [u64; LEN] = [0u64; LEN];
+
+#[no_mangle]
+unsafe fn main() -> i32 {
+ let p = 7u64;
+ let m = 998244353u64;
+ let iter: usize = 240000;
+ let mut cur = 0usize;
+ S[cur] = 1;
+ for i in 1..=iter {
+ let next = if cur + 1 == LEN { 0 } else { cur + 1 };
+ S[next] = S[cur] * p % m;
+ cur = next;
+ if i % 10000 == 0 {
+ println!("power_7 [{}/{}]", i, iter);
+ }
+ }
+ println!("{}^{} = {}(mod {})", p, iter, S[cur], m);
+ println!("Test power_7 OK!");
+ 0
+}
diff --git a/ch4/src/bin/03sleep.rs b/ch4/src/bin/03sleep.rs
new file mode 100644
index 00000000..f3d9f31b
--- /dev/null
+++ b/ch4/src/bin/03sleep.rs
@@ -0,0 +1,18 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sys_get_time, sys_yield};
+
+#[no_mangle]
+fn main() -> i32 {
+ let current_timer = sys_get_time();
+ let wait_for = current_timer + 10000000;
+ while sys_get_time() < wait_for {
+ sys_yield();
+ }
+ println!("Test sleep OK!");
+ 0
+}
\ No newline at end of file
diff --git a/ch4/src/console.rs b/ch4/src/console.rs
new file mode 100644
index 00000000..a826b8fe
--- /dev/null
+++ b/ch4/src/console.rs
@@ -0,0 +1,29 @@
+use core::fmt::{self, Write};
+use crate::syscall::{STDOUT, sys_write};
+
+struct Stdout;
+
+impl Write for Stdout {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ sys_write(STDOUT, s.as_bytes());
+ Ok(())
+ }
+}
+
+pub fn print(args: fmt::Arguments) {
+ Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+ }
+}
+
+#[macro_export]
+macro_rules! println {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+ }
+}
\ No newline at end of file
diff --git a/ch4/src/lang_items.rs b/ch4/src/lang_items.rs
new file mode 100644
index 00000000..c90d297f
--- /dev/null
+++ b/ch4/src/lang_items.rs
@@ -0,0 +1,10 @@
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+ let err = panic_info.message().unwrap();
+ if let Some(location) = panic_info.location() {
+ println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
+ } else {
+ println!("Panicked: {}", err);
+ }
+ loop {}
+}
\ No newline at end of file
diff --git a/ch4/src/lib.rs b/ch4/src/lib.rs
new file mode 100644
index 00000000..c072ef49
--- /dev/null
+++ b/ch4/src/lib.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![feature(llvm_asm)]
+#![feature(linkage)]
+#![feature(panic_info_message)]
+
+#[macro_use]
+pub mod console;
+mod syscall;
+mod lang_items;
+
+#[no_mangle]
+#[link_section = ".text.entry"]
+pub extern "C" fn _start() -> ! {
+ syscall::sys_exit(main());
+ panic!("unreachable after sys_exit!");
+}
+
+#[linkage = "weak"]
+#[no_mangle]
+fn main() -> i32 {
+ panic!("Cannot find main!");
+}
+
+
+pub use syscall::*;
\ No newline at end of file
diff --git a/ch4/src/linker.ld b/ch4/src/linker.ld
new file mode 100644
index 00000000..e05a98ba
--- /dev/null
+++ b/ch4/src/linker.ld
@@ -0,0 +1,29 @@
+
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x0;
+
+SECTIONS
+{
+ . = BASE_ADDRESS;
+ .text : {
+ *(.text.entry)
+ *(.text .text.*)
+ }
+ . = ALIGN(4K);
+ .rodata : {
+ *(.rodata .rodata.*)
+ }
+ . = ALIGN(4K);
+ .data : {
+ *(.data .data.*)
+ }
+ .bss : {
+ *(.bss .bss.*)
+ }
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.debug*)
+ }
+}
\ No newline at end of file
diff --git a/ch4/src/syscall.rs b/ch4/src/syscall.rs
new file mode 100644
index 00000000..8e4ff50b
--- /dev/null
+++ b/ch4/src/syscall.rs
@@ -0,0 +1,35 @@
+pub const STDOUT: usize = 1;
+
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+const SYSCALL_YIELD: usize = 124;
+const SYSCALL_GET_TIME: usize = 169;
+
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+ let mut ret: isize;
+ unsafe {
+ llvm_asm!("ecall"
+ : "={x10}" (ret)
+ : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
+ : "memory"
+ : "volatile"
+ );
+ }
+ ret
+}
+
+pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
+ syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_exit(xstate: i32) -> isize {
+ syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
+}
+
+pub fn sys_yield() -> isize {
+ syscall(SYSCALL_YIELD, [0, 0, 0])
+}
+
+pub fn sys_get_time() -> isize {
+ syscall(SYSCALL_GET_TIME, [0, 0, 0])
+}
\ No newline at end of file
diff --git a/ch5/.cargo/config b/ch5/.cargo/config
new file mode 100644
index 00000000..e5ded8a1
--- /dev/null
+++ b/ch5/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+ "-Clink-args=-Tsrc/linker.ld",
+]
diff --git a/ch5/.gitignore b/ch5/.gitignore
new file mode 100644
index 00000000..e420ee4b
--- /dev/null
+++ b/ch5/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/ch5/.idea/misc.xml b/ch5/.idea/misc.xml
new file mode 100644
index 00000000..56189180
--- /dev/null
+++ b/ch5/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch5/.idea/user.iml b/ch5/.idea/user.iml
new file mode 100644
index 00000000..b44d0258
--- /dev/null
+++ b/ch5/.idea/user.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch5/.idea/vcs.xml b/ch5/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/ch5/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch5/.idea/workspace.xml b/ch5/.idea/workspace.xml
new file mode 100644
index 00000000..b4ed534b
--- /dev/null
+++ b/ch5/.idea/workspace.xml
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1605727203780
+
+
+ 1605727203780
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch5/Cargo.toml b/ch5/Cargo.toml
new file mode 100644
index 00000000..aa6e20fe
--- /dev/null
+++ b/ch5/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "user_lib"
+version = "0.1.0"
+authors = ["Yifan Wu "]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+buddy_system_allocator = "0.6"
\ No newline at end of file
diff --git a/ch5/Makefile b/ch5/Makefile
new file mode 100644
index 00000000..5e0f87c3
--- /dev/null
+++ b/ch5/Makefile
@@ -0,0 +1,23 @@
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+APP_DIR := src/bin
+TARGET_DIR := target/$(TARGET)/$(MODE)
+APPS := $(wildcard $(APP_DIR)/*.rs)
+ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
+BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
+
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+elf: $(APPS)
+ @cargo build --release
+
+binary: elf
+ $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
+
+build: binary
+
+clean:
+ @cargo clean
+
+.PHONY: elf binary build clean
\ No newline at end of file
diff --git a/ch5/src/bin/exit.rs b/ch5/src/bin/exit.rs
new file mode 100644
index 00000000..5bde550b
--- /dev/null
+++ b/ch5/src/bin/exit.rs
@@ -0,0 +1,29 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{fork, yield_, waitpid, exit, wait};
+
+const MAGIC: i32 = -0x10384;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("I am the parent. Forking the child...");
+ let pid = fork();
+ if pid == 0 {
+ println!("I am the child.");
+ for _ in 0..7 { yield_(); }
+ exit(MAGIC);
+ } else {
+ println!("I am parent, fork a child pid {}", pid);
+ }
+ println!("I am the parent, waiting now..");
+ let mut xstate: i32 = 0;
+ assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == MAGIC);
+ assert!(waitpid(pid as usize, &mut xstate) < 0 && wait(&mut xstate) <= 0);
+ println!("waitpid {} ok.", pid);
+ println!("exit pass.");
+ 0
+}
+
diff --git a/ch5/src/bin/fantastic_text.rs b/ch5/src/bin/fantastic_text.rs
new file mode 100644
index 00000000..bb51db30
--- /dev/null
+++ b/ch5/src/bin/fantastic_text.rs
@@ -0,0 +1,44 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+macro_rules! color_text {
+ ($text:expr, $color:expr) => {{
+ format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
+ }};
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!(
+ "{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}",
+ color_text!("H", 31),
+ color_text!("e", 32),
+ color_text!("l", 33),
+ color_text!("l", 34),
+ color_text!("o", 35),
+ color_text!("R", 36),
+ color_text!("u", 37),
+ color_text!("s", 90),
+ color_text!("t", 91),
+ color_text!("u", 92),
+ color_text!("C", 93),
+ color_text!("o", 94),
+ color_text!("r", 95),
+ color_text!("e", 96),
+ color_text!("!", 97),
+ );
+
+ let text =
+ "reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m";
+ println!("\x1b[47m{}\x1b[0m", color_text!(text, 30));
+ for i in 31..38 {
+ println!("{}", color_text!(text, i));
+ }
+ for i in 90..98 {
+ println!("{}", color_text!(text, i));
+ }
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/forktest.rs b/ch5/src/bin/forktest.rs
new file mode 100644
index 00000000..fea6967c
--- /dev/null
+++ b/ch5/src/bin/forktest.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, exit};
+
+const MAX_CHILD: usize = 40;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for i in 0..MAX_CHILD {
+ let pid = fork();
+ if pid == 0 {
+ println!("I am child {}", i);
+ exit(0);
+ } else {
+ println!("forked child pid = {}", pid);
+ }
+ assert!(pid > 0);
+ }
+ let mut exit_code: i32 = 0;
+ for _ in 0..MAX_CHILD {
+ if wait(&mut exit_code) <= 0 {
+ panic!("wait stopped early");
+ }
+ }
+ if wait(&mut exit_code) > 0 {
+ panic!("wait got too many");
+ }
+ println!("forktest pass.");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/forktest2.rs b/ch5/src/bin/forktest2.rs
new file mode 100644
index 00000000..493fccd8
--- /dev/null
+++ b/ch5/src/bin/forktest2.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, getpid, exit, sleep, get_time};
+
+static NUM: usize = 30;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for _ in 0..NUM {
+ let pid = fork();
+ if pid == 0 {
+ let current_time = get_time();
+ let sleep_length = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000;
+ println!("pid {} sleep for {} ms", getpid(), sleep_length);
+ sleep(sleep_length as usize);
+ println!("pid {} OK!", getpid());
+ exit(0);
+ }
+ }
+
+ let mut xstate: i32 = 0;
+ for _ in 0..NUM {
+ assert!(wait(&mut xstate) > 0);
+ assert_eq!(xstate, 0);
+ }
+ assert!(wait(&mut xstate) < 0);
+ println!("forktest2 test passed!");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/forktest_simple.rs b/ch5/src/bin/forktest_simple.rs
new file mode 100644
index 00000000..1851aea0
--- /dev/null
+++ b/ch5/src/bin/forktest_simple.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, getpid, wait};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ assert_eq!(wait(&mut 0i32), -1);
+ println!("sys_wait without child process test passed!");
+ println!("parent start, pid = {}!", getpid());
+ let pid = fork();
+ if pid == 0 {
+ // child process
+ println!("hello child process!");
+ 100
+ } else {
+ // parent process
+ let mut xstate: i32 = 0;
+ println!("ready waiting on parent process!");
+ assert_eq!(pid, wait(&mut xstate));
+ assert_eq!(xstate, 100);
+ println!("child process pid = {}, exit code = {}", pid, xstate);
+ 0
+ }
+}
\ No newline at end of file
diff --git a/ch5/src/bin/forktree.rs b/ch5/src/bin/forktree.rs
new file mode 100644
index 00000000..26954b7a
--- /dev/null
+++ b/ch5/src/bin/forktree.rs
@@ -0,0 +1,37 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sleep, getpid, fork, exit, yield_};
+
+const DEPTH: usize = 4;
+
+fn fork_child(cur: &str, branch: char) {
+ let mut next = [0u8; DEPTH + 1];
+ let l = cur.len();
+ if l >= DEPTH {
+ return;
+ }
+ &mut next[..l].copy_from_slice(cur.as_bytes());
+ next[l] = branch as u8;
+ if fork() == 0 {
+ fork_tree(core::str::from_utf8(&next[..l + 1]).unwrap());
+ yield_();
+ exit(0);
+ }
+}
+
+fn fork_tree(cur: &str) {
+ println!("pid{}: {}", getpid(), cur);
+ fork_child(cur, '0');
+ fork_child(cur, '1');
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ fork_tree("");
+ sleep(3000);
+ 0
+}
diff --git a/ch5/src/bin/hello_world.rs b/ch5/src/bin/hello_world.rs
new file mode 100644
index 00000000..de4a6a92
--- /dev/null
+++ b/ch5/src/bin/hello_world.rs
@@ -0,0 +1,11 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("Hello world from user mode program!");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/initproc.rs b/ch5/src/bin/initproc.rs
new file mode 100644
index 00000000..6d30fdb6
--- /dev/null
+++ b/ch5/src/bin/initproc.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{
+ fork,
+ wait,
+ exec,
+ yield_,
+};
+
+#[no_mangle]
+fn main() -> i32 {
+ if fork() == 0 {
+ exec("user_shell\0");
+ } else {
+ loop {
+ let mut exit_code: i32 = 0;
+ let pid = wait(&mut exit_code);
+ if pid == -1 {
+ yield_();
+ continue;
+ }
+ println!(
+ "[initproc] Released a zombie process, pid={}, exit_code={}",
+ pid,
+ exit_code,
+ );
+ }
+ }
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/matrix.rs b/ch5/src/bin/matrix.rs
new file mode 100644
index 00000000..95904337
--- /dev/null
+++ b/ch5/src/bin/matrix.rs
@@ -0,0 +1,68 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, yield_, exit, getpid, get_time};
+
+static NUM: usize = 35;
+const N: usize = 10;
+static P: i32 = 10007;
+type Arr = [[i32; N]; N];
+
+fn work(times: isize) {
+ let mut a: Arr = Default::default();
+ let mut b: Arr = Default::default();
+ let mut c: Arr = Default::default();
+ for i in 0..N {
+ for j in 0..N {
+ a[i][j] = 1;
+ b[i][j] = 1;
+ }
+ }
+ yield_();
+ println!("pid {} is running ({} times)!.", getpid(), times);
+ for _ in 0..times {
+ for i in 0..N {
+ for j in 0..N {
+ c[i][j] = 0;
+ for k in 0..N {
+ c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % P;
+ }
+ }
+ }
+ for i in 0..N {
+ for j in 0..N {
+ a[i][j] = c[i][j];
+ b[i][j] = c[i][j];
+ }
+ }
+ }
+ println!("pid {} done!.", getpid());
+ exit(0);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for _ in 0..NUM {
+ let pid = fork();
+ if pid == 0 {
+ let current_time = get_time();
+ let times = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000;
+ work(times * 10);
+ }
+ }
+
+ println!("fork ok.");
+
+ let mut xstate: i32 = 0;
+ for _ in 0..NUM {
+ if wait(&mut xstate) < 0 {
+ panic!("wait failed.");
+ }
+ }
+ assert!(wait(&mut xstate) < 0);
+ println!("matrix passed.");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/sleep.rs b/ch5/src/bin/sleep.rs
new file mode 100644
index 00000000..439e8a65
--- /dev/null
+++ b/ch5/src/bin/sleep.rs
@@ -0,0 +1,30 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sleep, exit, get_time, fork, waitpid};
+
+fn sleepy() {
+ let time: usize = 100;
+ for i in 0..5 {
+ sleep(time);
+ println!("sleep {} x {} msecs.", i + 1, time);
+ }
+ exit(0);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ let current_time = get_time();
+ let pid = fork();
+ let mut xstate: i32 = 0;
+ if pid == 0 {
+ sleepy();
+ }
+ assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == 0);
+ println!("use {} msecs.", get_time() - current_time);
+ println!("sleep pass.");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/sleep_simple.rs b/ch5/src/bin/sleep_simple.rs
new file mode 100644
index 00000000..4c058f87
--- /dev/null
+++ b/ch5/src/bin/sleep_simple.rs
@@ -0,0 +1,19 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{get_time, sleep};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("into sleep test!");
+ let start = get_time();
+ println!("current time_msec = {}", start);
+ sleep(100);
+ let end = get_time();
+ println!("time_msec = {} after sleeping 100 ticks, delta = {}ms!", end, end - start);
+ println!("r_sleep passed!");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/stack_overflow.rs b/ch5/src/bin/stack_overflow.rs
new file mode 100644
index 00000000..e0ea471d
--- /dev/null
+++ b/ch5/src/bin/stack_overflow.rs
@@ -0,0 +1,17 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+fn f(d: usize) {
+ println!("d = {}",d);
+ f(d + 1);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("It should trigger segmentation fault!");
+ f(0);
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/user_shell.rs b/ch5/src/bin/user_shell.rs
new file mode 100644
index 00000000..973f8901
--- /dev/null
+++ b/ch5/src/bin/user_shell.rs
@@ -0,0 +1,70 @@
+#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+#[macro_use]
+extern crate user_lib;
+
+const LF: u8 = 0x0au8;
+const CR: u8 = 0x0du8;
+const DL: u8 = 0x7fu8;
+const BS: u8 = 0x08u8;
+
+use alloc::string::String;
+use user_lib::{fork, exec, waitpid, yield_};
+use user_lib::console::getchar;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("Rust user shell");
+ let mut line: String = String::new();
+ print!(">> ");
+ loop {
+ let c = getchar();
+ match c {
+ LF | CR => {
+ println!("");
+ if !line.is_empty() {
+ line.push('\0');
+ let pid = fork();
+ if pid == 0 {
+ // child process
+ if exec(line.as_str()) == -1 {
+ println!("Error when executing!");
+ return -4;
+ }
+ unreachable!();
+ } else {
+ let mut xstate: i32 = 0;
+ let mut exit_pid: isize;
+ loop {
+ exit_pid = waitpid(pid as usize, &mut xstate);
+ if exit_pid == -1 {
+ yield_();
+ } else {
+ assert_eq!(pid, exit_pid);
+ println!("Shell: Process {} exited with code {}", pid, xstate);
+ break;
+ }
+ }
+ }
+ line.clear();
+ }
+ print!(">> ");
+ }
+ BS | DL => {
+ if !line.is_empty() {
+ print!("{}", BS as char);
+ print!(" ");
+ print!("{}", BS as char);
+ line.pop();
+ }
+ }
+ _ => {
+ print!("{}", c as char);
+ line.push(c as char);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ch5/src/bin/usertests.rs b/ch5/src/bin/usertests.rs
new file mode 100644
index 00000000..d5d0d7d2
--- /dev/null
+++ b/ch5/src/bin/usertests.rs
@@ -0,0 +1,40 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+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};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for test in TESTS {
+ println!("Usertests: Running {}", test);
+ let pid = fork();
+ if pid == 0 {
+ exec(*test);
+ panic!("unreachable!");
+ } else {
+ let mut xstate: i32 = Default::default();
+ let wait_pid = waitpid(pid as usize, &mut xstate);
+ assert_eq!(pid, wait_pid);
+ println!("\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", test, pid, xstate);
+ }
+ }
+ println!("Usertests passed!");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/bin/yield.rs b/ch5/src/bin/yield.rs
new file mode 100644
index 00000000..55032e40
--- /dev/null
+++ b/ch5/src/bin/yield.rs
@@ -0,0 +1,17 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{getpid, yield_};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("Hello, I am process {}.", getpid());
+ for i in 0..5 {
+ yield_();
+ println!("Back in process {}, iteration {}.", getpid(), i);
+ }
+ println!("yield pass.");
+ 0
+}
\ No newline at end of file
diff --git a/ch5/src/console.rs b/ch5/src/console.rs
new file mode 100644
index 00000000..810ebba1
--- /dev/null
+++ b/ch5/src/console.rs
@@ -0,0 +1,39 @@
+use core::fmt::{self, Write};
+
+const STDIN: usize = 0;
+const STDOUT: usize = 1;
+
+use super::{read, write};
+
+struct Stdout;
+
+impl Write for Stdout {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ write(STDOUT, s.as_bytes());
+ Ok(())
+ }
+}
+
+pub fn print(args: fmt::Arguments) {
+ Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+ }
+}
+
+#[macro_export]
+macro_rules! println {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+ }
+}
+
+pub fn getchar() -> u8 {
+ let mut c = [0u8; 1];
+ read(STDIN, &mut c);
+ c[0]
+}
diff --git a/ch5/src/lang_items.rs b/ch5/src/lang_items.rs
new file mode 100644
index 00000000..c90d297f
--- /dev/null
+++ b/ch5/src/lang_items.rs
@@ -0,0 +1,10 @@
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+ let err = panic_info.message().unwrap();
+ if let Some(location) = panic_info.location() {
+ println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
+ } else {
+ println!("Panicked: {}", err);
+ }
+ loop {}
+}
\ No newline at end of file
diff --git a/ch5/src/lib.rs b/ch5/src/lib.rs
new file mode 100644
index 00000000..5c434e6d
--- /dev/null
+++ b/ch5/src/lib.rs
@@ -0,0 +1,75 @@
+#![no_std]
+#![feature(llvm_asm)]
+#![feature(linkage)]
+#![feature(panic_info_message)]
+#![feature(alloc_error_handler)]
+
+#[macro_use]
+pub mod console;
+mod syscall;
+mod lang_items;
+
+use syscall::*;
+use buddy_system_allocator::LockedHeap;
+
+const USER_HEAP_SIZE: usize = 16384;
+
+static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE];
+
+#[global_allocator]
+static HEAP: LockedHeap = LockedHeap::empty();
+
+#[alloc_error_handler]
+pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
+ panic!("Heap allocation error, layout = {:?}", layout);
+}
+
+#[no_mangle]
+#[link_section = ".text.entry"]
+pub extern "C" fn _start() -> ! {
+ unsafe {
+ HEAP.lock()
+ .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE);
+ }
+ exit(main());
+}
+
+#[linkage = "weak"]
+#[no_mangle]
+fn main() -> i32 {
+ panic!("Cannot find main!");
+}
+
+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) -> isize { sys_exec(path) }
+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 sleep(period_ms: usize) {
+ let start = sys_get_time();
+ while sys_get_time() < start + period_ms as isize {
+ sys_yield();
+ }
+}
\ No newline at end of file
diff --git a/ch5/src/linker.ld b/ch5/src/linker.ld
new file mode 100644
index 00000000..e05a98ba
--- /dev/null
+++ b/ch5/src/linker.ld
@@ -0,0 +1,29 @@
+
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x0;
+
+SECTIONS
+{
+ . = BASE_ADDRESS;
+ .text : {
+ *(.text.entry)
+ *(.text .text.*)
+ }
+ . = ALIGN(4K);
+ .rodata : {
+ *(.rodata .rodata.*)
+ }
+ . = ALIGN(4K);
+ .data : {
+ *(.data .data.*)
+ }
+ .bss : {
+ *(.bss .bss.*)
+ }
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.debug*)
+ }
+}
\ No newline at end of file
diff --git a/ch5/src/syscall.rs b/ch5/src/syscall.rs
new file mode 100644
index 00000000..6a6721f2
--- /dev/null
+++ b/ch5/src/syscall.rs
@@ -0,0 +1,59 @@
+const SYSCALL_READ: usize = 63;
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+const SYSCALL_YIELD: usize = 124;
+const SYSCALL_GET_TIME: usize = 169;
+const SYSCALL_GETPID: usize = 172;
+const SYSCALL_FORK: usize = 220;
+const SYSCALL_EXEC: usize = 221;
+const SYSCALL_WAITPID: usize = 260;
+
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+ let mut ret: isize;
+ unsafe {
+ llvm_asm!("ecall"
+ : "={x10}" (ret)
+ : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
+ : "memory"
+ : "volatile"
+ );
+ }
+ ret
+}
+
+pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize {
+ syscall(SYSCALL_READ, [fd, buffer.as_mut_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
+ syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_exit(exit_code: i32) -> ! {
+ syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]);
+ panic!("sys_exit never returns!");
+}
+
+pub fn sys_yield() -> isize {
+ syscall(SYSCALL_YIELD, [0, 0, 0])
+}
+
+pub fn sys_get_time() -> isize {
+ syscall(SYSCALL_GET_TIME, [0, 0, 0])
+}
+
+pub fn sys_getpid() -> isize {
+ syscall(SYSCALL_GETPID, [0, 0, 0])
+}
+
+pub fn sys_fork() -> isize {
+ syscall(SYSCALL_FORK, [0, 0, 0])
+}
+
+pub fn sys_exec(path: &str) -> isize {
+ syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0])
+}
+
+pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize {
+ syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0])
+}
\ No newline at end of file
diff --git a/ch6/.cargo/config b/ch6/.cargo/config
new file mode 100644
index 00000000..e5ded8a1
--- /dev/null
+++ b/ch6/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+ "-Clink-args=-Tsrc/linker.ld",
+]
diff --git a/ch6/.gitignore b/ch6/.gitignore
new file mode 100644
index 00000000..e420ee4b
--- /dev/null
+++ b/ch6/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/ch6/.idea/misc.xml b/ch6/.idea/misc.xml
new file mode 100644
index 00000000..56189180
--- /dev/null
+++ b/ch6/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch6/.idea/user.iml b/ch6/.idea/user.iml
new file mode 100644
index 00000000..b44d0258
--- /dev/null
+++ b/ch6/.idea/user.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch6/.idea/vcs.xml b/ch6/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/ch6/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch6/.idea/workspace.xml b/ch6/.idea/workspace.xml
new file mode 100644
index 00000000..b4ed534b
--- /dev/null
+++ b/ch6/.idea/workspace.xml
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1605727203780
+
+
+ 1605727203780
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ch6/Cargo.toml b/ch6/Cargo.toml
new file mode 100644
index 00000000..aa6e20fe
--- /dev/null
+++ b/ch6/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "user_lib"
+version = "0.1.0"
+authors = ["Yifan Wu "]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+buddy_system_allocator = "0.6"
\ No newline at end of file
diff --git a/ch6/Makefile b/ch6/Makefile
new file mode 100644
index 00000000..5e0f87c3
--- /dev/null
+++ b/ch6/Makefile
@@ -0,0 +1,23 @@
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+APP_DIR := src/bin
+TARGET_DIR := target/$(TARGET)/$(MODE)
+APPS := $(wildcard $(APP_DIR)/*.rs)
+ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
+BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
+
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+elf: $(APPS)
+ @cargo build --release
+
+binary: elf
+ $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
+
+build: binary
+
+clean:
+ @cargo clean
+
+.PHONY: elf binary build clean
\ No newline at end of file
diff --git a/ch6/src/bin/exit.rs b/ch6/src/bin/exit.rs
new file mode 100644
index 00000000..5bde550b
--- /dev/null
+++ b/ch6/src/bin/exit.rs
@@ -0,0 +1,29 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{fork, yield_, waitpid, exit, wait};
+
+const MAGIC: i32 = -0x10384;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("I am the parent. Forking the child...");
+ let pid = fork();
+ if pid == 0 {
+ println!("I am the child.");
+ for _ in 0..7 { yield_(); }
+ exit(MAGIC);
+ } else {
+ println!("I am parent, fork a child pid {}", pid);
+ }
+ println!("I am the parent, waiting now..");
+ let mut xstate: i32 = 0;
+ assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == MAGIC);
+ assert!(waitpid(pid as usize, &mut xstate) < 0 && wait(&mut xstate) <= 0);
+ println!("waitpid {} ok.", pid);
+ println!("exit pass.");
+ 0
+}
+
diff --git a/ch6/src/bin/fantastic_text.rs b/ch6/src/bin/fantastic_text.rs
new file mode 100644
index 00000000..bb51db30
--- /dev/null
+++ b/ch6/src/bin/fantastic_text.rs
@@ -0,0 +1,44 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+macro_rules! color_text {
+ ($text:expr, $color:expr) => {{
+ format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
+ }};
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!(
+ "{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}",
+ color_text!("H", 31),
+ color_text!("e", 32),
+ color_text!("l", 33),
+ color_text!("l", 34),
+ color_text!("o", 35),
+ color_text!("R", 36),
+ color_text!("u", 37),
+ color_text!("s", 90),
+ color_text!("t", 91),
+ color_text!("u", 92),
+ color_text!("C", 93),
+ color_text!("o", 94),
+ color_text!("r", 95),
+ color_text!("e", 96),
+ color_text!("!", 97),
+ );
+
+ let text =
+ "reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m";
+ println!("\x1b[47m{}\x1b[0m", color_text!(text, 30));
+ for i in 31..38 {
+ println!("{}", color_text!(text, i));
+ }
+ for i in 90..98 {
+ println!("{}", color_text!(text, i));
+ }
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/forktest.rs b/ch6/src/bin/forktest.rs
new file mode 100644
index 00000000..fea6967c
--- /dev/null
+++ b/ch6/src/bin/forktest.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, exit};
+
+const MAX_CHILD: usize = 40;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for i in 0..MAX_CHILD {
+ let pid = fork();
+ if pid == 0 {
+ println!("I am child {}", i);
+ exit(0);
+ } else {
+ println!("forked child pid = {}", pid);
+ }
+ assert!(pid > 0);
+ }
+ let mut exit_code: i32 = 0;
+ for _ in 0..MAX_CHILD {
+ if wait(&mut exit_code) <= 0 {
+ panic!("wait stopped early");
+ }
+ }
+ if wait(&mut exit_code) > 0 {
+ panic!("wait got too many");
+ }
+ println!("forktest pass.");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/forktest2.rs b/ch6/src/bin/forktest2.rs
new file mode 100644
index 00000000..493fccd8
--- /dev/null
+++ b/ch6/src/bin/forktest2.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, getpid, exit, sleep, get_time};
+
+static NUM: usize = 30;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for _ in 0..NUM {
+ let pid = fork();
+ if pid == 0 {
+ let current_time = get_time();
+ let sleep_length = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000;
+ println!("pid {} sleep for {} ms", getpid(), sleep_length);
+ sleep(sleep_length as usize);
+ println!("pid {} OK!", getpid());
+ exit(0);
+ }
+ }
+
+ let mut xstate: i32 = 0;
+ for _ in 0..NUM {
+ assert!(wait(&mut xstate) > 0);
+ assert_eq!(xstate, 0);
+ }
+ assert!(wait(&mut xstate) < 0);
+ println!("forktest2 test passed!");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/forktest_simple.rs b/ch6/src/bin/forktest_simple.rs
new file mode 100644
index 00000000..1851aea0
--- /dev/null
+++ b/ch6/src/bin/forktest_simple.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, getpid, wait};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ assert_eq!(wait(&mut 0i32), -1);
+ println!("sys_wait without child process test passed!");
+ println!("parent start, pid = {}!", getpid());
+ let pid = fork();
+ if pid == 0 {
+ // child process
+ println!("hello child process!");
+ 100
+ } else {
+ // parent process
+ let mut xstate: i32 = 0;
+ println!("ready waiting on parent process!");
+ assert_eq!(pid, wait(&mut xstate));
+ assert_eq!(xstate, 100);
+ println!("child process pid = {}, exit code = {}", pid, xstate);
+ 0
+ }
+}
\ No newline at end of file
diff --git a/ch6/src/bin/forktree.rs b/ch6/src/bin/forktree.rs
new file mode 100644
index 00000000..26954b7a
--- /dev/null
+++ b/ch6/src/bin/forktree.rs
@@ -0,0 +1,37 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sleep, getpid, fork, exit, yield_};
+
+const DEPTH: usize = 4;
+
+fn fork_child(cur: &str, branch: char) {
+ let mut next = [0u8; DEPTH + 1];
+ let l = cur.len();
+ if l >= DEPTH {
+ return;
+ }
+ &mut next[..l].copy_from_slice(cur.as_bytes());
+ next[l] = branch as u8;
+ if fork() == 0 {
+ fork_tree(core::str::from_utf8(&next[..l + 1]).unwrap());
+ yield_();
+ exit(0);
+ }
+}
+
+fn fork_tree(cur: &str) {
+ println!("pid{}: {}", getpid(), cur);
+ fork_child(cur, '0');
+ fork_child(cur, '1');
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ fork_tree("");
+ sleep(3000);
+ 0
+}
diff --git a/ch6/src/bin/hello_world.rs b/ch6/src/bin/hello_world.rs
new file mode 100644
index 00000000..de4a6a92
--- /dev/null
+++ b/ch6/src/bin/hello_world.rs
@@ -0,0 +1,11 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("Hello world from user mode program!");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/initproc.rs b/ch6/src/bin/initproc.rs
new file mode 100644
index 00000000..6d30fdb6
--- /dev/null
+++ b/ch6/src/bin/initproc.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{
+ fork,
+ wait,
+ exec,
+ yield_,
+};
+
+#[no_mangle]
+fn main() -> i32 {
+ if fork() == 0 {
+ exec("user_shell\0");
+ } else {
+ loop {
+ let mut exit_code: i32 = 0;
+ let pid = wait(&mut exit_code);
+ if pid == -1 {
+ yield_();
+ continue;
+ }
+ println!(
+ "[initproc] Released a zombie process, pid={}, exit_code={}",
+ pid,
+ exit_code,
+ );
+ }
+ }
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/matrix.rs b/ch6/src/bin/matrix.rs
new file mode 100644
index 00000000..95904337
--- /dev/null
+++ b/ch6/src/bin/matrix.rs
@@ -0,0 +1,68 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, yield_, exit, getpid, get_time};
+
+static NUM: usize = 35;
+const N: usize = 10;
+static P: i32 = 10007;
+type Arr = [[i32; N]; N];
+
+fn work(times: isize) {
+ let mut a: Arr = Default::default();
+ let mut b: Arr = Default::default();
+ let mut c: Arr = Default::default();
+ for i in 0..N {
+ for j in 0..N {
+ a[i][j] = 1;
+ b[i][j] = 1;
+ }
+ }
+ yield_();
+ println!("pid {} is running ({} times)!.", getpid(), times);
+ for _ in 0..times {
+ for i in 0..N {
+ for j in 0..N {
+ c[i][j] = 0;
+ for k in 0..N {
+ c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % P;
+ }
+ }
+ }
+ for i in 0..N {
+ for j in 0..N {
+ a[i][j] = c[i][j];
+ b[i][j] = c[i][j];
+ }
+ }
+ }
+ println!("pid {} done!.", getpid());
+ exit(0);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for _ in 0..NUM {
+ let pid = fork();
+ if pid == 0 {
+ let current_time = get_time();
+ let times = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000;
+ work(times * 10);
+ }
+ }
+
+ println!("fork ok.");
+
+ let mut xstate: i32 = 0;
+ for _ in 0..NUM {
+ if wait(&mut xstate) < 0 {
+ panic!("wait failed.");
+ }
+ }
+ assert!(wait(&mut xstate) < 0);
+ println!("matrix passed.");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/pipe_large_test.rs b/ch6/src/bin/pipe_large_test.rs
new file mode 100644
index 00000000..121987be
--- /dev/null
+++ b/ch6/src/bin/pipe_large_test.rs
@@ -0,0 +1,69 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+extern crate alloc;
+
+use user_lib::{fork, close, pipe, read, write, wait, get_time};
+use alloc::format;
+
+const LENGTH: usize = 3000;
+#[no_mangle]
+pub fn main() -> i32 {
+ // create pipes
+ // parent write to child
+ let mut down_pipe_fd = [0usize; 2];
+ // child write to parent
+ let mut up_pipe_fd = [0usize; 2];
+ pipe(&mut down_pipe_fd);
+ pipe(&mut up_pipe_fd);
+ let mut random_str = [0u8; LENGTH];
+ if fork() == 0 {
+ // close write end of down pipe
+ close(down_pipe_fd[1]);
+ // close read end of up pipe
+ close(up_pipe_fd[0]);
+ assert_eq!(read(down_pipe_fd[0], &mut random_str) as usize, LENGTH);
+ close(down_pipe_fd[0]);
+ let sum: usize = random_str.iter().map(|v| *v as usize).sum::();
+ println!("sum = {}(child)", sum);
+ let sum_str = format!("{}", sum);
+ write(up_pipe_fd[1], sum_str.as_bytes());
+ close(up_pipe_fd[1]);
+ println!("Child process exited!");
+ 0
+ } else {
+ // close read end of down pipe
+ close(down_pipe_fd[0]);
+ // close write end of up pipe
+ close(up_pipe_fd[1]);
+ // generate a long random string
+ for i in 0..LENGTH {
+ random_str[i] = get_time() as u8;
+ }
+ // send it
+ assert_eq!(write(down_pipe_fd[1], &random_str) as usize, random_str.len());
+ // close write end of down pipe
+ close(down_pipe_fd[1]);
+ // calculate sum(parent)
+ let sum: usize = random_str.iter().map(|v| *v as usize).sum::();
+ println!("sum = {}(parent)", sum);
+ // recv sum(child)
+ let mut child_result = [0u8; 32];
+ let result_len = read(up_pipe_fd[0], &mut child_result) as usize;
+ close(up_pipe_fd[0]);
+ // check
+ assert_eq!(
+ sum,
+ str::parse::(
+ core::str::from_utf8(&child_result[..result_len]).unwrap()
+ ).unwrap()
+ );
+ let mut _unused: i32 = 0;
+ wait(&mut _unused);
+ println!("pipe_large_test passed!");
+ 0
+ }
+}
\ No newline at end of file
diff --git a/ch6/src/bin/pipetest.rs b/ch6/src/bin/pipetest.rs
new file mode 100644
index 00000000..58237847
--- /dev/null
+++ b/ch6/src/bin/pipetest.rs
@@ -0,0 +1,42 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, close, pipe, read, write, wait};
+
+static STR: &str = "Hello, world!";
+
+#[no_mangle]
+pub fn main() -> i32 {
+ // create pipe
+ let mut pipe_fd = [0usize; 2];
+ pipe(&mut pipe_fd);
+ // read end
+ assert_eq!(pipe_fd[0], 3);
+ // write end
+ assert_eq!(pipe_fd[1], 4);
+ if fork() == 0 {
+ // child process, read from parent
+ // close write_end
+ close(pipe_fd[1]);
+ let mut buffer = [0u8; 32];
+ let len_read = read(pipe_fd[0], &mut buffer) as usize;
+ assert_eq!(core::str::from_utf8(&buffer[..len_read]).unwrap(), STR);
+ println!("Read OK, child process exited!");
+ 0
+ } else {
+ // parent process, write to child
+ // close read end
+ close(pipe_fd[0]);
+ assert_eq!(write(pipe_fd[1], STR.as_bytes()), STR.len() as isize);
+ // close write end
+ close(pipe_fd[1]);
+ let mut child_exit_code: i32 = 0;
+ wait(&mut child_exit_code);
+ assert_eq!(child_exit_code, 0);
+ println!("pipetest passed!");
+ 0
+ }
+}
\ No newline at end of file
diff --git a/ch6/src/bin/run_pipe_test.rs b/ch6/src/bin/run_pipe_test.rs
new file mode 100644
index 00000000..ca2afd15
--- /dev/null
+++ b/ch6/src/bin/run_pipe_test.rs
@@ -0,0 +1,21 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, exec, wait};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for i in 0..1000 {
+ if fork() == 0 {
+ exec("pipe_large_test\0");
+ } else {
+ let mut _unused: i32 = 0;
+ wait(&mut _unused);
+ println!("Iter {} OK.", i);
+ }
+ }
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/sleep.rs b/ch6/src/bin/sleep.rs
new file mode 100644
index 00000000..439e8a65
--- /dev/null
+++ b/ch6/src/bin/sleep.rs
@@ -0,0 +1,30 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sleep, exit, get_time, fork, waitpid};
+
+fn sleepy() {
+ let time: usize = 100;
+ for i in 0..5 {
+ sleep(time);
+ println!("sleep {} x {} msecs.", i + 1, time);
+ }
+ exit(0);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ let current_time = get_time();
+ let pid = fork();
+ let mut xstate: i32 = 0;
+ if pid == 0 {
+ sleepy();
+ }
+ assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == 0);
+ println!("use {} msecs.", get_time() - current_time);
+ println!("sleep pass.");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/sleep_simple.rs b/ch6/src/bin/sleep_simple.rs
new file mode 100644
index 00000000..4c058f87
--- /dev/null
+++ b/ch6/src/bin/sleep_simple.rs
@@ -0,0 +1,19 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{get_time, sleep};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("into sleep test!");
+ let start = get_time();
+ println!("current time_msec = {}", start);
+ sleep(100);
+ let end = get_time();
+ println!("time_msec = {} after sleeping 100 ticks, delta = {}ms!", end, end - start);
+ println!("r_sleep passed!");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/stack_overflow.rs b/ch6/src/bin/stack_overflow.rs
new file mode 100644
index 00000000..e0ea471d
--- /dev/null
+++ b/ch6/src/bin/stack_overflow.rs
@@ -0,0 +1,17 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+fn f(d: usize) {
+ println!("d = {}",d);
+ f(d + 1);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("It should trigger segmentation fault!");
+ f(0);
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/user_shell.rs b/ch6/src/bin/user_shell.rs
new file mode 100644
index 00000000..973f8901
--- /dev/null
+++ b/ch6/src/bin/user_shell.rs
@@ -0,0 +1,70 @@
+#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+#[macro_use]
+extern crate user_lib;
+
+const LF: u8 = 0x0au8;
+const CR: u8 = 0x0du8;
+const DL: u8 = 0x7fu8;
+const BS: u8 = 0x08u8;
+
+use alloc::string::String;
+use user_lib::{fork, exec, waitpid, yield_};
+use user_lib::console::getchar;
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("Rust user shell");
+ let mut line: String = String::new();
+ print!(">> ");
+ loop {
+ let c = getchar();
+ match c {
+ LF | CR => {
+ println!("");
+ if !line.is_empty() {
+ line.push('\0');
+ let pid = fork();
+ if pid == 0 {
+ // child process
+ if exec(line.as_str()) == -1 {
+ println!("Error when executing!");
+ return -4;
+ }
+ unreachable!();
+ } else {
+ let mut xstate: i32 = 0;
+ let mut exit_pid: isize;
+ loop {
+ exit_pid = waitpid(pid as usize, &mut xstate);
+ if exit_pid == -1 {
+ yield_();
+ } else {
+ assert_eq!(pid, exit_pid);
+ println!("Shell: Process {} exited with code {}", pid, xstate);
+ break;
+ }
+ }
+ }
+ line.clear();
+ }
+ print!(">> ");
+ }
+ BS | DL => {
+ if !line.is_empty() {
+ print!("{}", BS as char);
+ print!(" ");
+ print!("{}", BS as char);
+ line.pop();
+ }
+ }
+ _ => {
+ print!("{}", c as char);
+ line.push(c as char);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ch6/src/bin/usertests.rs b/ch6/src/bin/usertests.rs
new file mode 100644
index 00000000..d5d0d7d2
--- /dev/null
+++ b/ch6/src/bin/usertests.rs
@@ -0,0 +1,40 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+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};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ for test in TESTS {
+ println!("Usertests: Running {}", test);
+ let pid = fork();
+ if pid == 0 {
+ exec(*test);
+ panic!("unreachable!");
+ } else {
+ let mut xstate: i32 = Default::default();
+ let wait_pid = waitpid(pid as usize, &mut xstate);
+ assert_eq!(pid, wait_pid);
+ println!("\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", test, pid, xstate);
+ }
+ }
+ println!("Usertests passed!");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/bin/yield.rs b/ch6/src/bin/yield.rs
new file mode 100644
index 00000000..55032e40
--- /dev/null
+++ b/ch6/src/bin/yield.rs
@@ -0,0 +1,17 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{getpid, yield_};
+
+#[no_mangle]
+pub fn main() -> i32 {
+ println!("Hello, I am process {}.", getpid());
+ for i in 0..5 {
+ yield_();
+ println!("Back in process {}, iteration {}.", getpid(), i);
+ }
+ println!("yield pass.");
+ 0
+}
\ No newline at end of file
diff --git a/ch6/src/console.rs b/ch6/src/console.rs
new file mode 100644
index 00000000..810ebba1
--- /dev/null
+++ b/ch6/src/console.rs
@@ -0,0 +1,39 @@
+use core::fmt::{self, Write};
+
+const STDIN: usize = 0;
+const STDOUT: usize = 1;
+
+use super::{read, write};
+
+struct Stdout;
+
+impl Write for Stdout {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ write(STDOUT, s.as_bytes());
+ Ok(())
+ }
+}
+
+pub fn print(args: fmt::Arguments) {
+ Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+ }
+}
+
+#[macro_export]
+macro_rules! println {
+ ($fmt: literal $(, $($arg: tt)+)?) => {
+ $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+ }
+}
+
+pub fn getchar() -> u8 {
+ let mut c = [0u8; 1];
+ read(STDIN, &mut c);
+ c[0]
+}
diff --git a/ch6/src/lang_items.rs b/ch6/src/lang_items.rs
new file mode 100644
index 00000000..b5b98e08
--- /dev/null
+++ b/ch6/src/lang_items.rs
@@ -0,0 +1,12 @@
+use super::exit;
+
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+ let err = panic_info.message().unwrap();
+ if let Some(location) = panic_info.location() {
+ println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
+ } else {
+ println!("Panicked: {}", err);
+ }
+ exit(-1);
+}
\ No newline at end of file
diff --git a/ch6/src/lib.rs b/ch6/src/lib.rs
new file mode 100644
index 00000000..32482859
--- /dev/null
+++ b/ch6/src/lib.rs
@@ -0,0 +1,79 @@
+#![no_std]
+#![feature(llvm_asm)]
+#![feature(linkage)]
+#![feature(panic_info_message)]
+#![feature(alloc_error_handler)]
+
+#[macro_use]
+pub mod console;
+mod syscall;
+mod lang_items;
+
+extern crate alloc;
+
+use syscall::*;
+use buddy_system_allocator::LockedHeap;
+
+const USER_HEAP_SIZE: usize = 16384;
+
+static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE];
+
+#[global_allocator]
+static HEAP: LockedHeap = LockedHeap::empty();
+
+#[alloc_error_handler]
+pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
+ panic!("Heap allocation error, layout = {:?}", layout);
+}
+
+#[no_mangle]
+#[link_section = ".text.entry"]
+pub extern "C" fn _start() -> ! {
+ unsafe {
+ HEAP.lock()
+ .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE);
+ }
+ exit(main());
+}
+
+#[linkage = "weak"]
+#[no_mangle]
+fn main() -> i32 {
+ panic!("Cannot find main!");
+}
+
+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) -> isize { sys_exec(path) }
+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 sleep(period_ms: usize) {
+ let start = sys_get_time();
+ while sys_get_time() < start + period_ms as isize {
+ sys_yield();
+ }
+}
\ No newline at end of file
diff --git a/ch6/src/linker.ld b/ch6/src/linker.ld
new file mode 100644
index 00000000..e05a98ba
--- /dev/null
+++ b/ch6/src/linker.ld
@@ -0,0 +1,29 @@
+
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x0;
+
+SECTIONS
+{
+ . = BASE_ADDRESS;
+ .text : {
+ *(.text.entry)
+ *(.text .text.*)
+ }
+ . = ALIGN(4K);
+ .rodata : {
+ *(.rodata .rodata.*)
+ }
+ . = ALIGN(4K);
+ .data : {
+ *(.data .data.*)
+ }
+ .bss : {
+ *(.bss .bss.*)
+ }
+ /DISCARD/ : {
+ *(.eh_frame)
+ *(.debug*)
+ }
+}
\ No newline at end of file
diff --git a/ch6/src/syscall.rs b/ch6/src/syscall.rs
new file mode 100644
index 00000000..76683649
--- /dev/null
+++ b/ch6/src/syscall.rs
@@ -0,0 +1,69 @@
+const SYSCALL_CLOSE: usize = 57;
+const SYSCALL_PIPE: usize = 59;
+const SYSCALL_READ: usize = 63;
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+const SYSCALL_YIELD: usize = 124;
+const SYSCALL_GET_TIME: usize = 169;
+const SYSCALL_GETPID: usize = 172;
+const SYSCALL_FORK: usize = 220;
+const SYSCALL_EXEC: usize = 221;
+const SYSCALL_WAITPID: usize = 260;
+
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+ let mut ret: isize;
+ unsafe {
+ llvm_asm!("ecall"
+ : "={x10}" (ret)
+ : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
+ : "memory"
+ : "volatile"
+ );
+ }
+ ret
+}
+
+pub fn sys_close(fd: usize) -> isize {
+ syscall(SYSCALL_CLOSE, [fd, 0, 0])
+}
+
+pub fn sys_pipe(pipe: &mut [usize]) -> isize {
+ syscall(SYSCALL_PIPE, [pipe.as_mut_ptr() as usize, 0, 0])
+}
+
+pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize {
+ syscall(SYSCALL_READ, [fd, buffer.as_mut_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
+ syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_exit(exit_code: i32) -> ! {
+ syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]);
+ panic!("sys_exit never returns!");
+}
+
+pub fn sys_yield() -> isize {
+ syscall(SYSCALL_YIELD, [0, 0, 0])
+}
+
+pub fn sys_get_time() -> isize {
+ syscall(SYSCALL_GET_TIME, [0, 0, 0])
+}
+
+pub fn sys_getpid() -> isize {
+ syscall(SYSCALL_GETPID, [0, 0, 0])
+}
+
+pub fn sys_fork() -> isize {
+ syscall(SYSCALL_FORK, [0, 0, 0])
+}
+
+pub fn sys_exec(path: &str) -> isize {
+ syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0])
+}
+
+pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize {
+ syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0])
+}
\ No newline at end of file
diff --git a/tests/chapter1/hello_world.rs b/tests/chapter1/hello_world.rs
deleted file mode 100644
index bf369df4..00000000
--- a/tests/chapter1/hello_world.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-/// Run this as a application on RV64 bare-metal.
-
-fn main() {
- println!("Hello, world!");
-}
diff --git a/tests/chapter2/power_bad.rs b/tests/chapter2/power_bad.rs
deleted file mode 100644
index 11226f33..00000000
--- a/tests/chapter2/power_bad.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-/// rCore application without user_lib.
-/// Load it manually to 0x80040000.
-/// It should not OK. Kernel should catch the page fault.
-
-const SIZE: usize = 10;
-const P: u32 = 3;
-const STEP: usize = 10000000;
-const MOD: u32 = 10007;
-fn main() -> ! {
- let mut pow = [0u32; SIZE];
- let mut index: usize = 0;
- pow[index] = 1;
- for i in 1..=STEP {
- let last = pow[index];
- index = (index + 1) % SIZE;
- pow[index] = last * P % MOD;
- if i % 10000 == 0 {
- println!("{}^{}={}", P, i, pow[index]);
- }
- if i == STEP / 2 {
- // TODO: Modify satp to a malicious value in order to destroy memory access mechanism.
- }
- }
- println!("Test power_bad OK!");
- loop {}
-}
\ No newline at end of file
diff --git a/tests/chapter3/write_a.rs b/tests/chapter3/write_a.rs
deleted file mode 100644
index a1a5f69b..00000000
--- a/tests/chapter3/write_a.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-/// rCore application without user_lib.
-/// Load it manually to 0x80040000.
-
-use user_lib::{sys_yield};
-
-fn main() -> ! {
- for _ in 0..10 {
- for _ in 0..10 {
- print!("A");
- }
- println!();
- sys_yield();
- }
- loop {}
-}
\ No newline at end of file
diff --git a/tests/chapter3/write_b.rs b/tests/chapter3/write_b.rs
deleted file mode 100644
index e2a2b12a..00000000
--- a/tests/chapter3/write_b.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-/// rCore application without user_lib.
-/// Load it manually to 0x80050000.
-
-use user_lib::{sys_yield};
-
-fn main() -> ! {
- for _ in 0..10 {
- for _ in 0..10 {
- print!("B");
- }
- println!();
- sys_yield();
- }
- loop {}
-}
\ No newline at end of file