From 335677a199598302aaab632613505cce3beb3930 Mon Sep 17 00:00:00 2001 From: TonyCrane Date: Tue, 10 Dec 2024 06:42:34 +0800 Subject: [PATCH] fix(lab6): imporve lab6 --- docs/lab6.md | 740 ++++++++++++++---------------- src/lab6/Makefile | 59 --- src/lab6/arch/riscv/Makefile | 10 - src/lab6/arch/riscv/include/sbi.h | 19 - src/lab6/disk.img.zip | Bin 0 -> 66573 bytes src/lab6/fs/Makefile | 6 +- src/lab6/fs/fat32.c | 72 +-- src/lab6/fs/fs.S | 7 - src/lab6/fs/fs.c | 46 ++ src/lab6/fs/mbr.c | 13 +- src/lab6/fs/vfs.c | 92 +--- src/lab6/fs/virtio.c | 62 +-- src/lab6/include/debug.h | 65 --- src/lab6/include/fat32.h | 15 +- src/lab6/include/fs.h | 34 +- src/lab6/include/mbr.h | 16 +- src/lab6/include/string.h | 15 - src/lab6/include/vfs.h | 11 +- src/lab6/include/virtio.h | 177 ++++--- src/lab6/lib/string.c | 28 -- src/lab6/user/.gitignore | 5 - src/lab6/user/Makefile | 29 +- src/lab6/user/forktest.c | 43 -- src/lab6/user/{shell.c => main.c} | 76 +-- src/lab6/user/printf.c | 363 +++++++++++---- src/lab6/user/ramdisk.S | 4 - src/lab6/user/stddef.h | 6 +- src/lab6/user/stdint.h | 113 +++++ src/lab6/user/stdio.h | 27 +- src/lab6/user/string.c | 9 + src/lab6/user/string.h | 12 +- src/lab6/user/syscall.h | 10 +- src/lab6/user/uapp.S | 4 + src/lab6/user/unistd.c | 20 +- src/lab6/user/unistd.h | 15 +- 35 files changed, 1019 insertions(+), 1204 deletions(-) delete mode 100644 src/lab6/Makefile delete mode 100644 src/lab6/arch/riscv/Makefile delete mode 100644 src/lab6/arch/riscv/include/sbi.h create mode 100644 src/lab6/disk.img.zip delete mode 100644 src/lab6/fs/fs.S create mode 100644 src/lab6/fs/fs.c delete mode 100644 src/lab6/include/debug.h delete mode 100644 src/lab6/include/string.h delete mode 100644 src/lab6/lib/string.c delete mode 100644 src/lab6/user/.gitignore delete mode 100644 src/lab6/user/forktest.c rename src/lab6/user/{shell.c => main.c} (69%) delete mode 100644 src/lab6/user/ramdisk.S create mode 100644 src/lab6/user/stdint.h create mode 100644 src/lab6/user/string.c create mode 100644 src/lab6/user/uapp.S diff --git a/docs/lab6.md b/docs/lab6.md index 06b4003..e191c4e 100644 --- a/docs/lab6.md +++ b/docs/lab6.md @@ -1,37 +1,37 @@ # Lab6: VFS & FAT32 文件系统 -!!! warning "实验尚未修改完成,偷跑有返工风险" - -本实验中不涉及 `fork` 的实现和缺页异常,只需要完成 Lab4 即可开始本实验(当然,本实验也兼容 Lab5 )。 +!!! abstract "本实验为 bonus,不做硬性要求" ## 实验目的 -* 为用户态的 Shell 提供 `read` 和 `write` syscall 的实现(完成该部分的所有实现方得 60 分)。 -* 实现 FAT32 文件系统的基本功能,并对其中的文件进行读写(完成该部分的所有实现方得 40 分)。 +* 为用户态的 Shell 提供 `read` 和 `write` syscall 的实现(完成该部分的所有实现方得 60 分) +* 实现 FAT32 文件系统的基本功能,并对其中的文件进行读写(完成该部分的所有实现方得 40 分) ## 实验环境 -与先前的实验中使用的环境相同。 +* Environment in previous labs ## 背景知识 ### VFS -虚拟文件系统(VFS)或虚拟文件系统交换机是位于更具体的文件系统之上的抽象层。VFS的目的是允许客户端应用程序以统一的方式访问不同类型的具体文件系统。例如,可以使用VFS透明地访问本地和网络存储设备,而客户机应用程序不会注意到其中的差异。它可以用来弥合Windows、经典Mac OS/macOS和Unix文件系统之间的差异,这样应用程序就可以访问这些类型的本地文件系统上的文件,而不必知道它们正在访问什么类型的文件系统。 +虚拟文件系统(Virtual File System,VFS)或虚拟文件系统交换机是位于更具体的文件系统之上的抽象层。VFS 的目的是允许客户端应用程序以统一的方式访问不同类型的具体文件系统。例如,可以使用 VFS 透明地访问本地和网络存储设备,而客户机应用程序不会注意到其中的差异。它可以用来弥合 Windows、macOS 等不同操作系统所使用的文件系统之间的差异,这样应用程序就可以访问这些类型的本地文件系统上的文件,而不必知道它们正在访问什么类型的文件系统。 -VFS指定内核和具体文件系统之间的接口(或“协议”)。因此,只需完成协议,就可以很容易地向内核添加对新文件系统类型的支持。协议可能会随着版本的不同而不兼容地改变,这将需要重新编译具体的文件系统支持,并且可能在重新编译之前进行修改,以允许它与新版本的操作系统一起工作;或者操作系统的供应商可能只对协议进行向后兼容的更改,以便为操作系统的给定版本构建的具体文件系统支持将与操作系统的未来版本一起工作。 +VFS 指定内核和具体文件系统之间的接口(或“协议”)。因此,只需完成协议,就可以很容易地向内核添加对新文件系统类型的支持。协议可能会随着版本的不同而不兼容地改变,这将需要重新编译具体的文件系统支持,并且可能在重新编译之前进行修改,以允许它与新版本的操作系统一起工作;或者操作系统的供应商可能只对协议进行向后兼容的更改,以便为操作系统的给定版本构建的具体文件系统支持将与操作系统的未来版本一起工作。 ### VirtIO -Virtio 是一个开放标准,它定义了一种协议,用于不同类型的驱动程序和设备之间的通信。在 QEMU 上我们可以基于 VirtIO 使用许多模拟出来的外部设备,在本次实验中我们使用 VirtIO 模拟存储设备,并在其上构建文件系统。 +VirtIO 是一个开放标准,它定义了一种协议,用于不同类型的驱动程序和设备之间的通信。在 QEMU 上我们可以基于 VirtIO 使用许多模拟出来的外部设备,在本次实验中我们使用 VirtIO 模拟存储设备,并在其上构建文件系统。 + +!!! note "本次实验中涉及 VirtIO 的部分已经为大家实现好了,同学们只需要调用相关函数即可" ### MBR -主引导记录(英语:Master Boot Record,缩写:MBR),又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的首个扇区,它在硬盘上位于第一个扇区。在深入讨论主引导扇区内部结构的时候,有时也将其开头的446字节内容特指为“主引导记录”(MBR),其后是4个16字节的“磁盘分区表”(DPT),以及2字节的结束标志(55AA)。因此,在使用“主引导记录”(MBR)这个术语的时候,需要根据具体情况判断其到底是指整个主引导扇区,还是主引导扇区的前446字节。 +主引导记录(Master Boot Record,MBR),又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的首个扇区,它在硬盘上位于第一个扇区。在深入讨论主引导扇区内部结构的时候,有时也将其开头的 446 字节内容特指为“主引导记录”(MBR),其后是 4 个 16 字节的“磁盘分区表”(DPT),以及 2 字节的结束标志(55AA)。因此,在使用“主引导记录”(MBR)这个术语的时候,需要根据具体情况判断其到底是指整个主引导扇区,还是主引导扇区的前 446 字节。 ### FAT32 -文件分配表(File Allocation Table,首字母缩略字:FAT),是一种由微软发明并拥有部分专利的文件系统,供MS-DOS使用,也是所有非NT核心的Windows系统使用的文件系统。最早的 FAT 文件系统直接使用扇区号来作为存储的索引,但是这样做的缺点是显然易见的:当磁盘的大小不断扩大,存储扇区号的位数越来越多,越发占用存储空间。以 32 位扇区号的文件系统为例,如果磁盘的扇区大小为 512B,那么文件系统能支持的最大磁盘大小仅为 2TB。 +文件分配表(File Allocation Table,FAT),是一种由微软发明并拥有部分专利的文件系统,供 MS-DOS 使用,也是所有非 NT 核心的 Windows 系统使用的文件系统。最早的 FAT 文件系统直接使用扇区号来作为存储的索引,但是这样做的缺点是显然易见的:当磁盘的大小不断扩大,存储扇区号的位数越来越多,越发占用存储空间。以 32 位扇区号的文件系统为例,如果磁盘的扇区大小为 512B,那么文件系统能支持的最大磁盘大小仅为 2TB。 所以在 FAT32 中引入的新的存储管理单位“簇”,一个簇包含一个或多个扇区,文件系统中记录的索引不再为扇区号,而是**簇号**,以此来支持更大的存储设备。你可以参考这些资料来学习 FAT32 文件系统的标准与实现: @@ -39,300 +39,260 @@ Virtio 是一个开放标准,它定义了一种协议,用于不同类型的 - [Microsoft FAT Specification](https://academy.cba.mit.edu/classes/networking_communications/SD/FAT.pdf) !!! note "上述两个材料对于完成本次实验非常关键,希望同学们仔细阅读" - - [FAT32文件系统?盘它!](https://www.youtube.com/watch?v=YfDU6g0CmZE&t=344s) 里提到的“块”可以对标本次实验里的“扇区”(sector)。`fat32_init` 函数里各种参数的计算方式可以参考本视频; - - [Microsoft FAT Specification](https://academy.cba.mit.edu/classes/networking_communications/SD/FAT.pdf) 是本次实验参考的标准。`fat32_bpb` `fat32_dir_entry` 等结构体各个字段的含义,可以直接参考本材料。例如: + - [FAT32文件系统?盘它!](https://www.youtube.com/watch?v=YfDU6g0CmZE&t=344s) 里提到的“块”可以对标本次实验里的“扇区”(sector) + - `fat32_init` 函数里各种参数的计算方式可以参考本视频; + - [Microsoft FAT Specification](https://academy.cba.mit.edu/classes/networking_communications/SD/FAT.pdf) 是本次实验参考的标准,`fat32_bpb` `fat32_dir_entry` 等结构体各个字段的含义,可以直接参考本材料,例如: - Page 7 讲解了 `fat32_bpb`,在 `fat32_init` 中用得上; - - Page 23 处讲解了 `fat32_dir_entry`,在文件操作函数中用得上; + - Page 23 处讲解了 `fat32_dir_entry`,在文件操作函数中用得上。 ## 实验步骤 +本次实验的内容分为两个部分: + +- Shell:实现 VFS,编写虚拟文件设备 stdin, stdout, stderr 的读写操作; +- FAT32:实现 FAT32 文件系统的读写操作。 + +!!! note "本实验中不涉及 fork 的实现和缺页异常,只需要完成 Lab4 即可开始本实验(当然,本实验也兼容 Lab5)" + +### 准备工作 + +!!! tip "代码与调试建议" + 框架代码中可能会使用到 `Log` `Err` 宏或者其他你可能未定义的东西,如果编译时出现报错可以自行补充,如果有不清楚的奇怪报错可以询问助教。 + + 在本次实验中,以往实验的部分可能会变得完全不重要,因为本次实验中只需要考虑文件系统和系统调用,所以你可以选择将原来调试用的 Log 宏禁用,以减少输出,比如在根目录下的 Makefile 中: + + ```Makefile + LOG := 1 + CFLAG := ... -DLOG=$(LOG) + ``` + + 然后在 `include/printk.h` 中: + + ```c title="include/printk.h" + #if LOG + #define Log(format, ...) \ + printk("\33[1;35m[%s,%d,%s] " format "\33[0m\n", \ + __FILE__, __LINE__, __func__, ## __VA_ARGS__) + #else + #define Log(format, ...); + #endif + ``` + + 这样在 `make run LOG=0` 时就不会输出 Log 信息了。 + +此次实验基于 lab4/5 同学所实现的代码进行。 + +* 从仓库同步以下文件: + ```text + src/lab6 + ├── disk.img.zip // FAT32 磁盘镜像,需解压 + ├── fs + │   ├── Makefile + │   ├── fat32.c // FAT32 文件系统实现 + │   ├── fs.c // 供系统内核使用的文件系统相关函数实现 + │   ├── mbr.c // MBR 初始化(无需修改) + │   ├── vfs.c // VFS 实现 + │   └── virtio.c // VirtIO 驱动(无需修改) + ├── include + │   ├── fat32.h // FAT32 相关数据结构与函数声明 + │   ├── fs.h // 供系统内核使用的文件系统相关数据结构及函数声明 + │   ├── mbr.h // MBR 相关数据结构与函数声明(无需关注) + │   ├── vfs.h // VFS 操作函数声明 + │   └── virtio.h // VirtIO 驱动相关数据结构与函数声明(无需关注) + └── user // 用户态程序部分不需同学们修改,所以这里给出完整的代码 + ├── Makefile // 未作修改 + ├── link.lds // 未作修改 + ├── main.c // nish 用户态程序(需要阅读) + ├── printf.c // 未作修改 + ├── start.S // 未作修改 + ├── stddef.h // 未作修改 + ├── stdint.h // 同步了内核的 stdint.h + ├── stdio.h // 未作修改 + ├── string.c // 新文件,添加了 strlen + ├── string.h // 新文件,添加了 strlen + ├── syscall.h // 更新了系统调用号 + ├── uapp.S // 未作修改 + ├── unistd.c // 系统调用实现(需要阅读) + └── unistd.h + ``` +* 同时需要修改 proc.h/c,在初始化时只创建一个用户态进程 + ### Shell: 与内核进行交互 -我们为大家提供了 `nish` 来与我们在实验中完成的 kernel 进行交互。`nish` (Not Implemented SHell) 提供了简单的用户交互和文件读写功能,有如下的命令。 +我们为大家提供了用户态程序 "nish" (Not Implemented SHell) 来与我们在实验中完成的 kernel 进行交互。它提供了简单的用户交互和文件读写功能,有如下的命令: ```bash -echo [string] # 将 string 输出到 stdout -cat [path] # 将路径为 path 的文件的内容输出到 stdout -edit [path] [offset] [string] # 将路径为 path 的文件, - # 偏移量为 offset 的部分开始,写为 string -``` - -同步 `os24fall-stu` 中的 `user` 文件夹,替换原有的用户态程序为 `nish`。为了能够正确启动 QEMU,需要下载[磁盘镜像](https://drive.google.com/file/d/1CZF8z2v8ZyAYXT1DlYMwzOO1ohAj41-W/view?usp=sharing)并放置在项目目录下。同时,还需要将 `NR_TASKS` 修改为 2,也就是仅初始化 `nish` 这一个用户态进程。 - -```plaintext -lab6 -├── Makefile -├── disk.img -├── arch -│ └── riscv -│ ├── Makefile -│ └── include -│ └── sbi.h -├── fs -│ ├── Makefile -│ ├── fat32.c -│ ├── fs.S -│ ├── mbr.c -│ ├── vfs.c -│ └── virtio.c -├── include -│ ├── fat32.h -│ ├── fs.h -│ ├── mbr.h -│ ├── string.h -│ ├── debug.h -│ ├── vfs.h -│ └── virtio.h -├── lib -│ └── string.c -└── user - ├── Makefile - ├── forktest.c - ├── link.lds - ├── printf.c - ├── ramdisk.S - ├── shell.c - ├── start.S - ├── stddef.h - ├── stdio.h - ├── string.h - ├── syscall.h - ├── unistd.c - └── unistd.h +echo [string] # 将 string 输出到 stdout +cat [path] # 将路径为 path 的文件的内容输出到 stdout +edit [path] [offset] [string] # 将路径为 path 的文件,偏移量为 offset 的部分开始,写为 string ``` -此外,可能还要向 `include/types.h` 中补充一些类型别名 - -```c -typedef unsigned long uint64_t; -typedef long int64_t; -typedef unsigned int uint32_t; -typedef int int32_t; -typedef unsigned short uint16_t; -typedef short int16_t; -typedef uint64_t* pagetable_t; -typedef char int8_t; -typedef unsigned char uint8_t; -typedef uint64_t size_t; -``` - -还要修改一下 `arch/riscv/kernel/vmlinux.lds` 中的 `_sramdisk` 符号部分(将 uapp 修改为 ramdisk) - -``` - _sramdisk = .; - *(.ramdisk .ramdisk*) - _eramdisk = .; -``` - -完成这一步后,可能你还需要调整一部分头文件引用和 `Makefile`,以让项目能够成功编译并运行。 - -我们在启动一个用户态程序时默认打开了三个文件,`stdin`,`stdout` 和 `stderr`,他们对应的 file descriptor 分别为 `0`,`1`,`2`。在 `nish` 启动时,会首先向 `stdout` 和 `stderr` 分别写入一段内容,用户态的代码如下所示。 - -```c -// user/shell.c +我们在启动一个用户态程序(包括 nish)时默认打开了三个文件,`stdin`,`stdout` 和 `stderr`,他们对应的 file descriptor 分别为 `0`,`1`,`2`。在 nish 启动时,会首先向 `stdout` 和 `stderr` 分别写入一段内容作为测试: +```c title="user/main.c" linenums="134" write(1, "hello, stdout!\n", 15); write(2, "hello, stderr!\n", 15); ``` -#### 处理 `stdout` 的写入 - -我们在用户态已经像上面这样实现好了 `write` 函数来向内核发起 syscall,我们先在内核态完成真实的写入过程,也即将写入的字符输出到串口。 - -```c -// arch/riscv/include/syscall.h - -int64_t sys_write(unsigned int fd, const char* buf, uint64_t count); - -// arch/riscv/include/syscall.c - -void trap_handler(uint64_t scause, uint64_t sepc, struct pt_regs *regs) { - ... - if (scause == 0x8) { // syscalls - uint64_t sys_call_num = regs->a7; - ... - if (sys_call_num == SYS_WRITE) { - regs->a0 = sys_write(regs->a0, (const char*)(regs->a1), regs->a2); - regs->sepc = regs->sepc + 4; - } else { - printk("Unhandled Syscall: 0x%lx\n", regs->a7); - while (1); - } - } - ... -} -``` +而在这之前,我们也曾处理过 write 系统调用,不过当时是根据 stdin 的 fd 来特判的。接下来在本次实验中我们会将这一操作包装为文件系统的操作。 -注意到我们使用的是 `fd` 来索引打开的文件,所以在该进程的内核态需要维护当前进程打开的文件,将这些文件的信息储存在一个表中,并在 `task_struct` 中指向这个表。 +#### 文件系统抽象 -```c -// include/fs.h +我们在 `include/fs.h` 中定义了文件系统的数据结构: -struct file { - uint32_t opened; - uint32_t perms; - int64_t cfo; - uint32_t fs_type; +```c title="include/fs.h" linenums="31" +struct file { // Opened file in a thread. + uint32_t opened; // 文件是否打开 + uint32_t perms; // 文件的读写权限 + int64_t cfo; // 当前文件指针偏移量 + uint32_t fs_type; // 文件系统类型 union { - struct fat32_file fat32_file; + struct fat32_file fat32_file; // 后续 FAT32 文件系统的文件需要的额外信息 }; - int64_t (*lseek) (struct file* file, int64_t offset, uint64_t whence); - int64_t (*write) (struct file* file, const void* buf, uint64_t len); - int64_t (*read) (struct file* file, void* buf, uint64_t len); + int64_t (*lseek) (struct file *file, int64_t offset, uint64_t whence); // 文件指针操作 + int64_t (*write) (struct file *file, const void *buf, uint64_t len); // 写文件 + int64_t (*read) (struct file *file, void *buf, uint64_t len); // 读文件 - char path[MAX_PATH_LENGTH]; + char path[MAX_PATH_LENGTH]; // 文件路径 }; -// arch/riscv/include/proc.h - -struct task_struct { - ... - struct file *files; - ... +struct files_struct { + struct file fd_array[MAX_FILE_NUMBER]; }; ``` -首先要做的是在创建进程时为进程初始化文件,当初始化进程时,先完成打开的文件的列表的初始化,这里我们的方式是直接分配一个页,并用 `files` 指向这个页。 - -```c -// fs/vfs.c +可以看出我们为每个文件都保存了三个函数指针,这样针对不同文件的同一操作就可以调用到不同函数了。同时为了方便实现,我们直接通过数组来保存 file 结构体,默认情况下会创建 `MAX_FILE_NUMBER=16` 个可用的文件描述符。 -struct file* file_init() { - struct file *ret = (struct file*)alloc_page(); +接下来需要同学们修改 proc.h,为进程 task_struct 结构体添加一个指向文件表的指针: - // stdin - ret[0].opened = 1; - ... - - // stdout - ret[1].opened = 1; - ret[1].perms = FILE_WRITABLE; - ret[1].cfo = 0; - ret[1].lseek = NULL; - ret[1].write = /* todo */; - ret[1].read = NULL; - memcpy(ret[1].path, "stdout", 7); - - // stderr - ret[2].opened = 1; +```c title="arch/riscv/include/proc.h" +struct task_struct { ... + struct files_struct *files; +}; +``` - return ret; -} +#### stdout/err/in 初始化 -int64_t stdout_write(struct file* file, const void* buf, uint64_t len) { - char to_print[len + 1]; - for (int i = 0; i < len; i++) { - to_print[i] = ((const char*)buf)[i]; - } - to_print[len] = 0; - return printk(buf); -} +在 `fs/fs.c` 文件中,我们定义了一个函数 `file_init`: -// arch/riscv/kernel/proc.c -void task_init() { - ... - // Initialize the stdin, stdout, and stderr. - task[1]->files = file_init(); - printk("[S] proc_init done!\n"); - ... +```c title="fs/fs.c" linenums="8" +struct files_struct *file_init() { + // todo: alloc pages for files_struct, and initialize stdin, stdout, stderr + struct files_struct *ret = NULL; + return ret; } ``` -可以看到每一个被打开的文件对应三个函数指针,这三个函数指针抽象出了每个被打开的文件的操作。也对应了 `SYS_LSEEK`,`SYS_WRITE`,和 `SYS_READ` 这三种 syscall. 最终由函数 `sys_write` 调用 `stdout` 对应的 `struct file` 中的函数指针 `write` 来执行对应的写串口操作。我们这里直接给出 `stdout_write` 的实现,只需要直接把这个函数指针赋值给 `stdout` 对应 `struct file` 中的 `write` 即可。 +这个函数需要大家在 proc.c 中的 `task_init` 函数中为每个进程调用,创建文件表并保存在 task struct 中。 -接着你需要实现 `sys_write` syscall,来间接调用我们赋值的 `stdout` 对应的函数指针。 +在这个函数中,你需要: -```c +- 根据 `files_struct` 的大小分配页空间 +- 为 stdin、stdout、stderr 赋值,比如 stdin 你可以: + ```c + ret->fd_array[0].opened = 1; + ret->fd_array[0].perms = FILE_READABLE; + ret->fd_array[0].cfo = 0; + ret->fd_array[0].lseek = NULL; + ret->fd_array[0].write = NULL; + ret->fd_array[0].read = ...; + ``` + - 这里的 read / write 函数可以留到等下来实现 +- 保证其他未使用的文件的 `opened` 字段为 0 -// arch/riscv/kernel/syscall.c +#### 处理 stdout/err 的写入 -int64_t sys_write(unsigned int fd, const char* buf, uint64_t count) { +正如 4.2 开头所说,用户态程序在开始的时候会通过 `write` 函数来向内核发起 syscall 进行测试。在捕获到 write 的 syscall 之后,我们就可以查找对应的 fd,并通过对应的 write 函数调用来进行输出了。一个参考实现如下: + +```c +int64_t sys_write(uint64_t fd, const char *buf, uint64_t len) { int64_t ret; - struct file* target_file = &(current->files[fd]); - if (target_file->opened) { - /* todo: indirect call */ + struct file *file = &(current->files->fd_array[fd]); + if (file->opened == 0) { + printk("file not opened\n"); + return ERROR_FILE_NOT_OPEN; } else { - printk("file not open\n"); - ret = ERROR_FILE_NOT_OPEN; + // check perms and call write function of file } return ret; } + +void do_syscall(struct pt_regs* regs) { + switch (regs->a7) { + case SYS_WRITE: + regs->a0 = sys_write(regs->a0, (const char *)regs->a1, regs->a2); + break; + case SYS_GETPID: + regs->a0 = current->pid; + break; + case SYS_CLONE: + regs->a0 = do_fork(regs); + break; + default: + Err("not support syscall id = %d", regs->a7); + } + regs->sepc += 4; +} ``` -至此,你已经能够打印出 `stdout` 的输出了。 +对于 stdout 和 stderr 的输出,我们直接通过 `printk` 进行串口输出即可: -```plaintext -2024 Hello RISC-V -hello, stdout! -``` +```c title="fs/vfs.c" linenums="22" +int64_t stdout_write(struct file *file, const void *buf, uint64_t len) { + char to_print[len + 1]; + for (int i = 0; i < len; i++) { + to_print[i] = ((const char *)buf)[i]; + } + to_print[len] = 0; + return printk(buf); +} -#### 处理 `stderr` 的写入 +int64_t stdout_write(struct file *file, const void *buf, uint64_t len) { + // todo +} +``` -仿照 `stdout` 的输出过程,完成 `stderr` 的写入,让 `nish` 可以正确打印出 +实现好后,你应该已经能够打印出输出了: -```plaintext -2024 Hello RISC-V +```text +2024 ZJU Operating System hello, stdout! hello, stderr! SHELL > ``` -#### 处理 `stdin` 的读取 +#### 处理 stdin 的读取 -此时 `nish` 已经打印出命令行等待输入命令以进行交互了,但是还需要读入从终端输入的命令才能够与人进行交互,所以我们要实现 `stdin` 以获取键盘键入的内容。 +此时 nish 已经打印出命令行等待输入命令以进行交互了,但是还需要读入从终端输入的命令才能够与人进行交互,所以我们要实现 `stdin` 以获取键盘键入的内容。 -在终端中已经实现了不断读 `stdin` 文件来获取键入的内容,并解析出命令,你需要完成的只是响应如下的系统调用: - -```c -// user/shell.c +对于输入的读取就是对于 fd=0 的 stdin 文件进行 read 操作,所以需要我们实现 vfs.c 中的 stdin_read 函数。而对于终端的输入,我们需要通过 sbi 来完成,需要大家在 `arch/riscv/include/sbi.h` 中添加函数: -read(0, read_buf, 1); +```c title="arch/riscv/include/sbi.h" +struct sbiret sbi_debug_console_read(uint64_t num_bytes, uint64_t base_addr_lo, uint64_t base_addr_hi); ``` -代码框架中已经实现了一个在内核态用于向终端读取一个字符的函数,你需要调用这个函数来实现你的 `stdin_read`. +并在 sbi.c 中进行实现(eid 和 `console_write_byte` 一样为 `0x4442434e`,fid 为 2)。其中参数 `num_bytes` 为读取的字节数,`base_addr_lo` 和 `base_addr_hi` 为写入的目的地址(`base_addr_hi` 在 64 位架构中不会用到)。 -```c -// fs/vfs.c +接下来在 vfs.c 中我们为大家定义了函数 `uart_getchar()`,因为 `sbi_debug_console_read` 是非阻塞的,所以我们需要一个函数来不断进行读取,直到读到了有效字符,然后在 `stdin_read` 中只需要这样读取 `len` 个字符就好了。 -char uart_getchar() { - /* already implemented in the file */ -} +!!! note "关于 read 阻塞的说明" + 同学们可以发现我们这里实现的 `read` 其实是完全阻塞的,即读取到了 `len` 个字符才会返回,实际上这只是简化的操作。 -int64_t stdin_read(struct file* file, void* buf, uint64_t len) { - /* todo: use uart_getchar() to get chars */ -} -``` + 实际情况下 `read` 只会在没有任何输入的情况下阻塞,一旦可以读到数据,则不论是否达到 `len` 都会立即返回,但本次实验中不考虑这种情况即可。 -接着参考 `syscall_write` 的实现,来实现 `syscall_read`. +完成了 `stdin_read` 后,还需要捕获 63 号系统调用 read,来和 write 一样类似处理即可。 -```c - -// arch/riscv/kernel/syscall.c +全部完成后,你应该就可以在 nish 中使用 `echo` 命令了: -int64_t sys_read(unsigned int fd, char* buf, uint64_t count) { - int64_t ret; - struct file* target_file = &(current->files[fd]); - if (target_file->opened) { - /* todo: indirect call */ - } else { - printk("file not open\n"); - ret = ERROR_FILE_NOT_OPEN; - } - return ret; -} +```text +SHELL > echo "test" +test ``` -至此,就可以在 `nish` 中使用 `echo` 命令了。 - -``` -SHELL > echo "this is echo" -this is echo -``` - -### FAT32: 持久存储 +### FAT32:持久存储 在本次实验中我们仅需实现 FAT32 文件系统中很小一部分功能,我们为实验中的测试做如下限制: @@ -344,163 +304,170 @@ this is echo #### 准备工作 ##### 利用 VirtIO 为 QEMU 添加虚拟存储 -我们为大家构建好了[磁盘镜像](https://drive.google.com/file/d/1CZF8z2v8ZyAYXT1DlYMwzOO1ohAj41-W/view?usp=sharing),其中包含了一个 MBR 分区表以及一个存储有一些文件的 FAT32 分区。可以使用如下的命令来启动 QEMU,并将该磁盘连接到 QEMU 的一个 VirtIO 接口上,构成一个 `virtio-blk-device`。 +我们为大家准备了一个 FAT32 的磁盘映像,其中包含了一个 MBR 分区表以及一个存储有一些文件的 FAT32 分区,需要大家解压 `src/lab6/disk.img.zip` 得到 `disk.img` 并放在根目录下。 + +接下来可以使用如下的命令来启动 QEMU,这会将磁盘连接到 QEMU 的一个 VirtIO 接口上,构成一个 `virtio-blk-device`: ```Makefile run: all - @echo Launch the qemu ...... - @qemu-system-riscv64 \ - -machine virt \ - -nographic \ - -bios default \ - -kernel vmlinux \ - -global virtio-mmio.force-legacy=false \ - -drive file=disk.img,if=none,format=raw,id=hd0 \ - -device virtio-blk-device,drive=hd0 + @echo Launch qemu... + @qemu-system-riscv64 -nographic -machine virt -kernel vmlinux -bios default \ + -global virtio-mmio.force-legacy=false \ + -drive file=disk.img,if=none,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 + +debug: all + @echo Launch qemu for debug... + @qemu-system-riscv64 -nographic -machine virt -kernel vmlinux -bios default \ + -global virtio-mmio.force-legacy=false \ + -drive file=disk.img,if=none,format=raw,id=hd0 \ + -device virtio-blk-device,drive=hd0 -S -s ``` -`virtio` 所需的驱动我们已经为大家编写完成了,在 `fs/virtio.c` 中给出。 - -然后在 `setup_vm_final` 创建虚拟内存映射时,还需要添加映射 VritIO 外设部分的映射。 +VirtIO 所需的驱动我们已经为大家编写完成了,在 `fs/virtio.c` 中给出,为了正常使用这部分外设,还需要在 `setup_vm_final` 中添加对 VritIO 外设的映射: -```c -// arch/riscv/kernel/vm.c +```c title="arch/riscv/kernel/vm.c" create_mapping(swapper_pg_dir, io_to_virt(VIRTIO_START), VIRTIO_START, VIRTIO_SIZE * VIRTIO_COUNT, PTE_W | PTE_R | PTE_V); ``` +##### 初始化 VirtIO 与 MBR -##### 初始化 MBR +除了 VirtIO,我们还为大家实现了读取 MBR 这一磁盘初始化过程。该过程会搜索磁盘中存在的分区,然后对分区进行初步的初始化。 -我们为大家实现了读取 MBR 这一磁盘初始化过程。该过程会搜索磁盘中存在的分区,然后对分区进行初步的初始化。 +这两部分分别需要使用 `virtio_dev_init()` 和 `mbr_init()` 进行初始化,需要在 `head.S` 中 `task_init` 结束后调用。 -对 VirtIO 和 MBR 进行初始化的逻辑可以被添加在初始化第一个进程的 `task_init` 中 +#### 初始化 FAT32 分区 -```c -// arch/riscv/kernel/proc.c -void task_init() { - ... - printk("[S] proc_init done!\n"); - - virtio_dev_init(); - mbr_init(); -} -``` +在 FAT32 分区的第一个扇区中存储了关于这个分区的元数据,首先需要读取并解析这些元数据。我们提供了两个数据结构的定义,`fat32_bpb` 为 FAT32 BIOS Parameter Block 的简写。这是一个物理扇区,其中对应的是这个分区的元数据。首先需要将该扇区的内容读到一个 `fat32_bpb` 数据结构中进行解析。 -这样从第一个用户态进程被初始化完成开始,就能够直接使用 VirtIO,并使用初始化完成的 MBR 表了。 - -##### 初始化 FAT32 分区 - -在 FAT32 分区的第一个扇区中存储了关于这个分区的元数据,首先需要读取并解析这些元数据。我们提供了两个数据结构的定义,`fat32_bpb` 为 FAT32 BIOS Parameter Block 的简写。这是一个物理扇区,其中对应的是这个分区的元数据。首先需要将该扇区的内容读到一个 `fat32_bpb` 数据结构中进行解析。`fat32_volume` 是用来存储我们实验中需要用到的元数据的,需要根据 `fat32_bpb` 中的数据来进行计算并初始化。 +`fat32_volume` 是用来存储我们后续代码中需要用到的元数据的,需要根据 `fat32_bpb` 中的数据来进行计算并初始化。 !!! note "为了简单起见,本次实验中每个簇都只包含 1 个扇区。所以你的代码可能会以各种灵车的方式跑起来,但是你仍需要对簇和扇区有所区分,并在报告里有所体现。" -```c -// fs/fat32.c - -struct fat32_bpb fat32_header; // FAT32 metadata in the disk -struct fat32_volume fat32_volume; // FAT32 metadata to initialize - +```c title="fs/fat32.c" linenums="26" void fat32_init(uint64_t lba, uint64_t size) { - virtio_blk_read_sector(lba, (void*)&fat32_header); - fat32_volume.first_fat_sec = /* to calculate */; - fat32_volume.sec_per_cluster = /* to calculate */; - fat32_volume.first_data_sec = /* to calculate */; - fat32_volume.fat_sz = /* to calculate */; - - virtio_blk_read_sector(fat32_volume.first_data_sec, fat32_buf); // Get the root directory - struct fat32_dir_entry *dir_entry = (struct fat32_dir_entry *)fat32_buf; + virtio_blk_read_sector(lba, (void*)&fat32_header); // 从第 lba 个扇区读取 FAT32 BPB + fat32_volume.first_fat_sec = /* to calculate */; // 记录第一个 FAT 表所在的扇区号 + fat32_volume.sec_per_cluster = /* to calculate */; // 每个簇的扇区数 + fat32_volume.first_data_sec = /* to calculate */; // 记录第一个数据簇所在的扇区号 + fat32_volume.fat_sz = /* to calculate */; // 记录每个 FAT 表占的扇区数(并未用到) } - ``` +!!! tip "指导与调试建议" + 为了完成这一部分,你需要了解 FAT32 中扇区的排列方式与用途。 + + 即 `lba` 扇区为 BPB,这之后紧接着的是一些保留扇区,然后是第一个 FAT 表所在的扇区(这个表会占很多个扇区,需要从 bpb 读取),接下来是第二个 FAT 表所在的扇区(为第一个 FAT 表的备份),然后接下来是数据区。 + + 在得到这些之后,你可以尝试从 `fat32_volume.first_fat_sec` 读取一个扇区到 `fat32_buf` 中,如果正确的话,开头四个字节应该是 `F8FFFF0F`。 + !!! tip "物理层面读写扇区的原语" - 出于实验难度的考虑,物理层面通过 virtio 读写扇区的驱动函数已经写好; - `virtio_blk_read_sector` 可以将扇区号对应的扇区读入到一个 buffer 内; - `virtio_blk_write_sector` 可以将一个 buffer 的内容写入到特定扇区号的扇区里; - - 不论是 `openat` `read` `write` `lseek`,都可能需要分别调用读或者写原语,来完成跟扇区内容的交互; + - 不论是 `openat` `read` `write` `lseek`,都可能需要调用这两个函数,来完成跟扇区内容的交互; -#### 读取 FAT32 文件 +!!! tip "更多调试建议" + 你可以使用 16 进制文件查看软件打开 `disk.img` 文件,来查看其中的内容,每个扇区的大小都为 `VIRTIO_BLK_SECTOR_SIZE`。 + 虽然文件中大部分内容都为 0,但是你可以搜索得到一些有意义的部分: + - 字符串 `mkfs.fat`,这八个字节是 bpb 中的 oem 代号,即 `fat32_bpb->oem_name`; + - 根据这个位置,你可以找到 BPB 的开始位置; + - 字节序列 `F8 FF FF 0F`,这是 FAT 表的开头,也是第一个 FAT 项的内容; + - 字符串 `EMAIL`,这是我们要读取的文件的名字,它会在数据区开头的根目录扇区中出现,标志着一个短文件名目录项的开始; + - 你可能会看到有其他的目录项,你可以不用管它们,在本次实验中不会用到; + - 字符串 `From`,这是要读取的文件内容的开头,它也会处于一个扇区的开头。 -在读取文件之前,首先需要打开对应的文件,这需要实现 `openat` syscall. +#### 完善系统调用 -```c -// arch/riscv/syscall.c +在读取文件之前,首先需要打开对应的文件,这需要实现 `openat` syscall,调用号为 56。你需要寻找一个空闲的文件描述符,然后调用 `file_open` 函数来初始化这个文件描述符。 -int64_t sys_openat(int dfd, const char* filename, int flags) { - int fd = -1; +!!! note "关于判断文件系统类型" + 我们使用最简单的判别文件系统的方式,文件前缀为 `/fat32/` 的即是本次 FAT32 文件系统中的文件,例如,在 `nish` 中我们尝试读取文件,使用的命令是 `cat /fat32/$FILENAME`. `file_open` 会根据前缀决定是否调用 `fat32_open_file` 函数(后面实现)。 - // Find an available file descriptor first - for (int i = 0; i < PGSIZE / sizeof(struct file); i++) { - if (!current->files[i].opened) { - fd = i; - break; - } - } + 注意因为我们的文件一定在 FAT32 的根目录下,也即 `/fat32/` 下,所以无需实现与目录遍历相关的逻辑。此外需要注意的是,需要将文件名统一转换为大写或小写,因为我们的实现是不区分大小写的。 - // Do actual open - file_open(&(current->files[fd]), filename, flags); +读取完成后 nish 会调用 close 来关闭文件,所以你需要实现 57 号系统调用 close,来为指定的文件描述符关闭文件。 - return fd; -} +最后在 nish 处理 edit 的时候,会先进行 lseek 调整文件指针,然后再进行 write,所以你需要参考 write 和 read 实现类似的 62 号系统调用 lseek。 -void file_open(struct file* file, const char* path, int flags) { - file->opened = 1; - file->perms = flags; - file->cfo = 0; - file->fs_type = get_fs_type(path); - memcpy(file->path, path, strlen(path) + 1); - - if (file->fs_type == FS_TYPE_FAT32) { - file->lseek = fat32_lseek; - file->write = fat32_write; - file->read = fat32_read; - file->fat32_file = fat32_open_file(path); - } else if (file->fs_type == FS_TYPE_EXT2) { - printk("Unsupport ext2\n"); - while (1); - } else { - printk("Unknown fs type: %s\n", path); - while (1); - } -} -``` +#### 打开文件 -我们使用最简单的判别文件系统的方式,文件前缀为 `/fat32/` 的即是本次 FAT32 文件系统中的文件,例如,在 `nish` 中我们尝试读取文件,使用的命令是 `cat /fat32/$FILENAME`. `file_open` 会根据前缀决定是否调用 `fat32_open_file` 函数。注意因为我们的文件一定在根目录下,也即 `/fat32/` 下,无需实现与目录遍历相关的逻辑。此外需要注意的是,需要将文件名统一转换为大写或小写,因为我们的实现是不区分大小写的。 +接下来就是本次实验中比较复杂的部分了,首先在 `fat32_open_file` 函数中,我们需要读取出被打开的文件所在的簇和目录项位置的信息,来供后面 read write lseek 使用,目标是获取到: -```c -// arch/riscv/syscall.c +```c title="include/fs.h" linenums="21" +struct fat32_dir { + uint32_t cluster; // 文件的目录项所在的簇 + uint32_t index; // 文件的目录项是该簇中的第几个目录项 +}; -struct fat32_file fat32_open_file(const char *path) { - struct fat32_file file; - /* todo: open the file according to path */ - return file; -} +struct fat32_file { + uint32_t cluster; // 文件开头所在的簇 + struct fat32_dir dir; // 文件的目录项信息 +}; ``` -!!! tip "为什么我总是打不开文件?" - 一般我们用 `memcmp` 逐一比较 FAT32 文件系统根目录下的各个文件名以及欲打开的文件名。但是需要注意的是,FAT32 文件系统根目录下的文件名格式可不是 `\0` 结尾的。8 byte 中超出长度的部分是什么呢?留给你们自己探索。 +你需要遍历数据区开头的根目录扇区,找到 name 和 `path` 末尾的 `filename` 相匹配的 `fat32_dir_entry` 目录项结构体,再从其中得到这些信息。 -在打开文件后自然是进行文件的读取操作,需要先实现 `lseek` syscall. 注意实现之后需要在打开文件时将对应的 `fat32_lseek` 赋值到打开的 FAT32 文件系统中的文件的 `lseek` 函数指针上。 +!!! tip "为什么我匹配不到要打开的文件" + 如果是使用 `memcmp` 来逐一比较 FAT32 文件系统根目录下的各个文件名和想要打开的文件名,则需要 8 个字节完全匹配。但是需要注意的是,FAT32 文件系统根目录下的文件名并不是以 "\0" 结尾的字符串,你需要参考 spec 或者 `disk.img` 中的内容来了解 FAT32 文件名的存储方式。 -```c -// arch/riscv/kernel/syscall.c +#### 读取与写入文件 -int64_t sys_lseek(int fd, int64_t offset, int whence) { - int64_t ret; - struct file* target_file = &(current->files[fd]); - if (target_file->opened) { - /* todo: indirect call */ - } else { - printk("file not open\n"); - ret = ERROR_FILE_NOT_OPEN; - } - return ret; -} +对于 FAT32 文件系统的文件读取,会调用到 `fat32_read` 函数,你需要根据 `file->fat32_file` 中的信息(即 open 的时候获取的信息)找到文件内容所在的簇,然后读取出文件内容到 `buf` 中。 + +!!! tip "Hint" + - 通过 `cluster_to_sector` 可以得到簇号对应的扇区号; + - 通过 `virtio_blk_read_sector` 对扇区进行读取; + - 你可能需要再通过读取目录项来获取文件长度; + - 读取是要从 `file->cfo` 开始的; + - 读取的长度可能会使得内容跨越了一个或几个扇区,你需要多次读取; + - 读取到了文件末尾就应该截止,并返回实际读取的长度; + - 如果 read 返回了 0,则说明已经读取到了文件末尾,这样用户态程序就知道文件已经完全读取结束了。 + +正确实现后,你应该可以看到 `cat /fat32/email` 的输出了: + +??? success "示例输出" + ```text + ...buddy_init done! + ...mm_init done! + ...proc_init done! + ...virtio_blk_init done! + ...fat32 partition #1 init done! + 2024 ZJU Operating System + hello, stdout! + hello, stderr! + SHELL > cat /fat32/email + From: TORVALDS@klaava.Helsinki.FI (Linus Benedict Torvalds) + Newsgroups: comp.os.minix + Subject: What would you like to see most in minix? + Summary: small poll for my new operating system + Message-ID: + Date: 25 Aug 91 20:57:08 GMT + Organization: University of Helsinki + + Hello everybody out there using minix - + + I'm doing a (free) operating system (just a hobby, won't be big and professional like gnu) for 386(486) AT cones. This has been brewing since april, and is starting to get ready. I'd Tike any feedback on things people like/dislike in minix, as my 0S resembles it somewhat (same physical layout of the file-system (due to practical reasons) among other things) . + + I've currently ported bash(1.08) and gcc(1.40), and things seem to work. This implies that I'll get something practical within a few months, andI'd like to know what features most people would want. Any suggestions are welcome, but I won't promise I'll implement them :-) + + Linus (torvalds@kruuna.helsinki.fi) + + PS. Yes . it's free of any minix code, and it has a multi-threaded fs. It is NOT protable (uses 386 task switching etc), and it probably never will support anything other than AT-hard disks, as that's all I have :-($ + SHELL > + ``` + +write 操作和 read 非常相似,只需要修改后再通过 `virtio_blk_write_sector` 写回扇区即可。 -// fs/fat32.c +!!! tip "本次实验不要求实现根据访问时间更新目录项信息" +#### lseek 操作 + +在 nish 处理 edit 的时候,会先进行 lseek 调整文件指针,然后再进行 write。而 lseek 在做的就是调整指针 `file->cfo` 的值,你需要实现: + +```c title="fs/fat32.c" linenums="67" int64_t fat32_lseek(struct file* file, int64_t offset, uint64_t whence) { if (whence == SEEK_SET) { file->cfo = /* to calculate */; @@ -517,51 +484,46 @@ int64_t fat32_lseek(struct file* file, int64_t offset, uint64_t whence) { } ``` -然后需要完成 `fat32_read` 并将其赋值给打开的 FAT32 文件的 `read` 函数指针。 - -```c -// fs/fat32.c - -int64_t fat32_read(struct file* file, void* buf, uint64_t len) { - /* todo: read content to buf, and return read length */ -} -``` - -完成 FAT32 读的部分后,就已经可以在 `nish` 中使用 `cat /fat32/email` 来读取到在磁盘中预先存储的一个名为 email 的文件了。 - -当然,最后还需要完成 `close` syscall 来将文件关闭。 +具体 `whence` 的含义请自行搜索 spec。 + +#### 测试 + +全部实现完成后,就可以实现文件的读写操作了: + +???+ success "示例输出" + ```text + ...buddy_init done! + ...mm_init done! + ...proc_init done! + ...virtio_blk_init done! + ...fat32 partition #1 init done! + 2024 ZJU Operating System + hello, stdout! + hello, stderr! + SHELL > echo "test" + test + SHELL > cat /fat32/email + From: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds) + ... + SHELL > edit /fat32/email 6 TORVALDS + SHELL > cat /fat32/email + From: TORVALDS@klaava.Helsinki.FI (Linus Benedict Torvalds) + ... + SHELL > + ``` -#### 写入 FAT32 文件 + 这里通过 `edit /fat32/email 6 TORVALDS` 来将文件 "From" 后面的 torvalds 改成了大写。除此之外你也可以发现重新 `make run` 运行,并直接 `cat /fat32/email`,输出的也是修改后的大写 TORVALDS,因为这部分修改是持久保存在 disk.img 磁盘中的。 -在完成读取后,就可以仿照读取的函数完成对文件的修改。在测试时可以使用 `edit` 命令在 `nish` 中对文件做出修改。需要实现 `fat32_write`,可以参考前面的 `fat32_read` 来进行实现。 +## 实验任务与要求 -```c -// fs/fat32.c +!!! note "本次实验为 bonus,无思考题" -int64_t fat32_write(struct file* file, const void* buf, uint64_t len) { - /* todo: fat32_write */ -} -``` +- 请各位同学独立完成作业,任何抄袭行为都将使本次作业判为 0 分。 +- 在学在浙大中提交: + - 整个工程代码的压缩包(提交之前请使用 `make clean` 清除所有构建产物) + - pdf 格式的实验报告: + - 记录实验过程并截图(4.1-4.3),并对每一步的命令以及结果进行必要的解释; + - 记录遇到的问题和心得体会。 -## 测试 +!!! tip "关于实验报告内容要求,可见:[常见问题及解答 - 实验提交要求](faq.md#_2)" -``` -[S] buddy_init done! -[S] proc_init done! -[S] virtio_blk_init done! -[S] fat32 partition init done! -2024 Hello RISC-V -... -... -hello, stdout! -hello, stderr! -SHELL > echo "this is echo" -this is echo -SHELL > cat /fat32/email -From: torvalds@klaava.Helsinki.FI (Linus Benedict Torvalds) -... -SHELL > edit /fat32/email 6 TORVALDS -SHELL > cat /fat32/email -From: TORVALDS@klaava.Helsinki.FI (Linus Benedict Torvalds) -... -``` diff --git a/src/lab6/Makefile b/src/lab6/Makefile deleted file mode 100644 index 780e234..0000000 --- a/src/lab6/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -export -CROSS_=riscv64-linux-gnu- -GCC=${CROSS_}gcc -LD=${CROSS_}ld -OBJCOPY=${CROSS_}objcopy -SCHEDULE ?= SJF - -ISA=rv64imafd_zifencei -ABI=lp64 - -INCLUDE = -I $(shell pwd)/include -I $(shell pwd)/arch/riscv/include -CF = -march=$(ISA) -mabi=$(ABI) -mcmodel=medany -fno-builtin -ffunction-sections -fdata-sections -nostartfiles -nostdlib -nostdinc -static -lgcc -Wl,--nmagic -Wl,--gc-sections -g -D$(SCHEDULE) -CFLAG = ${CF} ${INCLUDE} - -.PHONY:all run debug clean flash noflash qemu -all: clean - ${MAKE} -C user all - ${MAKE} -C lib all - ${MAKE} -C init all - ${MAKE} -C fs all - ${MAKE} -C arch/riscv all - @echo -e '\n'Build Finished OK - -debug: all - @echo Launch the qemu ...... - @qemu-system-riscv64 \ - -machine virt \ - -nographic \ - -bios default \ - -kernel vmlinux \ - -global virtio-mmio.force-legacy=false \ - -drive file=disk.img,if=none,format=raw,id=hd0 \ - -device virtio-blk-device,drive=hd0 \ - -S -s - -run: all - @echo Launch the qemu ...... - @qemu-system-riscv64 \ - -machine virt \ - -nographic \ - -bios default \ - -kernel vmlinux \ - -global virtio-mmio.force-legacy=false \ - -drive file=disk.img,if=none,format=raw,id=hd0 \ - -device virtio-blk-device,drive=hd0 - -clean: - ${MAKE} -C lib clean - ${MAKE} -C user clean - ${MAKE} -C fs clean - ${MAKE} -C init clean - ${MAKE} -C arch/riscv clean - $(shell test -f vmlinux && rm vmlinux) - $(shell test -f System.map && rm System.map) - @echo -e '\n'Clean Finished - -compile_commands.json: clean - bear -- make - sed -i 's/_zifencei//g' compile_commands.json diff --git a/src/lab6/arch/riscv/Makefile b/src/lab6/arch/riscv/Makefile deleted file mode 100644 index d51ec2c..0000000 --- a/src/lab6/arch/riscv/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - ${MAKE} -C kernel all - ${LD} -T kernel/vmlinux.lds kernel/*.o ../../init/*.o ../../lib/*.o ../../user/ramdisk ../../fs/*.o -o ../../vmlinux - $(shell test -d boot || mkdir -p boot) - ${OBJCOPY} -O binary ../../vmlinux ./boot/Image - nm ../../vmlinux > ../../System.map - -clean: - ${MAKE} -C kernel clean - $(shell test -d boot && rm -rf boot) diff --git a/src/lab6/arch/riscv/include/sbi.h b/src/lab6/arch/riscv/include/sbi.h deleted file mode 100644 index 3a9043e..0000000 --- a/src/lab6/arch/riscv/include/sbi.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _SBI_H -#define _SBI_H - -#define SBI_PUTCHAR 0x1 -#define SBI_GETCHAR 0x2 - -#include "types.h" - -struct sbiret { - long error; - long value; -}; - -struct sbiret sbi_ecall(int ext, int fid, uint64_t arg0, - uint64_t arg1, uint64_t arg2, - uint64_t arg3, uint64_t arg4, - uint64_t arg5); - -#endif diff --git a/src/lab6/disk.img.zip b/src/lab6/disk.img.zip new file mode 100644 index 0000000000000000000000000000000000000000..8639e2e1cf43c76f451d5a363731ef5c37aca724 GIT binary patch literal 66573 zcmeI)=~GkZ9tZFsP!PE)Q!J<~POIWV2|6GJQ*51~)GM1nSY#Ve$wgK{!WyC=;($QC zh=>8PDqEsWD^PY)pgz0x#7-2MeIR^j{YwINn3^0CI#`$N-K zUhUW~qV->#IBHg#_J&Pg9LR|HyvBVw`YVzPi6PQ7 zgoqz?f@XZaLAyUvQXi$=cQ`R-$zCYPPx2bi(bU^Pod1@eHN^=%M-fGSBYeLYQqvc} z5K}uVLzjB9Cjh)lfFJxJMk7ox*!O)`{3CB;#x-)0a?ynT^U$-tF>Pq3cX}nreb$UG~E7< zA&4{L>IEdQ*zwt;F$?qD!HM+gj+zqM=-Gw7EHA(R?r?{DfB0PYB*T+J z94}7sQJ>$+RNdNg+x$kTdWrBqG!|2<$wsgH;{40V=h0^-TBw@lR>GL6le@Xi7uBgm z>pFLO#xwVLpPOlCj7^m-vvuwgtvpp24Z6R`ipL)Rd-4kZi~XtDi-u*vI;nStq79M` z+PnS8BWcYgRo-feP)>>?GaDs-E##bvEJ0Sa{!{Nule!1fMkA-y{EgqWcZ|*0+U4_X z_zaE9uf~@Q@_71FcC-`Mh0pxY_3|9b_jc=9)fHGM>?Uqwy9!*bqv#~QIorL9)Li%W zcJNi+5<}B)deG-Hme|W-bfD67qL*7m(9ql%+6NEZjLhdQ}={!+vUy6Gf0% z1N9ZgN|SOH=eoWQ39wRHG|14|N4Ib9)t?%9l0UiE-_ZW)|5@UySw+Tthmv$(zShbW?*VC+o8Pec-Zy9 z1mg-%_Y&^G4?6^GD-R_d!*2uj3yWhbLNn7nYqCOLJA^&&u;woqs0{{msCDvOqG-3> zGP!&FbnUc^)coGGTvoTW5`0D%AmAOHafKmY;| zfB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tZ)N3;e2}uxYIr;JX!e@`DYZ1_2-d z0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00I!G)6!5}|1p4=b1W7d z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2yA!(rKq4i>$LzQ z(r(2Kp9TRS009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|I5b@= zvw5``;K~CjTcA>~UKW4@1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa z0SG|g(-WY}*qoNxq$R7m8cmUq{$-+bPT@`uimc39R5?Y0`u`(8zPi>u?-Syy=i^Ty p+shMVcb;*igobY+$jHsDj+a}T!{)U7XKUkRRyV8FbK&|-{{!lHn{EIA literal 0 HcmV?d00001 diff --git a/src/lab6/fs/Makefile b/src/lab6/fs/Makefile index aad01b5..56ba896 100644 --- a/src/lab6/fs/Makefile +++ b/src/lab6/fs/Makefile @@ -1,11 +1,9 @@ -# ASM_SRC = $(filter-out ramdisk.S, $(sort $(wildcard *.S))) C_SRC = $(sort $(wildcard *.c)) -OBJ = $(patsubst %.S,%.o,$(ASM_SRC)) $(patsubst %.c,%.o,$(C_SRC)) +OBJ = $(patsubst %.c,%.o,$(C_SRC)) all:$(OBJ) %.o:%.c ${GCC} ${CFLAG} -c $< - clean: - $(shell rm *.o 2>/dev/null) \ No newline at end of file + $(shell rm *.o 2>/dev/null) diff --git a/src/lab6/fs/fat32.c b/src/lab6/fs/fat32.c index 4279e44..87e48c6 100644 --- a/src/lab6/fs/fat32.c +++ b/src/lab6/fs/fat32.c @@ -1,12 +1,11 @@ -#include -#include -#include -#include -#include -#include +#include "fat32.h" +#include "printk.h" +#include "virtio.h" +#include "string.h" +#include "mbr.h" +#include "mm.h" struct fat32_bpb fat32_header; - struct fat32_volume fat32_volume; uint8_t fat32_buf[VIRTIO_BLK_SECTOR_SIZE]; @@ -30,9 +29,6 @@ void fat32_init(uint64_t lba, uint64_t size) { fat32_volume.sec_per_cluster = 0/* to calculate */; fat32_volume.first_data_sec = 0/* to calculate */; fat32_volume.fat_sz = 0/* to calculate */; - - virtio_blk_read_sector(fat32_volume.first_data_sec, fat32_buf); // Get the root directory - struct fat32_dir_entry *dir_entry = (struct fat32_dir_entry *)fat32_buf; } int is_fat32(uint64_t lba) { @@ -43,7 +39,7 @@ int is_fat32(uint64_t lba) { return 1; } -int next_slash(const char* path) { +int next_slash(const char* path) { // util function to be used in fat32_open_file int i = 0; while (path[i] != '\0' && path[i] != '/') { i++; @@ -54,7 +50,7 @@ int next_slash(const char* path) { return i; } -void to_upper_case(char *str) { +void to_upper_case(char *str) { // util function to be used in fat32_open_file for (int i = 0; str[i] != '\0'; i++) { if (str[i] >= 'a' && str[i] <= 'z') { str[i] -= 32; @@ -87,60 +83,12 @@ uint64_t fat32_table_sector_of_cluster(uint32_t cluster) { return fat32_volume.first_fat_sec + cluster / (VIRTIO_BLK_SECTOR_SIZE / sizeof(uint32_t)); } -int64_t fat32_extend_filesz(struct file* file, uint64_t new_size) { - uint64_t sector = cluster_to_sector(file->fat32_file.dir.cluster) + file->fat32_file.dir.index / FAT32_ENTRY_PER_SECTOR; - - virtio_blk_read_sector(sector, fat32_table_buf); - uint32_t index = file->fat32_file.dir.index % FAT32_ENTRY_PER_SECTOR; - uint32_t original_file_len = ((struct fat32_dir_entry *)fat32_table_buf)[index].size; - ((struct fat32_dir_entry *)fat32_table_buf)[index].size = new_size; - - virtio_blk_write_sector(sector, fat32_table_buf); - - uint32_t clusters_required = new_size / (fat32_volume.sec_per_cluster * VIRTIO_BLK_SECTOR_SIZE); - uint32_t clusters_original = original_file_len / (fat32_volume.sec_per_cluster * VIRTIO_BLK_SECTOR_SIZE); - uint32_t new_clusters = clusters_required - clusters_original; - - uint32_t cluster = file->fat32_file.cluster; - while (1) { - uint32_t next_cluster_number = next_cluster(cluster); - if (next_cluster_number >= 0x0ffffff8) { - break; - } - cluster = next_cluster_number; - } - - for (int i = 0; i < new_clusters; i++) { - uint32_t cluster_to_append; - for (int j = 2; j < fat32_volume.fat_sz * VIRTIO_BLK_SECTOR_SIZE / sizeof(uint32_t); j++) { - if (next_cluster(j) == 0) { - cluster_to_append = j; - break; - } - } - uint64_t fat_sector = fat32_table_sector_of_cluster(cluster); - virtio_blk_read_sector(fat_sector, fat32_table_buf); - uint32_t index_in_sector = cluster * 4 % VIRTIO_BLK_SECTOR_SIZE; - *(uint32_t*)(fat32_table_buf + index_in_sector) = cluster_to_append; - virtio_blk_write_sector(fat_sector, fat32_table_buf); - cluster = cluster_to_append; - } - - uint64_t fat_sector = fat32_table_sector_of_cluster(cluster); - virtio_blk_read_sector(fat_sector, fat32_table_buf); - uint32_t index_in_sector = cluster * 4 % VIRTIO_BLK_SECTOR_SIZE; - *(uint32_t*)(fat32_table_buf + index_in_sector) = 0x0fffffff; - virtio_blk_write_sector(fat_sector, fat32_table_buf); - - return 0; -} - int64_t fat32_read(struct file* file, void* buf, uint64_t len) { - return 0; /* todo: read content to buf, and return read length */ + return 0; } int64_t fat32_write(struct file* file, const void* buf, uint64_t len) { - return 0; /* todo: fat32_write */ + return 0; } \ No newline at end of file diff --git a/src/lab6/fs/fs.S b/src/lab6/fs/fs.S deleted file mode 100644 index 7cf8436..0000000 --- a/src/lab6/fs/fs.S +++ /dev/null @@ -1,7 +0,0 @@ -.section .ext4fs - -.incbin "../fs.ext4" - -.section .fat32fs - -.incbin "../fs.fat32" \ No newline at end of file diff --git a/src/lab6/fs/fs.c b/src/lab6/fs/fs.c new file mode 100644 index 0000000..e243e41 --- /dev/null +++ b/src/lab6/fs/fs.c @@ -0,0 +1,46 @@ +#include "fs.h" +#include "vfs.h" +#include "mm.h" +#include "string.h" +#include "printk.h" +#include "fat32.h" + +struct files_struct *file_init() { + // todo: alloc pages for files_struct, and initialize stdin, stdout, stderr + struct files_struct *ret = NULL; + return ret; +} + +uint32_t get_fs_type(const char *filename) { + uint32_t ret; + if (memcmp(filename, "/fat32/", 7) == 0) { + ret = FS_TYPE_FAT32; + } else if (memcmp(filename, "/ext2/", 6) == 0) { + ret = FS_TYPE_EXT2; + } else { + ret = -1; + } + return ret; +} + +int32_t file_open(struct file* file, const char* path, int flags) { + file->opened = 1; + file->perms = flags; + file->cfo = 0; + file->fs_type = get_fs_type(path); + memcpy(file->path, path, strlen(path) + 1); + + if (file->fs_type == FS_TYPE_FAT32) { + file->lseek = fat32_lseek; + file->write = fat32_write; + file->read = fat32_read; + file->fat32_file = fat32_open_file(path); + // todo: check if fat32_file is valid (i.e. successfully opened) and return + } else if (file->fs_type == FS_TYPE_EXT2) { + printk(RED "Unsupport ext2\n" CLEAR); + return -1; + } else { + printk(RED "Unknown fs type: %s\n" CLEAR, path); + return -1; + } +} \ No newline at end of file diff --git a/src/lab6/fs/mbr.c b/src/lab6/fs/mbr.c index 2c9d917..62ac7d1 100644 --- a/src/lab6/fs/mbr.c +++ b/src/lab6/fs/mbr.c @@ -1,9 +1,8 @@ -#include -#include -#include +#include "mbr.h" +#include "virtio.h" +#include "fat32.h" uint8_t mbr_buf[VIRTIO_BLK_SECTOR_SIZE]; - struct partition_info partitions[MBR_MAX_PARTITIONS]; void mbr_init() { @@ -12,8 +11,6 @@ void mbr_init() { for (int i = 0; i < 4; i++) { if (mbr->partition_table[i].type == 0x83) { uint32_t lba = mbr->partition_table[i].lba_first_sector; - // virtio_blk_read_sector(lba, mbr_buf); - // printk("[S] Detect partition%d offset: %d, len:%d, type:%x\n", i+1, lba, mbr->partition_table[i].sector_count, mbr->partition_table[i].type); partition_init(i + 1, lba, mbr->partition_table[i].sector_count); } } @@ -22,6 +19,6 @@ void mbr_init() { void partition_init(int partion_number, uint64_t start_lba, uint64_t sector_count) { if (is_fat32(start_lba)) { fat32_init(start_lba, sector_count); - printk("[S] fat32 partition init done!\n", partion_number); + printk("...fat32 partition #%d init done!\n", partion_number); } -} \ No newline at end of file +} diff --git a/src/lab6/fs/vfs.c b/src/lab6/fs/vfs.c index 05b2ffd..d045232 100644 --- a/src/lab6/fs/vfs.c +++ b/src/lab6/fs/vfs.c @@ -1,99 +1,33 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -struct file* file_init() { - struct file *ret = (struct file*)alloc_page(); - - // stdin - ret[0].opened = 1; - // ... - - // stdout - ret[1].opened = 1; - ret[1].perms = FILE_WRITABLE; - ret[1].cfo = 0; - ret[1].lseek = NULL; - ret[1].write = NULL/* todo */; - ret[1].read = NULL; - memcpy(ret[1].path, "stdout", 7); - - // stderr - ret[2].opened = 1; - // ... - - return ret; -} +#include "fs.h" +#include "vfs.h" +#include "sbi.h" +#include "defs.h" +#include "printk.h" char uart_getchar() { char ret; while (1) { - struct sbiret sbi_result = sbi_ecall(SBI_GETCHAR, 0, 0, 0, 0, 0, 0, 0); - if (sbi_result.error != -1) { - ret = sbi_result.error; + struct sbiret sbi_result = sbi_debug_console_read(1, ((uint64_t)&ret - PA2VA_OFFSET), 0); + if (sbi_result.error == 0 && sbi_result.value == 1) { break; } } return ret; } -int64_t stdin_read(struct file* file, void* buf, uint64_t len) { - /* todo: use uart_getchar() to get chars */ +int64_t stdin_read(struct file *file, void *buf, uint64_t len) { + // todo: use uart_getchar() to get `len` chars } -int64_t stdout_write(struct file* file, const void* buf, uint64_t len) { +int64_t stdout_write(struct file *file, const void *buf, uint64_t len) { char to_print[len + 1]; for (int i = 0; i < len; i++) { - to_print[i] = ((const char*)buf)[i]; + to_print[i] = ((const char *)buf)[i]; } to_print[len] = 0; return printk(buf); } -int64_t stderr_write(struct file* file, const void* buf, uint64_t len) { - /* todo */ +int64_t stdout_write(struct file *file, const void *buf, uint64_t len) { + // todo } - -uint32_t get_fs_type(const char* filename) { - uint32_t ret; - if (memcmp(filename, "/fat32/", 7) == 0) { - ret = FS_TYPE_FAT32; - } else if (memcmp(filename, "/ext2/", 6) == 0) { - ret = FS_TYPE_EXT2; - } else { - printk("Unknown fs type: %s\n", filename); - while (1); - } - return ret; -} - -void file_open(struct file* file, const char* path, int flags) { - file->opened = 1; - file->perms = flags; - file->cfo = 0; - file->fs_type = get_fs_type(path); - memcpy(file->path, path, strlen(path) + 1); - - if (file->fs_type == FS_TYPE_FAT32) { - file->lseek = fat32_lseek; - file->write = fat32_write; - file->read = fat32_read; - file->fat32_file = fat32_open_file(path); - } else if (file->fs_type == FS_TYPE_EXT2) { - // file->lseek = ext2_lseek; - printk("Unsupport ext2\n"); - while (1); - // file->write = ext2_write; - // file->read = ext2_read; - } else { - printk("Unknown fs type: %s\n", path); - while (1); - } - // file->cfo = fat32_open(filename, flags); - // memcpy(file->path, filename); -} \ No newline at end of file diff --git a/src/lab6/fs/virtio.c b/src/lab6/fs/virtio.c index 6e97701..44f1b29 100644 --- a/src/lab6/fs/virtio.c +++ b/src/lab6/fs/virtio.c @@ -1,7 +1,8 @@ -#include -#include -#include -#include +#include "virtio.h" +#include "mm.h" +#include "clock.h" + +#define virt_to_phys(va) ((uint64_t)(va) - PA2VA_OFFSET) volatile struct virtio_regs * virtio_blk_regs = NULL; struct vring virtio_blk_ring; @@ -12,15 +13,6 @@ void virtio_blk_driver_init() { virtio_blk_regs->Status |= DEVICE_ACKNOWLEDGE; virtio_blk_regs->Status |= DEVICE_DRIVER; memory_barrier(); - - // printk("[S] virtio_blk_regs->Status: %08x\n", virtio_blk_regs->Status); - - memory_barrier(); - - // printk("[S] virtio_blk_regs->Status: %08x\n", virtio_blk_regs->Status); - - // printk("[S] virtio_blk_regs->QueueNumMax: %08x\n", virtio_blk_regs->QueueNumMax); - memory_barrier(); } void virtio_blk_feature_init() { @@ -30,13 +22,8 @@ void virtio_blk_feature_init() { virtio_blk_regs->DriverFeatures = 0x30000200; virtio_blk_regs->DriverFeaturesSel = 1; virtio_blk_regs->DriverFeatures = 0x0; - - virtio_blk_regs->Status |= DEVICE_FEATURES_OK; memory_barrier(); - // printk("[S] virtio_blk_regs->DeviceFeatures: %08x\n", virtio_blk_regs->DeviceFeatures); - - // printk("[S] virtio_blk_regs->DriverFeatures: %08x\n", virtio_blk_regs->DriverFeatures); } void virtio_blk_queue_init() { @@ -50,7 +37,6 @@ void virtio_blk_queue_init() { virtio_blk_ring.desc = (struct virtio_desc*)(pages); virtio_blk_ring.avail = (struct virtio_avail*)(pages + PGSIZE); virtio_blk_ring.used = (struct virtio_used*)(pages + 2*PGSIZE); - virtio_blk_ring.avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT; for (int i = 1; i < VIRTIO_QUEUE_SIZE; i++) { @@ -67,8 +53,6 @@ void virtio_blk_queue_init() { virtio_blk_regs->QueueUsedHigh = virt_to_phys((uint64_t)virtio_blk_ring.used) >> 32; memory_barrier(); - // virtio_blk_regs->Queue - virtio_blk_regs->QueueReady = 1; memory_barrier(); } @@ -76,21 +60,12 @@ void virtio_blk_queue_init() { void virtio_blk_config_init() { volatile struct virtio_blk_config *config = (struct virtio_blk_config*)(&virtio_blk_regs->Config); uint64_t capacity = ((uint64_t)config->capacity_hi << 32) | config->capacity_lo; - // printk("[S] virtio-blk Disk Capacity: %016llx\n", capacity); } -// char virtio_blk_buf[VIRTIO_BLK_SECTOR_SIZE]; char virtio_blk_status; struct virtio_blk_req virtio_blk_req; void virtio_blk_cmd(uint32_t type, uint32_t sector, void* buf) { - - // printk("avail->idx: %d, used->idx: %d\n", virtio_blk_ring.avail->idx, virtio_blk_ring.used->idx); - - // for (int i = 1; i < VIRTIO_QUEUE_SIZE; i++) { - // virtio_blk_ring.desc[i - 1].next = i; - // } - virtio_blk_req.type = type; virtio_blk_req.sector = sector; @@ -127,38 +102,21 @@ void virtio_blk_read_sector(uint64_t sector, void *buf) { while (1) { if (virtio_blk_ring.used->idx != original_idx) { break; - } else { - // printk("[S] wait for disk read to finish...\n"); } } - // printk("[S] virtio_blk_ring.used len: %d\n", virtio_blk_ring.desc[virtio_blk_ring.used->ring[virtio_blk_ring.used->idx].id].len); - uint64_t used_index = (virtio_blk_ring.used->idx - 1) % VIRTIO_QUEUE_SIZE; - uint64_t used_len = virtio_blk_ring.used->ring[used_index].len; - // printk("[S] used_len: %d\n", used_len); - uint64_t descriptor_id = virtio_blk_ring.used->ring[used_index].id; - uint64_t descriptor_len = virtio_blk_ring.desc[descriptor_id].len; } void virtio_blk_write_sector(uint64_t sector, const void *buf) { uint64_t original_idx = virtio_blk_ring.used->idx; - // virtio_blk_cmd(VIRTIO_BLK_T_IN | VIRTIO_BLK_T_OUT, sector, (void*)buf); virtio_blk_cmd(VIRTIO_BLK_T_OUT, sector, (void*)buf); while (1) { if (virtio_blk_ring.used->idx != original_idx) { break; - } else { - // printk("[S] wait for disk write to finish...\n"); } } - uint64_t used_index = (virtio_blk_ring.used->idx - 1) % VIRTIO_QUEUE_SIZE; - uint64_t used_len = virtio_blk_ring.used->ring[used_index].len; - // printk("[S] used_len: %d\n", used_len); - uint64_t descriptor_id = virtio_blk_ring.used->ring[used_index].id; - uint64_t descriptor_len = virtio_blk_ring.desc[descriptor_id].len; } void virtio_blk_init() { - // First, tell the device that the driver is ok virtio_blk_driver_init(); // Second, initialize the features @@ -168,11 +126,9 @@ void virtio_blk_init() { // Now, start initialize the vring virtio_blk_queue_init(); - // virtio_blk_cmd(VIRTIO_BLK_T_IN, 1); char buf[VIRTIO_BLK_SECTOR_SIZE]; virtio_blk_read_sector(0, buf); - // dump_memory((uint64_t)buf, 512); // test if the disk is added properly char boot_signature[2]; @@ -180,13 +136,10 @@ void virtio_blk_init() { boot_signature[1] = buf[511]; if (boot_signature[0] != 0x55 || boot_signature[1] != 0xaa) { - printk("[S] mbr boot signature not found!\n"); - while (1); - } else { - // printk("[S] mbr boot signature found!\n"); + Err("[S] mbr boot signature not found!"); } - printk("[S] virtio_blk_init done!\n"); + printk("...virtio_blk_init done!\n"); } int virtio_dev_test(uint64_t virtio_addr) { @@ -194,7 +147,6 @@ int virtio_dev_test(uint64_t virtio_addr) { struct virtio_regs *virtio_header = virtio_space; if (in32(&virtio_header->DeviceID) == ID_VIRTIO_BLK) { - // printk("[S] virtio_blk found!\n"); virtio_blk_regs = virtio_space; } diff --git a/src/lab6/include/debug.h b/src/lab6/include/debug.h deleted file mode 100644 index 9c6226b..0000000 --- a/src/lab6/include/debug.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _DEBUG_H -#define _DEBUG_H - -#include -#include - -static inline void dump_page(uint64_t va) { - for (int i = 0; i < 0x1000; i++) { - if (i % 16 == 0) { - printk("%04x: ", i); - } - printk("%02x", ((char*)va)[i]); - if ((i + 1) % 16 == 0) { - printk("\n"); - } else if ((i + 1) % 4 == 0) { - printk(" "); - } - } -} - -static inline void dump_memory(uint64_t va, uint64_t size) { - printk("----------------------------------------------------\n"); - for (int i = 0; i < size / 4; i += 1) { - if (i % 16 == 0) { - printk("%04x: ", i * 4); - } - printk("%08x", ((uint32_t*)va)[i]); - if ((i + 1) % 16 == 0) { - printk("\n"); - } else if ((i + 1) % 4 == 0) { - printk(" "); - } else { - printk("|"); - } - } -} - -static inline int isalnum(char c) { - return (c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z'); -} - -static inline void dump_char(uint64_t va, uint64_t size) { - for (int i = 0; i < size; i += 1) { - if (i % 32 == 0) { - printk("%04x: ", i * 4); - } - - if (isalnum(((uint8_t*)va)[i])) - printk("%c", ((uint8_t*)va)[i]); - else - printk("."); - - if ((i + 1) % 32 == 0) { - printk("\n"); - } else if ((i + 1) % 8 == 0) { - printk(" "); - // } else { - // printk("|"); - } - } -} - -#endif \ No newline at end of file diff --git a/src/lab6/include/fat32.h b/src/lab6/include/fat32.h index a88af1e..172ef65 100644 --- a/src/lab6/include/fat32.h +++ b/src/lab6/include/fat32.h @@ -1,10 +1,8 @@ -#ifndef _FAT32_H -#define _FAT32_H - -#include -// #include -#include +#ifndef __FAT32_H__ +#define __FAT32_H__ +#include "defs.h" +#include "fs.h" struct __attribute__((packed)) fat32_bpb { uint8_t jmp_boot[3]; @@ -62,13 +60,8 @@ struct fat32_dir_entry { }; void dump_fat32_bpb(struct fat32_bpb *bpb); - void fat32_init(uint64_t lba, uint64_t size); - int is_fat32(uint64_t lba); - -// void fat32_open_dir(struct fat32_dir* dir, const char* path); - struct fat32_file fat32_open_file(const char *path); int64_t fat32_lseek(struct file* file, int64_t offset, uint64_t whence); diff --git a/src/lab6/include/fs.h b/src/lab6/include/fs.h index 1cbfbcb..05f172a 100644 --- a/src/lab6/include/fs.h +++ b/src/lab6/include/fs.h @@ -1,11 +1,10 @@ -#ifndef _FS_H -#define _FS_H +#ifndef __FS_H__ +#define __FS_H__ -#include -// #include +#include "defs.h" #define MAX_PATH_LENGTH 80 -#define MAX_FILE_NUMBER (PGSIZE / sizeof(file_open)) +#define MAX_FILE_NUMBER 16 #define SEEK_SET 0 #define SEEK_CUR 1 @@ -20,9 +19,8 @@ #define FS_TYPE_EXT2 0x2 struct fat32_dir { - uint32_t cluster; - // entry index in the cluster - uint32_t index; + uint32_t cluster; + uint32_t index; // entry index in the cluster }; struct fat32_file { @@ -30,8 +28,7 @@ struct fat32_file { struct fat32_dir dir; }; -// Opened file in a thread. -struct file { +struct file { // Opened file in a thread. uint32_t opened; uint32_t perms; int64_t cfo; @@ -41,18 +38,19 @@ struct file { struct fat32_file fat32_file; }; - int64_t (*lseek) (struct file* file, int64_t offset, uint64_t whence); - int64_t (*write) (struct file* file, const void* buf, uint64_t len); - int64_t (*read) (struct file* file, void* buf, uint64_t len); + int64_t (*lseek) (struct file *file, int64_t offset, uint64_t whence); + int64_t (*write) (struct file *file, const void *buf, uint64_t len); + int64_t (*read) (struct file *file, void *buf, uint64_t len); char path[MAX_PATH_LENGTH]; }; -// Return a file array, that can contain MAX_FILE_NUMBER opened files. -struct file* file_init(); - -void file_open(struct file* file, const char* path, int flags); +struct files_struct { + struct file fd_array[MAX_FILE_NUMBER]; +}; -struct volumn {}; +struct files_struct *file_init(); +int32_t file_open(struct file *file, const char *path, int flags); +uint32_t get_fs_type(const char *filename); #endif \ No newline at end of file diff --git a/src/lab6/include/mbr.h b/src/lab6/include/mbr.h index 3d76908..1f9c089 100644 --- a/src/lab6/include/mbr.h +++ b/src/lab6/include/mbr.h @@ -1,7 +1,7 @@ -#ifndef _MBR_H -#define _MBR_H +#ifndef __MBR_H__ +#define __MBR_H__ -#include +#include "defs.h" #define FAT_ENTRY_SIZE 32 #define FAT_PARTITION_NUMBER 1 @@ -75,10 +75,10 @@ struct __attribute__((packed)) ext2_super_block { }; struct partition_info { - uint32_t lba; - uint32_t size; - uint8_t status; - uint8_t type; + uint32_t lba; + uint32_t size; + uint8_t status; + uint8_t type; }; #define MBR_MAX_PARTITIONS 4 @@ -87,6 +87,4 @@ void mbr_init(); void partition_init(int partion_number, uint64_t start_lba, uint64_t sector_count); -// void bpb_print(struct bpb_layout *bpb); - #endif \ No newline at end of file diff --git a/src/lab6/include/string.h b/src/lab6/include/string.h deleted file mode 100644 index c133d1c..0000000 --- a/src/lab6/include/string.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "types.h" - -void* memset(void *, int, uint64_t); -void *memcpy(void *str1, const void *str2, size_t n); -int memcmp(const void *cs, const void *ct, size_t count); - -static inline int strlen(const char *str) -{ - int len = 0; - while (*str++) - len++; - return len; -} \ No newline at end of file diff --git a/src/lab6/include/vfs.h b/src/lab6/include/vfs.h index 4955f69..2a1c11b 100644 --- a/src/lab6/include/vfs.h +++ b/src/lab6/include/vfs.h @@ -1,9 +1,8 @@ -#ifndef _VFS_H -#define _VFS_H +#ifndef __VFS_H__ +#define __VFS_H__ -int64_t stdout_write(struct file* file, const void* buf, uint64_t len); -int64_t stderr_write(struct file* file, const void* buf, uint64_t len); -int64_t stdin_read(struct file* file, void* buf, uint64_t len); -uint32_t get_fs_type(const char* filename); +int64_t stdout_write(struct file *file, const void *buf, uint64_t len); +int64_t stderr_write(struct file *file, const void *buf, uint64_t len); +int64_t stdin_read(struct file *file, void *buf, uint64_t len); #endif \ No newline at end of file diff --git a/src/lab6/include/virtio.h b/src/lab6/include/virtio.h index 5b90a19..9522481 100644 --- a/src/lab6/include/virtio.h +++ b/src/lab6/include/virtio.h @@ -1,9 +1,8 @@ -#ifndef _VIRTIO_H -#define _VIRTIO_H +#ifndef __VIRTIO_H__ +#define __VIRTIO_H__ -#include -#include -#include +#include "defs.h" +#include "printk.h" #define DEVICE_ACKNOWLEDGE 1 #define DEVICE_DRIVER 2 @@ -50,110 +49,110 @@ #define IOMAP_OFFSET (0xffffffc800000000) struct virtio_regs { - uint32_t MagicValue; - uint32_t Version; - uint32_t DeviceID; - uint32_t VendorID; - uint32_t DeviceFeatures; - uint32_t DeviceFeaturesSel; - uint32_t _reserved0[2]; - uint32_t DriverFeatures; - uint32_t DriverFeaturesSel; - uint32_t _reserved1[2]; - uint32_t QueueSel; - uint32_t QueueNumMax; - uint32_t QueueNum; - uint32_t _reserved2[2]; - uint32_t QueueReady; - uint32_t _reserved3[2]; - uint32_t QueueNotify; - uint32_t _reserved4[3]; - uint32_t InterruptStatus; - uint32_t InterruptACK; - uint32_t _reserved5[2]; - uint32_t Status; - uint32_t _reserved6[3]; - uint32_t QueueDescLow; - uint32_t QueueDescHigh; - uint32_t _reserved7[2]; - uint32_t QueueAvailLow; - uint32_t QueueAvailHigh; - uint32_t _reserved8[2]; - uint32_t QueueUsedLow; - uint32_t QueueUsedHigh; - uint32_t _reserved9[21]; - uint32_t ConfigGeneration; - uint32_t Config[0]; + uint32_t MagicValue; + uint32_t Version; + uint32_t DeviceID; + uint32_t VendorID; + uint32_t DeviceFeatures; + uint32_t DeviceFeaturesSel; + uint32_t _reserved0[2]; + uint32_t DriverFeatures; + uint32_t DriverFeaturesSel; + uint32_t _reserved1[2]; + uint32_t QueueSel; + uint32_t QueueNumMax; + uint32_t QueueNum; + uint32_t _reserved2[2]; + uint32_t QueueReady; + uint32_t _reserved3[2]; + uint32_t QueueNotify; + uint32_t _reserved4[3]; + uint32_t InterruptStatus; + uint32_t InterruptACK; + uint32_t _reserved5[2]; + uint32_t Status; + uint32_t _reserved6[3]; + uint32_t QueueDescLow; + uint32_t QueueDescHigh; + uint32_t _reserved7[2]; + uint32_t QueueAvailLow; + uint32_t QueueAvailHigh; + uint32_t _reserved8[2]; + uint32_t QueueUsedLow; + uint32_t QueueUsedHigh; + uint32_t _reserved9[21]; + uint32_t ConfigGeneration; + uint32_t Config[0]; }; struct virtio_desc { - uint64_t addr; - uint32_t len; - uint16_t flags; - uint16_t next; + uint64_t addr; + uint32_t len; + uint16_t flags; + uint16_t next; }; struct virtio_avail { - uint16_t flags; - uint16_t idx; - uint16_t ring[VIRTIO_QUEUE_SIZE]; - uint16_t used_event; + uint16_t flags; + uint16_t idx; + uint16_t ring[VIRTIO_QUEUE_SIZE]; + uint16_t used_event; }; struct virtio_used_elem { - uint32_t id; - uint32_t len; + uint32_t id; + uint32_t len; }; struct virtio_used { - uint16_t flags; - uint16_t idx; - struct virtio_used_elem ring[VIRTIO_QUEUE_SIZE]; - uint16_t avail_event; + uint16_t flags; + uint16_t idx; + struct virtio_used_elem ring[VIRTIO_QUEUE_SIZE]; + uint16_t avail_event; }; struct vring { - uint16_t num; - struct virtio_desc *desc; - struct virtio_avail *avail; - struct virtio_used *used; + uint16_t num; + struct virtio_desc *desc; + struct virtio_avail *avail; + struct virtio_used *used; }; struct virtio_blk_req { - uint32_t type; - uint32_t reserved; - uint64_t sector; - // uint8_t status; + uint32_t type; + uint32_t reserved; + uint64_t sector; + // uint8_t status; }; struct virtio_blk_config { - uint32_t capacity_lo; - uint32_t capacity_hi; - uint32_t size_max; - uint32_t seg_max; - uint16_t geometry[4]; - uint32_t blk_size; - uint8_t physical_block_exp; - uint8_t alignment_offset; - uint16_t min_io_size; - uint32_t opt_io_size; - uint8_t wce; - uint8_t unused; - uint16_t num_queues; - uint32_t reserved; + uint32_t capacity_lo; + uint32_t capacity_hi; + uint32_t size_max; + uint32_t seg_max; + uint16_t geometry[4]; + uint32_t blk_size; + uint8_t physical_block_exp; + uint8_t alignment_offset; + uint16_t min_io_size; + uint32_t opt_io_size; + uint8_t wce; + uint8_t unused; + uint16_t num_queues; + uint32_t reserved; }; enum blk_request_type { - VIRTIO_BLK_T_IN = 0, - VIRTIO_BLK_T_OUT = 1, - /* This bit says it's a scsi command, not an actual read or write. */ - VIRTIO_BLK_T_SCSI_CMD = 2, - /* Cache flush command */ - VIRTIO_BLK_T_FLUSH = 4, - /* Get device ID command */ - VIRTIO_BLK_T_GET_ID = 8, - /* Barrier before this op. */ - VIRTIO_BLK_T_BARRIER = 0x80000000, + VIRTIO_BLK_T_IN = 0, + VIRTIO_BLK_T_OUT = 1, + /* This bit says it's a scsi command, not an actual read or write. */ + VIRTIO_BLK_T_SCSI_CMD = 2, + /* Cache flush command */ + VIRTIO_BLK_T_FLUSH = 4, + /* Get device ID command */ + VIRTIO_BLK_T_GET_ID = 8, + /* Barrier before this op. */ + VIRTIO_BLK_T_BARRIER = 0x80000000, }; @@ -177,16 +176,12 @@ static inline void out32(uint32_t *memory_address, uint32_t data) { int virtio_dev_test(uint64_t virtio_addr); void virtio_dev_init(); - - void vring_init(struct vring *vr, uint32_t num, void *p, uint64_t align); - -void virtio_blk_read_sector(uint64_t sector, void* buf); - +void virtio_blk_read_sector(uint64_t sector, void *buf); void virtio_blk_write_sector(uint64_t sector, const void *buf); static inline void memory_barrier() { - asm volatile ("fence rw, rw"); // Full memory fence for both read and write + asm volatile ("fence rw, rw"); // Full memory fence for both read and write } static inline uint64_t io_to_virt(uint64_t pa) { diff --git a/src/lab6/lib/string.c b/src/lab6/lib/string.c deleted file mode 100644 index ac7e225..0000000 --- a/src/lab6/lib/string.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include -#include - -void *memset(void *dst, int c, uint64_t n) { - char *cdst = (char *)dst; - for (uint64_t i = 0; i < n; ++i) - cdst[i] = c; - - return dst; -} - -void *memcpy(void *str1, const void *str2, size_t n) { - for (int i = 0; i < n; i++) { - ((char*)str1)[i] = ((const char*)str2)[i]; - } - return str1; -} - -int memcmp(const void *cs, const void *ct, size_t count) -{ - const unsigned char *su1, *su2; - int res = 0; - for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) - if ((res = *su1 - *su2) != 0) - break; - return res; -} \ No newline at end of file diff --git a/src/lab6/user/.gitignore b/src/lab6/user/.gitignore deleted file mode 100644 index 5f3cd34..0000000 --- a/src/lab6/user/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -uapp -uapp.bin -uapp.elf -uapp.mid -ramdisk \ No newline at end of file diff --git a/src/lab6/user/Makefile b/src/lab6/user/Makefile index 65098cc..00506a6 100644 --- a/src/lab6/user/Makefile +++ b/src/lab6/user/Makefile @@ -1,27 +1,28 @@ -ASM_SRC = $(filter-out ramdisk.S, $(sort $(wildcard *.S))) -C_SRC = $(sort $(wildcard *.c)) -OBJ = $(patsubst %.S,%.o,$(ASM_SRC)) $(patsubst %.c,%.o,$(C_SRC)) +ASM_SRC = $(filter-out uapp.S, $(sort $(wildcard *.S))) +C_SRC = $(sort $(wildcard *.c)) +OBJ = $(patsubst %.S,%.o,$(ASM_SRC)) $(patsubst %.c,%.o,$(C_SRC)) -CFLAG = -march=$(ISA) -mabi=$(ABI) -mcmodel=medany -fno-builtin -ffunction-sections -fdata-sections -nostartfiles -nostdlib -nostdinc -static -lgcc -Wl,--nmagic,--build-id=none -O0 +CFLAG = -march=$(ISA) -mabi=$(ABI) -mcmodel=medany -fno-builtin -ffunction-sections -fdata-sections -nostartfiles -nostdlib -nostdinc -static -lgcc -Wl,--nmagic,--build-id=none -O0 -all: ramdisk +all: uapp.o -ramdisk: ramdisk.S uapp.bin uapp - ${GCC} ${CFLAG} -c ramdisk.S -o ramdisk +uapp.o: uapp.S uapp.bin uapp + ${GCC} ${CFLAG} -c uapp.S + ${OBJDUMP} -S uapp > uapp.asm + ${OBJDUMP} -S uapp.elf > uapp.elf.asm %.o:%.c ${GCC} ${CFLAG} -c $< %.o:%.S - ${GCC} ${CFLAG} -c $< + ${GCC} ${CFLAG} -c $< uapp.bin: $(OBJ) - ${GCC} ${CFLAG} -fpie -T link.lds -o uapp.mid ${OBJ} - ${OBJCOPY} uapp.mid -O binary uapp.bin - rm uapp.mid + ${GCC} ${CFLAG} -fpie -T link.lds -o uapp.elf ${OBJ} + ${OBJCOPY} uapp.elf -O binary uapp.bin + +clean: + $(shell rm uapp *.o uapp.o uapp.elf uapp.bin *.asm 2>/dev/null) uapp: $(OBJ) ${GCC} ${CFLAG} -o uapp ${OBJ} - -clean: - $(shell rm *.o ramdisk uapp.elf uapp.bin uapp.mid uapp 2>/dev/null) \ No newline at end of file diff --git a/src/lab6/user/forktest.c b/src/lab6/user/forktest.c deleted file mode 100644 index a76539d..0000000 --- a/src/lab6/user/forktest.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "syscall.h" -#include "stdio.h" - -static inline long getpid() { - long ret; - asm volatile ("li a7, %1\n" - "ecall\n" - "mv %0, a0\n" - : "+r" (ret) - : "i" (SYS_GETPID)); - return ret; -} - -static inline long fork() -{ - long ret; - asm volatile ("li a7, %1\n" - "ecall\n" - "mv %0, a0\n" - : "+r" (ret) : "i" (SYS_CLONE)); - return ret; -} - -int global_variable = 0; - -// int main() { -// int pid; - -// pid = fork(); - -// if (pid == 0) { -// while (1) { -// printf("[U-CHILD] pid: %ld is running!, global_variable: %d\n", getpid(), global_variable++); -// for (unsigned int i = 0; i < 0x7FFFFFF; i++); -// } -// } else { -// while (1) { -// printf("[U-PARENT] pid: %ld is running!, global_variable: %d\n", getpid(), global_variable++); -// for (unsigned int i = 0; i < 0x7FFFFFF; i++); -// } -// } -// return 0; -// } \ No newline at end of file diff --git a/src/lab6/user/shell.c b/src/lab6/user/main.c similarity index 69% rename from src/lab6/user/shell.c rename to src/lab6/user/main.c index ec025c3..bb83114 100644 --- a/src/lab6/user/shell.c +++ b/src/lab6/user/main.c @@ -4,10 +4,6 @@ #define CAT_BUF_SIZE 509 -#define REDIR_TYPE_NONE 0 -#define REDIR_TYPE_APPEND 1 -#define REDIR_TYPE_COVER 2 - char string_buf[2048]; char filename[2048]; @@ -20,7 +16,7 @@ int atoi(char* str) { return ret; } -char* get_param(char* cmd) { +char *get_param(char *cmd) { while (*cmd == ' ') { cmd++; } @@ -32,7 +28,7 @@ char* get_param(char* cmd) { return string_buf; } -char* get_string(char* cmd) { +char *get_string(char *cmd) { while (*cmd == ' ') { cmd++; } @@ -50,56 +46,25 @@ char* get_string(char* cmd) { } } -int get_redir_type_and_filename(char* cmd) { - int ret = REDIR_TYPE_NONE; - - while (*cmd == ' ' && *cmd != '\0') { - cmd++; - } - - if (*cmd == '>') { - cmd++; - if (*cmd == '>') { - return REDIR_TYPE_APPEND; - } else { - return REDIR_TYPE_COVER; - } - } - - while (*cmd == ' ' && *cmd != '\0') { - cmd++; - } - - int pos = 0; - while (*cmd != ' ' && *cmd != '\0') { - filename[pos++] = *(cmd++); - } - - return ret; -} - -void parse_cmd(char* cmd, int len) { +void parse_cmd(char *cmd, int len) { if (cmd[0] == 'e' && cmd[1] == 'c' && cmd[2] == 'h' && cmd[3] == 'o') { - // char* param = get_param(cmd + 4, len - 4); - // int len = strlen(param); - // char *param = cmd += 4; - char* echo_content = get_string(cmd); + char *echo_content = get_string(cmd); len = strlen(echo_content); - // write(1, echo_content, len); cmd += len; write(1, echo_content, len); - write(1, "\n\r", 2); + write(1, "\n", 1); } else if (cmd[0] == 'c' && cmd[1] == 'a' && cmd[2] == 't') { - char* filename = get_param(cmd + 3); + char *filename = get_param(cmd + 3); char last_char; int fd = open(filename, O_RDONLY); + if (fd == -1) { + printf("can't open file: %s\n", filename); + return; + } char cat_buf[CAT_BUF_SIZE]; - // printf("fd: %d\n", fd); - // printf("cat_buf: %016llx\n", cat_buf); while (1) { int num_chars = read(fd, cat_buf, CAT_BUF_SIZE); - // printf("***** num_chars: %d\n", num_chars); if (num_chars == 0) { if (last_char != '\n') { printf("$\n"); @@ -110,14 +75,12 @@ void parse_cmd(char* cmd, int len) { if (cat_buf[i] == 0) { write(1, "x", 1); } else { - write (1, &cat_buf[i], 1); + write(1, &cat_buf[i], 1); } - // printf("%c", cat_buf[i]); last_char = cat_buf[i]; } } close(fd); - // modify } else if (cmd[0] == 'e' && cmd[1] == 'd' && cmd[2] == 'i' && cmd[3] == 't' ) { cmd += 4; while (*cmd == ' ' && *cmd != '\0') { @@ -158,12 +121,6 @@ void parse_cmd(char* cmd, int len) { int offset_int = atoi(offset); - - printf("%s\n", filename); - printf("%d\n", offset_int); - printf("%s\n", content); - - // while (1); int fd = open(filename, O_RDWR); lseek(fd, offset_int, SEEK_SET); write(fd, content, len); @@ -176,14 +133,12 @@ void parse_cmd(char* cmd, int len) { int main() { write(1, "hello, stdout!\n", 15); write(2, "hello, stderr!\n", 15); - // printf("echoing\n"); char read_buf[2]; char line_buf[128]; - int char_in_line = 0; - write(1, "SHELL > ", 8); + int char_in_line = 0; + printf(YELLOW "SHELL > " CLEAR); while (1) { read(0, read_buf, 1); - // printf("%x\n", read_buf[0]); if (read_buf[0] == '\r') { write(1, "\n", 1); } else if (read_buf[0] == 0x7f) { @@ -195,13 +150,10 @@ int main() { } write(1, read_buf, 1); if (read_buf[0] == '\r') { - // write(1, line_buf, char_in_line); line_buf[char_in_line] = '\0'; parse_cmd(line_buf, char_in_line); char_in_line = 0; - // write(1, "\n", 1); - // write(1, "\r", 1); - write(1, "SHELL > ", 8); + printf(YELLOW "SHELL > " CLEAR); } else { line_buf[char_in_line++] = read_buf[0]; } diff --git a/src/lab6/user/printf.c b/src/lab6/user/printf.c index 31d52fa..9726694 100644 --- a/src/lab6/user/printf.c +++ b/src/lab6/user/printf.c @@ -1,112 +1,295 @@ +// credit: 45gfg9 <45gfg9@45gfg9.net> #include "stdio.h" #include "syscall.h" int tail = 0; char buffer[1000] = {[0 ... 999] = 0}; -void putc(char c) { +int putc(int c) { buffer[tail++] = (char)c; + return (char)c; } -static int vprintfmt(void(*putch)(char), int fd, const char *fmt, va_list vl) { - int in_format = 0, longarg = 0; - size_t pos = 0; - for( ; *fmt; fmt++) { - if (in_format) { - switch(*fmt) { - case 'l': { - longarg = 1; - break; +#ifndef __MAX +#define __MAX(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif + +struct fmt_flags { + bool in_format; + bool longflag; + bool sharpflag; + bool zeroflag; + bool spaceflag; + bool sign; + int width; + int prec; +}; + +int isspace(int c) { + return c == ' ' || (c >= '\t' && c <= '\r'); +} + +long strtol(const char *restrict nptr, char **restrict endptr, int base) { + long ret = 0; + bool neg = false; + const char *p = nptr; + + while (isspace(*p)) { + p++; + } + + if (*p == '-') { + neg = true; + p++; + } else if (*p == '+') { + p++; + } + + if (base == 0) { + if (*p == '0') { + p++; + if (*p == 'x' || *p == 'X') { + base = 16; + p++; + } else { + base = 8; + } + } else { + base = 10; + } + } + + while (1) { + int digit; + if (*p >= '0' && *p <= '9') { + digit = *p - '0'; + } else if (*p >= 'a' && *p <= 'z') { + digit = *p - ('a' - 10); + } else if (*p >= 'A' && *p <= 'Z') { + digit = *p - ('A' - 10); + } else { + break; + } + + if (digit >= base) { + break; + } + + ret = ret * base + digit; + p++; + } + + if (endptr) { + *endptr = (char *)p; + } + + return neg ? -ret : ret; +} + +// puts without newline +static int puts_wo_nl(int (*putch)(int), const char *s) { + if (!s) { + s = "(null)"; + } + const char *p = s; + while (*p) { + putch(*p++); + } + return p - s; +} + +static int print_dec_int(int (*putch)(int), unsigned long num, bool is_signed, struct fmt_flags *flags) { + if (is_signed && num == 0x8000000000000000UL) { + // special case for 0x8000000000000000 + return puts_wo_nl(putch, "-9223372036854775808"); + } + + if (flags->prec == 0 && num == 0) { + return 0; + } + + bool neg = false; + + if (is_signed && (long)num < 0) { + neg = true; + num = -num; + } + + char buf[20]; + int decdigits = 0; + + bool has_sign_char = is_signed && (neg || flags->sign || flags->spaceflag); + + do { + buf[decdigits++] = num % 10 + '0'; + num /= 10; + } while (num); + + if (flags->prec == -1 && flags->zeroflag) { + flags->prec = flags->width; + } + + int written = 0; + + for (int i = flags->width - __MAX(decdigits, flags->prec) - has_sign_char; i > 0; i--) { + putch(' '); + ++written; + } + + if (has_sign_char) { + putch(neg ? '-' : flags->sign ? '+' : ' '); + ++written; + } + + for (int i = decdigits; i < flags->prec - has_sign_char; i++) { + putch('0'); + ++written; + } + + for (int i = decdigits - 1; i >= 0; i--) { + putch(buf[i]); + ++written; + } + + return written; +} + +int vprintfmt(int (*putch)(int), const char *fmt, va_list vl) { + static const char lowerxdigits[] = "0123456789abcdef"; + static const char upperxdigits[] = "0123456789ABCDEF"; + + struct fmt_flags flags = {}; + + int written = 0; + + for (; *fmt; fmt++) { + if (flags.in_format) { + if (*fmt == '#') { + flags.sharpflag = true; + } else if (*fmt == '0') { + flags.zeroflag = true; + } else if (*fmt == 'l' || *fmt == 'z' || *fmt == 't' || *fmt == 'j') { + // l: long, z: size_t, t: ptrdiff_t, j: intmax_t + flags.longflag = true; + } else if (*fmt == '+') { + flags.sign = true; + } else if (*fmt == ' ') { + flags.spaceflag = true; + } else if (*fmt == '*') { + flags.width = va_arg(vl, int); + } else if (*fmt >= '1' && *fmt <= '9') { + flags.width = strtol(fmt, (char **)&fmt, 10); + fmt--; + } else if (*fmt == '.') { + fmt++; + if (*fmt == '*') { + flags.prec = va_arg(vl, int); + } else { + flags.prec = strtol(fmt, (char **)&fmt, 10); + fmt--; } - - case 'x': { - long num = longarg ? va_arg(vl, long) : va_arg(vl, int); - - int hexdigits = 2 * (longarg ? sizeof(long) : sizeof(int)) - 1; - for(int halfbyte = hexdigits; halfbyte >= 0; halfbyte--) { - int hex = (num >> (4*halfbyte)) & 0xF; - char hexchar = (hex < 10 ? '0' + hex : 'a' + hex - 10); - putch(hexchar); - pos++; - } - longarg = 0; in_format = 0; - break; + } else if (*fmt == 'x' || *fmt == 'X' || *fmt == 'p') { + bool is_long = *fmt == 'p' || flags.longflag; + + unsigned long num = is_long ? va_arg(vl, unsigned long) : va_arg(vl, unsigned int); + + if (flags.prec == 0 && num == 0 && *fmt != 'p') { + flags.in_format = false; + continue; } - - case 'd': { - long num = longarg ? va_arg(vl, long) : va_arg(vl, int); - if (num < 0) { - num = -num; putch('-'); - pos++; - } - int bits = 0; - char decchar[25] = {'0', 0}; - for (long tmp = num; tmp; bits++) { - decchar[bits] = (tmp % 10) + '0'; - tmp /= 10; - } - - if (bits == 0) bits ++; - - for (int i = bits - 1; i >= 0; i--) { - putch(decchar[i]); - } - pos += bits + 1; - longarg = 0; in_format = 0; - break; + + // 0x prefix for pointers, or, if # flag is set and non-zero + bool prefix = *fmt == 'p' || (flags.sharpflag && num != 0); + + int hexdigits = 0; + const char *xdigits = *fmt == 'X' ? upperxdigits : lowerxdigits; + char buf[2 * sizeof(unsigned long)]; + + do { + buf[hexdigits++] = xdigits[num & 0xf]; + num >>= 4; + } while (num); + + if (flags.prec == -1 && flags.zeroflag) { + flags.prec = flags.width - 2 * prefix; } - case 'u': { - unsigned long num = longarg ? va_arg(vl, long) : va_arg(vl, int); - int bits = 0; - char decchar[25] = {'0', 0}; - for (long tmp = num; tmp; bits++) { - decchar[bits] = (tmp % 10) + '0'; - tmp /= 10; - } - - if (bits == 0) bits ++; - - for (int i = bits - 1; i >= 0; i--) { - putch(decchar[i]); - } - pos += bits - 1; - longarg = 0; in_format = 0; - break; + for (int i = flags.width - 2 * prefix - __MAX(hexdigits, flags.prec); i > 0; i--) { + putch(' '); + ++written; } - case 's': { - const char* str = va_arg(vl, const char*); - while (*str) { - putch(*str); - pos++; - str++; - } - longarg = 0; in_format = 0; - break; + if (prefix) { + putch('0'); + putch(*fmt == 'X' ? 'X' : 'x'); + written += 2; } - case 'c': { - char ch = (char)va_arg(vl,int); - putch(ch); - pos++; - longarg = 0; in_format = 0; - break; + for (int i = hexdigits; i < flags.prec; i++) { + putch('0'); + ++written; } - default: - break; + + for (int i = hexdigits - 1; i >= 0; i--) { + putch(buf[i]); + ++written; + } + + flags.in_format = false; + } else if (*fmt == 'd' || *fmt == 'i' || *fmt == 'u') { + long num = flags.longflag ? va_arg(vl, long) : va_arg(vl, int); + + written += print_dec_int(putch, num, *fmt != 'u', &flags); + flags.in_format = false; + } else if (*fmt == 'n') { + if (flags.longflag) { + long *n = va_arg(vl, long *); + *n = written; + } else { + int *n = va_arg(vl, int *); + *n = written; + } + flags.in_format = false; + } else if (*fmt == 's') { + const char *s = va_arg(vl, const char *); + written += puts_wo_nl(putch, s); + flags.in_format = false; + } else if (*fmt == 'c') { + int ch = va_arg(vl, int); + putch(ch); + ++written; + flags.in_format = false; + } else if (*fmt == '%') { + putch('%'); + ++written; + flags.in_format = false; + } else { + putch(*fmt); + ++written; + flags.in_format = false; } - } - else if(*fmt == '%') { - in_format = 1; - } - else { + } else if (*fmt == '%') { + flags = (struct fmt_flags) {.in_format = true, .prec = -1}; + } else { putch(*fmt); - pos++; + ++written; } } - long syscall_ret; + return written; +} + +int printf(const char* s, ...) { + int res = 0; + va_list vl; + va_start(vl, s); + res = vprintfmt(putc, s, vl); + long syscall_ret, fd = 1; buffer[tail++] = '\0'; asm volatile ("li a7, %1\n" "mv a0, %2\n" @@ -116,15 +299,7 @@ static int vprintfmt(void(*putch)(char), int fd, const char *fmt, va_list vl) { "mv %0, a0\n" : "+r" (syscall_ret) : "i" (SYS_WRITE), "r" (fd), "r" (&buffer), "r" (tail)); - return syscall_ret; -} - -int printf(const char* s, ...) { - int res = 0; - va_list vl; - va_start(vl, s); tail = 0; - res = vprintfmt(putc, 1, s, vl); va_end(vl); return res; } diff --git a/src/lab6/user/ramdisk.S b/src/lab6/user/ramdisk.S deleted file mode 100644 index ae1b364..0000000 --- a/src/lab6/user/ramdisk.S +++ /dev/null @@ -1,4 +0,0 @@ -.section .ramdisk - -.incbin "uapp" - diff --git a/src/lab6/user/stddef.h b/src/lab6/user/stddef.h index 497d701..e9b6b6b 100644 --- a/src/lab6/user/stddef.h +++ b/src/lab6/user/stddef.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef __STDDEF_H__ +#define __STDDEF_H__ typedef __PTRDIFF_TYPE__ ptrdiff_t; typedef __SIZE_TYPE__ size_t; @@ -14,5 +15,4 @@ typedef __builtin_va_list va_list; #define va_arg(v,l) __builtin_va_arg(v,l) #define va_copy(d,s) __builtin_va_copy(d,s) -typedef unsigned long uint64_t; -typedef unsigned int int64_t; \ No newline at end of file +#endif diff --git a/src/lab6/user/stdint.h b/src/lab6/user/stdint.h new file mode 100644 index 0000000..f0151b6 --- /dev/null +++ b/src/lab6/user/stdint.h @@ -0,0 +1,113 @@ +#ifndef __STDINT_H__ +#define __STDINT_H__ + +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; + +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; + +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; + +typedef int64_t intmax_t; + +typedef int64_t intptr_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +typedef uint64_t uintmax_t; + +typedef uint64_t uintptr_t; + +#define INT8_MIN ((int8_t)0x80) +#define INT16_MIN ((int16_t)0x8000) +#define INT32_MIN ((int32_t)0x80000000) +#define INT64_MIN ((int64_t)0x8000000000000000) + +#define INT_FAST8_MIN ((int_fast8_t)INT8_MIN) +#define INT_FAST16_MIN ((int_fast16_t)INT16_MIN) +#define INT_FAST32_MIN ((int_fast32_t)INT32_MIN) +#define INT_FAST64_MIN ((int_fast64_t)INT64_MIN) + +#define INT_LEAST8_MIN ((int_least8_t)INT8_MIN) +#define INT_LEAST16_MIN ((int_least16_t)INT16_MIN) +#define INT_LEAST32_MIN ((int_least32_t)INT32_MIN) +#define INT_LEAST64_MIN ((int_least64_t)INT64_MIN) + +#define INTPTR_MIN ((intptr_t)INT64_MIN) + +#define INTMAX_MIN ((intmax_t)INT64_MIN) + +#define INT8_MAX ((int8_t)0x7f) +#define INT16_MAX ((int16_t)0x7fff) +#define INT32_MAX ((int32_t)0x7fffffff) +#define INT64_MAX ((int64_t)0x7fffffffffffffff) + +#define INT_FAST8_MAX ((int_fast8_t)INT8_MAX) +#define INT_FAST16_MAX ((int_fast16_t)INT16_MAX) +#define INT_FAST32_MAX ((int_fast32_t)INT32_MAX) +#define INT_FAST64_MAX ((int_fast64_t)INT64_MAX) + +#define INT_LEAST8_MAX ((int_least8_t)INT8_MAX) +#define INT_LEAST16_MAX ((int_least16_t)INT16_MAX) +#define INT_LEAST32_MAX ((int_least32_t)INT32_MAX) +#define INT_LEAST64_MAX ((int_least64_t)INT64_MAX) + +#define INTPTR_MAX ((intptr_t)INT64_MAX) + +#define INTMAX_MAX ((intmax_t)INT64_MAX) + +#define UINT8_MAX ((uint8_t)0xff) +#define UINT16_MAX ((uint16_t)0xffff) +#define UINT32_MAX ((uint32_t)0xffffffff) +#define UINT64_MAX ((uint64_t)0xffffffffffffffff) + +#define UINT_FAST8_MAX ((uint_fast8_t)UINT8_MAX) +#define UINT_FAST16_MAX ((uint_fast16_t)UINT16_MAX) +#define UINT_FAST32_MAX ((uint_fast32_t)UINT32_MAX) +#define UINT_FAST64_MAX ((uint_fast64_t)UINT64_MAX) + +#define UINT_LEAST8_MAX ((uint_least8_t)UINT8_MAX) +#define UINT_LEAST16_MAX ((uint_least16_t)UINT16_MAX) +#define UINT_LEAST32_MAX ((uint_least32_t)UINT32_MAX) +#define UINT_LEAST64_MAX ((uint_least64_t)UINT64_MAX) + +#define UINTPTR_MAX ((uintptr_t)UINT64_MAX) + +#define UINTMAX_MAX ((uintmax_t)UINT64_MAX) + +#define INT8_C(c) ((int8_t)c) +#define INT16_C(c) ((int16_t)c) +#define INT32_C(c) ((int32_t)c) +#define INT64_C(c) ((int64_t)c##LL) + +#define INTMAX_C(c) ((intmax_t)INT64_C(c)) + +#define UINT8_C(c) ((uint8_t)c) +#define UINT16_C(c) ((uint16_t)c) +#define UINT32_C(c) ((uint32_t)c) +#define UINT64_C(c) ((uint64_t)c##ULL) + +#define UINTMAX_C(c) ((uintmax_t)UINT64_C(c)) + +#endif diff --git a/src/lab6/user/stdio.h b/src/lab6/user/stdio.h index 589f8b8..a1a8566 100644 --- a/src/lab6/user/stdio.h +++ b/src/lab6/user/stdio.h @@ -1,17 +1,24 @@ -#pragma once +#ifndef __STDIO_H__ +#define __STDIO_H__ #include "stddef.h" -void putc(char c); +#define bool _Bool +#define true 1 +#define false 0 -static int vprintfmt(void(*putch)(char), int fd, const char *fmt, va_list vl); +int printf(const char *, ...); -#ifdef DEBUG_LOG -#define Log(format, ...) \ - printf("[%s:%d %s] " format "\n", __FILE__, __LINE__, __func__, ## __VA_ARGS__); -#else -#define Log(format, ...); -#endif +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define PURPLE "\033[35m" +#define DEEPGREEN "\033[36m" +#define CLEAR "\033[0m" +#define Log(format, ...) \ + printf("\33[1;35m[%s,%d,%s] " format "\33[0m\n", \ + __FILE__, __LINE__, __func__, ## __VA_ARGS__) -int printf(const char *, ...); +#endif diff --git a/src/lab6/user/string.c b/src/lab6/user/string.c new file mode 100644 index 0000000..904976c --- /dev/null +++ b/src/lab6/user/string.c @@ -0,0 +1,9 @@ +#include "string.h" +#include "stdint.h" + +int strlen(const char *str) { + int len = 0; + while (*str++) + len++; + return len; +} \ No newline at end of file diff --git a/src/lab6/user/string.h b/src/lab6/user/string.h index 95677b5..fcd3de2 100644 --- a/src/lab6/user/string.h +++ b/src/lab6/user/string.h @@ -1,12 +1,6 @@ -#ifndef _STRING_H_ -#define _STRING_H_ +#ifndef __STRING_H__ +#define __STRING_H__ -static inline int strlen(const char *str) -{ - int len = 0; - while (*str++) - len++; - return len; -} +int strlen(const char *str); #endif \ No newline at end of file diff --git a/src/lab6/user/syscall.h b/src/lab6/user/syscall.h index b9ececa..70c2410 100644 --- a/src/lab6/user/syscall.h +++ b/src/lab6/user/syscall.h @@ -1,5 +1,5 @@ -#ifndef _SYSCALL_H -#define _SYSCALL_H +#ifndef __SYSCALL_H__ +#define __SYSCALL_H__ #define SYS_OPENAT 56 #define SYS_CLOSE 57 @@ -9,10 +9,4 @@ #define SYS_GETPID 172 #define SYS_CLONE 220 -typedef unsigned long uint64_t; - -uint64_t sys_write(unsigned int fd, const char* buf, uint64_t count); - -uint64_t sys_getpid(); - #endif \ No newline at end of file diff --git a/src/lab6/user/uapp.S b/src/lab6/user/uapp.S new file mode 100644 index 0000000..c87cfda --- /dev/null +++ b/src/lab6/user/uapp.S @@ -0,0 +1,4 @@ +.section .uapp + +.incbin "uapp" + diff --git a/src/lab6/user/unistd.c b/src/lab6/user/unistd.c index 4f94c83..d0c6194 100644 --- a/src/lab6/user/unistd.c +++ b/src/lab6/user/unistd.c @@ -1,7 +1,7 @@ #include "unistd.h" #include "syscall.h" -int64_t write(int fd, const void *buf, uint64_t count) { +int write(int fd, const void *buf, uint64_t count) { char temp_buf[count + 1]; for (int i = 0; i < count; i++) { temp_buf[i] = ((char*)buf)[i]; @@ -16,11 +16,11 @@ int64_t write(int fd, const void *buf, uint64_t count) { "ecall\n" "mv %0, a0\n" : "+r" (syscall_ret) - : "i" (SYS_WRITE), "r" (fd), "r" (&temp_buf), "r" (count)); + : "i" (SYS_WRITE), "r" ((int64_t)fd), "r" (&temp_buf), "r" (count)); return syscall_ret; } -int64_t read(int fd, void *buf, uint64_t count) { +int read(int fd, void *buf, uint64_t count) { long syscall_ret; asm volatile ("li a7, %1\n" "mv a0, %2\n" @@ -29,11 +29,11 @@ int64_t read(int fd, void *buf, uint64_t count) { "ecall\n" "mv %0, a0\n" : "+r" (syscall_ret) - : "i" (SYS_READ), "r" (fd), "r" (buf), "r" (count)); + : "i" (SYS_READ), "r" ((int64_t)fd), "r" (buf), "r" (count)); return syscall_ret; } -int64_t sys_openat(int dfd, char *filename, int flags) { +int sys_openat(int dfd, char *filename, int flags) { long syscall_ret; asm volatile ("li a7, %1\n" "mv a0, %2\n" @@ -42,11 +42,11 @@ int64_t sys_openat(int dfd, char *filename, int flags) { "ecall\n" "mv %0, a0\n" : "+r" (syscall_ret) - : "i" (SYS_OPENAT), "r" (dfd), "r" (filename), "r" (flags)); + : "i" (SYS_OPENAT), "r" ((int64_t)dfd), "r" (filename), "r" ((int64_t)flags)); return syscall_ret; } -int64_t open(char *filename, int flags) { +int open(char *filename, int flags) { return sys_openat(AT_FDCWD, filename, flags); } @@ -57,11 +57,11 @@ int close(int fd) { "ecall\n" "mv %0, a0\n" : "+r" (syscall_ret) - : "i" (SYS_CLOSE), "r" (fd)); + : "i" (SYS_CLOSE), "r" ((int64_t)fd)); return syscall_ret; } -int64_t lseek(int fd, int64_t offset, int whence) { +int lseek(int fd, int offset, int whence) { long syscall_ret; asm volatile ("li a7, %1\n" "mv a0, %2\n" @@ -70,6 +70,6 @@ int64_t lseek(int fd, int64_t offset, int whence) { "ecall\n" "mv %0, a0\n" : "+r" (syscall_ret) - : "i" (SYS_LSEEK), "r" (fd), "r" (offset), "r" (whence)); + : "i" (SYS_LSEEK), "r" ((int64_t)fd), "r" ((int64_t)offset), "r" ((int64_t)whence)); return syscall_ret; } \ No newline at end of file diff --git a/src/lab6/user/unistd.h b/src/lab6/user/unistd.h index 1a926ff..c317c2b 100644 --- a/src/lab6/user/unistd.h +++ b/src/lab6/user/unistd.h @@ -1,9 +1,10 @@ -#ifndef _UNISTD_H -#define _UNISTD_H +#ifndef __UNISTD_H__ +#define __UNISTD_H__ #include "stddef.h" +#include "stdint.h" -#define AT_FDCWD -100 +#define AT_FDCWD -100 #define O_RDONLY 0x0001 #define O_WRONLY 0x0002 #define O_RDWR 0x0003 @@ -12,10 +13,10 @@ #define SEEK_CUR 0x0002 #define SEEK_END 0x0003 -int64_t open(char *filename, int flags); -int64_t write(int fd, const void *buf, uint64_t count); -int64_t read(int fd, void *buf, uint64_t count); +int open(char *filename, int flags); +int write(int fd, const void *buf, uint64_t count); +int read(int fd, void *buf, uint64_t count); int close(int fd); -int64_t lseek(int fd, int64_t offset, int whence); +int lseek(int fd, int offset, int whence); #endif \ No newline at end of file