Linux0.12引导启动程序学习笔记(i386)

// 主题:Linux0.12引导启动程序学习笔记(i386)

// 作者:kevinjz2010@gmail.com

// 版权:kevinjz原创

// 平台:80386

// 发布日期:2011-09-03

// 最后修改:2011-09-03

// 注意事项:欢迎转载,但不得在转载的时候擅自修改、删除文章的任何部分

//-------------------------------------------------------------------------------------------------


一、启动程序执行顺序

       ROMBIOS->bootsect.S->setup.S->head.s->main.c,其中head.s以及main.c共同称为system模块。其中bootsect.S位于磁盘的第一扇区,setup.S位于随后的4个扇区,system模块则占据随后的约260扇区。

二、bootsect.S:磁盘引导块程序

       该程序驻留在磁盘的第一扇区中。

       完成功能及顺序:

1、bootsect.S将被BIOS程序加载到内存的0X7C00处开始执行;

2、bootsect.S将自己移动到0X90000处执行,通过movw指令,移动了512个字节;

3、将es,ds,ss地址都设置为代码段所在地址0x90000,并将堆栈设置在:0x90000:0xfef4(因为setup.S将要放置在0x90200处,且占据4个扇区几512*4个字节,所以SP至少要被安排在大于0x200+0x200*4+堆栈大小-12(参数表长度)的地方即至少大于0x90A00+堆栈大小-12处。此处SP设置为0x9ff00-12即0x9fef4,满足要求);

4、修改最大扇区数,方法是通过BIOS的0X1E中断将地址存于0x78处的原软驱参数(12字节)复制到0X90000:0XFEF4处,然后修改成最大18个扇区。最后再将新的软驱参数地址存入0x78(偏移)0x80(基地址);

5、移动setup.S代码至0X90200处。方法是利用BIOS中断0X13,将第二扇区的setup.S程序移至0X90200开始处,共读取四个扇区的数据,读取出错则重启驱动器重试,没有退路。

6、显示”Loading+回车+换行”共九个字节,方法是使用BIOS的0X10中断,功能好ah=0x03来读取光标位置,在使用功能号ah=0X13来显示字符串;

7、将system模块加载至0X10000处;

8、确定使用哪个根文件系统(根设备);

9、跳转至0X90200处执行setup.S程序;

三、setup.S程序

        该程序是一个操作系统加载程序。其主要功能是利用BIOS中断来读取及其系统数据,并将这些数据保存至0X90000处,将会覆盖bootsect.S的程序,这些参数将被内核相关程序使用。(包括光标位置、扩展内存数、硬盘参数表、根设备号等数据)。然后移动system模块至0x00000处,并加载IDT以及GDT,并进入开起保护模式,接着执行system模块的head.s程序。

完成功能及顺序:

1、使用BIOS中断,获取及其的系统参数,方法都类似,并将参数数据保存至0X90000。其中读取显卡信息的程序比较复杂,需要了解很多品牌显卡的参数才能明白;

2、加载完系统参数后,不再需要BIOS中断功能,所以可以将system模块移动到0X00000处,会覆盖掉BIOS设置的中断向量表。Linux0.12的system模块不会超过512kb,所以此处不会覆盖掉0X90000处保存的系统参数;

3、使用lidt以及lgdt指令从IDTR寄存器以及GDTR寄存器加载IDT以及GDT的基地址。其中IDT基地址为0X00000,限长也是0,所以现在的IDT是一个空表。GTD则设置为限长为0x800(256个表项,每个表项8字节),基地址则位于0x90200+GDT表在本程序中的偏移(暂时不明)。GDT的内容则为第一个表项空,第二个表项偏移为0x08,内容为00c09a00000007ff,此段内核代码段;第二个表项的偏移同为0x08,内容为00c09200000007ff,此段为内核数据段;

4、开启A20地址线,以访问1M以上的内存。并需要对两个8259A进行编程,重新设置中断

5、置位CR0中的PE位,开起保护模式;

6、跳转至0X00000处执行system模块中的head.s程序;

四、  head.s程序

       该程序位于system最前面的开始部分,从磁盘的第六个扇区开始放置。该程序目前位于内存的0地址处。

完成功能和顺序:

1、将所有段指向GDT中的第二段:内核数据段,然后设置堆栈段;

2、重新设置IDT。此处将IDT设置成256项,每一项都指向ignore_int中断门,之后执行lidt;

3、重新设置GDT,此处仅仅将限长从8M改为16M。并重新加载全局描述符寄存器;

4、测试A20地址线是否正确启用。方法是在0X000000处写一个值,然后在0X100000处读取,看是否是同一个值,如果是则就一直比较,进入死循环,表示A20未开启;

5、检测数字协处理器是否存在。方法是修改CR0,假设数字协处理器存在,并执行一条协处理器指令,如果出错则表示不存在。则置位PP仿真位,复位PP存在位;

6、将main()函数的参数以及地址压入堆栈,以便设置完页表后使用RET指令跳转入main函数;

7、设置页目录、4个页表,一一对应物理内存。就是将页表项都填好,内容是地址+7,从最后一项开始填,每填一项填入地址减去0x1000;

8、将页目录地址0X0000填入CR3。

9、置位PG位,开启页保护;

10、执行RET指令,弹出之前最后PUSH入栈的main()地址,并跳转执行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值