Linux上电初始化--BOSI启动和boot环境设置
2015-01-15 18:08
281 查看
BIOS:
上电启动时,最开始由硬件控制进入BIOS,BIOS代码一般存放在0xfe000~0xfffff最后几kb中;启动中cs:eip == 0xffff:0x00000 === 0xffff0 这是最开始启动的(BIOS执行的第一条指令)
下面是BIOS程序执行的内容:
1、在0x00000处开始的1kb内存空间(0x00000~0x003fff)安装中断向量表(256个中断,cs和ip各占2字节==>1kb)
2、在紧挨着它的位置用256字节创建BIOS数据区(0x400~0x4ff)
3、在0xe2ce处加载中断向量表相应的服务程序
当BIOS完成自检后,会调用中断0x19来加载第一扇区内容(启动扇区,引导扇区,bootsect)把该内容加载到0x7c00处
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
bootsect:
bootsect.s代码是磁盘引导程序,驻留在磁盘的第一号扇区中:0号磁道(柱面)、0号磁头、第一个扇区。第一号扇区是指虚拟的扇区号,比如:0号磁道、1号磁头、第一个扇区的扇区号为19(1.44Mb软驱磁道总扇区数为18,1.2Mb软驱磁道总扇区数为12);系统会检查0x7c00以后的256个字节是否为有效的引导扇区(最后两字节是否为 0xaa55)
下面是bootsect程序执行的内容:
1、规划内存
setuplen = 4 要加载的setup扇区数
bootseg = 0x07c0 (这是段地址,物理地址0x7c00),启动扇区被BIOS加载到的位置
initseg = 0x9000 将要把bootsect移动到的新位置,0x90000+0x1ff = 0x90200:后面接跟着setup
setupseg = 0x9020 setup被加载到的位置(bootsect和setup是连在一起的) 0x90200 + 512*4(十六进制为:0x800)=0x90a00
sysseg = 0x1000 内核被加载的位置,120个扇区 ==> 0x2e000
endseg = sysseg + syssize 内核的末尾位置
2、bootsect启动程序将自己512字节从0x7c00处复制到0x90000处,然后执行:
jmpi go, initseg
go:
mov ax, cs
mov ds, ax
跳转到新位置0x90000处继续执行下面的代码
3、用BIOS的系统中断调用(int 0x13)把setup(第二个扇区到第五个扇区)加载到setupseg(0x90200)
4、从第六个扇区开始把240多个扇区内核模块加载到sysseg(0x1000)处往后的120kb空间中(以磁道为单位加载),==> 0x2e000
5、确认根设备号,指的是设备
6、跳转到setup开始执行:jmpi 0,setupseg
注:堆栈ss=0x9000,sp=0xff00
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
setup:
主要是提前内核运行所需要的机器系数数据
1、提取光标和页面等数据,并调用中断向量0x41和0x46中获取硬盘参数表1和表2,分别放在0x9000:0x0080和0x9000:0x0090
2、把BIOS中提前到的数据存放在 0x90000~0x901fc (这段地址是bootsect位置,0x90000~0x901ff)
3、关中断cli,若在启动时未关中断,在销毁BIOS中断(实模式中断)与建立新中断描述符表IDT(保护模式)发生中断,系统不知道调用哪个中断系统;
4、把内核程序从0x10000处移动到0x0000处,覆盖了BIOS开始建立的中断向量表和数据区,内核从0x0000处开始
5、设置中断描述符表和全局描述符表;
中断描述符表:
32位中断机制是用中断描述符表IDT(表的位置不固定),由IDTR来跟踪访问;16位实模式中断机制是用中断向量表,位置固定在0x0000;此时IDT是一张空表,还没有开始填写中断服务程序(要使用某个中断服务程序时,把相应的位置添加到IDT表中);GDT全局描述符表,是进入保护模式的关键区别,存放各个段地址。
6、打开A20(历史遗留问题,0xfffff+1=0x0000地址回滚,24位地址时为了兼容大多数pc机上应用,也可以设置回顾)
7、对8259A中断控制芯片进行编程,在保护模式下,int 0x00~int 0x1f是被Intel保留作为内部(不可屏蔽)中断使用。
实模式下: 保护模式:
IRQ 0 ---> 0x00 0--->0x20
IRQ 1 ---> 0x01 0--->0x21
..... .....
IRQ 15---> 0x0f 15--->0x2f
8、设置CR0中的第0位PE为1,让cpu进入保护模式下运行
9、jmpi 0, 8 (1000)跳转到第一个段描述符的地方执行。进入保护模式后用跳转语句清空流水线(流水线里面的语句还是16位实模式下的)
10、跳转到的位置其实是0x0000,是内核代码开始的地方,也是head.s开始的地方
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
head:
head.s程序加载方式和前面两个不一样,它是由C语言写的编译器编译成目标代码,然后和内核一起链接成system模块。head在内核前面,也即是head在0x0000开始处,占由的内存是25KB+184B;大概要完成的工作是实现分页机制,GDT,IDT。最重要的一点是,head在0x0000处创建GDT、IDT等,这就意味着自己杀害自己来为内核提供运行条件;
1、设置系统堆栈;(为什么要这么设置不是很清楚)
2、设置临时的中断描述符表idt(其中有256项),把所有的中断描述符中服务程序都设置指向ignore_int(里面什么也没做就打印些信息),属性值设置为0x8E00,同时加载到lidt中断描述符寄存器中。其实这些中断描述符都是假的,真正有效的中断门需要到实际使用中定义(主要在asm.s和Traps.c)
3、设置临时全局描述符表gdt(三个描述符:代码描述符、数据描述符、系统段描述符),其实呢,实现很简单:开始加载全局描述符表段寄存器,而加载的是 gdt_descr,其中真正定义描述符的是 _gdt(在head文件最后);
4、检测A20是否开启,采用在0x00处写入某个数值,在1M地址上获取数据看看是否相等,若相等,则一直循环比较(也就是死机);
5、检测数学协处理器是否存在(对数学协处理器不是很熟悉),方法是修改CR0,然后执行数学协处理器命令,看是否出错;
6、为执行main函数设置环境,最后把main函数的地址压栈,然后装作调用返回(其实根本没调用,而是依靠堆栈性质)用ret(call和ret并不是非要对应出现的,这里就是只使用ret,让cpu误以为是调用返回到main中)跳转到main函数执行。
7、设置有关分页机制,在0地址开始处顺序定义5个物理页分不做页目录表(所有进程共有),4个页表(内核专用,其他新进程要使用可以在主内存中定义新的页表来处理映射)(4×1024×4kb=16MB,寻址整个16MB的主内存),把页表和主内存物理页一一映射起来。打开CR0分页机制,把页目录表地址赋值给CR3。
8、最后一步:ret,“返回到”main函数(使用堆栈性质跳转到main),开始Linux操作系统一系列的初始化工作了。
转载请注明作者和原文出处,原文地址:/article/1530634.html
若有不正确之处,望大家指正,共同学习!谢谢!!!
上电启动时,最开始由硬件控制进入BIOS,BIOS代码一般存放在0xfe000~0xfffff最后几kb中;启动中cs:eip == 0xffff:0x00000 === 0xffff0 这是最开始启动的(BIOS执行的第一条指令)
下面是BIOS程序执行的内容:
1、在0x00000处开始的1kb内存空间(0x00000~0x003fff)安装中断向量表(256个中断,cs和ip各占2字节==>1kb)
2、在紧挨着它的位置用256字节创建BIOS数据区(0x400~0x4ff)
3、在0xe2ce处加载中断向量表相应的服务程序
当BIOS完成自检后,会调用中断0x19来加载第一扇区内容(启动扇区,引导扇区,bootsect)把该内容加载到0x7c00处
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
bootsect:
bootsect.s代码是磁盘引导程序,驻留在磁盘的第一号扇区中:0号磁道(柱面)、0号磁头、第一个扇区。第一号扇区是指虚拟的扇区号,比如:0号磁道、1号磁头、第一个扇区的扇区号为19(1.44Mb软驱磁道总扇区数为18,1.2Mb软驱磁道总扇区数为12);系统会检查0x7c00以后的256个字节是否为有效的引导扇区(最后两字节是否为 0xaa55)
下面是bootsect程序执行的内容:
1、规划内存
setuplen = 4 要加载的setup扇区数
bootseg = 0x07c0 (这是段地址,物理地址0x7c00),启动扇区被BIOS加载到的位置
initseg = 0x9000 将要把bootsect移动到的新位置,0x90000+0x1ff = 0x90200:后面接跟着setup
setupseg = 0x9020 setup被加载到的位置(bootsect和setup是连在一起的) 0x90200 + 512*4(十六进制为:0x800)=0x90a00
sysseg = 0x1000 内核被加载的位置,120个扇区 ==> 0x2e000
endseg = sysseg + syssize 内核的末尾位置
2、bootsect启动程序将自己512字节从0x7c00处复制到0x90000处,然后执行:
jmpi go, initseg
go:
mov ax, cs
mov ds, ax
跳转到新位置0x90000处继续执行下面的代码
3、用BIOS的系统中断调用(int 0x13)把setup(第二个扇区到第五个扇区)加载到setupseg(0x90200)
4、从第六个扇区开始把240多个扇区内核模块加载到sysseg(0x1000)处往后的120kb空间中(以磁道为单位加载),==> 0x2e000
5、确认根设备号,指的是设备
6、跳转到setup开始执行:jmpi 0,setupseg
注:堆栈ss=0x9000,sp=0xff00
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
setup:
主要是提前内核运行所需要的机器系数数据
1、提取光标和页面等数据,并调用中断向量0x41和0x46中获取硬盘参数表1和表2,分别放在0x9000:0x0080和0x9000:0x0090
2、把BIOS中提前到的数据存放在 0x90000~0x901fc (这段地址是bootsect位置,0x90000~0x901ff)
3、关中断cli,若在启动时未关中断,在销毁BIOS中断(实模式中断)与建立新中断描述符表IDT(保护模式)发生中断,系统不知道调用哪个中断系统;
4、把内核程序从0x10000处移动到0x0000处,覆盖了BIOS开始建立的中断向量表和数据区,内核从0x0000处开始
5、设置中断描述符表和全局描述符表;
中断描述符表:
32位中断机制是用中断描述符表IDT(表的位置不固定),由IDTR来跟踪访问;16位实模式中断机制是用中断向量表,位置固定在0x0000;此时IDT是一张空表,还没有开始填写中断服务程序(要使用某个中断服务程序时,把相应的位置添加到IDT表中);GDT全局描述符表,是进入保护模式的关键区别,存放各个段地址。
6、打开A20(历史遗留问题,0xfffff+1=0x0000地址回滚,24位地址时为了兼容大多数pc机上应用,也可以设置回顾)
7、对8259A中断控制芯片进行编程,在保护模式下,int 0x00~int 0x1f是被Intel保留作为内部(不可屏蔽)中断使用。
实模式下: 保护模式:
IRQ 0 ---> 0x00 0--->0x20
IRQ 1 ---> 0x01 0--->0x21
..... .....
IRQ 15---> 0x0f 15--->0x2f
8、设置CR0中的第0位PE为1,让cpu进入保护模式下运行
9、jmpi 0, 8 (1000)跳转到第一个段描述符的地方执行。进入保护模式后用跳转语句清空流水线(流水线里面的语句还是16位实模式下的)
10、跳转到的位置其实是0x0000,是内核代码开始的地方,也是head.s开始的地方
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
head:
head.s程序加载方式和前面两个不一样,它是由C语言写的编译器编译成目标代码,然后和内核一起链接成system模块。head在内核前面,也即是head在0x0000开始处,占由的内存是25KB+184B;大概要完成的工作是实现分页机制,GDT,IDT。最重要的一点是,head在0x0000处创建GDT、IDT等,这就意味着自己杀害自己来为内核提供运行条件;
1、设置系统堆栈;(为什么要这么设置不是很清楚)
2、设置临时的中断描述符表idt(其中有256项),把所有的中断描述符中服务程序都设置指向ignore_int(里面什么也没做就打印些信息),属性值设置为0x8E00,同时加载到lidt中断描述符寄存器中。其实这些中断描述符都是假的,真正有效的中断门需要到实际使用中定义(主要在asm.s和Traps.c)
3、设置临时全局描述符表gdt(三个描述符:代码描述符、数据描述符、系统段描述符),其实呢,实现很简单:开始加载全局描述符表段寄存器,而加载的是 gdt_descr,其中真正定义描述符的是 _gdt(在head文件最后);
4、检测A20是否开启,采用在0x00处写入某个数值,在1M地址上获取数据看看是否相等,若相等,则一直循环比较(也就是死机);
5、检测数学协处理器是否存在(对数学协处理器不是很熟悉),方法是修改CR0,然后执行数学协处理器命令,看是否出错;
6、为执行main函数设置环境,最后把main函数的地址压栈,然后装作调用返回(其实根本没调用,而是依靠堆栈性质)用ret(call和ret并不是非要对应出现的,这里就是只使用ret,让cpu误以为是调用返回到main中)跳转到main函数执行。
7、设置有关分页机制,在0地址开始处顺序定义5个物理页分不做页目录表(所有进程共有),4个页表(内核专用,其他新进程要使用可以在主内存中定义新的页表来处理映射)(4×1024×4kb=16MB,寻址整个16MB的主内存),把页表和主内存物理页一一映射起来。打开CR0分页机制,把页目录表地址赋值给CR3。
8、最后一步:ret,“返回到”main函数(使用堆栈性质跳转到main),开始Linux操作系统一系列的初始化工作了。
转载请注明作者和原文出处,原文地址:/article/1530634.html
若有不正确之处,望大家指正,共同学习!谢谢!!!
相关文章推荐
- linux的几个内核镜像格式Image 和 u-boot启动内核和文件系统时的一些环境变量的设置
- Linux 系统启动文件,bash 启动文件,设置环境变量的位置
- 在linux系统中通过fw_printenv查看和设置u-boot中的环境变量
- Linux 环境下tomcat 开机启动设置
- Linux 启动文件、设置环境变量的位置
- mips架构linux启动分析(四)(接收bios信息和环境初始化)
- Linux 启动文件、设置环境变量的位置
- 关于JAVA在linux启动时的环境变量设置
- Linux环境设置Oracle随系统启动
- linux环境下设置程序为自动启动程序
- 在linux环境下静默安装oracle 11g后设置自动启动
- Linux 启动时,先读取用户环境变量,即 /home/defonds/.bashrc 的设置。次读取所有用户环境变量,即 /etc/profile 中的配置。
- oracle的环境配置-设置开机(Linux)自动启动Oracle关机自动关闭Oracle
- Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建
- Linux启动文件、设置环境变量的位置
- Spring boot 设置启动是否WEB环境
- 启动期间的内存管理之bootmem_init初始化内存管理--Linux内存管理(十二)
- Tomcat 自启动环境参数的设置及 Linux 脚本的启动顺序
- linux 中设置开机启动,加入环境变量
- 重装Windows XP系统之后如何恢复Linux启动选单 以及boot设置 解决方法