MIT6.828 boot.S 文件分析

#include <inc/mmu.h>
 
# 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.
启动CPU,切换到32位保护模式,跳转到C代码
BIOS从硬盘的第一个扇区加载这个代码到
物理内存地址为0x7c00的地方,cs=0,ip=7c00
 
下面的3条.set指令类似于宏定义
内核代码段选择子
.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
 
定义一个全局名字start
.globl start
start:
CPU刚启动为16位模式
  .code16                     # Assemble for 16-bit mode
关中断
  cli                         # Disable interrupts
清方向标志
  cld                         # String operations increment
 
  # Set up the important data segment registers (DS, ES, SS).
设置重要的数据段寄存器
ax清零
  xorw    %ax,%ax             # Segment number zero
ds清零
  movw    %ax,%ds             # -> Data Segment
es清零
  movw    %ax,%es             # -> Extra Segment
ss清零
  movw    %ax,%ss             # -> Stack Segment
 
  # 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.
打开A20地址线
为了兼容早期的PC机,第20根地址线在实模式下不能使用
所以超过1MB的地址,默认就会返回到地址0,重新从0循环计数,
下面的代码打开A20地址线
 
seta20.1:
从0x64端口读入一个字节的数据到al中
  inb     $0x64,%al               # Wait for not busy
test指令可以当作and指令,只不过它不会影响操作数
  testb   $0x2,%al
如果上面的测试中发现al的第2位为0,就不执行该指令
否则就循环检查
  jnz     seta20.1
 
将0xd1写入到al中
  movb    $0xd1,%al               # 0xd1 -> port 0x64
将al中的数据写入到端口0x64中
  outb    %al,$0x64
 
seta20.2:
从0x64端口读取一个字节的数据到al中
  inb     $0x64,%al               # Wait for not busy
测试al的第2位是否为0
  testb   $0x2,%al
如果上面的测试中发现al的第2位为0,就不执行该指令
否则就循环检查
  jnz     seta20.2
 
将0xdf写入到al中
  movb    $0xdf,%al               # 0xdf -> port 0x60
将al中的数据写入到0x60端口中
  outb    %al,$0x60
 
  # 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
 
cr0中的第0位为1表示处于保护模式
cr0中的第0位为0,表示处于实模式
把控制寄存器cr0加载到eax中
  movl    %cr0, %eax
将eax中的第0位设置为1
  orl     $CR0_PE_ON, %eax
将eax中的值装入cr0中
  movl    %eax, %cr0
  
  # Jump to next instruction, but in 32-bit code segment.
  # Switches processor into 32-bit mode.
跳转到32位模式中的下一条指令
将处理器切换为32位工作模式
下面这条指令执行的结果会将$PROT_MODE_CSEG加载到cs中,cs对应的高速缓冲存储器会加载代码段描述符
同样将$protcseg加载到ip中
  ljmp    $PROT_MODE_CSEG, $protcseg
 
  .code32                     # Assemble for 32-bit mode
protcseg:
  # Set up the protected-mode data segment registers
设置保护模式下的数据寄存器
将数据段选择子装入到ax中
  movw    $PROT_MODE_DSEG, %ax    # Our data segment selector
将ax装入到其他数据段寄存器中,在装入的同时,
数据段描述符会自动的加入到这些段寄存器对应的高速缓冲寄存器中
  movw    %ax, %ds                # -> DS: Data Segment
  movw    %ax, %es                # -> ES: Extra Segment
  movw    %ax, %fs                # -> FS
  movw    %ax, %gs                # -> GS
  movw    %ax, %ss                # -> SS: Stack Segment
  
  # Set up the stack pointer and call into C.
设置栈指针,并且调用c函数
  movl    $start, %esp
调用main.c中的bootmain函数
  call bootmain
 
  # If bootmain returns (it shouldn't), loop.
如果bootmain返回的话,就一直循环
spin:
  jmp spin
 
# Bootstrap GDT
强制4字节对齐
.p2align 2                                # force 4 byte alignment
全局描述符表
gdt:
  SEG_NULL				# null seg
代码段描述符
  SEG(STA_X|STA_R, 0x0, 0xffffffff)	# code seg
数据段描述符
  SEG(STA_W, 0x0, 0xffffffff)	        # data seg
 
全局描述符表对应的描述符
gdtdesc:
  .word   0x17                            # sizeof(gdt) - 1
  .long   gdt                             # address gdt