Lab 1: Booting a PC

这篇博客详细介绍了基于MIT 2017年6.826课程的实验,涉及PC的启动过程,包括PC的物理地址空间、ROM BIOS、引导加载器和内核的加载。实验内容涵盖了实模式到保护模式的转换、bootloader的加载、内存映射和虚拟内存的使用。同时,博主分享了在搭建环境和理解代码过程中遇到的挑战和解决方案。
摘要由CSDN通过智能技术生成
这个实验是基于MIT的2017的6.826课程,搭建环境的时候踩了几个坑,但是当时没有记录下来,可惜~

Part 1: PC Bootstrap

介绍了如何安装qemu以及如同通过qemu来模拟操作系统的启动。

The PC’s Physical Address Space

  • 8088处理器的内存地址
    在这里插入图片描述

0 - 640K 内存区域
0xF0000 - 0xFFFFF BIOS区域
0xB8000 - 0xBFFF 显卡区域

The ROM BIOS

[f000:fff0] 0xffff0:    ljmp   $0xf000,$0xe05b

mit 6.828 lab1 Exercise2

这部分的代码是嵌入在ROM BIOS中的,因此在磁盘上并没有对应的代码。

使用si命令得到的前22条汇编指令如下。虽然能看懂每条指令的字面意思,但看不懂具体实现的功能,后来参考myk的6.828 Lab1大致理解了基本功能:设置ss和esp寄存器的值,打开A20门(为了后向兼容老芯片)、进入保护模式(需要设置cr0寄存器的PE标志)。

1 [ f000:fff0]    0xffff0:	ljmp   $0xf000,$0xe05b
2  [f000:e05b]    0xfe05b:	cmpl   $0x0,%cs:0x6ac8
3  [f000:e062]    0xfe062:	jne    0xfd2e1
4  [f000:e066]    0xfe066:	xor    %dx,%dx
5  [f000:e068]    0xfe068:	mov    %dx,%ss
6  [f000:e06a]    0xfe06a:	mov    $0x7000,%esp
7  [f000:e070]    0xfe070:	mov    $0xf34c2,%edx
8  [f000:e076]    0xfe076:	jmp    0xfd15c
9  [f000:d15c]    0xfd15c:	mov    %eax,%ecx
10 [f000:d15f]    0xfd15f:	cli    
11 [f000:d160]    0xfd160:	cld    
12 [f000:d161]    0xfd161:	mov    $0x8f,%eax
13 [f000:d167]    0xfd167:	out    %al,$0x70
14 [f000:d169]    0xfd169:	in     $0x71,%al
15 [f000:d16b]    0xfd16b:	in     $0x92,%al
16 [f000:d16d]    0xfd16d:	or     $0x2,%al
17 [f000:d16f]    0xfd16f:	out    %al,$0x92
18 [f000:d171]    0xfd171:	lidtw  %cs:0x6ab8
19 [f000:d177]    0xfd177:	lgdtw  %cs:0x6a74
20 [f000:d17d]    0xfd17d:	mov    %cr0,%eax
21 [f000:d180]    0xfd180:	or     $0x1,%eax
22 [f000:d184]    0xfd184:	mov    %eax,%cr0
23 [f000:d187]    0xfd187:	ljmpl  $0x8,$0xfd18f

参考MIT6.828——Lab 1. Part 2 启动qemu

代码笔记

  1. 第一条指令:[f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b
    因为8086加载或者复位的时候,CS = 0xFFFF, IP = 0x0000,所以它取的第一条指令位于0xFFFF0处。
    通过ljmp指令来跳转到地址0xfe05b,并改变CSIP的地址,CS = 0xF000IP = 0xe05b

  2. 第二、三条指令:[f000:e05b] 0xfe05b: cmpl $0x0,%cs:0x6ac8
    判断0xf6ac8是否等于0,具体意思不清楚。如果不为0,则跳转到0xfd2e1。

  3. 第四、五、六、七条指令
    ss为0,esp0x7000edx0xf34e2,之后再跳转到0xfd15c

  4. CLI:Clear Interupt,禁止中断发生。STI:Set Interupt,允许中断发生。CLI和STI是用来屏蔽中断和恢复中断用的,如设置栈基址SS和偏移地址SP时,需要CLI,因为如果这两条指令被分开了,那么很有可能SS被修改了,但由于中断,而代码跳去其它地方执行了,SP还没来得及修改,就有可能出错。

  5. CLD: Clear Director。STD:Set Director。在字行块传送时使用的,它们决定了块传送的方向。CLD使得传送方向从低地址到高地址,而STD则相反。这里要解释一下,要实现段之间的批量数据传送,比如movsb或者movsw,需要指定是正向传送还是反向传送,正向传送是指传送操作的方向是从内存区域的低地址端到高地址端。

  6. 第十二到十四条指令:
    处理器是通过端口来和外围设备打交道的。本质上,端口就是一些寄存器,端口是处理器和外围设备通过I/O接口交流的窗口,每一个I/O接口都可能拥有好几个端口,所有端口都是统一编号的,比如I/O接口A有3个端口,端口号分别为0x0021~0x0023。
    这里引入in,out操作:
    out %al, PortAddress 向端口地址为PortAddress的端口写入值,值为al寄存器中的值
    in PortAddres,%al 把端口地址为PortAddress的端口中的值读入寄存器al中
    0x70端口和0x71端口是用于控制系统中一个叫做CMOS的设备。
    操作CMOS存储器中的内容需要两个端口,一个是0x70另一个就是0x71。其中0x70可以叫做索引寄存器,这个8位寄存器的最高位是不可屏蔽中断(NMI)使能位。如果你把这个位置1,则NMI不会被响应。低7位用于指定CMOS存储器中的存储单元地址

  7. 第十五到十七条指令
    读端口0x92的值,并将0x92端口对应的寄存器的1号bit位置1。
    它控制的是 PS/2系统控制端口A,可以查到这个端口的bit1的功能是
    bit 1= 1 indicates A20 active
    即A20位,即第21个地址线被使能。

  8. lidt指令:加载中断向量表寄存器(IDTR)。这个指令会把从地址0xf6ab8起始的后面6个字节的数据读入到中断向量表寄存器(IDTR)中。中断是操作系统中非常重要的一部分,有了中断操作系统才能真正实现进程。每一种中断都有自己对应的中断处理程序,那么这个中断的处理程序的首地址就叫做这个中断的中断向量。中断向量表自然是存放所有中断向量的表了。

  9. 把从0xf6a74为起始地址处的6个字节的值加载到全局描述符表格寄存器中GDTR中。这个表实现保护模式非常重要的一部分。

  10. 第20-22指令
    CR0是处理器内部的控制寄存器,它是32位寄存器,包含了一系列用于控制处理器操作模式和运行状态的标志位。它的第1位是保护模式允许位,是开启保护模式大门的门把手。如果把该位置"1",则处理器进入保护模式。但是这里出现了问题,我们刚刚说过BIOS是工作在实模式之下,后面的boot loader开始的时候也是工作在实模式下,所以这里把它切换为保护模式,显然是自相矛盾。所以只能推测它在检测是否机器能工作在保护模式下。

  11. 23条指令之后
    https://en.wikibooks.org/wiki/X86_Assembly/Global_Descriptor_Table指出,如果刚刚加载完GDTR寄存器我们必须要重新加载所有的段寄存器的值,而其中CS段寄存器必须通过长跳转指令,即23号指令来进行加载。所以这些步骤是在第19步完成后必须要做的。这样才能是GDTR的值生效。

后面的就看不懂了,应该是调用了BIOS中的各种函数来检测各种底层的设备

Part 2: The Boot Loader

  • If the disk is bootable, the first sector is called the boot sector, since this is where the boot loader code resides。

  • For 6.828, however, we will use the conventional hard drive boot mechanism, which means that our boot loader must fit into a measly 512 bytes. The boot loader must perform two main functions:

    • First, the boot loader switches the processor from real mode to 32-bit protected mode, because it is only in this mode that software can access all the memory above 1MB in the processor’s physical address space.
    • Second, the boot loader reads the kernel from the hard disk by directly accessing the IDE disk device registers via the x86’s special I/O instructions.
  • 分析boot/boot.S的代码

00007c00 <start>:
    7c00:	fa                   	cli    
    7c01:	fc                   	cld    
    7c02:	31 c0                	xor    %eax,%eax
    7c04:	8e d8                	mov    %eax,%ds
    7c06:	8e c0                	mov    %eax,%es
    7c08:	8e d0                	mov    %eax,%ss
00007c0a <seta20.1>:
    7c0a:	e4 64                	in     $0x64,%al
    7c0c:	a8 02                	test   $0x2,%al
    7c0e:	75 fa                	jne    7c0a <seta20.1>
    7c10:	b0 d1                	mov    $0x
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验目标: 本实验的目标是完成一个可以在QEMU仿真器上运行的x86操作系统。具体地说,我们将编写引导扇区代码和内核代码,并将它们组合成一个可引导的磁盘映像。最后,我们将使用QEMU仿真器启动我们的操作系统。 实验步骤: 1. 准备工作 准备工作包括安装必要的软件和工具、下载实验代码和文档等。 2. 编写引导扇区代码 引导扇区是操作系统的第一个扇区,它需要被放置在磁盘的第一个扇区。引导扇区必须包含一个512字节的主引导记录(MBR),其中包括一个引导程序和分区表。我们需要编写一个能够在引导扇区中运行的汇编代码,它将加载内核并将控制权转交给内核。 3. 编写内核代码 内核是操作系统的核心部分,它负责管理计算机的硬件资源、提供系统调用接口等。我们需要编写一个简单的内核,该内核将输出“Hello, world!”并进入无限循环。我们可以使用C语言编写内核代码,并使用GCC编译器将其编译成汇编代码。 4. 构建磁盘映像 我们需要将引导扇区和内核代码组合成一个可引导的磁盘映像。为此,我们可以使用dd命令将引导扇区和内核代码写入一个空白磁盘映像中。 5. 启动操作系统 最后,我们需要使用QEMU仿真器启动我们的操作系统。我们可以使用以下命令启动QEMU并加载磁盘映像: ``` qemu-system-i386 -hda os.img ``` 实验结果: 经过以上步骤,我们成功地编写了一个简单的操作系统,并使用QEMU仿真器进行了测试。当我们启动操作系统时,它将输出“Hello, world!”并进入无限循环。 实验总结: 本实验让我了解了操作系统的基本概念和架构,并学会了如何编写一个简单的操作系统。通过实验,我更深入地理解了计算机系统的底层原理,对操作系统的工作原理有了更深入的了解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值