JOS(1) BootLoader

JOS在启动的过程(bootload过程)中一共经历了三个阶段,分别是BIOS加载以及硬件检测、实模式向保护模式的转变以及加载kernel。故本文组织过程也按照启动过程依次叙述。

一、BIOS加载

计算机系统在电源键开启后,首先接管整个系统的是BIOS。BIOS主要完成以下几个内容:

  • 对计算机的硬件进行检测
  • 加载启动文件

在更进一步的叙述BIOS之前,我们需要简单的了解x86的物理地址空间。下图是一个典型的x86地址空间,这里需要注意的是,BIOS的内存空间是从0x000F 0000 ~ 0x0010 0000共64KB。在启动电源后,BIOS将会加载到上述的内存空间内。

绘图1.jpg

按照惯例,BIOS将会被加载到CS:0xF000,IP:0xFFF0 处,即0x000F FFF0。由于此地址已经非常接近于0x0010 0000,留给BIOS执行的内存空间过少,所以BIOS加载执行的第一条指令就是:
ljmp $0xF000, $0xE05B
将自己移动到较低的地址空间,以保证足够的内存使其能够继续执行。在BIOS运行的过程中,将设立终中断述表以及初始化各种设备,当完成上述过程后,BIOS搜索能够启动的设备(如软盘、硬盘、CD-ROM等),对该设备的第一个扇区进行读取,最后将控制权转移给扇区中存放的bootloader
这里需要说明的是:(1)在上述启动的过程中,CPU是运行在实模式下; (2)对于硬盘来说,最基本存储单元是扇区(sector),每个扇区容量为512个字节。对于一个可启动的硬盘,其第一个扇区必须是bootloader,故bootloader不能占据过大的空间。

二、保护模式

在JOS中,bootloader将完成两部分工作:

  • 实模式转变为保护模式
  • 加载内核文件

在这部分中,我们将简要的叙述实模式向保护模式的转变过程(/boot/boot.S)。保护模式下,系统将具有更大的寻址空间,并提供虚拟内存、分段、分页等机制。保护模式下的JOS介绍将会在后续的文章中介绍。
在这里需要注意的是,开启保护模式涉及到了cr0寄存器下的PE位(即第0位),当PE置1后,CPU将开启保护模式,此时保护模式下的分段保护机制将会被一同开启(分页机制没有开启),故在开启保护模式前,需要设置好全局描述符表。
JOS中对于保护模式的开启有以下代码:

lgdt   gdtdesc                      # 加载全局描述符表
movl   %cr0,         %eax
orl    $CR0_PE_ON,   %eax           # CR0_PE_ON = 0x1
movl   %eax,         %cr0           # 开启保护模式

gdt为预先设定好的全局描述符表。对于全局描述符表的介绍将会在下一篇文章中涉及,在这里需要知道的是,全局描述符表的第0项存储的内容必须为空(即为0)

gdt:
    SEG_NULL                                 # 空项
    SEG(STA_X | STA_R, 0x0, 0xffffffff)      # 代码段
    SEG(STA_W, 0x0, 0xffffffff)              # 数据段
gdtdesc:
    .word 0x17        # 在全局描述符表中共设置了三项,每项8字节,共24字节,故在此处设为(24-1),即0x17
    .long gtd

对于SEG_NULL、SEG()定义如下:

#define SEG_NULL     \
        .word 0, 0;  \
        .byte 0, 0, 0, 0
#define SEG(type, base, lim)                                   \
        .word  (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
        .byte  (((base) >> 16) & 0xff), (0x90 | (type)),       \
               (0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)

可以看出,在gdt中,将代码段和数据段全部映射到了4GB内存空间中,这对于启动过程来说,是完全够用的。

三、加载内核

这一部分中,主要完成的工作就是将内核文件加载到内存中(/boot/main.c),并将控制权限交给内核。在更进一步的介绍之前,首先阐述ELF文件格式。对于ELF文件格式的定义在<inc/elf.h>中。我们无需深入的了解ELF文件格式(如希望深入了解的话,在MIT6.828的指定文献中列出了ELF文件的详细格式内容),实际上来说,ELF类似于一个超大的“结构体”,每一个部分都存放了一定的内容,而对于该内容的描述在“头部”中存放。这里给出了JOS下<inc/elf.h>中的定义以及解释。

struct Elf {
    uint32_t e_magic;   // must equal ELF_MAGIC
    uint8_t e_elf[12];
    uint16_t e_type;     // 表示该文件类型
    uint16_t e_machine;  // 运行该程序需要的体系结构
    uint32_t e_version;  // 文件版本
    uint32_t e_entry;    // 程序入口地址
    uint32_t e_phoff;    // Program header table在文件中的偏移量(以字节计数)
    uint32_t e_shoff;    // Section header table在文件中的偏移量
    uint32_t e_flags;    // 对于IA32来说,计为0
    uint16_t e_ehsize;   // 表示ELF header大小
    uint16_t e_phentsize; // Program header table中每一项目的大小
    uint16_t e_phnum;     // Program header table有多少个项目
    uint16_t e_shentsize; // Section header table中每一项目的大小
    uint16_t e_shnum;     // Section header table有多少个项目
    uint16_t e_shstrndx;  // 包含节名称的字符串是第几个节(0开始计数)
};

struct Proghdr {
    uint32_t p_type;     // 当前Program header所描述的段的类型
    uint32_t p_offset;   // 段的第一个字节在文件中的偏移
    uint32_t p_va;       // 段的一个字节在内存中的虚拟地址
    uint32_t p_pa;       // 在物理内存定位的相关系统中,此项是为物理地址保留的
    uint32_t p_filesz;   // 段在文件中的长度
    uint32_t p_memsz;    // 段在内存中的长度
    uint32_t p_flags;    // 与段相关的标志
    uint32_t p_align;    // 根据此值来确定段在文件以及内存中如何对齐
};

有了上述的认识,就可以很容易的读懂下述代码。下述代码主要是将内核读取到磁盘中,并最后将控制权移交给内核。

#define SECTSIZE    512
#define ELFHDR    ((struct Elf *) 0x10000) // scratch space

void readsect(void*, uint32_t);
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
void readseg(uint32_t, uint32_t, uint32_t);

void
bootmain(void)
{
    struct Proghdr *ph, *eph;

    // read 1st page off disk
    // 可以看出,内核加载于0x10000处之上,一共加载了512字节 * 8 = 4K,即分页模式下一个完整的页的大小
    readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);

    // is this a valid ELF? JOS中要求ELF文件的第一项必须为ELF_MAGIC
    if (ELFHDR->e_magic != ELF_MAGIC)
        goto bad;

    // load each program segment (ignores ph flags) 
    // 加载代码段, 可以看出每个代码段都规定了加载的位置以及大小
    ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
    eph = ph + ELFHDR->e_phnum;
    for (; ph < eph; ph++)
        // p_pa is the load address of this segment (as well
        // as the physical address)
        readseg(ph->p_pa, ph->p_memsz, ph->p_offset);

    // call the entry point from the ELF header
    // note: does not return!
    // 移交控制权,e_entry即为入口函数, 此函数不会返回,如果返回则意味着执行出现了某种问题,此后系统进入死循环,需要手动重启
    ((void (*)(void)) (ELFHDR->e_entry))();

bad:
    outw(0x8A00, 0x8A00);
    outw(0x8A00, 0x8E00);
    while (1)
        /* do nothing */;
}

对于内核加载部分还是很容易理解的,不过由于从磁盘加载内核过程中涉及到了大量的磁盘操作,而这些操作过于底层化,同时使用了c语言嵌套汇编(inb, insb等),这些函数的定义全部在<inc/x86.h>中,感兴趣的话可以去阅读。
以上内容就是JOS启动的过程,其中主要涉及了实模式向保护模式的转变以及内核加载的过程。内容还是相对容易理解的。在下一篇文章中,将会涉及JOS内存机制的建立。我也会按照MIT6.828实验的顺序依次写完。加油:{
PS:如果有想一同学习内核/JOS的童鞋,欢迎联系:zfzhang1992@126.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 1. 背景 原本计划自己学习写个操作系统的,但是工欲善其事必先利其器,先学习下别人是怎么做出来的,自己再动手,自然...
    pingpong_龘阅读 17,315评论 5 16
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,779评论 0 27
  • 转载声明:本文虽然不是本人100%原创,但也是辛辛苦苦整理的,可以转载,但请注明出处 这篇文章是关于折腾Windo...
    SOMCENT阅读 7,997评论 3 37
  • 无非是一堆虚妄之火 燃就的一抔矜持的灰烬 2017.6.6
    查文瑾阅读 495评论 2 4
  • 五颜六色的纸片脱离了黑色,展现着独特的颜色。五颜六色的花瓣脱离了花蕾,凋谢时换得了怜悯。五颜六色的彩虹脱离了天空,...
    bo_ss阅读 186评论 0 0