原文链接:

http://www.cppblog.com/airtrack/archive/2014/10/30/208729.html

Bootloader

我们知道计算机启动是从BIOS开始,再由BIOS决定从哪个设备启动以及启动顺序,比如先从DVD启动再从硬盘启动等。计算机启动后,BIOS根据配置找到启动设备,并读取这个设备的第0个扇区,把这个扇区的内容加载到0x7c00,之后让CPU从0x7c00开始执行,这时BIOS已经交出了计算机的控制权,由被加载的扇区程序接管计算机。
这第一个扇区的程序就叫Boot,它一般做一些准备工作,把操作系统内核加载进内存,并把控制权交给内核。由于Boot只能有一个扇区大小,即512字节,它所能做的工作很有限,因此它有可能不直接加载内核,而是加载一个叫Loader的程序,再由Loader加载内核。因为Loader不是BIOS直接加载的,所以它可以突破512字节的程序大小限制(在实模式下理论上可以达到1M)。如果Boot没有加载Loader而直接加载内核,我们可以把它叫做Bootloader。
Bootloader加载内核就要读取文件,在实模式下可以用BIOS的INT 13h中断。内核文件放在哪里,怎么查找读取,这里牵涉到文件系统,Bootloader要从硬盘(软盘)的文件系统中查找内核文件,因此Bootloader需要解析文件系统的能力。GRUB是一个专业的Bootloader,它对这些提供了很好的支持。
对于一个Toy操作系统来说,可以简单处理,把内核文件放到Bootloader之后,即从软盘的第1个扇区开始,这样我们可以不需要支持文件系统,直接读取扇区数据加载到内存即可。

实模式到保护模式

我们知道Intel x86系列CPU有实模式和保护模式,实模式从8086开始就有,保护模式从80386开始引入。为了兼容,Intel x86系列CPU都支持实模式。现代操作系统都是运行在保护模式下(Intel x86系列CPU)。计算机启动时,默认的工作模式是实模式,为了让内核能运行在保护模式下,Bootloader需要从实模式切换到保护模式,切换步骤如下:
  1. 准备好GDT(Global Descriptor Table)
  2. 关中断
  3. 加载GDT到GDTR寄存器
  4. 开启A20,让CPU寻址大于1M
  5. 开启CPU的保护模式,即把cr0寄存器第一个bit置1
  6. 跳转到保护模式代码
GDT是Intel CPU保护模式运行的核心数据结构,所有保护模式操作的数据都从GDT表开始查找,这里有GDT的详细介绍。
GDT中的每一个表项由8字节表示,如下图:
其中Access Byte和Flags如下图:
这里是详细说明。
GDTR是一个6字节的寄存器,有4字节表示GDT表的基地址,2字节表示GDT表的大小,即最大65536(实际值是65535,16位最大值是65535),每个表项8字节,那么GDT表最多可以有8192项。
实模式的寻址总线是20bits,为了让寻址超过1M,需要开启A20,可以通过以下指令开启:
    in al, 0x92
    or al, 2
    out 0x92, al
把上述步骤完成之后,我们就进入保护模式了。在保护模式下我们要使用GDT通过GDT Selector完成,它是GDT表项相对于起始地址的偏移,因此它的值一般是0x0 0x8 0x10 0x18等。

ELF文件

Bootloader程序是原始可执行文件,如果程序由汇编写成,汇编编译器编译生成的文件就是原始可执行文件,也可以使用C语言编写,编译成可执行文件之后通过objcopy转换成原始可执行文件,这篇文章介绍了用C语言写Bootloader。
那么内核文件是什么格式的呢?跟Bootloader一样的当然可以。内核一般使用C语言编写,每次编译链接完成之后调用objcopy是可以的。我们也可以支持通用的可执行文件格式,ELF(Executable and Linkable Format)即是一种通用的格式,它的维基百科
ELF文件有两种视图(View),链接视图和执行视图,如下图:

链接视图通过Section Header Table描述,执行视图通过Program Header Table描述。Section Header Table描述了所有Section的信息,包括所在的文件偏移和大小等;Program Header Table描述了所有Segment的信息,即Text Segment, Data Segment和BSS Segment,每个Segment中包含了一个或多个Section。

对于加载可执行文件,我们只需关注执行视图,即解析ELF文件,遍历Program Header Table中的每一项,把每个Program Header描述的Segment加载到对应的虚拟地址即可,然后从ELF header中取出Entry的地址,跳转过去就开始执行了。对于ELF格式的内核文件来说,这个工作就需要由Bootloader完成。Bootloader支持ELF内核文件加载之后,用C语言编写的内核编译完成之后就不需要objcopy了。

操作系统实现(一):从Bootloader到ELF内核(转载)的更多相关文章

  1. BootLoader与Linux内核的参数传递【转】

    本文转载自:http://blog.sina.com.cn/s/blog_476d8cf30100rttx.html 在嵌入式系统中,BootLoader 是用来初始化硬件,加载内核,传递参数.因为嵌 ...

  2. 操作系统开发系列—12.c.从Loader加载ELF内核,顺便解释下函数调用过程 ●

    实际上,我们要做的工作是根据内核的Program header table的信息进行类似下面这个C语言语句的内存复制: memcpy(p_vaddr, BaseOfLoaderPhyAddr+p_of ...

  3. 自制操作系统小样例——参考部分linux0.11内核源码

    详细代码戳这里. 一.启动引导 采用软件grub2进行引导,基于规范multiboot2进行启动引导加载.multiboot2的文档资料戳这里. 二.具体内容 开发环境 系统环境:Ubuntu 14. ...

  4. 操作系统学习笔记5 | 用户级线程 && 内核级线程

    在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...

  5. nrf528xx bootloader 模块介绍(转载)

    转载https://www.cnblogs.com/rfnets/p/8205521.html 1. bootloader 的基本功能: 启动应用 几个应用之间切换 初始化外设 nordic nrf5 ...

  6. Lab_1:练习4——分析bootloader加载ELF格式的OS的过程

    一.实验内容 通过阅读bootmain.c,了解bootloader如何加载ELF文件.通过分析源代码和通过qemu来运行并调试bootloader&OS, bootloader如何读取硬盘扇 ...

  7. qemu 模拟-arm-mini2440开发板-启动u-boot,kernel和nfs文件系统

    qemu 本文介绍了如何编译u-boot.linux kernel,然后用qemu启动u-boot和linux kernel,达到与开发板上一样的学习效果! 虽然已经买了2440开发板,但是在实际学习 ...

  8. qemu 模拟-arm-mini2440开发板-启动u-boot,kernel和nfs文件系统【转】

    转自:http://www.cnblogs.com/riskyer/p/3366001.html qemu 本文介绍了如何编译u-boot.linux kernel,然后用qemu启动u-boot和l ...

  9. ucore实验Lab1知识点总结

    Intel 80386 ucore目前支持的硬件环境是基于Intel 80386以上的计算机系统. Intel 80386是80x86系列中的第一种32位微处理器.80386的内部和外部数据总线都是3 ...

随机推荐

  1. IDEA 学习笔记之 Console显示日志大小

    Console显示日志大小: IntelliJ IDEA默认的Output输出缓存区大小只有1024KB,超过大小限制的就会被清除,而且还会显示[too much output to process] ...

  2. TCP/UDP的小事情

    UDP: 没有复杂的控制机制,面向无连接的通信服务. 常用于: 包总量少的通信 音视频传输(即时通信) TCP: 对传输.发送.通信.进行控制的协议.面向有连接的协议,只有在确认通信对端存在时才会发送 ...

  3. HTML5 video视频字幕的使用和制作

    一.video支持视频格式: 以下是三种最常用的格式 1. ogg格式:带有Theora视频编码(免费)+Vorbis音频编码的Ogg文件(免费) 支持的浏览器:firefox.chrome.oper ...

  4. SpringBoot自动注入分析

    我们经常会被问到这么一个问题:SpringBoot相对于spring有哪些优势呢?其中有一条答案就是SpringBoot自动注入.那么自动注入的原理是什么呢?我们进行如下分析. 1:首先我们分析项目的 ...

  5. SQL SERVER数据库,按年、月、日、时、分、秒计算两个时间字段之间的间隔时间样例

    使用DATEDIFF(取值,时间字段1,时间字段2) 举例: SELECT DATEDIFF(YEAR,DRYSJ,DCYSJ),* FROM YXHIS2019..TBZYBR2019 --SQL ...

  6. conversion function——转换函数

    类型转换函数 与 explicit关键字 1.类型转换函数 在C++中,可以使用构造函数将一个指定类型的数据转换为类的对象,也可以使用类型转换函数 (type conversion function) ...

  7. 当前机器的各种进程、服务信息的收集(win)

    当前机器的各种进程.服务信息的收集(win) 前言 我们在做渗透测试的过程中,遇到Windows系统的环境是最多的,然而在拿到一台Windows胸膛呢权限之后,我们要进行横向渗透测试或者纵向渗透测试, ...

  8. PHP next

    1.函数的作用:返回数组当前元素位置的下一个元素 2.函数的参数: @param array &$array 3. 例子一:数组拷贝时,内部指针的位置也一起拷贝 <?php $arr1 ...

  9. opencv实践::对象计数

    问题描述 真实案例,农业领域经常需要计算对象个数 或者在其它领域拍照自动计数,可以提供效率,减低成本 解决思路 通过二值分割+形态学处理+距离变换+连通区域计算 #include <opencv ...

  10. opencv::图像上采样和降采样

    图像金字塔概念 . 我们在图像处理中常常会调整图像大小,最常见的就是放大(zoom in)和缩小(zoom out),尽管几何变换也可以实现图像放大和缩小,但是这里我们介绍图像金字塔 . 一个图像金字 ...