首页 > 代码库 > [Operationg System Labs] 我对 Linux0.00 中 boot.s的理解和注释
[Operationg System Labs] 我对 Linux0.00 中 boot.s的理解和注释
(如有错误请立即指正,么么哒!)
! boot.s
!
! It then loads the system at 0x10000, using BIOS interrupts. Thereafter
! it disables all interrupts, changes to protected mode, and calls the
! 从0X10000开始,使用BIOS中断加载系统。
! 使所有的中断失灵,转入保护模式……
BOOTSEG = 0x07c0
! boot段,BIOS会自动将操作系统的程序加载到这一段中开始执行,这时连操作系统都不会存在。
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
! Linus注释说系统加载到0x10000,我们一会回来看这个SYSSEG是做什么用的……(算作遗留问题①)
SYSLEN = 17 ! sectors occupied.
! sectors的意思是删去,这里指占用了17个扇区。
! bochs中模拟的加载器是一个1.2MB的软盘,软盘这里一个扇区的大小是512B
entry start
start:
jmpi go,#BOOTSEG
! jmpi 是段内跳转,由于是在实模式下,后面的操作数是段的基地址。
! 这句的意思就是说跳到以#BOOTSEG为基址的段的偏移为go的地方,go在哪里?往下看:
go: mov ax,cs
mov ds,ax
mov ss,ax
! 上面的jmpi的意思就是从这个go开始的,接着是3条mov指令,把cs置给ds和ss
! 表示在这段代码中,代码段,数据段和堆栈段是重叠的
mov sp,#0x400 ! arbitrary value >>512
! arbitrary value:猜猜它是啥意思?(遗留问题②)">>"是远大于。
! ok, we‘ve written the message, now
! 好的,我们已经写好了message(这个message在哪里?遗留问题③)
load_system:
! 开始加载系统啦!
! int 0x13是BIOS自留的中断指令,显然在这里要读取磁盘的内容,并且装载到某一个地方,
! 这个地方是哪里呢?(遗留问题④)
mov dx,#0x0000 ! 32-bit下立即数都要以#开头
mov cx,#0x0002 ! 置dx和cx,为 int 0x13做准备
mov ax,#SYSSEG
mov es,ax ! 把#SYSSEG装载到了es中
xor bx,bx ! 清空bx
mov ax,#0x200+SYSLEN ! ah置为0x02,al置为0x11(十进制就是17)
int 0x13
! AH=02H 指明要读扇区
! AL:要读的扇区数
! DL:驱动器号
! DH:所读磁盘的磁头号(系统在0号驱动器的第0磁头所指位置)
! CH 磁道号的低8位数
! CL 低5位放入所读起始扇区号,位7-6表示磁道号的高2位
! ES:BX 读出数据的缓冲区地址。
! 返回参数:
! 如果CF=1,AX中存放出错状态。读出后的数据在ES:BX区域依次排列。
jnc ok_load
! 表示CF没有被置位,即没有出错,跳转到 ok_load 这里去执行,否则就die,死循环在这里
! 下面解决一下遗留问题④,系统代码被读到那里去了?ES:BX在调用int 0x13前是0x1000:0x0000,
! 请看文档开头,这确实也是作者希望系统被加载到的位置。同时解决了遗留问题①
die: jmp die
! now we want to move to protected mode ...
! 下面开始跳到保护模式啦!
ok_load:
cli ! no interrupts allowed !
! 关闭中断……这个时候因为要重新设置中断向量表,覆盖掉原来的中断描述符,所以要把中断关闭了
mov ax, #SYSSEG
mov ds, ax ! ds置成#SYSSEG,表示系统代码开始的地方
xor ax, ax ! ax清零
mov es, ax ! es置零
mov cx, #0x2000 !cx置0x2000
sub si,si
sub di,di ! si,di清零
rep
movw
! rep表示将movw指令重复cx寄存器中的数次,默认将DS:SI所指的数据移动到ES:DI所指的位置,ES是零,显然把0x1000位置的代码转移到0x0000
! 从ds:si = 0x1000:0x0000 复制到 es:di = 0x0000:0x0000为止
mov ax, #BOOTSEG
mov ds, ax
! 将ds段设置为0x7c00,这个位置是boot.s存储的位置,我认为目前boot.s不仅存在于这个0x7c00处,还存在于内存最开始处。
! 重新设置这个0x7c00的目的是为了从这里取数据啊!你看看这不马上就从这里取出idt_48和gdt_48了么!
! 17个扇区的系统有多大呢?17*512B < 9KB < 64KB(0x00000--0x10000),因此不会覆盖后面的东西
lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever appropriate
! 设置idtr和gdtr两个寄存器的值,具体的值参见后面idt_48和gdt_48这两个位置
! 进入保护模式之前要做三件事,或者说三个步骤:
! 1~设置好gdt
! 2~设置gdtr,因为这直接决定了进入保护模式后第一条指令的寻址方式
! 3~更改状态字
! absolute address 0x00000, in 32-bit protected mode.
mov ax,#0x0001 ! protected mode (PE) bit
! 在这里lmsw加载状态字,把PE置位后,进入保护模式……
lmsw ax ! This is it!
jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
! 跳到cs中的0处开始执行啦,就是进入head.s了吧
! 为什么是8呢?在这里,系统已经进入了保护模式了。8在这里表示的是段选择符。
! (段选择符16位,高13位为索引,第2位用来区分gdt(0)还是ldt(1),第1,0位用来表示特权级,
! 因此当选定8的时候,表示0000000000001000,求出选择哪一段的方法是将段选择符右移三位,8表示选中gdt表第1项,内核代码段)
! 下文设置了gdt全局描述符表,这个gdt符表的地址有"gdt"这个标记来标定
! gdt表项有8个字节,于是在gdt_48那里说到有256个entires,也就是256项
! 后面的数值具体表示的意思都在后面由Linus自己注释清楚了。
gdt: .word 0,0,0,0 ! dummy
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb),段长8MB
.word 0x0000 ! base address=0x00000,基地址0x00000,实模式下有20位地址码,2^20寻址空间。
.word 0x9A00 ! code read/exec,表示代码段,可读可执行
.word 0x00C0 ! granularity=4096, 386,颗粒度(我不懂)
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0x00000
.word 0x9200 ! data read/write,数据段,可读可写
.word 0x00C0 ! granularity=4096, 386
idt_48: .word 0 ! idt limit=0(这个地方就是所谓的屏蔽中断)
.word 0,0 ! idt base=0L
gdt_48: .word 0x7ff ! gdt limit=2048, 256 GDT entries
.word 0x7c00+gdt,0 ! gdt base = 07xxx
! 在这个状态下的gdt是设置在这个位置的。
! gdtr的高32位表示gdt基地址,低16位表示gdt的长度,在这里0x7ff表示0-2047,也就是limit=2048
.org 510
! 最后在说一下子这个指令,这个指令是让下面一条指令开始在510这个位置的,也就是说,它规定了boot.s的长度是512B,最后还有一个2B长的数
.word 0xAA55
[Operationg System Labs] 我对 Linux0.00 中 boot.s的理解和注释