MIT Operating System - 1.1 - 透视 boot.S 和 main.c

lab1 part1 PC启动过程

6.828课程的lab1主要集中在讲解8086的启动过程,其中有几道习题,我们把这节lab拆分到N篇文章中,分开记录

知识准备

汇编部分

  • 声明变量
    通过前缀 “%” 声明
  • 声明常量
    通过前缀 “$” 声明
  • 左边代表源,右边代表目标

    AT&T: movl %eax, %ebx 代表把eax的值放到ebx里面

  • 标识符大小说明
    由b、w、l前缀说明,分别代表bytewordlongword

整体流程

废话不说,先上图
MIT Operating System - 1.1 - 透视 boot.S 和 main.c_第1张图片

  1. 首先,系统开机,点火!
  2. 系统进入 0xffff0 地址,开始执行 BIOS(Basic Input and output System)
  3. BIOS进行一系列的设备初始化工作
  4. BIOS把引导设备(如硬盘、USB)的第一块扇区 读入内存(0x7c00 ~ 0x7dff)
  5. 系统跳转到 0x7c00 处,BIOS把权力转交给 引导设备的第一块扇区 (the boot loader)
  6. 最后,boot loader执行一系列必要的操作,包括从实模式转到32位保护模式,准备c的堆栈等,把kernel读入内存

代码实现(boot.S)

咱们不着急,一行一行的读

#include 

# Start the CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.

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

兄弟们,姐妹们,同志们,这块代码,就是BIOS同启动盘第一块扇区读出来的,我们自己的做的启动盘的第一块内容,大概率也是长这样子的,激不激动!兴不兴奋!
首先,8~10行,相当于C语言中的Define,具体什么意思咱们后文分述

接着看

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

这里就是汇编执行入口(梦开始的地方),cli 和 cld 分别把BIOS可能设置的中断和字符串读入方向清除掉,方便后续代码执行

不能停!

 # 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

第一句,把ax寄存器自己跟自己异或(你说啥意思呢?就是清零啊喂!)同时把DS,ES,SS寄存器全部清零

# 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
  testb   $0x2,%al
  jnz     seta20.1

  movb    $0xd1,%al               # 0xd1 -> port 0x64
  outb    %al,$0x64

seta20.2:
  inb     $0x64,%al               # Wait for not busy
  testb   $0x2,%al
  jnz     seta20.2

  movb    $0xdf,%al               # 0xdf -> port 0x60
  outb    %al,$0x60

这一段代码比较好玩,咱们仔细看一看,首先我们看注释,知道这段目的是 使能A20 地址线,这是为什么呢?这个时候,就需要拿出6.828这张著名内存图了
MIT Operating System - 1.1 - 透视 boot.S 和 main.c_第2张图片
早年间呐,电脑是个稀罕东西,内存(RAM)更别提了,所以,那个时候的内存只有1MB,而几乎一半还被BIOS等设备驱动占去了,而随着RAM变大,设计师们不得不对内存结构进行更改,所以呢,现在的操作系统都会留出这么一个洞,就是为了向前兼容。
OK,说到这里,聪明的同学大概已经知道这个 使能A20 是什么意思了,意思就是,在8086中只有20地址线,所以BIOS中会默认把20以上的地址线设为0,这段代码undo this,让我们有更多的地址可以支配,从而有更多可以使用的内存
首先,inb $0x64,%al

in 读取指定 IO端口状态 到 指定寄存器
out 向 IO 进行写操作、

意思是,把0x64端口状态读出到al寄存器,然后testb $0x2,%al

test指令和and 指令执行同样的操作,但test指令不送回操作结果,而仅仅影响标志位

所以,这段代码就是检测 al寄存器中的第二位,然而,为什么检测第二位就可以知道端口是不是正忙呢?我们看这个链接
端口大全
MIT Operating System - 1.1 - 透视 boot.S 和 main.c_第3张图片
我们可以看到,端口状态的第二位为1表示 input buffer full,一切都明白啦
我们再看下一句,movb $0xd1,%al outb %al,$0x64 ,意思是向0064端口写入0xd1,这又是在干什么呢?
在这里插入图片描述
所以,写入d1的意思是,下一个写向 0060 端口的 字节 将会写往 804x端口,我们向后peek一下,发现果然,下一个写操作
movb $0xdf,%al outb %al,$0x60,把df写到这个804x端口,这又是干什么呢?接着看端口大全
在这里插入图片描述
嘿嘿!被我逮到了,搞半天,还是为了使能A20嘛

好了好了,我们继续读代码

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

先看看解释,这段的意思就是转换了,从实模式转换到了保护模式,不得了啊,不行,太困了,明天再写,今天先洗洗睡了

你可能感兴趣的:(MIT,6.828,lab)