├── .gitignore ├── README.md ├── guide.md └── user ├── .cargo └── config ├── Cargo.toml ├── Makefile ├── build.py ├── ch2-build.py ├── ch3-build.py ├── rust-toolchain └── src ├── bin ├── _ch2_bad_instruction.rs ├── _ch2_bad_register.rs ├── _ch2t_bad_address.rs ├── ch2_exit.rs ├── ch2_hello_world.rs ├── ch2_power.rs ├── ch2_write1.rs ├── ch2t_write0.rs ├── ch3_0_setprio.rs ├── ch3_0_sleep.rs ├── ch3_0_sleep1.rs ├── ch3_1_yield0.rs ├── ch3_1_yield1.rs ├── ch3_1_yield2.rs ├── ch3_2_stride0.rs ├── ch3_2_stride1.rs ├── ch3_2_stride2.rs ├── ch3_2_stride3.rs ├── ch3_2_stride4.rs ├── ch3_2_stride5.rs ├── ch3t_deadloop.rs ├── ch4_mmap0.rs ├── ch4_mmap1.rs ├── ch4_mmap2.rs ├── ch4_mmap3.rs ├── ch4_unmap.rs ├── ch4_unmap2.rs ├── ch5_exit0.rs ├── ch5_exit1.rs ├── ch5_getchar.rs ├── ch5_getpid.rs ├── ch5_spawn0.rs ├── ch5_spawn1.rs ├── ch5_usershell.rs ├── ch5_usertest.rs ├── ch6_mail0.rs ├── ch6_mail1.rs ├── ch6_mail2.rs ├── ch6_mail3.rs ├── ch6_usertest.rs ├── ch7_file0.rs ├── ch7_file1.rs ├── ch7_file2.rs ├── ch7_usertest.rs ├── ch8_01.rs ├── ch8_02.rs ├── ch8_03.rs ├── ch8_04.rs ├── ch8_05.rs ├── ch8_06.rs ├── ch8_07.rs └── ch8_xx.rs ├── ch8.rs ├── console.rs ├── lang_items.rs ├── lib.rs ├── linker.ld └── syscall.rs /.gitignore: -------------------------------------------------------------------------------- 1 | user/target 2 | user/build 3 | user/Cargo.lock 4 | user/.idea 5 | .idea 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## rCore_tutorial_v3 TESTS 2 | 3 | ### 简介 4 | 这里的测试用例是用于[rCore Tutorial v3.5教程](https://rcore-os.github.io/rCore-Tutorial-Book-v3/index.html)中每一章后面的练习题测试。 5 | 6 | ### 通知 7 | - 2021.03.10: make 命令经过优化,使用格式改为 `make all CHAPTER=x` 可获得第 x 章的测例。 8 | 9 | ### 说明 10 | - 可选项 2, 2_bad, 3_0, 3_1, 3_2, 4, 5, 6, 7, x_only (x in 4, 5, 6, 7)。 11 | > x_only 仅生成 chx 的测例,用来单独测试该章节测例 12 | 13 | **重要**-加载地址更新: 14 | 15 | - chapter2 所有程序加载位置位于 0x80400000,与示例代码一致。 16 | - chapter3 测试程序分为 3 批,每一批的地址都为 0x80400000 + id\*0x20000,id 为程序在这一批中的序号。每一批都与参考代码一致,请分别测试。 17 | - chapter4-7 所有程序加载位置位于 0x0,与示例代码一致。 18 | 19 | lab5 默认加载程序的说明: 20 | 21 | 由于测例中没有 initproc 程序,大家在测试的时候可以手动替换为 ch5_usershell / ch5_usertest 等。最终提交时保持加载 initproc 即可,CI 测试中会进行名称替换。 22 | 23 | > `Makefile` 中会根据章节将 `chx_usertest.elf` 复制到 `initproc.elf`。如有需要,可以自行删除并用其他程序作为 `initproc` 24 | 25 | 可以在 `user/build/asm` 目录下查看汇编来确认加载地址。 26 | 27 | 其他内容详见 [guide](./guide.md) 。 28 | -------------------------------------------------------------------------------- /guide.md: -------------------------------------------------------------------------------- 1 | # OS实验的测试要求(v0.1) 2 | 3 | ### 概述 4 | 5 | 实现指导见 rcore_tutorial_book_v3,该文档仅解释测试程序。在实现文件系统之前,你应当以合适的方式(可参考知道书或者样例实现)将测试文件置于内存中某一个位置并设法运行。 6 | 7 | ### 测试文件说明 8 | 9 | 在 `/user` 目录下 `make chx` 即可得到实验x的测例,位于 `user/build`目录下,`elf` 和 `bin` 表示格式。(具体操作见 `user/Makefile`) 10 | 11 | `chx_*` 格式的文件表明属于实验x,`chxt_*` 格式的文件表明属于实验x,但仅仅是暂时实现,后续可能会去除,具体见下方描述。 12 | 13 | ## lab1 14 | 15 | ##### 核心目标 16 | 17 | boot 起来,能够输出就行。 18 | 19 | ##### 实现 20 | 21 | 完成基本初始化,主要是硬件加电后的硬件初始化,推荐使用SBI。OS需要知道内存大小,IO分布。 22 | 23 | 实现串口输出功能,方便后续调试(可依赖SBI)。 24 | 25 | ##### 测试 26 | 27 | 模仿样例实现 28 | 29 | * 获取内存布局并输出 30 | 31 | * 例如 32 | 33 | ```rust 34 | extern "C" { 35 | fn stext(); 36 | fn etext(); 37 | fn srodata(); 38 | fn erodata(); 39 | fn sdata(); 40 | fn edata(); 41 | fn sbss(); 42 | fn ebss(); 43 | fn boot_stack(); 44 | fn boot_stack_top(); 45 | }; 46 | clear_bss(); 47 | println!("Hello, world!"); 48 | println!(".text [{:#x}, {:#x})", stext as usize, etext as usize); 49 | println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize); 50 | println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize); 51 | println!("boot_stack [{:#x}, {:#x})", boot_stack as usize, boot_stack_top as usize); 52 | println!(".bss [{:#x}, {:#x})", sbss as usize, ebss as usize); 53 | ``` 54 | 55 | * 实现彩色化输出 56 | 57 | * 例如: 58 | 59 | ```Rust 60 | debug!("{}{}{}", a, b, c); 61 | info!("{}{}{}", c, d, e); 62 | error!("{}{}{}", A, B, C); 63 | ``` 64 | 65 | 可以参考[rCore](https://github.com/rcore-os/rCore.git) ,[垃圾os](https://github.com/DeathWish5/appdir)。 66 | 67 | * 本质是在print的内容之前/之后输出一段特殊的控制字符。 68 | 69 | ## lab2 70 | 71 | ##### 核心目标 72 | 73 | 实现内核/用户态隔离与切换。 74 | 75 | ##### 硬性约定 76 | 77 | * 用户程序用户栈大小为一个页,也就是0x1000 (4k),且按照0x1000对其。统一大小方便测试。 78 | 79 | ##### 实现 80 | 81 | * 隔离 82 | * 功能描述:U态不能访问 M/S 态的指令和寄存器,程序不能访问非法地址 83 | * 对应测例:`ch2[t]_bad_*`。暂时性说明:`ch2t_bad_address` 在lab4实现虚存后失效。 84 | 85 | * `sys_write`: 86 | * 功能描述:与 posix `sys_write` 基本一致 87 | * syscall ID:64 88 | * 功能:从内存缓冲区写入一段内容到文件/串口。 89 | * C 接口:`int sys_write(int fd, char *buf, int len)`; 90 | * Rust 接口:`fn sys_write(fd: i32, buf: *mut u8, len: i32) -> i32`; 91 | * 参数:**fd** 描述当前进程需要访问的文件,**buf** 表示保存即将写入文件的数据的缓冲区的地址,**len** 表示最大的写入字节数。 92 | * 返回值:如果出现了错误则返回 -1,否则返回实际写入的字节数。 93 | * 可能的错误: 94 | * 传入的 **fd** 不合法(目前仅支持 stdout) 95 | * 传入缓冲区位于用户地址之外(需要分别检查 .text .data .bss 各段以及用户栈,如果是 bin 格式会简单很多) 96 | * 备注:该 syscall 的实现可能是阻塞的。 97 | * 对应测例: 98 | * `ch2t_write0`: 测试错误检查。暂时性说明:在lab4实现虚存后失效。 99 | * `ch2_write1`: 测试参数正确时的功能实现。 100 | * `sys_exit`: 101 | * 功能描述:与 posix `sys_exit` 基本一致 102 | * syscall ID:93 103 | * 功能:退出当前进程。 104 | * C 接口:`int sys_exit(int status);` 105 | * Rust 接口:`fn sys_exit(status: i32) -> i32;` 106 | * 参数:**status** 描述当前进程的返回值,并应当由其父进程捕获到。 107 | * 返回值:正常情况下应不会返回。请在调用 exit 之后加入 panic 语句来确保这一点。 108 | * 可能的错误:没啥。 109 | * 对应测例: 110 | * `ch2_exit` 111 | * 所有用户程序最终都需要调用 112 | 113 | ## lab3 114 | 115 | ##### 核心目标 116 | 117 | 实现最简单的上下文的概念,可以切换进程。实现时钟中断。 118 | 119 | ##### 硬性约定 120 | 121 | * 进程优先级在 [2, i64_max] 之间。注意,这里的优先级不能为1和0是为了实现 stride 调度的方便。 122 | * 仅在当前实验中,给用户程序设定一个较大的运行时间上限,超出就杀死,这是为了确保实现了时钟中断。暂时性说明:这个单纯是为了通过死循环测试,lab4开始就删掉。 123 | 124 | ##### 使用说明 125 | 126 | 需要分别 make ch3_0/ch3_1/ch3_2 并执行测试。这是为了是的调度特征更加明显。 127 | 128 | ##### 实现 129 | 130 | * `sys_gettime` 131 | * 功能描述:与 posix `sys_gettime` 一致 132 | * syscall ID:169 133 | * 功能:获取当前时间。 134 | * C 接口:`int gettime(TimeVal* ts, int tz)`; 135 | * Rust 接口:`fn gettime(ts: &TimeVal, tz: usize) -> isize`; 136 | * 参数: 137 | * ts 为当前时间结构体 138 | ```rust 139 | #[repr(C)] 140 | #[derive(Debug)] 141 | pub struct TimeVal { 142 | pub sec: usize, 143 | pub usec: usize, 144 | } 145 | ``` 146 | * tz 表示时区,这里无需考虑,始终为0。 147 | * 返回值:正确返回 0,错误返回 -1。 148 | * 可能的错误:无。 149 | * 对应测例: 150 | 151 | * `ch3_0_sleep`:注意这里没有实现sleep系统调用,只是一个用户太模拟。 152 | 153 | * `sys_yield` 154 | * 功能描述:与 posix `sys_yield` 基本一致 155 | * syscall ID:124 156 | * 功能:主动交出当前进程的 CPU 使用权,从而使得 CPU 可以执行其他进程。 157 | * C 接口:`int yield();` 158 | * Rust 接口:`fn yield() -> i32;` 159 | * 参数:无参数。 160 | * 返回值:总是返回 0。 161 | * 可能的错误:没有正确切换。 162 | * 对应测例 163 | * `ch3_1_yield*`: 测试时需要以批处理形式并发运行`ch3_1_yield[012]`,正确输出为交替出现的 ABC。为了排除其他程序干扰,推荐不要运行除此之外的程序。 164 | * `sys_set_priority` 165 | * 功能描述:设定进程优先级 166 | * syscall ID: 140 167 | * 功能:设定进程优先级。 168 | * C 接口:`int setpriority(long long prio);` 169 | * Rust 接口:`fn setpriority(prio: isize) -> isize;` 170 | * 说明:设定自身进程优先级,只要 prio 在 [2, isize_max] 就成功,返回 prio,否则返回 -1。 171 | * 对应测例 172 | * `ch3_0_setprio` 173 | 174 | * 时钟中断 175 | * 功能描述:定期的时钟中断 176 | * 对应测试: 177 | * `ch3_t_deadloop`: 就是一个死循环,只有实现了时钟中断才能把它杀死。 178 | 179 | * stride 调度 180 | * 功能描述:见指导书对应章节。 181 | * 测试说明: 182 | * `ch3_3_stride`: 测试时需要以批处理形式并发运行`ch3_3_stride[012345]`,程序本质是过固定时间计数+1,最终输出的计数要和优先级基本成正比。 183 | 184 | ## lab4 185 | 186 | ##### 核心目标 187 | 188 | 实现虚存/物理内存管理。 189 | 190 | ##### 硬性约定 191 | 192 | * 注意删除 lab3 中杀死进程的逻辑 193 | * 该章节 `mmap`系统调用为非标准格式,含义也不尽相同。 194 | 195 | ##### 实现 196 | 197 | * mmap 198 | * syscall ID:222 199 | * C接口:`int mmap(void* start, unsigned long long len, int port);` 200 | * Rust接口:`fn mmap(start: usize, len: usize, port: usize) -> isize;` 201 | * 功能:申请长度为 len 字节的物理内存,并映射到 addr 开始的虚存。 202 | * 参数: 203 | * start 需要映射的虚存起始地址,要求按页对齐。 204 | * len:映射字节长度,可以为0(如果是则直接返回),暂时不考虑 len 过长的情况。实现中对页长度取上整。 205 | * port:第0位表示是否可读,第1位表示是否可写,第2位表示是否可执行。其他位无效。 206 | * 说明:正确返回实际 map size(为 4096 的倍数),错误返回 -1。为了简单: 207 | * start 若非按页对其按错误处理 208 | * 暂时不考虑分配失败时的物理内存回收(也就是内存泄漏) 209 | * 错误: 210 | * [start, start + len) 中部分或者全部已经映射。 211 | * 物理内存不足。 212 | 213 | * munmap 214 | * syscall ID:215 215 | * C接口:`int mmap(void* start, unsigned long long len);` 216 | * Rust接口:`fn mmap(start: usize, len: usize) -> i32;` 217 | * 功能:取消一块虚存的映射。 218 | * 参数:同 mmap。 219 | * 说明:正确返回实际 unmap size(为 4096 的倍数),错误返回 -1。为了简单: 220 | * addr 若非按页对其按错误处理 221 | * 暂时不考虑中途失败时的物理内存回收与恢复 222 | * 错误: 223 | * [start, start + len) 中部分或者全部没有被映射。 224 | 225 | ## lab5 226 | 227 | ##### 核心目标 228 | 229 | 实现进程管理。 230 | 231 | ##### 硬性约定 232 | 233 | * 该章节 `sys_spawn`系统调用为生造,实际上没有该系统调用,但是可以用 fork + exec 模拟。 234 | 235 | ##### 实现 236 | 237 | * getpid 238 | * syscall ID:172 239 | * 功能:获取当前进程的进程 ID。 240 | * C 接口:`int getpid();` 241 | * Rust 接口:`fn getpid() -> i32;` 242 | * 参数:无参数。 243 | * 返回值:返回当前进程的进程 ID。 244 | * 可能的错误:无。 245 | 246 | * spawn 247 | * syscall ID: 400 248 | * C 接口:`int spawn(char *file);` 249 | * Rust 接口:`fn spawn(file: *const u8);` 250 | * 功能:创建一个子进程并执行目标路径文件,暂时不考虑参数,不要求立即开始执行,相当于 fork + exec。 251 | * 说明:成功返回子进程 id,否则返回 -1。 252 | 253 | * waitpid 254 | * syscall ID:260 255 | * 功能:当前进程等待一个子进程结束,并获取其返回值。 256 | * C 接口:`int waitpid(int pid, int *status);` 257 | * Rust 接口: `fn waitpid(pid: i32, status: *mut i32) -> i32;` 258 | * 参数: 259 | * **pid** 表示要等待结束的子进程的进程 ID,如果为 0或者-1 的话表示等待任意一个子进程结束; 260 | * **status** 表示保存子进程返回值的地址,如果这个地址为 0 的话表示不必保存。 261 | * 返回值:如果出现了错误则返回 -1;否则返回结束的子进程的进程 ID。 262 | * 说明: 263 | * 该 syscall 会导致阻塞。 264 | * 可能的错误: 265 | * 进程无未结束子进程。 266 | * pid 非法或者指定的不是该进程的子进程。 267 | * 传入的地址 status 不为 0 但是不合法; 268 | 269 | ## lab6 270 | 271 | ##### 核心目标 272 | 273 | 实现基于邮箱的进程间通信。 274 | 275 | ##### 硬性约定 276 | 277 | * 该章节系统调用为生造,实际上没有真实的系统调用。 278 | 279 | * 邮箱说明: 280 | 281 | 每个进程默认拥有唯一一个邮箱,基于“数据报文”收发字节信息,利用环形buffer存储,读写顺序为 FIFO,不记录来源进程。每次读写单位必须为一个报文,如果缓冲区长度不够,舍弃超出的部分(也就是截断报文)。为了简单,邮箱中最多拥有16条报文,每条报文最大长度256字节。当邮箱满时,发送邮件(也就是写邮箱会失败)。不考虑读写邮箱的权限,也就是所有进程都能够随意读写其他进程的邮箱。 282 | 283 | ##### 实现 284 | 285 | * mailread 286 | * syscall ID:401 287 | * C接口:`int mailread(void* buf, int len)` 288 | * Rust接口: `fn mailread(buf: *mut u8, len: usize);` 289 | * 功能:读取一个报文,如果成功返回报文长度. 290 | * 参数:buf: 缓冲区头。len:缓冲区长度。 291 | * 说明: 292 | * len > 256 按 256 处理,len < 队列首报文长度且不为0,则截断报文。 293 | * len = 0,则不进行读取,如果没有报文读取,返回-1,否则返回0,这是用来测试是否有报文可读。 294 | * 可能的错误: 295 | * 邮箱空。 296 | * buf 无效。 297 | * mailwrite 298 | * syscall ID:402 299 | * C接口:`int mailwrite(int pid, void* buf, int len)` 300 | * Rust接口: `fn mailwrite(pid: usize, buf: *mut u8, len: usize);` 301 | * 功能:向对应进程邮箱插入一条报文. 302 | * 参数:pid: 目标进程id。buf: 缓冲区头。len:缓冲区长度。 303 | * 说明: 304 | * len > 256 按 256 处理, 305 | * len = 0,则不进行写入,如果邮箱满,返回-1,否则返回0,这是用来测试是否可以发报。 306 | * 可以向自己的邮箱写入报文。 307 | * 可能的错误: 308 | * 邮箱满。 309 | * buf 无效。 310 | 311 | ## lab7 312 | 313 | ##### 核心目标 314 | 315 | 实现磁盘简单读写与管理。 316 | 317 | ##### 硬性约定 318 | 319 | * 本章为扩展实验,请先理解参考代码并实现(可以拷贝代码)基础文件管理和读写。 320 | * 暂不考虑删除文件,不可以使用 unlink 删除文件。 321 | 322 | ##### 实现 323 | 324 | * open 325 | 326 | * syscall ID:56 327 | 328 | * 功能:打开一个文件,并返回可以访问它的文件描述符。 329 | 330 | * C 接口:`int open(int dirfd, char* path, unsigned int flags, unsigned int mode);` 331 | 332 | * Rust 接口:`fn open(dirfd: usize, path: *const u8, flags: u32, mode: u32);` 333 | 334 | * 参数: 335 | 336 | * **dirfd**: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100)。可以忽略。 337 | 338 | * **path** 描述要打开的文件的文件名(简单起见,文件系统不需要支持目录,所有的文件都放在根目录 / 下), 339 | 340 | * **flags** 描述打开文件的标志,具体含义(其他参数不考虑): 341 | 342 | ```c 343 | #define O_RDONLY 0x000 344 | #define O_WRONLY 0x001 345 | #define O_RDWR 0x002 // 可读可写 346 | #define O_CREATE 0x200 347 | ``` 348 | 349 | * **mode** 仅在创建文件时有用,表示创建文件的访问权限,为了简单,本次实验中中统一为 O_RDWR。 350 | 351 | * 说明: 352 | 353 | * 有 create 标志但文件存在时,忽略 create 标志,直接打开文件。 354 | 355 | * 返回值:如果出现了错误则返回 -1,否则返回可以访问给定文件的文件描述符。 356 | 357 | * 可能的错误: 358 | 359 | * 文件不存在且无 create 标志。 360 | * 标志非法(低两位为 0x3) 361 | * 打开文件数量达到上限。 362 | 363 | * close 364 | 365 | * syscall ID:57 366 | * 功能:关闭一个文件。 367 | * C 接口:`int close(int fd);` 368 | * Rust 接口:`fn close(fd: i32) -> i32;` 369 | * 参数:**fd** 为文件描述符。 370 | * 返回值:如果出现了错误则返回 -1,否则返回 0。 371 | * 可能的错误: 372 | * 传入的文件描述符 fd 并未被打开或者为保留句柄。 373 | 374 | * link 375 | 376 | * syscall ID: 37 377 | * 功能:创建一个文件的一个硬链接,含义课堂讲授。 378 | * C接口:`int linkat(int olddirfd, char* oldpath, int newdirfd, char* newpath, unsigned int flags)` 379 | * Rust 接口:`fn linkat(olddirfd: i32, oldpath: *const u8, newdirfd: i32, newpath: *const u8, flags: u32) -> i32` 380 | * 参数: 381 | * olddirfd,newdirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。 382 | * flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。 383 | * oldpath:原有文件路径 384 | * newpath: 新的链接文件路径。 385 | * 说明: 386 | * 为了方便,不考虑新文件路径已经存在的情况(属于未定义行为),除非链接同名文件。 387 | * 返回值:如果出现了错误则返回 -1,否则返回 0。 388 | * 可能的错误 389 | * 链接同名文件。 390 | 391 | * unlink 392 | 393 | * syscall ID: 35 394 | * 功能:取消一个文件路径到文件的链接。 395 | * C接口:`int unlinkat(int dirfd, char* path, unsigned int flags)` 396 | * Rust 接口:`fn unlinkat(dirfd: i32, path: *const u8, flags: u32) -> i32` 397 | * 参数: 398 | * dirfd: 仅为了兼容性考虑,本次实验中始终为 AT_FDCWD (-100),可以忽略。 399 | * flags: 仅为了兼容性考虑,本次实验中始终为 0,可以忽略。 400 | * path:文件路径。 401 | * 说明: 402 | * 为了方便,不考虑使用 unlink 彻底删除文件的情况。 403 | * 返回值:如果出现了错误则返回 -1,否则返回 0。 404 | * 可能的错误 405 | * 文件不存在。 406 | 407 | * fstat 408 | 409 | * syscall ID: 80 410 | 411 | * 功能:获取文件状态。 412 | 413 | * C接口:`int fstat(int fd, struct Stat* st)` 414 | 415 | * Rust 接口:`fn fstat(fd: i32, st: *mut Stat) -> i32` 416 | 417 | * 参数: 418 | 419 | * fd: 文件描述符 420 | 421 | * st: 文件状态结构体 422 | 423 | ``` 424 | struct Stat { 425 | uint64 dev, // 文件所在磁盘驱动器号,不考虑 426 | uint64 ino, // inode 文件所在 inode 编号 427 | uint32 mode, // 文件类型 428 | uint32 nlink, // 硬链接数量,初始为1 429 | uint64 pad[7], // 无需考虑,为了兼容性设计 430 | } 431 | 432 | // 文件类型只需要考虑: 433 | #define DIR 0o040000 // directory 434 | #define FILE 0o100000 // ordinary regular file 435 | ``` 436 | 437 | * 返回值:如果出现了错误则返回 -1,否则返回 0。 438 | 439 | * 可能的错误 440 | 441 | * fd 无效。 442 | * st 地址非法。 -------------------------------------------------------------------------------- /user/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64gc-unknown-none-elf" 3 | 4 | [target.riscv64gc-unknown-none-elf] 5 | rustflags = [ 6 | "-Clink-args=-Tsrc/linker.ld", 7 | ] -------------------------------------------------------------------------------- /user/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "user-lib" 3 | version = "0.1.0" 4 | authors = ["deathwish5 "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [dependencies] 9 | buddy_system_allocator = "0.6" 10 | bitflags = "1.2.1" 11 | lazy_static = { version = "1.4.0", features = ["spin_no_std"] } 12 | spin = "0.9" 13 | rand = { version = "0.8", default-features = false, features = ["alloc", "small_rng"] } 14 | -------------------------------------------------------------------------------- /user/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := riscv64gc-unknown-none-elf 2 | MODE := release 3 | APP_DIR := src/bin 4 | TARGET_DIR := target/$(TARGET)/$(MODE) 5 | BUILD_DIR := build 6 | OBJDUMP := rust-objdump --arch-name=riscv64 7 | OBJCOPY := rust-objcopy --binary-architecture=riscv64 8 | CP := cp 9 | PY := python3 10 | BUILD_SCRIPTS := build.py 11 | CH_TESTS := 12 | CHAPTER ?= 13 | HAVE_USERTESTS := 5 6 7 14 | 15 | CH2_TESTS := ch2_ ch2t_ 16 | CH2_TESTS_BAD := _ch2_ _ch2t_ 17 | CH3_TESTS_BASE := $(CH2_TESTS) ch3_0_ ch3t_ 18 | CH4_TESTS := ch2_ ch3_0_ ch4_ 19 | CH5_TESTS := $(CH4_TESTS) ch5_ 20 | CH6_TESTS := $(CH5_TESTS) ch6_ 21 | CH7_TESTS := $(CH6_TESTS) ch7_ 22 | CH8_TESTS := $(CH7_TESTS) ch8_ 23 | 24 | ifeq ($(CHAPTER), 2) 25 | CH_TESTS := $(CH2_TESTS) 26 | BUILD_SCRIPTS := ch2-build.py 27 | else ifeq ($(CHAPTER), 2_bad) 28 | CH_TESTS := $(CH2_TESTS_BAD) 29 | BUILD_SCRIPTS := ch2-build.py 30 | else ifeq ($(CHAPTER), 3_0) 31 | CH_TESTS := $(CH3_TESTS_BASE) 32 | BUILD_SCRIPTS := ch3-build.py 33 | else ifeq ($(CHAPTER), 3_1) 34 | CH_TESTS := ch3_1_ 35 | BUILD_SCRIPTS := ch3-build.py 36 | else ifeq ($(CHAPTER), 3_2) 37 | CH_TESTS := ch3_2_ 38 | BUILD_SCRIPTS := ch3-build.py 39 | else ifeq ($(CHAPTER), 4) 40 | CH_TESTS := $(CH4_TESTS) 41 | else ifeq ($(CHAPTER), 4_only) 42 | CH_TESTS := ch4 43 | else ifeq ($(CHAPTER), 5) 44 | CH_TESTS := $(CH5_TESTS) 45 | else ifeq ($(CHAPTER), 5_only) 46 | CH_TESTS := ch5 47 | else ifeq ($(CHAPTER), 6) 48 | CH_TESTS := $(CH6_TESTS) 49 | else ifeq ($(CHAPTER), 6_only) 50 | CH_TESTS := ch6 51 | else ifeq ($(CHAPTER), 7) 52 | CH_TESTS := $(CH7_TESTS) 53 | else ifeq ($(CHAPTER), 7_only) 54 | CH_TESTS := ch7 55 | else ifeq ($(CHAPTER), 8) 56 | CH_TESTS := $(CH8_TESTS) 57 | else ifeq ($(CHAPTER), 8_only) 58 | CH_TESTS := ch8 59 | endif 60 | 61 | APPS := $(foreach c, $(CH_TESTS), $(wildcard $(APP_DIR)/$(c)*.rs)) 62 | ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS)) 63 | 64 | binary: $(APPS) 65 | @$(PY) $(BUILD_SCRIPTS) $(CHAPTER) 66 | @$(foreach elf, $(ELFS), \ 67 | $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf)); \ 68 | $(CP) $(elf) $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.elf, $(elf)); \ 69 | $(OBJDUMP) $(elf) -d > $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.asm, $(elf));) 70 | 71 | pre: 72 | @rm -rf $(BUILD_DIR) 73 | @mkdir -p $(BUILD_DIR)/bin/ 74 | @mkdir -p $(BUILD_DIR)/elf/ 75 | @mkdir -p $(BUILD_DIR)/asm/ 76 | 77 | all: binary pre 78 | @$(foreach t, $(CH_TESTS), $(CP) $(TARGET_DIR)/$(t)*.bin $(BUILD_DIR)/bin/;) 79 | @$(foreach t, $(CH_TESTS), $(CP) $(TARGET_DIR)/$(t)*.elf $(BUILD_DIR)/elf/;) 80 | @$(foreach t, $(CH_TESTS), $(CP) $(TARGET_DIR)/$(t)*.asm $(BUILD_DIR)/asm/;) 81 | ifneq ($(filter $(CHAPTER), $(HAVE_USERTESTS)),) 82 | @cp $(BUILD_DIR)/elf/ch$(CHAPTER)_usertest.elf $(BUILD_DIR)/elf/initproc.elf 83 | @cp $(TARGET_DIR)/ch$(CHAPTER)_usertest $(TARGET_DIR)/initproc 84 | endif 85 | 86 | 87 | clean: 88 | @cargo clean 89 | @rm -rf $(BUILD_DIR) 90 | 91 | .PHONY: elf binary build clean ch2 ch3 92 | -------------------------------------------------------------------------------- /user/build.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | if __name__ == '__main__': 4 | os.system('cargo build --release') -------------------------------------------------------------------------------- /user/ch2-build.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def set_base_address(old, new): 4 | linker = 'src/linker.ld' 5 | lines = [] 6 | with open(linker, 'r') as f: 7 | for line in f.readlines(): 8 | line = line.replace(hex(old), hex(new)) 9 | lines.append(line) 10 | with open(linker, 'w+') as f: 11 | f.writelines(lines) 12 | 13 | if __name__ == '__main__': 14 | origin_base_address = 0x0 15 | target_base_address = 0x80400000 16 | set_base_address(origin_base_address, target_base_address) 17 | os.system('cargo build --release') 18 | set_base_address(target_base_address, origin_base_address) 19 | 20 | -------------------------------------------------------------------------------- /user/ch3-build.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | 4 | parser = argparse.ArgumentParser() 5 | parser.add_argument("CHAPTER", metavar="CHAPTER", type=str) 6 | 7 | def set_base_address(old, new): 8 | linker = 'src/linker.ld' 9 | lines = [] 10 | with open(linker, 'r') as f: 11 | for line in f.readlines(): 12 | line = line.replace(hex(old), hex(new)) 13 | lines.append(line) 14 | with open(linker, 'w+') as f: 15 | f.writelines(lines) 16 | 17 | def build(apps, base_address): 18 | app_id = 0 19 | address = base_address 20 | step = 0x20000 21 | for app in apps: 22 | os.system('cargo build --bin %s --release' % app) 23 | print('[build.py] application %s start with address %s' %(app, hex(address))) 24 | set_base_address(address, address+step) 25 | address = address+step 26 | set_base_address(address, base_address) 27 | 28 | if __name__ == '__main__': 29 | args = parser.parse_args() 30 | origin_base_address = 0x0 31 | target_base_address = 0x80400000 32 | set_base_address(origin_base_address, target_base_address) 33 | apps = os.listdir('src/bin') 34 | apps.sort() 35 | base, yield_, stride, others = [], [], [], [] 36 | for app in apps: 37 | app = app[:app.find('.')] 38 | if app.startswith('ch2') or app.startswith('ch3_0') or app.startswith('ch3t'): 39 | base.append(app) 40 | elif app.startswith('ch3_1'): 41 | yield_.append(app) 42 | elif app.startswith('ch3_2'): 43 | stride.append(app) 44 | else: 45 | others.append(app) 46 | 47 | if args.CHAPTER == "3_0": 48 | build(base, target_base_address) 49 | elif args.CHAPTER == "3_1": 50 | build(yield_, target_base_address) 51 | elif args.CHAPTER == "3_2": 52 | build(stride, target_base_address) 53 | else: 54 | build(others, target_base_address) 55 | set_base_address(target_base_address, origin_base_address) 56 | 57 | -------------------------------------------------------------------------------- /user/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2021-01-30 -------------------------------------------------------------------------------- /user/src/bin/_ch2_bad_instruction.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(llvm_asm)] 4 | 5 | extern crate user_lib; 6 | 7 | /// 由于 rustsbi 的问题,该程序无法正确退出 8 | /// > rustsbi 0.2.0-alpha.1 已经修复,可以正常退出 9 | 10 | #[no_mangle] 11 | pub fn main() -> ! { 12 | unsafe { 13 | llvm_asm!("sret"); 14 | } 15 | panic!("FAIL: T.T\n"); 16 | } 17 | -------------------------------------------------------------------------------- /user/src/bin/_ch2_bad_register.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(llvm_asm)] 4 | 5 | extern crate user_lib; 6 | 7 | /// 由于 rustsbi 的问题,该程序无法正确退出 8 | /// > rustsbi 0.2.0-alpha.1 已经修复,可以正常退出 9 | 10 | #[no_mangle] 11 | pub fn main() -> ! { 12 | let mut sstatus: usize; 13 | unsafe { 14 | llvm_asm!("csrr $0, sstatus" : "=r"(sstatus) ::: "volatile"); 15 | } 16 | panic!("(-_-) I get sstatus:{:x}\n", sstatus); 17 | } 18 | -------------------------------------------------------------------------------- /user/src/bin/_ch2t_bad_address.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(llvm_asm)] 4 | 5 | extern crate user_lib; 6 | 7 | /// 由于 rustsbi 的问题,该程序无法正确退出 8 | /// > rustsbi 0.2.0-alpha.1 已经修复,可以正常退出 9 | 10 | #[no_mangle] 11 | pub fn main() -> isize { 12 | unsafe { 13 | (0x0 as *mut u8).write_volatile(0); 14 | } 15 | panic!("FAIL: T.T\n"); 16 | } 17 | -------------------------------------------------------------------------------- /user/src/bin/ch2_exit.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | use user_lib::exit; 6 | 7 | const MAGIC: usize = 1234; 8 | 9 | /// 正确输出: 不输出 FAIL,以 1234 退出 10 | 11 | #[allow(unreachable_code)] 12 | #[no_mangle] 13 | pub fn main() -> i32 { 14 | exit(MAGIC as i32); 15 | panic!("FAIL: T.T\n"); 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /user/src/bin/ch2_hello_world.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | /// 正确输出: 8 | /// Hello world from user mode program! 9 | /// Test hello_world OK! 10 | 11 | #[no_mangle] 12 | pub fn main() -> i32 { 13 | println!("Hello world from user mode program!\nTest hello_world OK!"); 14 | 0 15 | } 16 | -------------------------------------------------------------------------------- /user/src/bin/ch2_power.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | const SIZE: usize = 10; 8 | const P: u32 = 3; 9 | const STEP: usize = 100000; 10 | const MOD: u32 = 10007; 11 | 12 | /// 正确输出: 13 | /// 3^10000=5079 14 | /// 3^20000=8202 15 | /// 3^30000=8824 16 | /// 3^40000=5750 17 | /// 3^50000=3824 18 | /// 3^60000=8516 19 | /// 3^70000=2510 20 | /// 3^80000=9379 21 | /// 3^90000=2621 22 | /// 3^100000=2749 23 | /// Test power OK! 24 | 25 | #[no_mangle] 26 | fn main() -> i32 { 27 | let mut pow = [0u32; SIZE]; 28 | let mut index: usize = 0; 29 | pow[index] = 1; 30 | for i in 1..=STEP { 31 | let last = pow[index]; 32 | index = (index + 1) % SIZE; 33 | pow[index] = last * P % MOD; 34 | if i % 10000 == 0 { 35 | println!("{}^{}={}", P, i, pow[index]); 36 | } 37 | } 38 | println!("Test power OK!"); 39 | 0 40 | } 41 | -------------------------------------------------------------------------------- /user/src/bin/ch2_write1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{write, STDOUT}; 7 | const DATA_STRING: &'static str = "string from data section\n"; 8 | 9 | /// 正确输出: 10 | /// string from data section 11 | /// strinstring from stack section 12 | /// strin 13 | /// Test write1 OK! 14 | 15 | #[no_mangle] 16 | pub fn main() -> i32 { 17 | assert_eq!(write(1234, DATA_STRING.as_bytes()), -1); 18 | assert_eq!( 19 | write(STDOUT, DATA_STRING.as_bytes()), 20 | DATA_STRING.len() as isize 21 | ); 22 | assert_eq!(write(STDOUT, &DATA_STRING.as_bytes()[..5]), 5); 23 | let stack_string = "string from stack section\n"; 24 | assert_eq!( 25 | write(STDOUT, stack_string.as_bytes()), 26 | stack_string.len() as isize 27 | ); 28 | assert_eq!(write(STDOUT, &stack_string.as_bytes()[..5]), 5); 29 | println!("\nTest write1 OK!"); 30 | 0 31 | } 32 | -------------------------------------------------------------------------------- /user/src/bin/ch2t_write0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(llvm_asm)] 4 | 5 | #[macro_use] 6 | extern crate user_lib; 7 | extern crate core; 8 | use core::slice; 9 | use user_lib::{write, STDOUT}; 10 | 11 | /// 正确输出: 12 | /// Test write0 OK! 13 | 14 | const STACK_SIZE: usize = 0x1000; 15 | 16 | unsafe fn r_sp() -> usize { 17 | let mut sp: usize; 18 | llvm_asm!("mv $0, sp": "=r"(sp) ::: "volatile"); 19 | sp 20 | } 21 | 22 | // 注意,这里要求 user_stack 大小为 4096 且按照 4096 字节对齐。 23 | // 请调整你内核中的用户栈的设定。 24 | 25 | unsafe fn stack_range() -> (usize, usize) { 26 | let sp = r_sp(); 27 | let top = (sp + STACK_SIZE - 1) & (!(STACK_SIZE - 1)); 28 | (top - STACK_SIZE, top) 29 | } 30 | 31 | #[no_mangle] 32 | pub unsafe fn main() -> i32 { 33 | assert_eq!( 34 | write(STDOUT, slice::from_raw_parts(0x0 as *const _, 10)), 35 | -1 36 | ); 37 | let (bottom, top) = stack_range(); 38 | assert_eq!( 39 | write(STDOUT, slice::from_raw_parts((top - 5) as *const _, 10)), 40 | -1 41 | ); 42 | assert_eq!( 43 | write(STDOUT, slice::from_raw_parts((bottom - 5) as *const _, 10)), 44 | -1 45 | ); 46 | // TODO: test string located in .data section 47 | println!("Test write0 OK!"); 48 | 0 49 | } 50 | -------------------------------------------------------------------------------- /user/src/bin/ch3_0_setprio.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::set_priority; 7 | 8 | /// 正确输出:(无报错信息) 9 | /// Test set_priority OK! 10 | 11 | #[no_mangle] 12 | pub fn main() -> i32 { 13 | assert_eq!(set_priority(10), 10); 14 | assert_eq!(set_priority(isize::MAX), isize::MAX); 15 | assert_eq!(set_priority(0), -1); 16 | assert_eq!(set_priority(1), -1); 17 | assert_eq!(set_priority(-10), -1); 18 | println!("Test set_priority OK!"); 19 | 0 20 | } 21 | -------------------------------------------------------------------------------- /user/src/bin/ch3_0_sleep.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{get_time, yield_}; 8 | 9 | /// 正确输出:(无报错信息) 10 | /// get_time OK! {...} 11 | /// Test sleep OK! 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let current_time = get_time(); 16 | assert!(current_time > 0); 17 | println!("get_time OK! {}", current_time); 18 | let wait_for = current_time + 3000; 19 | while get_time() < wait_for { 20 | yield_(); 21 | } 22 | println!("Test sleep OK!"); 23 | 0 24 | } 25 | -------------------------------------------------------------------------------- /user/src/bin/ch3_0_sleep1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{get_time, sleep}; 8 | 9 | #[no_mangle] 10 | pub fn main() -> i32 { 11 | let start = get_time(); 12 | println!("current time_msec = {}", start); 13 | sleep(100); 14 | let end = get_time(); 15 | println!( 16 | "time_msec = {} after sleeping 100 ticks, delta = {}ms!", 17 | end, 18 | end - start 19 | ); 20 | println!("Test sleep1 passed!"); 21 | 0 22 | } 23 | -------------------------------------------------------------------------------- /user/src/bin/ch3_1_yield0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::yield_; 8 | 9 | const WIDTH: usize = 10; 10 | const HEIGHT: usize = 5; 11 | 12 | /* 13 | 理想结果:三个程序交替输出 ABC 14 | */ 15 | 16 | #[no_mangle] 17 | fn main() -> i32 { 18 | for i in 0..HEIGHT { 19 | let buf = ['A' as u8; WIDTH]; 20 | println!( 21 | "{} [{}/{}]", 22 | core::str::from_utf8(&buf).unwrap(), 23 | i + 1, 24 | HEIGHT 25 | ); 26 | yield_(); 27 | } 28 | println!("Test write A OK!"); 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /user/src/bin/ch3_1_yield1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::yield_; 8 | 9 | const WIDTH: usize = 10; 10 | const HEIGHT: usize = 5; 11 | 12 | /* 13 | 理想结果:三个程序交替输出 ABC 14 | */ 15 | 16 | #[no_mangle] 17 | #[no_mangle] 18 | fn main() -> i32 { 19 | for i in 0..HEIGHT { 20 | let buf = ['B' as u8; WIDTH]; 21 | println!( 22 | "{} [{}/{}]", 23 | core::str::from_utf8(&buf).unwrap(), 24 | i + 1, 25 | HEIGHT 26 | ); 27 | yield_(); 28 | } 29 | println!("Test write B OK!"); 30 | 0 31 | } 32 | -------------------------------------------------------------------------------- /user/src/bin/ch3_1_yield2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::yield_; 8 | 9 | /* 10 | 理想结果:三个程序交替输出 ABC 11 | */ 12 | 13 | const WIDTH: usize = 10; 14 | const HEIGHT: usize = 5; 15 | 16 | #[no_mangle] 17 | #[no_mangle] 18 | fn main() -> i32 { 19 | for i in 0..HEIGHT { 20 | let buf = ['C' as u8; WIDTH]; 21 | println!( 22 | "{} [{}/{}]", 23 | core::str::from_utf8(&buf).unwrap(), 24 | i + 1, 25 | HEIGHT 26 | ); 27 | yield_(); 28 | } 29 | println!("Test write C OK!"); 30 | 0 31 | } 32 | -------------------------------------------------------------------------------- /user/src/bin/ch3_2_stride0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{get_time, set_priority}; 7 | 8 | /* 9 | 理想结果:6个进程退出时,输出 count 基本正比于 priority 10 | */ 11 | 12 | fn spin_delay() { 13 | let mut j = true; 14 | for _ in 0..10 { 15 | j = !j; 16 | } 17 | } 18 | 19 | // to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. 20 | const MAX_TIME: isize = 1000; 21 | pub fn count_during(prio: isize) -> isize { 22 | let start_time = get_time(); 23 | let mut acc = 0; 24 | set_priority(prio); 25 | loop { 26 | spin_delay(); 27 | acc += 1; 28 | if acc % 400 == 0 { 29 | let time = get_time() - start_time; 30 | if time > MAX_TIME { 31 | return acc; 32 | } 33 | } 34 | } 35 | } 36 | 37 | #[no_mangle] 38 | pub fn main() -> usize { 39 | let prio = 5; 40 | let count = count_during(prio); 41 | println!("priority = {}, exitcode = {}", prio, count); 42 | 0 43 | } 44 | -------------------------------------------------------------------------------- /user/src/bin/ch3_2_stride1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{get_time, set_priority}; 7 | 8 | fn spin_delay() { 9 | let mut j = true; 10 | for _ in 0..10 { 11 | j = !j; 12 | } 13 | } 14 | 15 | // to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. 16 | const MAX_TIME: isize = 1000; 17 | fn count_during(prio: isize) -> isize { 18 | let start_time = get_time(); 19 | let mut acc = 0; 20 | set_priority(prio); 21 | loop { 22 | spin_delay(); 23 | acc += 1; 24 | if acc % 400 == 0 { 25 | let time = get_time() - start_time; 26 | if time > MAX_TIME { 27 | return acc; 28 | } 29 | } 30 | } 31 | } 32 | 33 | #[no_mangle] 34 | pub fn main() -> usize { 35 | let prio = 6; 36 | let count = count_during(prio); 37 | println!("priority = {}, exitcode = {}", prio, count); 38 | 0 39 | } -------------------------------------------------------------------------------- /user/src/bin/ch3_2_stride2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{get_time, set_priority}; 7 | 8 | fn spin_delay() { 9 | let mut j = true; 10 | for _ in 0..10 { 11 | j = !j; 12 | } 13 | } 14 | 15 | // to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. 16 | const MAX_TIME: isize = 1000; 17 | fn count_during(prio: isize) -> isize { 18 | let start_time = get_time(); 19 | let mut acc = 0; 20 | set_priority(prio); 21 | loop { 22 | spin_delay(); 23 | acc += 1; 24 | if acc % 400 == 0 { 25 | let time = get_time() - start_time; 26 | if time > MAX_TIME { 27 | return acc; 28 | } 29 | } 30 | } 31 | } 32 | 33 | #[no_mangle] 34 | pub fn main() -> usize { 35 | let prio = 7; 36 | let count = count_during(prio); 37 | println!("priority = {}, exitcode = {}", prio, count); 38 | 0 39 | } -------------------------------------------------------------------------------- /user/src/bin/ch3_2_stride3.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{get_time, set_priority}; 7 | 8 | fn spin_delay() { 9 | let mut j = true; 10 | for _ in 0..10 { 11 | j = !j; 12 | } 13 | } 14 | 15 | // to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. 16 | const MAX_TIME: isize = 1000; 17 | fn count_during(prio: isize) -> isize { 18 | let start_time = get_time(); 19 | let mut acc = 0; 20 | set_priority(prio); 21 | loop { 22 | spin_delay(); 23 | acc += 1; 24 | if acc % 400 == 0 { 25 | let time = get_time() - start_time; 26 | if time > MAX_TIME { 27 | return acc; 28 | } 29 | } 30 | } 31 | } 32 | 33 | #[no_mangle] 34 | pub fn main() -> usize { 35 | let prio = 8; 36 | let count = count_during(prio); 37 | println!("priority = {}, exitcode = {}", prio, count); 38 | 0 39 | } -------------------------------------------------------------------------------- /user/src/bin/ch3_2_stride4.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{get_time, set_priority}; 7 | 8 | fn spin_delay() { 9 | let mut j = true; 10 | for _ in 0..10 { 11 | j = !j; 12 | } 13 | } 14 | 15 | // to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. 16 | const MAX_TIME: isize = 1000; 17 | fn count_during(prio: isize) -> isize { 18 | let start_time = get_time(); 19 | let mut acc = 0; 20 | set_priority(prio); 21 | loop { 22 | spin_delay(); 23 | acc += 1; 24 | if acc % 400 == 0 { 25 | let time = get_time() - start_time; 26 | if time > MAX_TIME { 27 | return acc; 28 | } 29 | } 30 | } 31 | } 32 | 33 | #[no_mangle] 34 | pub fn main() -> usize { 35 | let prio = 9; 36 | let count = count_during(prio); 37 | println!("priority = {}, exitcode = {}", prio, count); 38 | 0 39 | } -------------------------------------------------------------------------------- /user/src/bin/ch3_2_stride5.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{get_time, set_priority}; 7 | 8 | fn spin_delay() { 9 | let mut j = true; 10 | for _ in 0..10 { 11 | j = !j; 12 | } 13 | } 14 | 15 | // to get enough accuracy, MAX_TIME (the running time of each process) should > 1000 mseconds. 16 | const MAX_TIME: isize = 1000; 17 | fn count_during(prio: isize) -> isize { 18 | let start_time = get_time(); 19 | let mut acc = 0; 20 | set_priority(prio); 21 | loop { 22 | spin_delay(); 23 | acc += 1; 24 | if acc % 400 == 0 { 25 | let time = get_time() - start_time; 26 | if time > MAX_TIME { 27 | return acc; 28 | } 29 | } 30 | } 31 | } 32 | 33 | #[no_mangle] 34 | pub fn main() -> usize { 35 | let prio = 10; 36 | let count = count_during(prio); 37 | println!("priority = {}, exitcode = {}", prio, count); 38 | 0 39 | } -------------------------------------------------------------------------------- /user/src/bin/ch3t_deadloop.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | /* 7 | 理想结果:执行一段时间之后被正确杀死,不会陷入死循环。 8 | */ 9 | 10 | #[no_mangle] 11 | pub fn main() -> ! { 12 | loop {} 13 | } 14 | -------------------------------------------------------------------------------- /user/src/bin/ch4_mmap0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::mmap; 8 | 9 | /* 10 | 理想结果:输出 Test 04_1 OK! 11 | */ 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let start: usize = 0x10000000; 16 | let len: usize = 4096; 17 | let prot: usize = 3; 18 | assert_eq!(len as isize, mmap(start, len, prot)); 19 | for i in start..(start + len) { 20 | let addr: *mut u8 = i as *mut u8; 21 | unsafe { 22 | *addr = i as u8; 23 | } 24 | } 25 | for i in start..(start + len) { 26 | let addr: *mut u8 = i as *mut u8; 27 | unsafe { 28 | assert_eq!(*addr, i as u8); 29 | } 30 | } 31 | println!("Test 04_1 OK!"); 32 | 0 33 | } 34 | -------------------------------------------------------------------------------- /user/src/bin/ch4_mmap1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::mmap; 8 | 9 | /* 10 | 理想结果:程序触发访存异常,被杀死。不输出 error 就算过。 11 | */ 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let start: usize = 0x10000000; 16 | let len: usize = 4096; 17 | let prot: usize = 1; 18 | assert_eq!(len as isize, mmap(start, len, prot)); 19 | let addr: *mut u8 = start as *mut u8; 20 | unsafe { 21 | *addr = start as u8; 22 | } 23 | println!("Should cause error, Test 04_2 fail!"); 24 | 0 25 | } 26 | -------------------------------------------------------------------------------- /user/src/bin/ch4_mmap2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::mmap; 8 | 9 | /* 10 | 理想结果:程序触发访存异常,被杀死。不输出 error 就算过。 11 | */ 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let start: usize = 0x10000000; 16 | let len: usize = 4096; 17 | let prot: usize = 2; 18 | assert_eq!(len as isize, mmap(start, len, prot)); 19 | let addr: *mut u8 = start as *mut u8; 20 | unsafe { 21 | // *addr = start as u8; // can't write, R == 0 && W == 1 is illegal in riscv 22 | assert!(*addr != 0); 23 | } 24 | println!("Should cause error, Test 04_2 fail!"); 25 | 0 26 | } 27 | -------------------------------------------------------------------------------- /user/src/bin/ch4_mmap3.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::mmap; 8 | 9 | /* 10 | 理想结果:对于错误的 mmap 返回 -1,最终输出 Test 04_4 test OK! 11 | */ 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let start: usize = 0x10000000; 16 | let len: usize = 4096; 17 | let prot: usize = 3; 18 | assert_eq!(len as isize, mmap(start, len, prot)); 19 | assert_eq!(mmap(start - len, len + 1, prot), -1); 20 | assert_eq!(mmap(start + len + 1, len, prot), -1); 21 | assert_eq!(mmap(start + len, len, 0), -1); 22 | assert_eq!(mmap(start + len, len, prot | 8), -1); 23 | println!("Test 04_4 test OK!"); 24 | 0 25 | } 26 | -------------------------------------------------------------------------------- /user/src/bin/ch4_unmap.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{mmap, munmap}; 8 | 9 | /* 10 | 理想结果:输出 Test 04_5 ummap OK! 11 | */ 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let start: usize = 0x10000000; 16 | let len: usize = 4096; 17 | let prot: usize = 3; 18 | assert_eq!(len as isize, mmap(start, len, prot)); 19 | assert_eq!(mmap(start + len, len * 2, prot), (len * 2) as isize); 20 | assert_eq!(munmap(start, len), len as isize); 21 | assert_eq!(mmap(start - len, len + 1, prot), (len * 2) as isize); 22 | for i in (start - len)..(start + len * 3) { 23 | let addr: *mut u8 = i as *mut u8; 24 | unsafe { 25 | *addr = i as u8; 26 | } 27 | } 28 | for i in (start - len)..(start + len * 3) { 29 | let addr: *mut u8 = i as *mut u8; 30 | unsafe { 31 | assert_eq!(*addr, i as u8); 32 | } 33 | } 34 | println!("Test 04_5 ummap OK!"); 35 | 0 36 | } 37 | -------------------------------------------------------------------------------- /user/src/bin/ch4_unmap2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{mmap, munmap}; 8 | 9 | /* 10 | 理想结果:输出 Test 04_6 ummap2 OK! 11 | */ 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let start: usize = 0x10000000; 16 | let len: usize = 4096; 17 | let prot: usize = 3; 18 | assert_eq!(len as isize, mmap(start, len, prot)); 19 | assert_eq!(munmap(start, len + 1), -1); 20 | assert_eq!(munmap(start + 1, len - 1), -1); 21 | println!("Test 04_6 ummap2 OK!"); 22 | 0 23 | } 24 | -------------------------------------------------------------------------------- /user/src/bin/ch5_exit0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | use user_lib::exit; 6 | 7 | /* 8 | 辅助测例,正常退出,不输出 FAIL 即可。 9 | */ 10 | 11 | #[allow(unreachable_code)] 12 | #[no_mangle] 13 | pub fn main() -> i32 { 14 | exit(66778); 15 | panic!("FAIL: T.T\n"); 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /user/src/bin/ch5_exit1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | use user_lib::exit; 6 | 7 | /* 8 | 辅助测例,正常退出,不输出 FAIL 即可。 9 | */ 10 | 11 | #[allow(unreachable_code)] 12 | #[no_mangle] 13 | pub fn main() -> i32 { 14 | exit(-233); 15 | panic!("FAIL: T.T\n"); 16 | 0 17 | } 18 | -------------------------------------------------------------------------------- /user/src/bin/ch5_getchar.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::console::getchar; 8 | 9 | const N: usize = 10; 10 | 11 | /* 12 | 测试 sys_read(),目前只能从 stdin 读取。 13 | 程序行为:接受 N 个键盘输入并最终一齐输出(注意没有输入时不会显示),如果一致就算正确。不要单纯以 Test getchar passed! 作为判断。 14 | */ 15 | 16 | #[no_mangle] 17 | pub fn main() -> i32 { 18 | println!("please enter {} letters.", N); 19 | let mut line = [0u8; N]; 20 | for idx in 0..N { 21 | let c = getchar(); 22 | line[idx] = c; 23 | } 24 | println!("{} letters entered", N); 25 | for idx in 0..N { 26 | print!("{}", line[idx]); 27 | } 28 | println!("\n Test getchar passed!"); 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /user/src/bin/ch5_getpid.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::getpid; 8 | 9 | /* 10 | 理想结果:得到进程 pid,注意要关注 pid 是否符合内核逻辑,不要单纯以 Test OK! 作为判断。 11 | */ 12 | 13 | #[no_mangle] 14 | pub fn main() -> i32 { 15 | let pid = getpid(); 16 | println!("Test getpid OK! pid = {}", pid); 17 | 0 18 | } 19 | -------------------------------------------------------------------------------- /user/src/bin/ch5_spawn0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{spawn, wait}; 8 | const MAX_CHILD: usize = 40; 9 | 10 | /* 11 | 理想结果:生成 MAX_CHILD 个 getpid 的子进程,全部结束后,输出 Test spawn0 OK! 12 | */ 13 | 14 | #[no_mangle] 15 | pub fn main() -> i32 { 16 | for _ in 0..MAX_CHILD { 17 | let cpid = spawn("ch5_getpid\0"); 18 | assert!(cpid >= 0, "child pid invalid"); 19 | println!("new child {}", cpid); 20 | } 21 | let mut exit_code: i32 = 0; 22 | for _ in 0..MAX_CHILD { 23 | assert!(wait(&mut exit_code) > 0, "wait stopped early"); 24 | assert_eq!(exit_code, 0, "error exit ocde {}", exit_code); 25 | } 26 | assert!(wait(&mut exit_code) <= 0, "wait got too many"); 27 | println!("Test spawn0 OK!"); 28 | 0 29 | } 30 | -------------------------------------------------------------------------------- /user/src/bin/ch5_spawn1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{spawn, wait, waitpid}; 8 | 9 | /// 程序行为:先后产生 3 个有特定返回值的程序,检查 waitpid 能够获取正确返回值。 10 | 11 | /// 理想输出: 12 | /// new child i 13 | /// Test wait OK! 14 | /// Test waitpid OK! 15 | 16 | #[no_mangle] 17 | pub fn main() -> i32 { 18 | let cpid = spawn("ch5_exit0\0"); 19 | assert!(cpid >= 0, "child pid invalid"); 20 | println!("new child {}", cpid); 21 | let mut exit_code: i32 = 0; 22 | let exit_pid = wait(&mut exit_code); 23 | assert_eq!(exit_pid, cpid, "error exit pid"); 24 | assert_eq!(exit_code, 66778, "error exit code"); 25 | println!("Test wait OK!"); 26 | let (cpid0, cpid1) = (spawn("ch5_exit0\0"), spawn("ch5_exit1\0")); 27 | let exit_pid = waitpid(cpid1 as usize, &mut exit_code); 28 | assert_eq!(exit_pid, cpid1, "error exit pid"); 29 | assert_eq!(exit_code, -233, "error exit code"); 30 | let exit_pid = wait(&mut exit_code); 31 | assert_eq!(exit_pid, cpid0, "error exit pid"); 32 | assert_eq!(exit_code, 66778, "error exit code"); 33 | println!("Test waitpid OK!"); 34 | 0 35 | } 36 | -------------------------------------------------------------------------------- /user/src/bin/ch5_usershell.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | #[macro_use] 7 | extern crate user_lib; 8 | 9 | const LF: u8 = 0x0au8; 10 | const CR: u8 = 0x0du8; 11 | const DL: u8 = 0x7fu8; 12 | const BS: u8 = 0x08u8; 13 | 14 | use alloc::string::String; 15 | use user_lib::console::{flush, getchar}; 16 | use user_lib::{spawn, waitpid, yield_}; 17 | 18 | /// 不是测例,方便本地测试 19 | 20 | #[no_mangle] 21 | pub fn main() -> i32 { 22 | println!("Rust user shell"); 23 | let mut line: String = String::new(); 24 | print!(">> "); 25 | flush(); 26 | loop { 27 | let c = getchar(); 28 | match c { 29 | LF | CR => { 30 | println!(""); 31 | if !line.is_empty() { 32 | line.push('\0'); 33 | let cpid = spawn(line.as_str()); 34 | if cpid < 0 { 35 | println!("invalid file name"); 36 | } else { 37 | let mut xstate: i32 = 0; 38 | let mut exit_pid: isize; 39 | loop { 40 | exit_pid = waitpid(cpid as usize, &mut xstate); 41 | if exit_pid == -1 { 42 | yield_(); 43 | } else { 44 | assert_eq!(cpid, exit_pid); 45 | println!("Shell: Process {} exited with code {}", cpid, xstate); 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | line.clear(); 52 | print!(">> "); 53 | flush(); 54 | } 55 | BS | DL => { 56 | if !line.is_empty() { 57 | print!("{}", BS as char); 58 | print!(" "); 59 | print!("{}", BS as char); 60 | flush(); 61 | line.pop(); 62 | } 63 | } 64 | _ => { 65 | print!("{}", c as char); 66 | flush(); 67 | line.push(c as char); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /user/src/bin/ch5_usertest.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | static TESTS: &[&str] = &[ 8 | "ch2_hello_world\0", 9 | "ch2_power\0", 10 | "ch2_write1\0", 11 | "ch3_0_setprio\0", 12 | "ch3_0_sleep\0", 13 | "ch3_0_sleep1\0", 14 | "ch4_mmap0\0", 15 | "ch4_mmap1\0", 16 | "ch4_mmap2\0", 17 | "ch4_mmap3\0", 18 | "ch4_unmap\0", 19 | "ch4_unmap2\0", 20 | "ch5_getpid\0", 21 | "ch5_spawn0\0", 22 | "ch5_spawn1\0", 23 | ]; 24 | 25 | use user_lib::{spawn, waitpid}; 26 | 27 | /// 辅助测例,运行所有其他测例。 28 | 29 | #[no_mangle] 30 | pub fn main() -> i32 { 31 | for test in TESTS { 32 | println!("Usertests: Running {}", test); 33 | let pid = spawn(*test); 34 | let mut xstate: i32 = Default::default(); 35 | let wait_pid = waitpid(pid as usize, &mut xstate); 36 | assert_eq!(pid, wait_pid); 37 | println!( 38 | "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", 39 | test, pid, xstate 40 | ); 41 | } 42 | println!("ch5 Usertests passed!"); 43 | 0 44 | } 45 | -------------------------------------------------------------------------------- /user/src/bin/ch6_mail0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{getpid, mail_read, mail_write}; 8 | 9 | const BUF_LEN: usize = 256; 10 | 11 | /// 测试邮箱基本功能,输出 mail0 test OK! 就算正确。 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let pid = getpid(); 16 | let buffer0 = ['a' as u8; 27]; 17 | assert_eq!(mail_write(pid as usize, &buffer0), 27); 18 | let buffer1 = ['b' as u8; BUF_LEN + 1]; 19 | assert_eq!(mail_write(pid as usize, &buffer1), BUF_LEN as isize); 20 | let mut buf = [0u8; BUF_LEN]; 21 | assert_eq!(mail_read(&mut buf), 27); 22 | assert_eq!(buf[..27], buffer0); 23 | assert_eq!(mail_read(&mut buf[..27]), 27); 24 | assert_eq!(buf[..27], buffer1[..27]); 25 | println!("mail0 test OK!"); 26 | 0 27 | } 28 | -------------------------------------------------------------------------------- /user/src/bin/ch6_mail1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{getpid, mail_read, mail_write}; 8 | 9 | const BUF_LEN: usize = 256; 10 | const MAIL_MAX: usize = 16; 11 | 12 | /// 测试邮箱容量,输出 mail1 test OK! 就算正确。 13 | 14 | #[no_mangle] 15 | fn main() -> i32 { 16 | let pid = getpid(); 17 | let buffer0 = ['a' as u8; BUF_LEN]; 18 | for _ in 0..MAIL_MAX { 19 | assert_eq!(mail_write(pid as usize, &buffer0), BUF_LEN as isize); 20 | } 21 | assert_eq!(mail_write(pid as usize, &buffer0), -1); 22 | let mut buf = [0u8; BUF_LEN]; 23 | assert_eq!(mail_read(&mut buf), BUF_LEN as isize); 24 | assert_eq!(mail_write(pid as usize, &buffer0), BUF_LEN as isize); 25 | assert_eq!(mail_write(pid as usize, &buffer0), -1); 26 | println!("mail1 test OK!"); 27 | 0 28 | } 29 | -------------------------------------------------------------------------------- /user/src/bin/ch6_mail2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{exit, fork, mail_read, mail_write, sleep, wait}; 8 | 9 | const BUF_LEN: usize = 256; 10 | 11 | // 双进程邮箱测试,最终输出 mail2 test OK! 就算正确。 12 | 13 | #[no_mangle] 14 | fn main() -> i32 { 15 | let pid = fork(); 16 | if pid == 0 { 17 | println!("I am child"); 18 | let mut buffer = [0u8; BUF_LEN]; 19 | assert_eq!(mail_read(&mut buffer), -1); 20 | println!("child read 1 mail fail"); 21 | println!("child sleep 2s"); 22 | sleep(2000 as usize); 23 | for i in 0..16 { 24 | let mut buffer = [0u8; BUF_LEN]; 25 | assert_eq!(mail_read(&mut buffer), BUF_LEN as isize); 26 | assert_eq!(buffer, [i as u8; BUF_LEN]); 27 | } 28 | println!("child read 16 mails succeed"); 29 | assert_eq!(mail_read(&mut buffer), -1); 30 | println!("child read 1 mail fail"); 31 | println!("child sleep 1s"); 32 | sleep(1000 as usize); 33 | assert_eq!(mail_read(&mut buffer), BUF_LEN as isize); 34 | assert_eq!(buffer, [16 as u8; BUF_LEN]); 35 | println!("child read 1 mail succeed"); 36 | println!("child exit"); 37 | exit(0); 38 | } 39 | println!("I am father"); 40 | println!("father sleep 1s"); 41 | sleep(1000 as usize); 42 | for i in 0..16 { 43 | let buffer = [i as u8; BUF_LEN]; 44 | assert_eq!(mail_write(pid as usize, &buffer), BUF_LEN as isize); 45 | } 46 | println!("father wirte 16 mails succeed"); 47 | let buffer = [16 as u8; BUF_LEN]; 48 | assert_eq!(mail_write(pid as usize, &buffer), -1); 49 | println!("father wirte 1 mail fail"); 50 | println!("father sleep 1.5s"); 51 | sleep(1500 as usize); 52 | assert_eq!(mail_write(pid as usize, &buffer), BUF_LEN as isize); 53 | println!("father wirte 1 mail succeed"); 54 | 55 | let mut xstate: i32 = -100; 56 | assert!(wait(&mut xstate) > 0); 57 | assert_eq!(xstate, 0); 58 | println!("mail2 test OK!"); 59 | 0 60 | } 61 | -------------------------------------------------------------------------------- /user/src/bin/ch6_mail3.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate core; 5 | #[macro_use] 6 | extern crate user_lib; 7 | 8 | use core::slice; 9 | use user_lib::{getpid, mail_read, mail_write}; 10 | 11 | const BUF_LEN: usize = 256; 12 | const MAIL_MAX: usize = 16; 13 | const BAD_ADDRESS: usize = 0x90000000; 14 | 15 | /// 邮箱错误参数测试,输出 mail3 test OK! 就算正确。 16 | 17 | #[no_mangle] 18 | fn main() -> i32 { 19 | let pid = getpid(); 20 | let null = unsafe { slice::from_raw_parts(BAD_ADDRESS as *const _, 10) }; 21 | assert_eq!(mail_write(pid as usize, &null), -1); 22 | let mut empty = ['a' as u8; 0]; 23 | assert_eq!(mail_write(pid as usize, &empty), 0); 24 | assert_eq!(mail_read(&mut empty), -1); 25 | let buffer0 = ['a' as u8; BUF_LEN]; 26 | for _ in 0..MAIL_MAX { 27 | assert_eq!(mail_write(pid as usize, &buffer0), BUF_LEN as isize); 28 | } 29 | assert_eq!(mail_write(pid as usize, &empty), -1); 30 | assert_eq!(mail_read(&mut empty), 0); 31 | assert_eq!(mail_write(pid as usize, &empty), -1); 32 | println!("mail3 test OK!"); 33 | 0 34 | } 35 | -------------------------------------------------------------------------------- /user/src/bin/ch6_usertest.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | /// 辅助测例,运行所有其他测例。 8 | 9 | static TESTS: &[&str] = &[ 10 | "ch2_hello_world\0", 11 | "ch2_power\0", 12 | "ch2_write1\0", 13 | "ch3_0_setprio\0", 14 | "ch3_0_sleep\0", 15 | "ch3_0_sleep1\0", 16 | "ch4_mmap0\0", 17 | "ch4_mmap1\0", 18 | "ch4_mmap2\0", 19 | "ch4_mmap3\0", 20 | "ch4_unmap\0", 21 | "ch4_unmap2\0", 22 | "ch5_getpid\0", 23 | "ch5_spawn0\0", 24 | "ch5_spawn1\0", 25 | "ch6_mail0\0", 26 | "ch6_mail1\0", 27 | "ch6_mail2\0", 28 | "ch6_mail3\0", 29 | ]; 30 | 31 | use user_lib::{spawn, waitpid}; 32 | 33 | #[no_mangle] 34 | pub fn main() -> i32 { 35 | for test in TESTS { 36 | println!("Usertests: Running {}", test); 37 | let pid = spawn(*test); 38 | let mut xstate: i32 = Default::default(); 39 | let wait_pid = waitpid(pid as usize, &mut xstate); 40 | assert_eq!(pid, wait_pid); 41 | println!( 42 | "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", 43 | test, pid, xstate 44 | ); 45 | } 46 | println!("ch6 Usertests passed!"); 47 | 0 48 | } 49 | -------------------------------------------------------------------------------- /user/src/bin/ch7_file0.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::{close, open, read, write, OpenFlags}; 8 | 9 | /// 测试文件基本读写,输出 Test file0 OK! 就算正确。 10 | 11 | #[no_mangle] 12 | pub fn main() -> i32 { 13 | let test_str = "Hello, world!"; 14 | let fname = "fname\0"; 15 | let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY); 16 | assert!(fd > 0); 17 | let fd = fd as usize; 18 | write(fd, test_str.as_bytes()); 19 | close(fd); 20 | 21 | let fd = open(fname, OpenFlags::RDONLY); 22 | assert!(fd > 0); 23 | let fd = fd as usize; 24 | let mut buffer = [0u8; 100]; 25 | let read_len = read(fd, &mut buffer) as usize; 26 | close(fd); 27 | 28 | assert_eq!(test_str, core::str::from_utf8(&buffer[..read_len]).unwrap(),); 29 | println!("Test file0 OK!"); 30 | 0 31 | } 32 | -------------------------------------------------------------------------------- /user/src/bin/ch7_file1.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{close, fstat, open, OpenFlags, Stat, StatMode}; 7 | 8 | /// 测试 fstat,输出 Test fstat OK! 就算正确。 9 | 10 | #[no_mangle] 11 | pub fn main() -> i32 { 12 | let fname = "fname1\0"; 13 | let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY); 14 | assert!(fd > 0); 15 | let fd = fd as usize; 16 | let stat: Stat = Stat::new(); 17 | let ret = fstat(fd, &stat); 18 | assert_eq!(ret, 0); 19 | assert_eq!(stat.mode, StatMode::FILE); 20 | assert_eq!(stat.nlink, 1); 21 | close(fd); 22 | // unlink(fname); 23 | // It's recommended to rebuild the disk image. This program will not clean the file "fname1". 24 | println!("Test fstat OK!"); 25 | 0 26 | } 27 | -------------------------------------------------------------------------------- /user/src/bin/ch7_file2.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{close, fstat, link, open, read, unlink, write, OpenFlags, Stat}; 7 | 8 | /// 测试 link/unlink,输出 Test link OK! 就算正确。 9 | 10 | #[no_mangle] 11 | pub fn main() -> i32 { 12 | let test_str = "Hello, world!"; 13 | let fname = "fname2\0"; 14 | let (lname0, lname1, lname2) = ("linkname0\0", "linkname1\0", "linkname2\0"); 15 | let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY) as usize; 16 | link(fname, lname0); 17 | let stat = Stat::new(); 18 | fstat(fd, &stat); 19 | assert_eq!(stat.nlink, 2); 20 | link(fname, lname1); 21 | link(fname, lname2); 22 | fstat(fd, &stat); 23 | assert_eq!(stat.nlink, 4); 24 | write(fd, test_str.as_bytes()); 25 | close(fd); 26 | 27 | unlink(fname); 28 | let fd = open(lname0, OpenFlags::RDONLY) as usize; 29 | let stat2 = Stat::new(); 30 | let mut buf = [0u8; 100]; 31 | let read_len = read(fd, &mut buf) as usize; 32 | assert_eq!(test_str, core::str::from_utf8(&buf[..read_len]).unwrap(),); 33 | fstat(fd, &stat2); 34 | assert_eq!(stat2.dev, stat.dev); 35 | assert_eq!(stat2.ino, stat.ino); 36 | assert_eq!(stat2.nlink, 3); 37 | unlink(lname1); 38 | unlink(lname2); 39 | fstat(fd, &stat2); 40 | assert_eq!(stat2.nlink, 1); 41 | close(fd); 42 | unlink(lname0); 43 | // It's Ok if you don't delete the inode and data blocks. 44 | println!("Test link OK!"); 45 | 0 46 | } 47 | -------------------------------------------------------------------------------- /user/src/bin/ch7_usertest.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | /// 辅助测例,运行所有其他测例。 8 | 9 | static TESTS: &[&str] = &[ 10 | "ch2_hello_world\0", 11 | "ch2_power\0", 12 | "ch2_write1\0", 13 | "ch3_0_setprio\0", 14 | "ch3_0_sleep\0", 15 | "ch3_0_sleep1\0", 16 | "ch4_mmap0\0", 17 | "ch4_mmap1\0", 18 | "ch4_mmap2\0", 19 | "ch4_mmap3\0", 20 | "ch4_unmap\0", 21 | "ch4_unmap2\0", 22 | "ch5_getpid\0", 23 | "ch5_spawn0\0", 24 | "ch5_spawn1\0", 25 | "ch6_mail0\0", 26 | "ch6_mail1\0", 27 | "ch6_mail2\0", 28 | "ch6_mail3\0", 29 | "ch7_file0\0", 30 | "ch7_file1\0", 31 | "ch7_file2\0", 32 | ]; 33 | 34 | use user_lib::{spawn, waitpid}; 35 | 36 | #[no_mangle] 37 | pub fn main() -> i32 { 38 | for test in TESTS { 39 | println!("Usertests: Running {}", test); 40 | let pid = spawn(*test); 41 | let mut xstate: i32 = Default::default(); 42 | let wait_pid = waitpid(pid as usize, &mut xstate); 43 | assert_eq!(pid, wait_pid); 44 | println!( 45 | "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", 46 | test, pid, xstate 47 | ); 48 | } 49 | println!("ch7 Usertests passed!"); 50 | 0 51 | } 52 | -------------------------------------------------------------------------------- /user/src/bin/ch8_01.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate user_lib; 5 | 6 | use user_lib::fork; 7 | 8 | const NUM: usize = 10; 9 | 10 | #[no_mangle] 11 | pub fn main() -> i32 { 12 | for _ in 0..NUM { 13 | fork(); 14 | } 15 | 0 16 | } 17 | -------------------------------------------------------------------------------- /user/src/bin/ch8_02.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | 7 | use user_lib::mmap; 8 | 9 | const UNUSED_START: usize = 0x10000; 10 | const N: usize = 0x800; 11 | const LEN: usize = 0x10000; 12 | 13 | #[no_mangle] 14 | pub fn main() -> i32 { 15 | let prot = 3usize; 16 | for i in 0..(N * 2) { 17 | mmap(UNUSED_START + i * LEN, LEN, prot); 18 | } 19 | 0 20 | } 21 | -------------------------------------------------------------------------------- /user/src/bin/ch8_03.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate core; 7 | use core::slice; 8 | use user_lib::{ch8::*, mmap, open, read, OpenFlags}; 9 | 10 | #[no_mangle] 11 | pub unsafe fn main() -> i32 { 12 | let prot: usize = 3; 13 | mmap(0, 0x4000usize, prot); 14 | println!("mmap ..."); 15 | let time: *const TimeVal = (get_pc() + 6) as *mut _; 16 | raw_sys_gettime(time, 0); 17 | let fd = open("fname1-ch8_03\0", OpenFlags::CREATE | OpenFlags::WRONLY); 18 | let stat: *const Stat = (get_pc() + 8) as *mut _; 19 | raw_sys_fstat(fd as usize, stat); 20 | read( 21 | STDIN, 22 | slice::from_raw_parts_mut((get_pc() + 6) as *mut _, 10), 23 | ); 24 | let stat: *const Stat = TRAP_CONTEXT as *mut _; 25 | raw_sys_fstat(fd as usize, stat); 26 | 0 27 | } 28 | -------------------------------------------------------------------------------- /user/src/bin/ch8_04.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | extern crate core; 7 | use core::slice; 8 | use user_lib::{ch8, *}; 9 | 10 | #[no_mangle] 11 | pub unsafe fn main() -> i32 { 12 | let mut bug: [u8; 200] = [0; 200]; 13 | open("fname0\0", OpenFlags::CREATE | OpenFlags::WRONLY); 14 | open("fname1\0", OpenFlags::CREATE | OpenFlags::WRONLY); 15 | println!("GOOD LUCK"); 16 | read(1, &mut bug); 17 | write(65537, slice::from_raw_parts_mut(993 as *mut _, 233)); 18 | read( 19 | 13513543, 20 | slice::from_raw_parts_mut(0x500 as *mut _, 777777777777usize), 21 | ); 22 | close(233); 23 | close(0); 24 | close(1); 25 | close(2); 26 | println!("[ERROR]I need fuzzy ..."); 27 | open( 28 | "编程是一件危险的事情\0", 29 | OpenFlags::CREATE | OpenFlags::WRONLY, 30 | ); 31 | set_priority(-7); 32 | set_priority(isize::MAX); 33 | mail_write(100000, slice::from_raw_parts(0 as *const _, 53153)); 34 | mail_write(133, &bug); 35 | mail_write(0, slice::from_raw_parts(0x1ff0 as *const _, 53153)); 36 | link("nonono\0", "yesyesyes\0"); 37 | link("fname0\0", "fname1\0"); 38 | link("fname1\0", "fname0\0"); 39 | link("fname0\0", "fname0\0"); 40 | link("\0", "fname1\0"); 41 | let stat: *const Stat = 0 as *const _; 42 | ch8::raw_sys_fstat(0, stat); 43 | ch8::raw_sys_fstat(313, stat); 44 | sys_unlinkat(555, "➑➑➑➑➑➑\0", 1); 45 | sys_linkat(0, "QAQ\0", 7, "❆❆❆❆❆\0", 0); 46 | 0 47 | } 48 | -------------------------------------------------------------------------------- /user/src/bin/ch8_05.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{ch8::forktest, get_time, sleep}; 7 | 8 | fn heavy_fork_test() { 9 | for i in 0..30 { 10 | forktest(|_idx: usize| { 11 | let current_time = get_time(); 12 | let sleep_length = current_time * current_time % 1000 + 1000; 13 | sleep(sleep_length as usize); 14 | }); 15 | println!("Heavy fork test iteration {} success.", i); 16 | } 17 | } 18 | 19 | #[no_mangle] 20 | pub unsafe fn main() -> i32 { 21 | heavy_fork_test(); 22 | 0 23 | } 24 | -------------------------------------------------------------------------------- /user/src/bin/ch8_06.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::{ 7 | ch8::{forktest, hash}, 8 | mmap, 9 | }; 10 | 11 | const START: usize = 0x10000; 12 | const LEN: usize = 0x10000; 13 | 14 | #[no_mangle] 15 | pub unsafe fn main() -> i32 { 16 | let prot: usize = 3; 17 | mmap(START, LEN, prot); 18 | println!("mmap ..."); 19 | forktest(|idx: usize| { 20 | let addr: *mut u8 = (START + (hash(idx) % LEN)) as *mut _; 21 | *addr = 44; 22 | let addr: *mut u8 = (START + (hash(idx * 65536 + 1) % LEN)) as *mut _; 23 | *addr; 24 | }); 25 | 0 26 | } 27 | -------------------------------------------------------------------------------- /user/src/bin/ch8_07.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::ch8::hash; 7 | use user_lib::{open, unlink, OpenFlags}; 8 | 9 | fn file_test0(idx: usize) { 10 | let mut name: [u8; 20] = [0; 20]; 11 | let mut last: u8 = idx as u8; 12 | for c in &mut name { 13 | *c = hash(last.into()) as u8; 14 | last = *c; 15 | } 16 | name[19] = 0; 17 | let fname = unsafe { core::str::from_utf8_unchecked(&name) }; 18 | open(fname, OpenFlags::CREATE | OpenFlags::WRONLY); 19 | unlink(fname); 20 | } 21 | 22 | const NUM: usize = 65536; 23 | 24 | #[no_mangle] 25 | pub fn main() -> i32 { 26 | for idx in 0..NUM { 27 | file_test0(idx); 28 | } 29 | 0 30 | } 31 | -------------------------------------------------------------------------------- /user/src/bin/ch8_xx.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | #[macro_use] 5 | extern crate user_lib; 6 | use user_lib::ch8::*; 7 | use user_lib::{open, unlink, OpenFlags}; 8 | 9 | #[allow(dead_code)] 10 | const SYSCALL_NUM: usize = 20; 11 | 12 | #[allow(dead_code)] 13 | const SYSCALL_IDS: [usize; SYSCALL_NUM] = [ 14 | SYSCALL_OPENAT, // usize = 56; 15 | SYSCALL_CLOSE, // usize = 57; 16 | SYSCALL_READ, // usize = 63; 17 | SYSCALL_WRITE, // usize = 64; 18 | SYSCALL_UNLINKAT, // usize = 35; 19 | SYSCALL_LINKAT, // usize = 37; 20 | SYSCALL_FSTAT, // usize = 80; 21 | SYSCALL_EXIT, // usize = 93; 22 | SYSCALL_YIELD, // usize = 124; 23 | SYSCALL_GETTIMEOFDAY, // usize = 169; 24 | SYSCALL_GETPID, // usize = 172; 25 | SYSCALL_FORK, // usize = 220; 26 | SYSCALL_EXEC, // usize = 221; 27 | SYSCALL_WAITPID, // usize = 260; 28 | SYSCALL_SET_PRIORITY, // usize = 140; 29 | SYSCALL_MUNMAP, // usize = 215; 30 | SYSCALL_MMAP, // usize = 222; 31 | SYSCALL_SPAWN, // usize = 400; 32 | SYSCALL_MAIL_READ, // usize = 401; 33 | SYSCALL_MAIL_WRITE, // usize = 402; 34 | ]; 35 | 36 | #[allow(dead_code)] 37 | fn rand_syscall_id() -> usize { 38 | 0 39 | } 40 | 41 | #[no_mangle] 42 | pub fn main() -> i32 { 43 | // TODO: a naive fuzzy 44 | 0 45 | } 46 | -------------------------------------------------------------------------------- /user/src/ch8.rs: -------------------------------------------------------------------------------- 1 | pub use super::*; 2 | use alloc::sync::Arc; 3 | use lazy_static::lazy_static; 4 | use rand::distributions::{Distribution, Standard}; 5 | use rand::rngs::SmallRng; 6 | use rand::{Fill, Rng, SeedableRng}; 7 | use spin::mutex::Mutex; 8 | pub use syscall::*; 9 | 10 | pub const PAGE_SIZE: usize = 4096; 11 | pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; 12 | pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE; 13 | 14 | pub fn forktest(func: F) 15 | where 16 | F: FnOnce(usize), 17 | { 18 | let n: usize = 200; 19 | let mut cnt = 0; // 统计 fork 成功的次数 20 | for idx in 0..n { 21 | let pid = fork(); 22 | if pid == 0 { 23 | func(idx); 24 | exit(0); 25 | } else if pid > 0 { 26 | cnt += 1; 27 | } 28 | } 29 | let mut exit_code: i32 = 0; 30 | for _ in 0..cnt { 31 | assert!(wait(&mut exit_code) > 0); 32 | assert_eq!(exit_code, 0); 33 | } 34 | assert!(wait(&mut exit_code) < 0); 35 | } 36 | pub fn get_pc() -> usize { 37 | let mut ra: usize; 38 | unsafe { 39 | llvm_asm!("mv $0, ra" : "=r"(ra) ::: "volatile"); 40 | } 41 | ra 42 | } 43 | 44 | pub fn raw_sys_gettime(tx: *const TimeVal, tz: usize) -> isize { 45 | syscall(SYSCALL_GETTIMEOFDAY, [tx as usize, tz, 0]) 46 | } 47 | 48 | pub fn raw_sys_fstat(fd: usize, st: *const Stat) -> isize { 49 | syscall(SYSCALL_FSTAT, [fd, st as usize, 0]) 50 | } 51 | 52 | pub fn raw_syscall(id: usize, args: [usize; 6]) -> isize { 53 | syscall6(id, args) 54 | } 55 | 56 | lazy_static! { 57 | static ref PRNG: Arc> = { 58 | type Seed = [u8; 32]; 59 | Arc::new(Mutex::new(SmallRng::from_seed(Seed::default()))) 60 | }; 61 | } 62 | 63 | pub fn xorshift64(mut x: usize) -> usize { 64 | x = x ^ x << 13; 65 | x = x ^ x >> 7; 66 | x = x ^ x << 17; 67 | x 68 | } 69 | 70 | pub fn rand() -> T 71 | where 72 | Standard: Distribution, 73 | { 74 | let mut rng = PRNG.lock(); 75 | rng.gen() 76 | } 77 | 78 | pub fn fill(dest: &mut T) { 79 | let mut rng = PRNG.lock(); 80 | rng.fill(dest) 81 | } 82 | 83 | pub fn hash(x: usize) -> usize { 84 | xorshift64(x) 85 | } 86 | -------------------------------------------------------------------------------- /user/src/console.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::vec_deque::VecDeque; 2 | use alloc::sync::Arc; 3 | use core::fmt::{self, Write}; 4 | use spin::mutex::Mutex; 5 | 6 | pub const STDIN: usize = 0; 7 | pub const STDOUT: usize = 1; 8 | 9 | const CONSOLE_BUFFER_SIZE: usize = 256 * 10; 10 | 11 | use super::{read, write}; 12 | use lazy_static::*; 13 | 14 | struct ConsoleBuffer(VecDeque); 15 | 16 | lazy_static! { 17 | static ref CONSOLE_BUFFER: Arc> = { 18 | let buffer = VecDeque::::with_capacity(CONSOLE_BUFFER_SIZE); 19 | Arc::new(Mutex::new(ConsoleBuffer(buffer))) 20 | }; 21 | } 22 | 23 | impl ConsoleBuffer { 24 | fn flush(&mut self) -> isize { 25 | let s: &[u8] = self.0.make_contiguous(); 26 | let ret = write(STDOUT, s); 27 | self.0.clear(); 28 | ret 29 | } 30 | } 31 | 32 | impl Write for ConsoleBuffer { 33 | fn write_str(&mut self, s: &str) -> fmt::Result { 34 | for c in s.as_bytes().iter() { 35 | self.0.push_back(*c); 36 | if *c == '\n' as u8 || self.0.len() == CONSOLE_BUFFER_SIZE { 37 | if -1 == self.flush() { 38 | return Err(fmt::Error); 39 | } 40 | } 41 | } 42 | Ok(()) 43 | } 44 | } 45 | 46 | pub fn print(args: fmt::Arguments) { 47 | let mut buf = CONSOLE_BUFFER.lock(); 48 | // buf.write_fmt(args).unwrap(); 49 | // BUG FIX: 关闭 stdout 后,本函数不能触发 panic,否则会造成死锁 50 | buf.write_fmt(args); 51 | } 52 | 53 | #[macro_export] 54 | macro_rules! print { 55 | ($fmt: literal $(, $($arg: tt)+)?) => { 56 | $crate::console::print(format_args!($fmt $(, $($arg)+)?)); 57 | } 58 | } 59 | 60 | #[macro_export] 61 | macro_rules! println { 62 | ($fmt: literal $(, $($arg: tt)+)?) => { 63 | $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); 64 | } 65 | } 66 | 67 | pub fn getchar() -> u8 { 68 | let mut c = [0u8; 1]; 69 | read(STDIN, &mut c); 70 | c[0] 71 | } 72 | 73 | pub fn flush() { 74 | let mut buf = CONSOLE_BUFFER.lock(); 75 | buf.flush(); 76 | } 77 | -------------------------------------------------------------------------------- /user/src/lang_items.rs: -------------------------------------------------------------------------------- 1 | use super::exit; 2 | 3 | #[panic_handler] 4 | fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { 5 | let err = panic_info.message().unwrap(); 6 | if let Some(location) = panic_info.location() { 7 | println!( 8 | "Panicked at {}:{}, {}", 9 | location.file(), 10 | location.line(), 11 | err 12 | ); 13 | } else { 14 | println!("Panicked: {}", err); 15 | } 16 | exit(-1); 17 | } 18 | -------------------------------------------------------------------------------- /user/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(llvm_asm)] 3 | #![feature(linkage)] 4 | #![feature(panic_info_message)] 5 | #![feature(alloc_error_handler)] 6 | 7 | #[macro_use] 8 | pub mod console; 9 | pub mod ch8; 10 | mod lang_items; 11 | mod syscall; 12 | 13 | extern crate alloc; 14 | extern crate core; 15 | #[macro_use] 16 | extern crate bitflags; 17 | 18 | use buddy_system_allocator::LockedHeap; 19 | pub use console::{flush, STDIN, STDOUT}; 20 | pub use syscall::*; 21 | 22 | const USER_HEAP_SIZE: usize = 16384; 23 | 24 | static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; 25 | 26 | #[global_allocator] 27 | static HEAP: LockedHeap = LockedHeap::empty(); 28 | 29 | #[alloc_error_handler] 30 | pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { 31 | panic!("Heap allocation error, layout = {:?}", layout); 32 | } 33 | 34 | #[no_mangle] 35 | #[link_section = ".text.entry"] 36 | pub extern "C" fn _start() -> ! { 37 | unsafe { 38 | HEAP.lock() 39 | .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); 40 | } 41 | exit(main()); 42 | } 43 | 44 | #[linkage = "weak"] 45 | #[no_mangle] 46 | fn main() -> i32 { 47 | panic!("Cannot find main!"); 48 | } 49 | 50 | bitflags! { 51 | pub struct OpenFlags: u32 { 52 | const RDONLY = 0; 53 | const WRONLY = 1 << 0; 54 | const RDWR = 1 << 1; 55 | const CREATE = 1 << 9; 56 | const TRUNC = 1 << 10; 57 | } 58 | } 59 | 60 | #[repr(C)] 61 | #[derive(Debug)] 62 | pub struct TimeVal { 63 | pub sec: usize, 64 | pub usec: usize, 65 | } 66 | 67 | impl TimeVal { 68 | pub fn new() -> Self { 69 | TimeVal { sec: 0, usec: 0 } 70 | } 71 | } 72 | 73 | #[repr(C)] 74 | #[derive(Debug)] 75 | pub struct Stat { 76 | /// ID of device containing file 77 | pub dev: u64, 78 | /// inode number 79 | pub ino: u64, 80 | /// file type and mode 81 | pub mode: StatMode, 82 | /// number of hard links 83 | pub nlink: u32, 84 | /// unused pad 85 | pad: [u64; 7], 86 | } 87 | 88 | impl Stat { 89 | pub fn new() -> Self { 90 | Stat { 91 | dev: 0, 92 | ino: 0, 93 | mode: StatMode::NULL, 94 | nlink: 0, 95 | pad: [0; 7], 96 | } 97 | } 98 | } 99 | 100 | bitflags! { 101 | pub struct StatMode: u32 { 102 | const NULL = 0; 103 | /// directory 104 | const DIR = 0o040000; 105 | /// ordinary regular file 106 | const FILE = 0o100000; 107 | } 108 | } 109 | 110 | const AT_FDCWD: isize = -100; 111 | 112 | pub fn open(path: &str, flags: OpenFlags) -> isize { 113 | sys_openat(AT_FDCWD as usize, path, flags.bits, OpenFlags::RDWR.bits) 114 | } 115 | 116 | pub fn close(fd: usize) -> isize { 117 | if fd == STDOUT { 118 | console::flush(); 119 | } 120 | sys_close(fd) 121 | } 122 | 123 | pub fn read(fd: usize, buf: &mut [u8]) -> isize { 124 | sys_read(fd, buf) 125 | } 126 | 127 | pub fn write(fd: usize, buf: &[u8]) -> isize { 128 | sys_write(fd, buf) 129 | } 130 | 131 | pub fn link(old_path: &str, new_path: &str) -> isize { 132 | sys_linkat(AT_FDCWD as usize, old_path, AT_FDCWD as usize, new_path, 0) 133 | } 134 | 135 | pub fn unlink(path: &str) -> isize { 136 | sys_unlinkat(AT_FDCWD as usize, path, 0) 137 | } 138 | 139 | pub fn fstat(fd: usize, st: &Stat) -> isize { 140 | sys_fstat(fd, st) 141 | } 142 | 143 | pub fn mail_read(buf: &mut [u8]) -> isize { 144 | sys_mail_read(buf) 145 | } 146 | 147 | pub fn mail_write(pid: usize, buf: &[u8]) -> isize { 148 | sys_mail_write(pid, buf) 149 | } 150 | 151 | pub fn exit(exit_code: i32) -> ! { 152 | console::flush(); 153 | sys_exit(exit_code); 154 | } 155 | 156 | pub fn yield_() -> isize { 157 | sys_yield() 158 | } 159 | 160 | pub fn get_time() -> isize { 161 | let time = TimeVal::new(); 162 | match sys_get_time(&time, 0) { 163 | 0 => ((time.sec & 0xffff) * 1000 + time.usec / 1000) as isize, 164 | _ => -1, 165 | } 166 | } 167 | 168 | pub fn getpid() -> isize { 169 | sys_getpid() 170 | } 171 | 172 | pub fn fork() -> isize { 173 | sys_fork() 174 | } 175 | 176 | pub fn exec(path: &str) -> isize { 177 | sys_exec(path) 178 | } 179 | 180 | pub fn set_priority(prio: isize) -> isize { 181 | sys_set_priority(prio) 182 | } 183 | 184 | pub fn wait(exit_code: &mut i32) -> isize { 185 | loop { 186 | match sys_waitpid(-1, exit_code as *mut _) { 187 | -2 => { 188 | sys_yield(); 189 | } 190 | n => { 191 | return n; 192 | } 193 | } 194 | } 195 | } 196 | 197 | pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { 198 | loop { 199 | match sys_waitpid(pid as isize, exit_code as *mut _) { 200 | -2 => { 201 | sys_yield(); 202 | } 203 | n => { 204 | return n; 205 | } 206 | } 207 | } 208 | } 209 | 210 | pub fn sleep(period_ms: usize) { 211 | let start = get_time(); 212 | while get_time() < start + period_ms as isize { 213 | sys_yield(); 214 | } 215 | } 216 | 217 | pub fn mmap(start: usize, len: usize, prot: usize) -> isize { 218 | sys_mmap(start, len, prot) 219 | } 220 | 221 | pub fn munmap(start: usize, len: usize) -> isize { 222 | sys_munmap(start, len) 223 | } 224 | 225 | pub fn spawn(path: &str) -> isize { 226 | sys_spawn(path) 227 | } 228 | 229 | pub fn dup(fd: usize) -> isize { sys_dup(fd) } 230 | pub fn pipe(pipe_fd: &mut [usize]) -> isize { sys_pipe(pipe_fd) } 231 | -------------------------------------------------------------------------------- /user/src/linker.ld: -------------------------------------------------------------------------------- 1 | 2 | OUTPUT_ARCH(riscv) 3 | ENTRY(_start) 4 | 5 | BASE_ADDRESS = 0x0; 6 | 7 | SECTIONS 8 | { 9 | . = BASE_ADDRESS; 10 | .text : { 11 | *(.text.entry) 12 | *(.text .text.*) 13 | } 14 | . = ALIGN(4K); 15 | .rodata : { 16 | *(.rodata .rodata.*) 17 | } 18 | . = ALIGN(4K); 19 | .data : { 20 | *(.data .data.*) 21 | } 22 | .bss : { 23 | *(.bss .bss.*) 24 | } 25 | /DISCARD/ : { 26 | *(.eh_frame) 27 | *(.debug*) 28 | } 29 | } -------------------------------------------------------------------------------- /user/src/syscall.rs: -------------------------------------------------------------------------------- 1 | use super::{Stat, TimeVal}; 2 | 3 | pub const SYSCALL_OPENAT: usize = 56; 4 | pub const SYSCALL_CLOSE: usize = 57; 5 | pub const SYSCALL_READ: usize = 63; 6 | pub const SYSCALL_WRITE: usize = 64; 7 | pub const SYSCALL_UNLINKAT: usize = 35; 8 | pub const SYSCALL_LINKAT: usize = 37; 9 | pub const SYSCALL_FSTAT: usize = 80; 10 | pub const SYSCALL_EXIT: usize = 93; 11 | pub const SYSCALL_YIELD: usize = 124; 12 | pub const SYSCALL_GETTIMEOFDAY: usize = 169; 13 | pub const SYSCALL_GETPID: usize = 172; 14 | pub const SYSCALL_FORK: usize = 220; 15 | pub const SYSCALL_EXEC: usize = 221; 16 | pub const SYSCALL_WAITPID: usize = 260; 17 | pub const SYSCALL_SET_PRIORITY: usize = 140; 18 | pub const SYSCALL_MUNMAP: usize = 215; 19 | pub const SYSCALL_MMAP: usize = 222; 20 | pub const SYSCALL_SPAWN: usize = 400; 21 | pub const SYSCALL_MAIL_READ: usize = 401; 22 | pub const SYSCALL_MAIL_WRITE: usize = 402; 23 | pub const SYSCALL_DUP: usize = 24; 24 | pub const SYSCALL_PIPE: usize = 59; 25 | 26 | pub fn syscall(id: usize, args: [usize; 3]) -> isize { 27 | let mut ret: isize; 28 | unsafe { 29 | llvm_asm!("ecall" 30 | : "={x10}" (ret) 31 | : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id) 32 | : "memory" 33 | : "volatile" 34 | ); 35 | } 36 | ret 37 | } 38 | 39 | pub fn syscall6(id: usize, args: [usize; 6]) -> isize { 40 | let mut ret: isize; 41 | unsafe { 42 | llvm_asm!("ecall" 43 | : "={x10}" (ret) 44 | : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x13}" (args[3]), 45 | "{x14}" (args[4]), "{x15}" (args[5]), "{x17}" (id) 46 | : "memory" 47 | : "volatile" 48 | ); 49 | } 50 | ret 51 | } 52 | 53 | pub fn sys_openat(dirfd: usize, path: &str, flags: u32, mode: u32) -> isize { 54 | syscall6( 55 | SYSCALL_OPENAT, 56 | [ 57 | dirfd, 58 | path.as_ptr() as usize, 59 | flags as usize, 60 | mode as usize, 61 | 0, 62 | 0, 63 | ], 64 | ) 65 | } 66 | 67 | pub fn sys_close(fd: usize) -> isize { 68 | syscall(SYSCALL_CLOSE, [fd, 0, 0]) 69 | } 70 | 71 | pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize { 72 | syscall( 73 | SYSCALL_READ, 74 | [fd, buffer.as_mut_ptr() as usize, buffer.len()], 75 | ) 76 | } 77 | 78 | pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { 79 | syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) 80 | } 81 | 82 | pub fn sys_linkat( 83 | old_dirfd: usize, 84 | old_path: &str, 85 | new_dirfd: usize, 86 | new_path: &str, 87 | flags: usize, 88 | ) -> isize { 89 | syscall6( 90 | SYSCALL_LINKAT, 91 | [ 92 | old_dirfd, 93 | old_path.as_ptr() as usize, 94 | new_dirfd, 95 | new_path.as_ptr() as usize, 96 | flags, 97 | 0, 98 | ], 99 | ) 100 | } 101 | 102 | pub fn sys_unlinkat(dirfd: usize, path: &str, flags: usize) -> isize { 103 | syscall(SYSCALL_UNLINKAT, [dirfd, path.as_ptr() as usize, flags]) 104 | } 105 | 106 | pub fn sys_fstat(fd: usize, st: &Stat) -> isize { 107 | syscall(SYSCALL_FSTAT, [fd, st as *const _ as usize, 0]) 108 | } 109 | 110 | pub fn sys_mail_read(buffer: &mut [u8]) -> isize { 111 | syscall( 112 | SYSCALL_MAIL_READ, 113 | [buffer.as_ptr() as usize, buffer.len(), 0], 114 | ) 115 | } 116 | 117 | pub fn sys_mail_write(pid: usize, buffer: &[u8]) -> isize { 118 | syscall( 119 | SYSCALL_MAIL_WRITE, 120 | [pid, buffer.as_ptr() as usize, buffer.len()], 121 | ) 122 | } 123 | 124 | pub fn sys_exit(exit_code: i32) -> ! { 125 | syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]); 126 | panic!("sys_exit never returns!"); 127 | } 128 | 129 | pub fn sys_yield() -> isize { 130 | syscall(SYSCALL_YIELD, [0, 0, 0]) 131 | } 132 | 133 | pub fn sys_get_time(time: &TimeVal, tz: usize) -> isize { 134 | syscall(SYSCALL_GETTIMEOFDAY, [time as *const _ as usize, tz, 0]) 135 | } 136 | 137 | pub fn sys_getpid() -> isize { 138 | syscall(SYSCALL_GETPID, [0, 0, 0]) 139 | } 140 | 141 | pub fn sys_fork() -> isize { 142 | syscall(SYSCALL_FORK, [0, 0, 0]) 143 | } 144 | 145 | pub fn sys_exec(path: &str) -> isize { 146 | syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0]) 147 | } 148 | 149 | pub fn sys_waitpid(pid: isize, xstatus: *mut i32) -> isize { 150 | syscall(SYSCALL_WAITPID, [pid as usize, xstatus as usize, 0]) 151 | } 152 | 153 | pub fn sys_set_priority(prio: isize) -> isize { 154 | syscall(SYSCALL_SET_PRIORITY, [prio as usize, 0, 0]) 155 | } 156 | 157 | pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize { 158 | syscall(SYSCALL_MMAP, [start, len, prot]) 159 | } 160 | 161 | pub fn sys_munmap(start: usize, len: usize) -> isize { 162 | syscall(SYSCALL_MUNMAP, [start, len, 0]) 163 | } 164 | 165 | pub fn sys_spawn(path: &str) -> isize { 166 | syscall(SYSCALL_SPAWN, [path.as_ptr() as usize, 0, 0]) 167 | } 168 | 169 | pub fn sys_dup(fd: usize) -> isize { 170 | syscall(SYSCALL_DUP, [fd, 0, 0]) 171 | } 172 | 173 | pub fn sys_pipe(pipe: &mut [usize]) -> isize { 174 | syscall(SYSCALL_PIPE, [pipe.as_mut_ptr() as usize, 0, 0]) 175 | } 176 | --------------------------------------------------------------------------------