清华大学实验lab1

实验目的:
操作系统是一个软件,也需要通过某种机制加载并运行它。在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作。为此,我们需要完成一个能够切换到x86的保护模式并显示字符的bootloader,为启动操作系统ucore做准备。lab1提供了一个非常小的bootloader和ucore OS,整个bootloader执行代码小于512个字节,这样才能放到硬盘的主引导扇区中。通过分析和实现这个bootloader和ucore OS,读者可以了解到:

计算机原理

CPU的编址与寻址: 基于分段机制的内存管理
CPU的中断机制
外设:串口/并口/CGA,时钟,硬盘

Bootloader软件

编译运行bootloader的过程
调试bootloader的方法
PC启动bootloader的过程
ELF执行文件的格式和加载
外设访问:读硬盘,在CGA上显示字符串

ucore OS软件

编译运行ucore OS的过程
ucore OS的启动过程
调试ucore OS的方法
函数调用关系:在汇编级了解函数调用栈的结构和处理过程
中断管理:与软件相关的中断处理
外设管理:时钟

练习1:理解通过make生成执行文件的过程。

1、操作系统镜像文件ucore.img是如何一步一步生成的?(需要比较详细地解释Makefile中每一条相关命令和命令参数的含义,以及说明命令导致的结果)
2、一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
看视频可以知道,老师说重点掌握ucore.img的形成过程,老师教的方法是利用 make V= 命令把过程信息打印出来。

********+ cc kern/init/init.c
gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o

  • cc kern/libs/readline.c
    gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.o
  • cc kern/libs/stdio.c
    gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.o
  • cc kern/debug/kdebug.c
    gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.o
  • cc kern/debug/kmonitor.c
    gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.o
  • cc kern/debug/panic.c
    gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.o
  • cc kern/driver/clock.c
    gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.o
  • cc kern/driver/console.c
    gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/console.c -o obj/kern/driver/console.o
  • cc kern/driver/intr.c
    gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/intr.c -o obj/kern/driver/intr.o
  • cc kern/driver/picirq.c
    gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/picirq.c -o obj/kern/driver/picirq.o
  • cc kern/trap/trap.c
    gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.o
  • cc kern/trap/trapentry.S
    gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trapentry.S -o obj/kern/trap/trapentry.o
  • cc kern/trap/vectors.S
    gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.o
  • cc kern/mm/pmm.c
    gcc -Ikern/mm/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.o
  • cc libs/printfmt.c
    gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -c libs/printfmt.c -o obj/libs/printfmt.o
  • cc libs/string.c
    gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -c libs/string.c -o obj/libs/string.o
  • ld bin/kernel
    ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o obj/kern/libs/readline.o obj/kern/libs/stdio.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/debug/panic.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/intr.o obj/kern/driver/picirq.o obj/kern/trap/trap.o obj/kern/trap/trapentry.o obj/kern/trap/vectors.o obj/kern/mm/pmm.o obj/libs/printfmt.o obj/libs/string.o
  • cc boot/bootasm.S
    gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
  • cc boot/bootmain.c
    gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
  • cc tools/sign.c
    gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
    gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
  • ld bin/bootblock
    ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
    ‘obj/bootblock.out’ size: 488 bytes
    build 512 bytes boot sector: ‘bin/bootblock’ success!
    dd if=/dev/zero of=bin/ucore.img count=10000
    10000+0 records in
    10000+0 records out
    5120000 bytes (5.1 MB) copied, 0.0370098 s, 138 MB/s
    dd if=bin/bootblock of=bin/ucore.img conv=notrunc
    1+0 records in
    1+0 records out
    512 bytes (512 B) copied, 0.000210094 s, 2.4 MB/s
    dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
    146+1 records in
    146+1 records out
    74923 bytes (75 kB) copied, 0.000409831 s, 183 MB/s
    大差不差有这么多,其中我们可以看到生成ucore.img需要kernel和bootblock
    $(UCOREIMG): $(kernel) $(bootblock)
    (V)ddif=/dev/zeroof=(V)dd if=/dev/zero of=@ count=10000
    (V)ddif=(V)dd if=(bootblock) [email protected] conv=notrunc
    (V)ddif=(V)dd* if=(kernel) [email protected] seek=1 conv=notrunc
    就是这么两行。
    为了生成bootblock,首先需要生成bootasm.o、bootmain.o、sign
    生成bootasm.o需要bootasm.S
    实际命令为

    gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs
    -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc
    -c boot/bootasm.S -o obj/boot/bootasm.o
    其中关键的参数为
    -ggdb 生成可供gdb使用的调试信息
    -m32 生成适用于32位环境的代码
    -gstabs 生成stabs格式的调试信息
    -nostdinc 不在标准系统文件夹寻找头文件,只在-I等参数指定的文件夹中搜索头文件
    -fno-stack-protector 不生成用于检测缓冲区溢出的代码
    -Os 为减小代码大小而进行优化
    -I 添加搜索头文件的路径,优先查找头文件的地方
    生成bootmain.o需要bootmain.c
    实际命令为
    gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -g
    stabs -nostdinc
    -fno-stack-protector -Ilibs/ -Os -nostdinc
    -c boot/bootmain.c -o obj/boot/bootmain.o
    新出现的关键参数有
    -fno-builtin 除非用__builtin_前缀,否则不进行builtin函数的
    优化
    生成sign工具的makefile代码为
    $(call add_files_host,tools/sign.c,sign,sign)
    $(call create_target_host,sign,sign)
    实际命令为
    gcc -Itools/ -g -Wall -O2 -c tools/sign.c
    -o obj/sign*/tools/sign.o
    gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
    首先生成bootblock.o
    ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00
    obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
    其中关键的参数为
    -m 模拟为i386上的连接器
    -N 设置代码和数据段是可读可写的,对数据段不做page-align
    -e 指定入口
    -Ttext 制定代码段开始位置
    拷贝二进制代码bootblock.o到bootblock.out
    objcopy -S -O binary obj/bootblock.o obj/bootblock.out
    其中关键的参数为
    -S 移除所有符号和重定位信息
    -O 指定输出格式
    使用sign工具处理bootblock.out,生成bootblock
    bin/sign obj/bootblock.out bin/bootblock
    生成kernel的相关代码为
    $(kernel): tools/kernel.ld
    $(kernel): $(KOBJS)
    @echo + ld [email protected]
    (V)(V)(LD) $(LDFLAGS) -T tools/kernel.ld -o [email protected] (KOBJS)@(KOBJS) @(OBJDUMP) -S [email protected] > (callasmfile,kernel)@(call asmfile,kernel) @(OBJDUMP) -t [email protected] | KaTeX parse error: Expected group after '^' at position 40: …; s/ .* / /; /^̲$/d’ > $(call symfile,kernel)
    为了生成kernel,首先需要 kernel.ld init.o readline.o stdio.o kdebug.o
    kmonitor.o panic.o clock.o console.o intr.o picirq.o trap.o
    trapentry.o vectors.o pmm.o printfmt.o string.o
    生成kernel的细节就不写了,就是.o文件的链接
    生成一个有10000个块的文件,每个块默认512字节,用0填充
    dd if=/dev/zero of=bin/ucore.img count=10000
    把bootblock中的内容写到第一个块
    dd if=bin/bootblock of=bin/ucore.img conv=notrunc
    从第二个块开始写kernel中的内容
    dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
    dd的一些参数的含义:
    -if表示输入文件,如果不指定,那么会默认从stdin中读取输入
    -of表示输出文件,如果不指定,那么会stdout
    bs表示以字节为单位的块大小
    count表示被赋值的块数
    /dev/zero是一个字符设备,会不断返回0值字节\0
    conv = notrunc 不截短输出文件
    seek=blocks 从输出文件开头跳过blocks个块后再开始复制

一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?

tools/sign.c

按照这个文件的描述,需要检查以下几点:

输入的主引导扇区的记录必须是510字节以内(446+64)
输出的主引导扇区的最后两个字节是55AA

bootblock就是需要用到的主引导扇区

练习2:使用qemu执行并调试lab1中的软件。

这个Task主要是为了熟悉GDB以及熟悉操作系统的启动过程,下面是调试BIOS的一些过程。
首先修改gdbinit为:
1、set architecture i8086
2、target remote :1234
3、define hook-stop
4、x/i $pc
5、end
然后输入 make debug
清华大学ucore实验lab
我们可以获取当前 $cs 和 $eip 的值。
清华大学ucore实验lab
也就是说,BIOS开始的地址应该是
$cs << 4 | 0xe05b = 0xfe05b
设置断点到0x7c00

清华大学ucore实验lab
这个地方就是bootloader的地址了。

练习3:分析bootloader进入保护模式的过程。

现在使用的CPU都是通过键盘控制器8042来控制A20地址线。默认情况下,A20地址线是关闭的(第20bit的地址线限制为0),因此在进入保护模式(需要访问超过1MB的内存空间)前,我们需要开启A20地址线(第20bit的地址线可为0或者1)。A20的开启过程请参考bootasm.S文件。
CPU是如何从实模式转换到保护模式
这个特别简单,我们需要在开启A20地址线后,将$CR0(control register 0)的PE(bit0)置为1就行了。具体代码请参考bootasm.S文件

相关文章:

  • 2021-08-27
  • 2022-02-07
  • 2021-11-18
  • 2021-11-18
  • 2022-02-04
  • 2021-09-26
  • 2021-11-10
  • 2021-10-22
猜你喜欢
  • 2022-03-11
  • 2021-06-22
  • 2021-11-25
  • 2022-12-23
  • 2021-07-24
  • 2021-04-08
  • 2021-08-28
相关资源
相似解决方案