Skip to content

Commit 2730c60

Browse files
committed
Init the ch2
1 parent 5e5a160 commit 2730c60

File tree

14 files changed

+509
-23
lines changed

14 files changed

+509
-23
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
- uses: actions/checkout@v4
1515
- name: Build doc
1616
run: |
17+
git clone https://github.com/LearningOS/rCore-Tutorial-Test.git user
1718
cd os
1819
make
1920
cargo doc --no-deps --verbose

os/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ edition = "2021"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10+
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
1011
log = "0.4"
11-
[profile.release]
12-
debug = true
12+
riscv = { git = "https://gitee.com/rcore-os/riscv", features = ["inline-asm"] }

os/Makefile

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,27 @@ KERNEL_ELF := target/$(TARGET)/$(MODE)/os
55
KERNEL_BIN := $(KERNEL_ELF).bin
66
DISASM_TMP := target/$(TARGET)/$(MODE)/asm
77

8-
# Building mode argument
9-
ifeq ($(MODE), release)
10-
MODE_ARG := --release
11-
endif
12-
138
# BOARD
149
BOARD := qemu
1510
SBI ?= rustsbi
1611
BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin
1712

13+
# Building mode argument
14+
ifeq ($(MODE), release)
15+
MODE_ARG := --release
16+
endif
17+
1818
# KERNEL ENTRY
1919
KERNEL_ENTRY_PA := 0x80200000
2020

2121
# Binutils
2222
OBJDUMP := rust-objdump --arch-name=riscv64
2323
OBJCOPY := rust-objcopy --binary-architecture=riscv64
2424

25+
CHAPTER ?= $(shell git rev-parse --abbrev-ref HEAD | sed -E 's/ch([0-9])/\1/')
26+
TEST ?= $(CHAPTER)
27+
BASE ?= 1
28+
2529
# Disassembly
2630
DISASM ?= -x
2731

@@ -37,6 +41,7 @@ $(KERNEL_BIN): kernel
3741
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
3842

3943
kernel:
44+
@make -C ../user build TEST=$(TEST) CHAPTER=$(CHAPTER) BASE=$(BASE)
4045
@echo Platform: $(BOARD)
4146
@cargo build $(MODE_ARG)
4247

os/build.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//! Building applications linker
2+
3+
use std::fs::{read_dir, File};
4+
use std::io::{Result, Write};
5+
6+
fn main() {
7+
println!("cargo:rerun-if-changed=../user/src/");
8+
println!("cargo:rerun-if-changed={}", TARGET_PATH);
9+
insert_app_data().unwrap();
10+
}
11+
12+
static TARGET_PATH: &str = "../user/build/bin/";
13+
14+
/// get app data and build linker
15+
fn insert_app_data() -> Result<()> {
16+
let mut f = File::create("src/link_app.S").unwrap();
17+
let mut apps: Vec<_> = read_dir("../user/build/bin/")
18+
.unwrap()
19+
.into_iter()
20+
.map(|dir_entry| {
21+
let mut name_with_ext = dir_entry.unwrap().file_name().into_string().unwrap();
22+
name_with_ext.drain(name_with_ext.find('.').unwrap()..name_with_ext.len());
23+
name_with_ext
24+
})
25+
.collect();
26+
apps.sort();
27+
28+
writeln!(
29+
f,
30+
r#"
31+
.align 3
32+
.section .data
33+
.global _num_app
34+
_num_app:
35+
.quad {}"#,
36+
apps.len()
37+
)?;
38+
39+
for i in 0..apps.len() {
40+
writeln!(f, r#" .quad app_{}_start"#, i)?;
41+
}
42+
writeln!(f, r#" .quad app_{}_end"#, apps.len() - 1)?;
43+
44+
for (idx, app) in apps.iter().enumerate() {
45+
println!("app_{}: {}", idx, app);
46+
writeln!(
47+
f,
48+
r#"
49+
.section .data
50+
.global app_{0}_start
51+
.global app_{0}_end
52+
app_{0}_start:
53+
.incbin "{2}{1}.bin"
54+
app_{0}_end:"#,
55+
idx, app, TARGET_PATH
56+
)?;
57+
}
58+
Ok(())
59+
}

os/src/batch.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//! batch subsystem
2+
3+
use crate::sync::UPSafeCell;
4+
use crate::trap::TrapContext;
5+
use core::arch::asm;
6+
use lazy_static::*;
7+
8+
const USER_STACK_SIZE: usize = 4096 * 2;
9+
const KERNEL_STACK_SIZE: usize = 4096 * 2;
10+
const MAX_APP_NUM: usize = 16;
11+
const APP_BASE_ADDRESS: usize = 0x80400000;
12+
const APP_SIZE_LIMIT: usize = 0x20000;
13+
14+
#[repr(align(4096))]
15+
struct KernelStack {
16+
data: [u8; KERNEL_STACK_SIZE],
17+
}
18+
19+
#[repr(align(4096))]
20+
struct UserStack {
21+
data: [u8; USER_STACK_SIZE],
22+
}
23+
24+
static KERNEL_STACK: KernelStack = KernelStack {
25+
data: [0; KERNEL_STACK_SIZE],
26+
};
27+
static USER_STACK: UserStack = UserStack {
28+
data: [0; USER_STACK_SIZE],
29+
};
30+
31+
impl KernelStack {
32+
fn get_sp(&self) -> usize {
33+
self.data.as_ptr() as usize + KERNEL_STACK_SIZE
34+
}
35+
pub fn push_context(&self, cx: TrapContext) -> &'static mut TrapContext {
36+
let cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
37+
unsafe {
38+
*cx_ptr = cx;
39+
}
40+
unsafe { cx_ptr.as_mut().unwrap() }
41+
}
42+
}
43+
44+
impl UserStack {
45+
fn get_sp(&self) -> usize {
46+
self.data.as_ptr() as usize + USER_STACK_SIZE
47+
}
48+
}
49+
50+
struct AppManager {
51+
num_app: usize,
52+
current_app: usize,
53+
app_start: [usize; MAX_APP_NUM + 1],
54+
}
55+
56+
impl AppManager {
57+
pub fn print_app_info(&self) {
58+
println!("[kernel] num_app = {}", self.num_app);
59+
for i in 0..self.num_app {
60+
println!(
61+
"[kernel] app_{} [{:#x}, {:#x})",
62+
i,
63+
self.app_start[i],
64+
self.app_start[i + 1]
65+
);
66+
}
67+
}
68+
69+
unsafe fn load_app(&self, app_id: usize) {
70+
if app_id >= self.num_app {
71+
println!("All applications completed!");
72+
use crate::board::QEMUExit;
73+
crate::board::QEMU_EXIT_HANDLE.exit_success();
74+
}
75+
println!("[kernel] Loading app_{}", app_id);
76+
// clear app area
77+
core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, APP_SIZE_LIMIT).fill(0);
78+
let app_src = core::slice::from_raw_parts(
79+
self.app_start[app_id] as *const u8,
80+
self.app_start[app_id + 1] - self.app_start[app_id],
81+
);
82+
let app_dst = core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, app_src.len());
83+
app_dst.copy_from_slice(app_src);
84+
// Memory fence about fetching the instruction memory
85+
// It is guaranteed that a subsequent instruction fetch must
86+
// observes all previous writes to the instruction memory.
87+
// Therefore, fence.i must be executed after we have loaded
88+
// the code of the next app into the instruction memory.
89+
// See also: riscv non-priv spec chapter 3, 'Zifencei' extension.
90+
asm!("fence.i");
91+
}
92+
93+
pub fn get_current_app(&self) -> usize {
94+
self.current_app
95+
}
96+
97+
pub fn move_to_next_app(&mut self) {
98+
self.current_app += 1;
99+
}
100+
}
101+
102+
lazy_static! {
103+
static ref APP_MANAGER: UPSafeCell<AppManager> = unsafe {
104+
UPSafeCell::new({
105+
extern "C" {
106+
fn _num_app();
107+
}
108+
let num_app_ptr = _num_app as usize as *const usize;
109+
let num_app = num_app_ptr.read_volatile();
110+
let mut app_start: [usize; MAX_APP_NUM + 1] = [0; MAX_APP_NUM + 1];
111+
let app_start_raw: &[usize] =
112+
core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1);
113+
app_start[..=num_app].copy_from_slice(app_start_raw);
114+
AppManager {
115+
num_app,
116+
current_app: 0,
117+
app_start,
118+
}
119+
})
120+
};
121+
}
122+
123+
/// init batch subsystem
124+
pub fn init() {
125+
print_app_info();
126+
}
127+
128+
/// print apps info
129+
pub fn print_app_info() {
130+
APP_MANAGER.exclusive_access().print_app_info();
131+
}
132+
133+
/// run next app
134+
pub fn run_next_app() -> ! {
135+
let mut app_manager = APP_MANAGER.exclusive_access();
136+
let current_app = app_manager.get_current_app();
137+
unsafe {
138+
app_manager.load_app(current_app);
139+
}
140+
app_manager.move_to_next_app();
141+
drop(app_manager);
142+
// before this we have to drop local variables related to resources manually
143+
// and release the resources
144+
extern "C" {
145+
fn __restore(cx_addr: usize);
146+
}
147+
unsafe {
148+
__restore(KERNEL_STACK.push_context(TrapContext::app_init_context(
149+
APP_BASE_ADDRESS,
150+
USER_STACK.get_sp(),
151+
)) as *const _ as usize);
152+
}
153+
panic!("Unreachable in batch::run_current_app!");
154+
}

os/src/main.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,54 @@
11
//! The main module and entrypoint
22
//!
3-
//! The operating system and app also starts in this module. Kernel code starts
3+
//! Various facilities of the kernels are implemented as submodules. The most
4+
//! important ones are:
5+
//!
6+
//! - [`trap`]: Handles all cases of switching from userspace to the kernel
7+
//! - [`syscall`]: System call handling and implementation
8+
//!
9+
//! The operating system also starts in this module. Kernel code starts
410
//! executing from `entry.asm`, after which [`rust_main()`] is called to
5-
//! initialize various pieces of functionality [`clear_bss()`]. (See its source code for
11+
//! initialize various pieces of functionality. (See its source code for
612
//! details.)
713
//!
8-
//! We then call [`println!`] to display `Hello, world!`.
14+
//! We then call [`batch::run_next_app()`] and for the first time go to
15+
//! userspace.
916
1017
#![deny(missing_docs)]
1118
#![deny(warnings)]
1219
#![no_std]
1320
#![no_main]
1421
#![feature(panic_info_message)]
22+
#[macro_use]
23+
extern crate log;
1524

1625
use core::arch::global_asm;
26+
#[path = "boards/qemu.rs"]
27+
mod board;
1728
use log::*;
18-
1929
#[macro_use]
2030
mod console;
21-
mod lang_items;
22-
mod logging;
23-
mod sbi;
24-
25-
#[path = "boards/qemu.rs"]
26-
mod board;
31+
pub mod batch;
32+
pub mod lang_items;
33+
pub mod logging;
34+
pub mod sbi;
35+
pub mod sync;
36+
pub mod syscall;
37+
pub mod trap;
2738

2839
global_asm!(include_str!("entry.asm"));
40+
global_asm!(include_str!("link_app.S"));
2941

3042
/// clear BSS segment
31-
pub fn clear_bss() {
43+
fn clear_bss() {
3244
extern "C" {
3345
fn sbss();
3446
fn ebss();
3547
}
36-
(sbss as usize..ebss as usize).for_each(|a| unsafe { (a as *mut u8).write_volatile(0) });
48+
unsafe {
49+
core::slice::from_raw_parts_mut(sbss as usize as *mut u8, ebss as usize - sbss as usize)
50+
.fill(0);
51+
}
3752
}
3853

3954
/// the rust entry-point of os
@@ -72,8 +87,7 @@ pub fn rust_main() -> ! {
7287
boot_stack_top as usize, boot_stack_lower_bound as usize
7388
);
7489
error!("[kernel] .bss [{:#x}, {:#x})", sbss as usize, ebss as usize);
75-
76-
use crate::board::QEMUExit;
77-
crate::board::QEMU_EXIT_HANDLE.exit_success(); // CI autotest success
78-
//crate::board::QEMU_EXIT_HANDLE.exit_failure(); // CI autoest failed
90+
trap::init();
91+
batch::init();
92+
batch::run_next_app();
7993
}

os/src/sync/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//! Synchronization and interior mutability primitives
2+
3+
mod up;
4+
5+
pub use up::UPSafeCell;

os/src/sync/up.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//! Uniprocessor interior mutability primitives
2+
use core::cell::{RefCell, RefMut};
3+
4+
/// Wrap a static data structure inside it so that we are
5+
/// able to access it without any `unsafe`.
6+
///
7+
/// We should only use it in uniprocessor.
8+
///
9+
/// In order to get mutable reference of inner data, call
10+
/// `exclusive_access`.
11+
pub struct UPSafeCell<T> {
12+
/// inner data
13+
inner: RefCell<T>,
14+
}
15+
16+
unsafe impl<T> Sync for UPSafeCell<T> {}
17+
18+
impl<T> UPSafeCell<T> {
19+
/// User is responsible to guarantee that inner struct is only used in
20+
/// uniprocessor.
21+
pub unsafe fn new(value: T) -> Self {
22+
Self {
23+
inner: RefCell::new(value),
24+
}
25+
}
26+
/// Panic if the data has been borrowed.
27+
pub fn exclusive_access(&self) -> RefMut<'_, T> {
28+
self.inner.borrow_mut()
29+
}
30+
}

0 commit comments

Comments
 (0)