笔记来自哈工大李志军老师课程,另补充了源码
1、通电时x86pc的cpu处于实模式,
2、CS = 0xFFFF IP = 0000
3、寻址0xFFFF0 (这是属于bios中的一块内存空间)
4、检查RAM、键盘、显示器、软硬磁盘
5、将磁盘0道0扇区读入0x7c00处,(512Byte,OS引导)(MBR :最多446字节的启动代码、4个硬盘分区表项(每个表项16字节,共64字节)、2个签名字节(0x55,0xAA))
6、设置CS=07c0H, IP=0x0000,
在linux0.11中MBR启动指令是文件bootsect.s(as86汇编)
bootsect.s中的常量
其中第一个扇区加载到07c00H处后,就立即被移动到了90000H,源码中移动了512个字节,刚好将第一个扇区完全移动走
jmpi go INITSEG (go(代表程序起始的位置到go这个指令所在的位置) 对应IP INITSEG(初始直接赋值为9000H)对应CS) 这句话保证程序移动后依然可以正常按顺序执行
7、......经过一些指令后 int 13 (执行13号中断程序) (前面一段指令就是为13号中断程序做准备)
13号中断 是读磁盘扇区的中断,ah =0x02 代表读磁盘 al为扇区数量(为4),ch柱面号(为2,因为boot在第一个扇区) ,cl 开始位置 ,
dh 磁头号00 dl驱动号00 es:bx 内存地址(因为要紧挨着bootsect,因此这里应被之前置为90200H),
8、打印出屏幕开机提示,loading system。。。此时就在屏幕显示这句话时,cpu将os读到内存中(10000H),
9、jmpi 0 SETUPSEG(IP:0 CS:9020H) (跳转到setup.s程序中执行)
此时操作系统在10000H处
setup.s
1、...执行几句指令后(这些指令将所有段寄存器,都置为9000),通过#0x888 为参数 int 0x15 中断程序 获取内存的大小 获取的值放入ax中 ,
然后将ax 赋值到 [2] (间接寻址,前面默认加上段寄存器,就是这里的地址) 0x90002 这里存放着扩展内存大小 (最初只有1M内存(x86,24根地址线),1M 以后的内存就叫做扩展内存)
1、取出光标的 位置,包括 其他参数到0x90000处
2、int0x15中断 ,获取内存的大小放入到0x90002位置处 ,(最初x86机器内存大小1M(20位地址线),多余的就叫做扩展内存)
3、执行do_move将操作系统移动到由0开始的内存空间中 (linux 0.11,中将10000H 移动到00000H,移动了 0x8000次),
之前将bootsect 从0x 07c00处移动走也是因为要为这里移动操作系统留下空间
ds si es di (一般movw,movsw(移动一个字) 的源地址和目的地址)
并且不修改任何操作数:CMP destination,source ,所以在linux中cmp ax #0x9000 后ax 还是 1000
4、setup 引导cpu进入保护模式,(具体指令是在setup.s最后 mov ax , #0x0001 mov cro ax jmpi 0 8 (这是第一条32位指令, 意思是跳转到0地址处,cs<<4 +IP已经不是这条指令的取址方式了) )
32 位下cpu的解释程序发生了变化,
CR0寄存器:
一个32位寄存器 【|PG |.......(30位)......|PE|】 PE = 0就是实模式 16位 PE = 1 就是保护模式 PG =1 启动分页
(上面指令,把1 给了ax 再传到PE位上)
5、保护模式下地址翻译和中断处理
保护模式下地址翻译 :gdt global descriptor table( 是通过 cpu 硬件实现的 ,为了地址翻译够快)
此时cs 叫做选择子 ,里面存放的是查表的下标,就是gdt表项的索引,真正的基地址放在表项中,
setup会初始化gdt表 ,然后lgdt gdt 48 (这条指令是用来设置保护模式下中断和寻址方式的)
每个表项64位,8是通过字节寻址8*8,就是第二个表项
此时最后的 jmpi 0 8 彻底进入到操作系统中了
这里既然要进行严格控制每段程序的存放位置和执行程序,因此在编译的过程中要严格按照顺序进行编译,因此需要用一个编译工具 make系统源码中有makefile
head.s
( setup.s进入保护模式后,这里还有一些初始化操作 设置系统栈,开启A20等 ,这里是32位汇编代码,源操作数在左目标操作数在右侧,16位下是源在右目标在左侧)
head.s
在此程序最后 执行 setup_paging设置页表,最后ret,弹出setup_paging 后就进入到c文件范围,执行了一个永不停止的main 程序
执行main.c 中间的mian函数,
跳出方式,-----c语言组后都需要编译成汇编 , c函数跳转的方式,也就和这里跳转的方式一样
c函数执行过程
A (){B();返回位置} 这里在调用B 函数时会先记录 B函数的返回地址,然后 jmp B 程序开始地址 B函数在执行结束后会ret 到这个返回地址上继续执行
head.s源码部分
这里清楚的说明了不能返回到L6,L6有个死循环程序,这里可以猜出os是个永远不会有返回结果的一段程序,永远执行不完
main.c
main中做了一系列初始化操作
概括:整个内容可以包括两个部分,1,读入内存 2,初始化