[从 0 开始写一个操作系统] 三、Bootloader 的实现

从 0 开始写一个操作系统

作者:解琛
时间:2020 年 8 月 30 日

三、Bootloader 的实现

安装开发环境。

sudo apt install build-essential git qemu-system-x86 vim-gnome gdb make diffutils exuberant-ctags tmux openssh-server cscope meld

3.1 项目整体框架

项目完整的代码见:lab1_result

.
├── boot
│   ├── asm.h
│   ├── bootasm.S
│   └── bootmain.c
├── kern
│   ├── debug
│   │   ├── assert.h
│   │   ├── kdebug.c
│   │   ├── kdebug.h
│   │   ├── kmonitor.c
│   │   ├── kmonitor.h
│   │   ├── panic.c
│   │   └── stab.h
│   ├── driver
│   │   ├── clock.c
│   │   ├── clock.h
│   │   ├── console.c
│   │   ├── console.h
│   │   ├── intr.c
│   │   ├── intr.h
│   │   ├── kbdreg.h
│   │   ├── picirq.c
│   │   └── picirq.h
│   ├── init
│   │   └── init.c
│   ├── libs
│   │   ├── readline.c
│   │   └── stdio.c
│   ├── mm
│   │   ├── memlayout.h
│   │   ├── mmu.h
│   │   ├── pmm.c
│   │   └── pmm.h
│   └── trap
│       ├── trap.c
│       ├── trapentry.S
│       ├── trap.h
│       └── vectors.S
├── libs
│   ├── defs.h
│   ├── elf.h
│   ├── error.h
│   ├── printfmt.c
│   ├── stdarg.h
│   ├── stdio.h
│   ├── string.c
│   ├── string.h
│   └── x86.h
├── Makefile
└── tools
    ├── function.mk
    ├── gdbinit
    ├── grade.sh
    ├── kernel.ld
    ├── lab1init
    ├── moninit
    ├── sign.c
    └── vector.c

10 directories, 48 files

3.1.1 bootloader

  • boot/bootasm.S
    • 定义并实现了 bootloader 最先执行的函数 start;
    • 此函数进行了一定的初始化;
    • 完成了从实模式到保护模式的转换;
    • 调用 bootmain.c 中的 bootmain 函数;
  • boot/bootmain.c
    • 定义并实现了 bootmain 函数实现了通过屏幕、串口和并口显示字符串;
    • bootmain 函数加载 ucore 操作系统到内存,然后跳转到 ucore 的入口处执行;
  • boot/asm.h
    • 是 bootasm.S 汇编文件所需要的头文件,主要是一些与 X86 保护模式的段访问方式相关的宏定义。

3.1.2 ucore 操作系统

3.1.2.1 系统初始化
  • kern/init/init.c:ucore 操作系统的初始化启动代码。
3.1.2.2 内存管理
  • kern/mm/memlayout.h:ucore 操作系统有关段管理(段描述符编号、段号等)的一些宏定义;
  • kern/mm/mmu.h:ucore 操作系统有关 X86 MMU 等硬件相关的定义;
    • 包括 EFLAGS 寄存器中各位的含义;
    • 应用、系统段类型;
    • 中断门描述符定义;
    • 段描述符定义;
    • 任务状态段定义;
    • NULL 段声明的宏 SEG_NULL;
    • 特定段声明的宏 SEG;
    • 设置中断门描述符的宏 SETGATE;
  • kern/mm/pmm.[ch]:设定了 ucore 操作系统在段机制中要用到的全局变量;
    • 任务状态段 ts;
    • 全局描述符表 gdt[];
    • 加载全局描述符表寄存器的函数 lgdt;
    • 临时的内核栈 stack0;
    • 对全局描述符表和任务状态段的初始化函数 gdt_init。
3.1.2.3 外设驱动
  • kern/driver/intr.[ch]:实现了通过设置 CPU 的 eflags 来屏蔽和使能中断的函数;
  • kern/driver/picirq.[ch]:实现了对中断控制器 8259A 的初始化和使能操作;
  • kern/driver/clock.[ch]:实现了对时钟控制器 8253 的初始化操作;
  • kern/driver/console.[ch]:实现了对串口和键盘的中断方式的处理操作。
3.1.2.4 中断处理
  • kern/trap/vectors.S:包括 256 个中断服务例程的入口地址和第一步初步处理实现;
    • 此文件是由 tools/vector.c 在编译 ucore 期间动态生成的;
  • kern/trap/trapentry.S
    • 紧接着第一步初步处理后,进一步完成第二步初步处理;
    • 并且有恢复中断上下文的处理,即中断处理完毕后的返回准备工作;
  • kern/trap/trap.[ch]:紧接着第二步初步处理后,继续完成具体的各种中断处理操作。
3.1.2.5 内核调试
  • kern/debug/kdebug.[ch]:提供源码和二进制对应关系的查询功能,用于显示调用栈关系;
  • kern/debug/kmonitor.[ch]:实现提供动态分析命令的 kernel monitor,便于在 ucore 出现 bug 或问题后,能够进入 kernel monitor 中,查看当前调用关系;
  • kern/debug/panic.c | assert.h:提供了 panic 函数和 assert 宏,便于在发现错误后,调用 kernel monitor;
    • 可以充分利用 assert 宏和 panic 函数,提高查找错误的效率。

3.1.3 公共库

  • libs/defs.h:包含一些无符号整型的缩写定义;
  • libs/x86.h:一些用 GNU C 嵌入式汇编实现的 C 函数(由于使用了 inline 关键字,所以可以理解为宏)。

3.1.4 工具

  • Makefile 和 function.mk:指导 make 完成整个软件项目的编译,清除等工作;
  • sign.c:一个 C 语言小程序,是辅助工具,用于生成一个符合规范的硬盘主引导扇区;
  • tools/vector.c:生成vectors.S,此文件包含了中断向量处理的统一实现。

3.2 bootloader 进入保护模式

完整的代码见 boot/bootasm.S

定义基本的段信息和标志位。

#include <asm.h>

.set PROT_MODE_CSEG,        0x08                    # kernel code segment selector
.set PROT_MODE_DSEG,        0x10                    # kernel data segment selector
.set CR0_PE_ON,             0x01                    # protected mode enable flag

机器上电,从 %cs=0 $pc=0x7c00,进入后:

# start address should be 0:7c00, in real mode, the beginning address of the running bootloader
.globl start
start:

3.2.1 清理环境

首先,清理环境,包括将 flag 置 0 和将段寄存器置 0。

.code16                                             # Assemble for 16-bit mode
    cli                                             # Disable interrupts
    cld                                             # String operations increment

    # Set up the important data segment registers (DS, ES, SS).
    xorw %ax, %ax                                   # Segment number zero
    movw %ax, %ds                                   # -> Data Segment
    movw %ax, %es                                   # -> Extra Segment
    movw %ax, %ss                                   # -> Stack Segment

3.2.2 使能 A20

开启 A20:通过将键盘控制器上的 A20 线置于高电位,全部 32 条地址线可用,可以访问 4G 的内存空间。

    #  Enable A20:
    #  For backwards compatibility with the earliest PCs, physical
    #  address line 20 is tied low, so that addresses higher than
    #  1MB wrap around to zero by default. This code undoes this.
seta20.1:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.1

    movb $0xd1, %al                                 # 0xd1 -> port 0x64,发送写8042输出端口的指令;
    outb %al, $0x64                                 # 0xd1 means: write data to 8042's P2 port

seta20.2:
    inb $0x64, %al                                  # Wait for not busy(8042 input buffer empty).
    testb $0x2, %al
    jnz seta20.2

    movb $0xdf, %al                                 # 0xdf -> port 0x60,打开 A20;
    outb %al, $0x60                                 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1

3.2.3 初始化 GDT 表

一个简单的 GDT 表和其描述符已经静态储存在引导区中,载入即可。

    # Switch from real to protected mode, using a bootstrap GDT
    # and segment translation that makes virtual addresses
    # identical to physical addresses, so that the
    # effective memory map does not change during the switch.
    lgdt gdtdesc

在文件的最后定义 gdtdesc。

# Bootstrap GDT
.p2align 2                                          # force 4 byte alignment
gdt:
    SEG_NULLASM                                     # null seg
    SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)           # code seg for boo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

解琛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值