了解x86系统启动原理
1、 地址空间分配
https://blog.csdn.net/pwl999/article/details/78212508
http://117.50.182.35/Linux%E5%86%85%E6%A0%B8/x86CPU%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4%E5%88%86%E9%85%8D/
2、引导扇区 https://zhuanlan.zhihu.com/p/38562168
https://www.cnblogs.com/CasonChan/p/4546658.html
3、引导过程 https://zhuanlan.zhihu.com/p/358796403
使用 grub启动ArceOS
按下面的流程完成实验, 要求 U盘能启动显示 ArceOS 启动Logo(文字版本)
https://gitee.com/chenlongos/os-x86-iso
如何制作u盘启动OS的iso文件
Thanks to https://os.phil-opp.com/multiboot-kernel/
下载项目
$ git clone https://gitee.com/chenlongos/os-x86-iso.git
Cloning into 'os-x86-iso'...
remote: Enumerating objects: 14, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 14 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (14/14), done.
$ cd os-x86-iso/
$ tree
.
├── Makefile
├── README.md
└── src
└── arch
└── x86_64
├── boot.asm
├── grub.cfg
├── linker.ld
└── multiboot_header.asm
Ubuntu 安装依赖
sudo aptitude install nasm mtools xorriso
无需编译,直接验证
快速烧写验证
最终iso文件 ./build/os-x86_64.iso 已经推入仓库,可以直接用 make dd 进行烧写验证
$ make dd
diskutil unmountDisk /dev/disk3
Unmount of all volumes on disk3 was successful
sudo dd if=./build/os-x86_64.iso of=/dev/disk3
Password:
22580+0 records in
22580+0 records out
11560960 bytes transferred in 22.977369 secs (503146 bytes/sec)
diskutil eject /dev/disk3
Disk /dev/disk3 ejected
编译源码,了解细节
编译生成 iso
$ make
mkdir -p build/arch/x86_64
nasm -felf64 src/arch/x86_64/boot.asm -o build/arch/x86_64/boot.o
mkdir -p build/arch/x86_64
nasm -felf64 src/arch/x86_64/multiboot_header.asm -o build/arch/x86_64/multiboot_header.o
ld -n -T src/arch/x86_64/linker.ld -o build/kernel-x86_64.bin build/arch/x86_64/boot.o build/arch/x86_64/multiboot_header.o
$ make iso
mkdir -p build/isofiles/boot/grub
cp build/kernel-x86_64.bin build/isofiles/boot/kernel.bin
cp src/arch/x86_64/grub.cfg build/isofiles/boot/grub
grub-mkrescue -o build/os-x86_64.iso build/isofiles 2> /dev/null
rm -r build/isofiles
$ ls -l build/os-x86_64.iso
-rw-r--r-- 1 root root 9097216 Oct 5 22:56 build/os-x86_64.iso
烧写iso文件到u盘
$ sudo dd if=./build/os-x86_64.iso of=/dev/disk3
Password:
17768+0 records in
17768+0 records out
9097216 bytes transferred in 17.312507 secs (525471 bytes/sec)
ArceOS VGA驱动/彩色显示
-
参考代码 https://github.com/phil-opp/blog_os/blob/post-12/src/vga_buffer.rs
-
redox 参考代码 跟踪源码,也可以找到和 vga 显示有关的代码在这里: https://gitlab.redox-os.org/redox-os/bootloader/-/blob/master/src/os/bios/mod.rs
const VGA_ADDR: usize = 0xB8000;`
pub(crate) static VGA: Mutex<Vga> = Mutex::new(
unsafe { Vga::new(VGA_ADDR, 80, 25) }
);
这个基地址和下面这个文件代码里的 0xb8000 是一致的
https://github.com/phil-opp/blog_os/blob/post-12/src/vga_buffer.rs
pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
column_position: 0,
color_code: ColorCode::new(Color::Yellow, Color::Black),
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
});
vga 这个 mod 的实现是在这个文件 https://gitlab.redox-os.org/redox-os/bootloader/-/blob/master/src/os/bios/vga.rs
- ArceOS 已经实现的版本参考 代码已提交在 https://github.com/wfly1998/arceos/tree/feature/vga,测试用的命令为
make A=apps/helloworld ARCH=x86_64 LOG=trace GRAPHIC=on run
-
显示framebuffer 驱动例子 https://github.com/rust-osdev/vga/blob/master/src/vga.rs
-
已经实现的 彩色显示的 https://github.com/wfly1998/arceos/tree/feature/vga
make A=apps/helloworld ARCH=x86_64 LOG=trace GRAPHIC=on run
make A=apps/fs/shell ARCH=x86_64 GRAPHIC=on BLK=y run
显示 输出 实验要求:
基于项目 https://github.com/wfly1998/arceos/tree/feature/vga
做扩展实现
-
参考现在的彩色实现函数
-
增加 pinfo ,pdev, pdebug 函数
例子: "[INFO] hello"
-
要求有 level 控制
例如 debug = 3 代表可以 显示info,dev,debug
第二阶段:键盘驱动
- rust ps2鼠标驱动例子
https://github.com/rust-osdev/ps2-mouse 鼠标驱动
- rust ps2 键盘驱动例子
https://gitlab.redox-os.org/redox-os/drivers/-/blob/master/ps2d/src/controller.rs ps2键盘驱动的例子
-
键盘码和扫描码的对应 http://osdev.foofun.cn/index.php?title=PS/2_Keyboard
-
X86 读取键盘片段参考
; read_key函数,从键盘控制器的数据端口读取扫描码,并保存在al寄存器中
read_key:
in al, 0x60 ; 从端口0x60读取扫描码
ret ; 返回
; print_key函数,将al寄存器中的扫描码转换为ASCII码,并打印在屏幕上
print_key:
cmp al, 0x01 ; 比较扫描码是否为Esc键(0x01)
je exit ; 如果是,跳转到exit标签,结束程序
cmp al, 0x02 ; 比较扫描码是否为1键(0x02)
je print_1 ; 如果是,跳转到print_1标签,打印1
cmp al, 0x03 ; 比较扫描码是否为2键(0x03)
je print_2 ; 如果是,跳转到print_2标签,打印2
cmp al, 0x04 ; 比较扫描码是否为3键(0x04)
je print_3 ; 如果是,跳转到print_3标签,打印3
cmp al, 0x05 ; 比较扫描码是否为4键(0x05)
je print_4 ; 如果是,跳转到print_4标签,打印4
cmp al, 0x06 ; 比较扫描码是否为5键(0x06)
je print_5 ; 如果是,跳转到print_5标签,打印5
cmp al, 0x07 ; 比较扫描码是否为6键(0x07)
je print_6 ; 如果是,跳转到print_6标签,打印6
cmp al, 0x08 ; 比较扫描码是否为7键(0x08)
je print_7 ; 如果是,跳转到print_7标签,打印7
cmp al, 0x09 ; 比较扫描码是否为8键(0x09)
je print_8 ; 如果是,跳转到print_8标签,打印8
cmp al, 0x0a ; 比较扫描码是否为9键(0x0a)
je print_9 ; 如果是,跳转到print_9标签,打印9
cmp al, 0x0b ; 比较扫描码是否为0键(0x0b)
je print_0 ; 如果是,跳转到print_0标签,打印0
ArceOS X86 输入输出设备串口代码分析
键盘驱动和串口驱动都有共同点,可以作为输入设备。 参考串口的输入设备编写键盘的输入接口。
-
代码地址 https://github.com/rcore-os/arceos/blob/main/modules/axhal/src/platform/x86_pc/uart16550.rs
-
串口的类似原理
https://wenku.baidu.com/view/ea660c60862458fb770bf78a6529647d272834b0.html?wkts=1697784322531&needWelcomeRecommand=1
https://max.book118.com/html/2017/0729/124989181.shtm
根据以上资料 了解串口的初始化,波特率设置,读写
- 目标任务
- 根据以上资料和代码 了解 端口的操作函数
- 了解 串口的硬件知识
- 分析 代码 如何实现 串口的 操作
- 串口 如何作为ArceOS的 输入输出 设备
键盘驱动 实验要求
-
根据 vga_buffer.rs uart16550.rs 规划一个ps2_key.rs 驱动框架
-
代码里 需要访问 ps2键盘端口,读键盘
-
获取的键盘值 转换成 键盘码
-
作为输入设备 返回结果,此时类似 串口的输入设备
实验结果验证
键盘输入 命令,vga可以显示输出
适配arceos的X86键盘驱动需求
支持ps2键盘
即能从键盘特定端口读入数据,处理中断等
支持keymap
建立键盘码和返回值的映射表
键盘码参考文件https://wenku.baidu.com/view/438682116edb6f1aff001fb2?aggId=4566dd740d22590102020740be1e650e53eacf4f&fr=catalogMain_text_ernie_recall_backup_new%3Awk_recommend_main_graph&wkts=1699936794712&bdQuery=ps2%E9%94%AE%E7%9B%98%E7%BC%96%E7%A0%81
返回值参考文件https://github.com/Wangzhike/HIT-Linux-0.11/blob/master/linux-0.11/kernel/chr_drv/keyboard.S
字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 |
---|---|---|---|---|---|---|---|
s0 | 00 | esc | 01 | 1 | 02 | 2 | 03 |
3 | 04 | 4 | 05 | 5 | 06 | 6 | 07 |
7 | 08 | 8 | 09 | 9 | 0A | 0 | 0B |
+ | 0C | ‘ | 0D | bs | 0E | tab | 0F |
q | 10 | w | 11 | e | 12 | r | 13 |
t | 14 | y | 15 | u | 16 | i | 17 |
o | 18 | p | 19 | } | 1A | ^ | 1B |
enter | 1C | ctrl | 1D | a | 1E | s | 1F |
字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 |
---|---|---|---|---|---|---|---|
d | 20 | f | 21 | g | 22 | h | 23 |
j | 24 | k | 25 | l | 26 | | | 27 |
{ | 28 | para | 29 | shift | 2A | , | 2B |
z | 2C | x | 2D | c | 2E | v | 2F |
b | 30 | n | 31 | m | 32 | , | 33 |
. | 34 | - | 35 | rshift | 36 | * | 37 |
alt | 38 | sp | 39 | caps | 3A | f1 | 3B |
f2 | 3C | f3 | 3D | f4 | 3E | f5 | 3F |
字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 |
---|---|---|---|---|---|---|---|
f6 | 40 | f7 | 41 | f8 | 42 | f9 | 43 |
f10 | 44 | num | 45 | scr | 46 | home | 47 |
up | 48 | pgup | 49 | - | 4A | left | 4B |
n5 | 4C | right | 4D | + | 4E | end | 4F |
dn | 50 | pgdn | 51 | ins | 52 | del | 53 |
sysreq | 54 | ? | 55 | < | 56 | f11 | 57 |
f12 | 58 | 59 | 5A | 5B |
字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 | 字符 | 返回值 |
---|---|---|---|---|---|---|---|
9C | unctrl | 9D | 9E | 9F | |||
A8 | A9 | unlshift | AA | AB | |||
B4 | B5 | unfshift | B6 | B7 | |||
unalt | B8 | B9 | uncaps | BA | BB | ||
e0 | E0 | e1 | E1 | E2 | E3 |
支持拓展键
说明:因为默认是从缓冲区读取一个u8大小的字符,但是有的键盘码占两位(甚至三位),所以我们要做特殊处理
特别注意:E0拓展键
需读取两个字符,即第一个u8字符为E0,还要继续读取一个u8字符才能知道这个码对应的是什么。
Rctrl、RAlt键
当这些键按下的时候,我们需要处理他们和crtl和win的矛盾关系
如crtl和Rctrl同时按下怎么办?可能需要加两个判断
U_ARROW、L_ARROW、D_ARROW、R_ARROW等键
up | down | left | right | |
---|---|---|---|---|
键码通码 | E0 75 | E0 72 | E0 6B | E0 74 |
ins | home | pgup | del | |
E0 70 | E0 6C | E0 7D | E0 71 | |
end | pgdn | KP / | Kp end | |
E0 69 | E0 7A | E0 4A | E0 5A |
小数字键盘上的键处理
字符 | * | - | + | 0 | 1 | 2 | 3 |
---|---|---|---|---|---|---|---|
键码通码 | 7C | 7B | 79 | 70 | 69 | 72 | 7A |
字符 | 4 | 5 | 6 | 7 | 8 | 9 | |
键码通码 | 6B | 73 | 74 | 6C | 75 | 7D |
特别注意:E1拓展键-pause
需读取三个字符
特别注意:F0断码
需读取两个字符,且对于ctrl、shift、alt键的断码输出对应的值
支持系统input接口
目前想法是将读取到的字符存入KeyboardBuffer缓冲区,然后在其他部分可以调用该缓冲区
第三阶段:文件系统分析
- 阅读 ArceOS 文件系统实现源代码, 简单阅读即可
https://github.com/rcore-os/arceos/tree/main/modules/axfs/src/fs
- 阅读 文件使用使用源代码 do_cat 函数 https://github.com/rcore-os/arceos/blob/main/apps/fs/shell/src/cmd.rs#L124
fn do_cat(args: &str) {
if args.is_empty() {
print_err!("cat", "no file specified");
return;
}
fn cat_one(fname: &str) -> io::Result<()> {
let mut buf = [0; 1024];
let mut file = File::open(fname)?;
loop {
let n = file.read(&mut buf)?;
if n > 0 {
io::stdout().write_all(&buf[..n])?;
} else {
return Ok(());
}
}
}
for fname in args.split_whitespace() {
if let Err(e) = cat_one(fname) {
print_err!("cat", fname, e);
}
}
}
作业要求
- 参考例子 运行
make PLATFORM=x86_64 A=apps/fs/shell
- 测试 读一个 index.html
index.html为一个 静态页面 内容 可以自己编辑。参考例子
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>辰龙操作系统(ChenLongOS) </title>
<meta property="og:title" content="辰龙操作系统(ChenLongOS)" />
</head>
<body>
<div class="container-lg px-3 my-5 markdown-body">
<h1><a href="https://chenlongos.com/">欢迎来带辰龙社区</a></h1>
</div>
</body>
</html>
第四阶段:httpserver代码分析
-
阅读ArceOS httpserver源代码 https://github.com/rcore-os/arceos/blob/main/apps/net/httpserver/src/main.rs
-
修改返回的内容
const CONTENT: &str = r#"<html>
<head>
<title>Hello, ArceOS</title>
</head>
<body>
<center>
<h1>Hello, <a href="https://github.com/rcore-os/arceos">ArceOS</a></h1>
</center>
<hr>
<center>
<i>Powered by <a href="https://github.com/rcore-os/arceos/tree/main/apps/net/httpserver">ArceOS example HTTP server</a> v0.1.0</i>
</center>
</body>
</html>
"#;
- 仿照参考例子 ,修改成在 x86下可以执行
make A=apps/net/httpserver ARCH=aarch64 LOG=info SMP=4 run NET=y
寻找文档,并成功执行httpserver例子
实验: httpserver访问index.html文件
-
替换 以上 代码中 固定的内容 ,采用fs的read函数 读取 index.html 内容
-
按照 上面httpserver例子 成功完成 显示,内容 为index.html即为成功。
问答&bug
错误一、运行1.1的测试命令qemu还是这样然后就直接退出了,该怎么解决呢
3.Arceos 已经实现的版本参考代码已提交在
https://github.com/wfly1998/arceos/tree/feature/vga,测试用的命令为
make A=apps/helloworld ARCH=x86_64 LOG=trace GRAPHIC=on run
运行时直接闪退了。
【答】: 可能是没有切换到 feature/vga 分支
错误二、虚拟机下运行1.1的测试命令 make A=apps/fs/shell ARCH=x86_64 GRAPHIC=on BLK=y run 显示找不到disk.img
qemu-system-x86 64: -drive id-disko,if=none,format=raw,file=disk.img: Could not open 'disk.img': No such file or directory
【答】: fs依赖块设备文件,执行 make disk_img 命令生产一个disk.img即可。
错误三、提示 no global memory allocator found but one is required; link to std or add #[global_allocator] to a static item that implements the GlobalAlloc trait
【答】: 删掉 modules/axhal/src/lib.rs 中 extren crate alloc 这行即可