硬件开机
从硬件开机到系统启动,都经历了什么 BIOS做完计算机的初始化后,将会选择启动设备的第一个扇区进行加载,并将CPU控制权转移到 0x7C00的位置,将由Bootloader完成从实模式到保护模式的切换
bootsect.s
start
start就是Linux的bios启动程序入口,被加载到0x7c00处 给ds赋值BOOTSEG作为数据加载段,es为INITSEG作为代码拷贝处,使用rep汇编指令身内存拷贝到INITSEG,并跳转到INITSEG执行。
start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG
INITSEG
设置堆栈0x9ff00
go: mov ax,cs
mov ds,ax
mov es,ax
! put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
使用BIOS中断INT 13从磁盘第二个扇区加载setup
load_setup:
! ah = 0x02 - 读磁盘扇区到内存;al = 需要读出的扇区数量;
! ch = 磁道(柱面)号的低8 位; cl = 开始扇区(0-5 位),磁道号高2 位(6-7);
! dh = 磁头号; dl = 驱动器号(如果是硬盘则要置位7);
! es:bx ??指向数据缓冲区; 如果出错则CF 标志置位。
mov dx,#0x0000 ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
mov dx,#0x0000
mov ax,#0x0000 ! reset the diskette
int 0x13
j load_setup
打印字符串(省略代码),验证读取的扇区并跳转到setup.s 程序的开始处,程序继续执行,bootsect.s职责完成。
seg cs
mov ax,root_dev
cmp ax,#0
jne root_defined
seg cs
mov bx,sectors
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
cmp bx,#15
je root_defined
mov ax,#0x021c ! /dev/PS0 - 1.44Mb
cmp bx,#18
je root_defined
undef_root:
jmp undef_root
root_defined:
seg cs
mov root_dev,ax
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
jmpi 0,SETUPSEG
setup.s
setup负责从bios中读取数据并将它复制到系统内存中
start
...
!获取扩展内存大小
mov ah,#0x88
int 0x15
mov [2],ax
!获取显示卡的模式
mov ah,#0x0f
int 0x10
mov [4],bx ! bh = display page
mov [6],ax ! al = video mode, ah = window width
!检查显示方式(EGA/VGA)并取参数。
mov ah,#0x12
mov bl,#0x10
int 0x10
mov [8],ax
mov [10],bx
mov [12],cx
...
移动内存
将system移到正确的地方,把从0x10000 到0x8ffff移动到0x00000位置。
mov ax,#0x0000
cld ! 'direction'=0, movs moves forward
do_move:
mov es,ax ! destination segment
add ax,#0x1000
cmp ax,#0x9000
jz end_move
mov ds,ax ! source segment
sub di,di
sub si,si
mov cx,#0x8000
rep
movsw
jmp do_move
IDT,GDT
idt_48:
!描述表长度
.word 0 ! idt limit=0
!描述表基地址
.word 0,0 ! idt base=0L
gdt_48:
!段最大长度限制
.word 0x800 ! gdt limit=2048, 256 GDT entries
!描述表基地址
.word 512+gdt,0x9 ! gdt base = 0X9xxxx
将内存移动完成后,使用lidt 加载中断描述符寄存器(IDT)。lgdt加载全局描述符表
end_move:
!恢复ax寄存器的SETUPSEG值
mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)
mov ds,ax
lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever appropriate
保护模式
主要是通过修改CR0寄存器进入保护模式
段选择符长度为16位 位0-1表示请求的特权级0-3(0-3环) 位1-2标识是全局或者局部描述符 位3-15是描述符表项的索引 段8(0b0000,0000,0000,1000) 表示请求特权级0、使用全局描述符表中的第1 项,由下面的代码可知基地址是0
gdt:
.word 0,0,0,0 ! dummy
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386
lmsw修改cr0寄存器进入保护模式,然后跳转到cs段8 offset0处,执行system的代码
mov ax,#0x0001 ! protected mode (PE) bit
lmsw ax ! This is it!
!跳转到cs段8 offset0处
jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
head.s
head.s是从绝对地址0x00000000开始
startup_32
startup_32:
!装载段寄存器
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
!设置系统堆栈
lss _stack_start,%esp
!装载idt,gdt子程序
call setup_idt
call setup_gdt
movl $0x10,%eax # reload all the segment registers
mov %ax,%ds # after changing gdt. CS was already
mov %ax,%es # reloaded in 'setup_gdt'
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
!A20 地址线是否已经开启
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b
...
jmp after_page_tables
调用main函数
after_page_tables:
pushl $0 # These are the parameters to main :-)
pushl $0
pushl $0
pushl $L6
!压入跳转的main地址
pushl $_main
jmp setup_paging
...
!首先对5 页内存(1 页目录 + 4 页页表)清零
movl $1024*5,%ecx
xorl %eax,%eax
xorl %edi,%edi
cld;rep;stosl
!设置页目录中的项
movl $pg0+7,_pg_dir
movl $pg1+7,_pg_dir+4
movl $pg2+7,_pg_dir+8
movl $pg3+7,_pg_dir+12
movl $pg3+4092,%edi
!最后1 项对应物理内存页面的地址是0xfff000,加上属性标志7,即为0xfff007
movl $0xfff007,%eax
std
1: stosl
subl $0x1000,%eax
jge 1b
xorl %eax,%eax
!设置启动使用分页处理(cr0 的PG 标志,位31)
movl %eax,%cr3
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0
!弹出压入的main地址,开始执行main.c程序
ret
head.s执行完后的内存视图
关注我的技术公众号 不定期分析各种技术文章
|