了解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驱动/彩色显示

  1. 参考代码 https://github.com/phil-opp/blog_os/blob/post-12/src/vga_buffer.rs

  2. 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

  1. ArceOS 已经实现的版本参考 代码已提交在 https://github.com/wfly1998/arceos/tree/feature/vga,测试用的命令为
make A=apps/helloworld ARCH=x86_64 LOG=trace GRAPHIC=on run
  1. 显示framebuffer 驱动例子 https://github.com/rust-osdev/vga/blob/master/src/vga.rs

  2. 已经实现的 彩色显示的 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

做扩展实现

  1. 参考现在的彩色实现函数

  2. 增加 pinfo ,pdev, pdebug 函数

    例子: "[INFO] hello"

  3. 要求有 level 控制

    例如 debug = 3 代表可以 显示info,dev,debug

第二阶段:键盘驱动

  1. rust ps2鼠标驱动例子

https://github.com/rust-osdev/ps2-mouse 鼠标驱动

  1. rust ps2 键盘驱动例子

https://gitlab.redox-os.org/redox-os/drivers/-/blob/master/ps2d/src/controller.rs ps2键盘驱动的例子

  1. 键盘码和扫描码的对应 http://osdev.foofun.cn/index.php?title=PS/2_Keyboard

  2. 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 输入输出设备串口代码分析

键盘驱动和串口驱动都有共同点,可以作为输入设备。 参考串口的输入设备编写键盘的输入接口。

  1. 代码地址 https://github.com/rcore-os/arceos/blob/main/modules/axhal/src/platform/x86_pc/uart16550.rs

  2. 串口的类似原理

https://wenku.baidu.com/view/ea660c60862458fb770bf78a6529647d272834b0.html?wkts=1697784322531&needWelcomeRecommand=1

https://max.book118.com/html/2017/0729/124989181.shtm

根据以上资料 了解串口的初始化,波特率设置,读写

  1. 目标任务
  • 根据以上资料和代码 了解 端口的操作函数
  • 了解 串口的硬件知识
  • 分析 代码 如何实现 串口的 操作
  • 串口 如何作为ArceOS的 输入输出 设备

键盘驱动 实验要求

  1. 根据 vga_buffer.rs uart16550.rs 规划一个ps2_key.rs 驱动框架

  2. 代码里 需要访问 ps2键盘端口,读键盘

  3. 获取的键盘值 转换成 键盘码

  4. 作为输入设备 返回结果,此时类似 串口的输入设备

实验结果验证

键盘输入 命令,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

字符返回值字符返回值字符返回值字符返回值
s000esc01102203
304405506607
70880990A00B
+0C0Dbs0Etab0F
q10w11e12r13
t14y15u16i17
o18p19}1A^1B
enter1Cctrl1Da1Es1F
字符返回值字符返回值字符返回值字符返回值
d20f21g22h23
j24k25l26|27
{28para29shift2A,2B
z2Cx2Dc2Ev2F
b30n31m32,33
.34-35rshift36*37
alt38sp39caps3Af13B
f23Cf33Df43Ef53F
字符返回值字符返回值字符返回值字符返回值
f640f741f842f943
f1044num45scr46home47
up48pgup49-4Aleft4B
n54Cright4D+4Eend4F
dn50pgdn51ins52del53
sysreq54?55<56f1157
f1258595A5B
字符返回值字符返回值字符返回值字符返回值
9Cunctrl9D9E9F
A8A9unlshiftAAAB
B4B5unfshiftB6B7
unaltB8B9uncapsBABB
e0E0e1E1E2E3

支持拓展键

说明:因为默认是从缓冲区读取一个u8大小的字符,但是有的键盘码占两位(甚至三位),所以我们要做特殊处理

特别注意:E0拓展键

需读取两个字符,即第一个u8字符为E0,还要继续读取一个u8字符才能知道这个码对应的是什么。

Rctrl、RAlt键

当这些键按下的时候,我们需要处理他们和crtl和win的矛盾关系

如crtl和Rctrl同时按下怎么办?可能需要加两个判断

U_ARROW、L_ARROW、D_ARROW、R_ARROW等键

updownleftright
键码通码E0 75E0 72E0 6BE0 74
inshomepgupdel
E0 70E0 6CE0 7DE0 71
endpgdnKP /Kp end
E0 69E0 7AE0 4AE0 5A

小数字键盘上的键处理

字符*-+0123
键码通码7C7B797069727A
字符456789
键码通码6B73746C757D

特别注意:E1拓展键-pause

需读取三个字符

特别注意:F0断码

需读取两个字符,且对于ctrl、shift、alt键的断码输出对应的值

支持系统input接口

目前想法是将读取到的字符存入KeyboardBuffer缓冲区,然后在其他部分可以调用该缓冲区

第三阶段:文件系统分析

  1. 阅读 ArceOS 文件系统实现源代码, 简单阅读即可

https://github.com/rcore-os/arceos/tree/main/modules/axfs/src/fs

  1. 阅读 文件使用使用源代码 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);
        }
    }
}

作业要求

  1. 参考例子 运行
make PLATFORM=x86_64 A=apps/fs/shell
  1. 测试 读一个 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代码分析

  1. 阅读ArceOS httpserver源代码 https://github.com/rcore-os/arceos/blob/main/apps/net/httpserver/src/main.rs

  2. 修改返回的内容

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>
"#;
  1. 仿照参考例子 ,修改成在 x86下可以执行
make A=apps/net/httpserver ARCH=aarch64 LOG=info SMP=4 run NET=y

寻找文档,并成功执行httpserver例子

实验: httpserver访问index.html文件

  1. 替换 以上 代码中 固定的内容 ,采用fs的read函数 读取 index.html 内容

  2. 按照 上面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即可。

【答】: 删掉 modules/axhal/src/lib.rs 中 extren crate alloc 这行即可