美文网首页我爱编程
mit6.828 lab1 PART1

mit6.828 lab1 PART1

作者: jaseabvv | 来源:发表于2018-02-26 06:41 被阅读0次

lab1包括3个部分, 第一部分主要是熟悉x86汇编语言,qemu模拟器的使用和PC上电启动的过程,第二部分是熟悉kernel的boot loader的代码和运行过程,第三部分深入了解jos的模板部分。

Part 1: PC Bootstrap

这一部分就是介绍x86 assembly language and the PC bootstrap process以及怎么使用qemu和gdb来debug 内核...
1.Getting Started with x86 assembly
熟悉汇编语言
课程给出的资源是PC Assembly Language BookBrennan's Guide to Inline Assembly的syntax部分
我个人推荐CSAPP的汇编语言有关部分,CSAPP可以说是操作系统最好的的先修课程了。

打开两个terminal,都进入到lab目录,然后其中一个输入make qemu-gdb 另一个输入make gdb,即可看到下面的画面
2.Simulating the x86
使用qemu来模拟x86系统的运行

athena% cd lab
athena% make
athena% make qemu
image.png

"6828 decimal..."之后的信息都是jos内核显示出来的,到这我们就实现了x86系统的仿真和jos内核的运行了!

3.The PC's Physical Address Space
下面我们来看看PC的物理地址是怎么分布的

image.png

第一代PC处理器是16位字长的Intel 8088处理器,这类处理器只能访问1MB的地址空间,即0x00000000~0x000FFFFF。但是这1MB也不是用户都能利用到的,只有低640KB(0x00000000~0x000A0000)的地址空间是用户程序可以使用的。如图所示。剩下的0x000A0000到0x000FFFFF之间的384KB区域被硬件保留用于特殊用途,例如保存在非易失性存储器(ROM)中的视频显示缓冲区(VGA)和固件。该保留区域最重要的部分是基本输入/输出系统(BIOS),它占用从0x000F0000到0x000FFFFF的64KB区域。 BIOS负责执行基本的系统初始化,例如激活显卡并检查机器内存大小等,然后加载操作系统,并将机器的控制权交给操作系统。

虽然Intel处理器突破了1MB内存空间,在80286和80386上已经实现了16MB,4GB的地址空间,但是PC的架构必须仍旧把原来的1MB的地址空间的结构保留下来,这样才能兼容以前的软件。所以现代计算机的地址 0x000A0000~0x00100000区间是一个空洞,不会被使用。
由于xv6操作系统设计的一些限制,它只利用256MB的物理地址空间,即它假设用户的主机只有256MB的内存。

The ROM BIOS
来到lab目录下输入make qemu-gdb,然后打开另外一个teminal来到lab目录下,输入make gdb

image.png

可以看到,PC启动时进入到实模式并且把CS和IP寄存器分别设置为0xf000 ,0xfff0 确保了BIOS在开机后能控制机器

我们停在BIOS的第一条指令 0xffff0: ljmp $0xf000, $0xe05b

这是一条长跳转指令,跳转到0xfe05b地址处。当PC启动时,CPU运行在实模式(real mode)下,而进入操作系统内核后,将会运行在保护模式下(protected mode)。实模式是早期CPU,比如8088处理器的工作模式,这类处理器由于只有20根地址线,所以它们只能访问1MB的内存空间。但是CPU也在不断的发展,之后的80286/80386已经具备32位地址总线,能够访问4GB内存空间,为了能够很好的管理这么大的内存空间,保护模式被研发出来。所以现代处理器都是工作在保护模式下的。但是为了实现向后兼容性,即原来运行在8088处理器上的软件仍旧能在现代处理器上运行,所以现代的CPU都是在启动时运行于实模式,启动完成后运行于保护模式。BIOS就是PC刚启动时运行的软件,所以它必然工作在实模式。

至于这两个模式下的运行原理,可以看这个链接:http://blog.csdn.net/zdwzzu2006/article/details/4030948

地址的计算方法:
物理地址 = 段基址<<4 + 偏移地址. 所以, 当 PC 把 CS 设置为 0xf000 并 把IP 设置成 0xfff0时, 指向的物理地址:

16 * 0xf000 + 0xfff0 # in hex multiplication by 16 is
= 0xf0000 + 0xfff0 # easy--just append a 0.
= 0xffff0

查了查qemu的文档, 发现qemu的BIOS是一个叫seaBIOS的开源16位x86 BIOS, 具体的流程在seaBIOS的文档里。

主要分为几个阶段
1.POST phase。CPU开始在16位模式执行0xFFFF0000:FFF0处的指令。 过程首先调用romlayout.S文件的函数entry_post() 再然后在32位模式下调用 post.c文件的handle_post()

注意:下面的汇编代码跟jos的BIOS执行过程是一一对应的

entry_post:
        cmpl $0, %cs:HaveRunPost                // Check for resume/reboot
        jnz entry_resume     
        ENTRY_INTO32 _cfunc32flat_handle_post   // Normal entry point
image.png

HaveRunPost定义在misc.c

// Indicator if POST phase has been started (and if it has completed).
int HaveRunPost VARFSEG;

所以上面cmpl指令检查了POST阶段是不是已经完成,没有
就继续向下执行ENTRY_INTO32

ENTRY_INTO32是entryfuncs.S:153处的宏,编译时替换成下面的代码

// Reset stack, transition to 32bit mode, and call a C function.
        .macro ENTRY_INTO32 cfunc
        xorw %dx, %dx
        movw %dx, %ss
        movl $ BUILD_STACK_ADDR , %esp
        movl $ \cfunc , %edx
        jmp transition32
        .endm

image.png

主要是设置堆栈寄存器,%ss = 0, %sp = $BUILD_STACK_ADDR
并把%edx 设为 _cfunc32flat_handle_post,最后跳转到transition32

"transition32" 在romlayout.S 第26行 ,完成从16位模式到32位模式(保护模式)的转变。

// Place CPU into 32bit mode from 16bit mode.
// %edx = return location (in 32bit mode)
// %edx保存了等下要跳转的地址$ \cfunc, 刚刚设置的值
// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
        DECLFUNC transition32
        .global transition32_nmi_off
transition32:
        // Disable irqs (and clear direction flag)
        cli
        cld

cli关闭中断,cld设置方向标识位为0,表示后续的串操作比如MOVS操作,内存地址的变化方向,如果为0代表从低地址值递加到高地址

       // Disable nmi
        movl %eax, %ecx
        movl $CMOS_RESET_CODE|NMI_DISABLE_BIT, %eax
        outb %al, $PORT_CMOS_INDEX
        inb $PORT_CMOS_DATA, %al
image.png

关闭NMI中断
  这个CMOS可以控制跟PC相关的多个功能,其中最重要的就是时钟设备(Real Time Clock)的 ,它还可以控制是否响应不可屏蔽中断NMI(Non-Maskable Interrupt)

        // enable a20
        inb $PORT_A20, %al
        orb $A20_ENABLE_BIT, %al
        outb %al, $PORT_A20
        movl %ecx, %eax
image.png

之后就是把a20这根地址线激活,以后就可以访问20MB以上的内存,准备进入保护模式

继续向下执行

transition32_nmi_off:
        // Set segment descriptors
        lidtw %cs:pmode_IDT_info
        lgdtw %cs:rombios32_gdt_48
image.png

lidt指令:把以%cs:pmode_IDT_info为起始地址处的6个字节的值加载到中断向量表寄存器(IDTR)。中断是操作系统中非常重要的一部分。每一种中断都有自己对应的中断处理程序,那么这个中断的处理程序的首地址就叫做这个中断的中断向量。

lgdtw指令:把以%cs:rombios32_gdt_48为起始地址处的6个字节的值加载到全局描述符表格寄存器中GDTR中。这个表是实现保护模式非常重要的一部分。
输入info registers查看cs的值


image.png

输入 x/6x 0xf6ab8查看0xf6ab8地址的6个双字的内容


image.png
所以加载到全局描述符表寄存器(GDTR)的值是0x000f6abe0000
中断描述符表寄存器IDTR是一个48位的寄存器,其低16位保存中断描述符表的大小,高32位保存IDT的[基址]

对GDTR进行类似操作

image.png

所以加载到中断向量表寄存器(IDTR)的值是0x000f6a800037
这些48位地址是进入到保护模式后的地址,我们目前还不能访问
使能保护模式:
CR0寄存器的0bit是PE位,启动保护位,当该位被置1,代表开启了保护模式

        // Enable protected mode
        movl %cr0, %ecx
        andl $~(CR0_PG|CR0_CD|CR0_NW), %ecx
        orl $CR0_PE, %ecx
        movl %ecx, %cr0

这时我们有了所谓的虚拟内存机制, 我们可以查看
中断描述表和全局描述符表的内容

image.png
对应misc.c
有点晕,看不太懂, 先跳过了
/****************************************************************
 * GDT and IDT tables
 ****************************************************************/

// Real mode IDT descriptor
struct descloc_s rmode_IDT_info VARFSEG = {
    .length = sizeof(struct rmode_IVT) - 1,
    .addr = (u32)MAKE_FLATPTR(SEG_IVT, 0),
};

// Dummy IDT that forces a machine shutdown if an irq happens in
// protected mode.
u8 dummy_IDT VARFSEG;

// Protected mode IDT descriptor
struct descloc_s pmode_IDT_info VARFSEG = {
    .length = sizeof(dummy_IDT) - 1,
    .addr = (u32)&dummy_IDT,
};

// GDT
u64 rombios32_gdt[] VARFSEG __aligned(8) = {
    // First entry can't be used.
    0x0000000000000000LL,
    // 32 bit flat code segment (SEG32_MODE32_CS)
    GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_B,
    // 32 bit flat data segment (SEG32_MODE32_DS)
    GDT_GRANLIMIT(0xffffffff) | GDT_DATA | GDT_B,
    // 16 bit code segment base=0xf0000 limit=0xffff (SEG32_MODE16_CS)
    GDT_LIMIT(BUILD_BIOS_SIZE-1) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR),
    // 16 bit data segment base=0x0 limit=0xffff (SEG32_MODE16_DS)
    GDT_LIMIT(0x0ffff) | GDT_DATA,
    // 16 bit code segment base=0xf0000 limit=0xffffffff (SEG32_MODE16BIG_CS)
    GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR),
    // 16 bit data segment base=0 limit=0xffffffff (SEG32_MODE16BIG_DS)
    GDT_GRANLIMIT(0xffffffff) | GDT_DATA,
};

// GDT descriptor
struct descloc_s rombios32_gdt_48 VARFSEG = {
    .length = sizeof(rombios32_gdt) - 1,
    .addr = (u32)rombios32_gdt,
};

通过长跳转指令设置CS:IP(其实就是下一条指令1: movl $SEG32_MODE32_DS, %ecx的地址)并进入保护模式

        // start 32bit protected mode code
        ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f)
.code32
        // init data segments
1:      movl $SEG32_MODE32_DS, %ecx
        movw %cx, %ds
        movw %cx, %es
        movw %cx, %ss
        movw %cx, %fs
        movw %cx, %gs
        jmpl *%edx

接下来就跳转了, 到了哪呢?

push %ebx 
sub $0x2c, %esp
image.png

两条指令显然表明跳转到了用C编写的部分中(不知道的话得读读csapp了。。。)
总结一下目前为止这个 BIOS 干了什么吧:
打开 A20 地址总线、初始化中断描述符表和全局描述符表、进入 x86 保护模式……
而且目前为止它还没有初始化 display ……

从seaBIOS流程或者之前设置的%edx的值可以知道我们到了handle_post()
handle_post:

// Entry point for Power On Self Test (POST) - the BIOS initilization
// phase.  This function makes the memory at 0xc0000-0xfffff
// read/writable and then calls dopost().

void VISIBLE32FLAT
handle_post(void)
{
    if (!CONFIG_QEMU && !CONFIG_COREBOOT)
        return;

    serial_debug_preinit();
    debug_banner();

    // Check if we are running under Xen.
    xen_preinit();

    // Allow writes to modify bios area (0xf0000)
    make_bios_writable();

    // Now that memory is read/writable - start post process.
    dopost();
}

之后的过程比较复杂, BIOS完成一系列硬件,驱动的检查和初始化(如磁盘)后,再进入到Boot phase找到可引导设备(bootable device)并把操作系统的boot loader加载到内存中并执行从而把控制权转交给操作系统。

参考资料:
https://www.cnblogs.com/fatsheep9146/p/5078179.html
https://pdos.csail.mit.edu/6.828/2016/labs/lab1/
http://cs3210.cc.gatech.edu/l/lec02_notes.txt
https://github.com/veertuinc/seabios-veertudesktop/blob/master/docs/Execution_and_code_flow.md
http://www.cnblogs.com/fatsheep9146/p/5078102.html
https://imzhwk.com/2017/12/6.828-lab-1/

相关文章

网友评论

    本文标题:mit6.828 lab1 PART1

    本文链接:https://www.haomeiwen.com/subject/rkmstftx.html