自己动手写一个操作系统——loader

Posted Li-Yongjun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己动手写一个操作系统——loader相关的知识,希望对你有一定的参考价值。

前言

上篇文章中,我们写了一个简单的 loader 作为被加载的演示对象。我们知道 loader 是用来加载 kernel 的,今天我们就编写 loader 代码完成这件事情。

实模式下的内存地址

我们计划把 kernel 加载到内存的 0x10000 地址处。

不过面临一个挑战,实模式下地址线为 20 位,寄存器只有 16 位,要想通过寄存器去构成这 20 位的主存地址,必须采取一种特殊的方式。
当指令要想访问某个内存地址时,它通常需要用下面的这种格式来表示:
(段基址:段内偏移)
其中第一个字段是段基址,它的值由段寄存器来提供(一般来说,段寄存器有 6 种,分别为 cs、ds、ss、es、fs、gs),其中 cs 不可以直接通过汇编指令赋值,其它可以。
最终:
物理地址 = (段基址 << 4) + 段内偏移

段基址

我们想要把 kernel 加载到 0x10000 处,就要计算段基址和段内偏移,可知段基址为 0x1000,段内偏移为 0x0。
所以就要给段基址寄存器赋值 0x1000,段偏移寄存器赋值 0x0。

代码

loader.S

.code16
.global _start

_start:
    mov $0x1000, %ax
    mov %ax, %ds
	mov %ax, %ss
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
    # 加载 loader 到内存
    mov $0x0000, %bx    # 要加载到的内存地址
    mov $0x0003, %cx    # ch:磁道号(0x00),cl:起始扇区号(0x03),(扇区 0x01 为 MBR, 扇区 0x02 为 loader)
    mov $0x02, %ah      # ah:读磁盘命令
    mov $1, %al         # al:读取的扇区数量,必须小于128,暂时设置成 1 个扇区
    mov $0x0080, %dx    # dh:磁头号,dl:驱动器号0x80(磁盘1)
    int $0x13

    # 跳转到 loader
    jmp $0x1000, $0x0000

    jmp .

我们可以通过 mov 指令,向 ds、ss、es、fs、gs 段寄存器赋值 0x1000,但是 cs 段基址寄存器就不可以使用 mov 指令赋值了,需要使用 jmp 指令。如 jmp $0x1000, $0x0000 就会将 cs 赋值为 0x1000。

start.S

.code16
.global _start

_start:
    jmp .

kernel 的代码目前作为演示就写个简例。
不过注意要在链接时指定代码入口地址为 0x0000(为调试做准备)。
Makefile


OUTPUT=../_build/kernel

all:
	mkdir -p $OUTPUT
	as --32 -g -o $OUTPUT/start.o start.S
	ld -m elf_i386 -Ttext=0x00000 $OUTPUT/start.o -o $OUTPUT/start.elf
	objcopy -O binary $OUTPUT/start.elf $OUTPUT/kernel.bin	

	objdump -x -d -S  $OUTPUT/start.elf > $OUTPUT/start_dis.txt

clean:
	-rm $OUTPUT/*

调试

最终 loader 将 kernel 加载到内存的 0x10000 处,并跳转到该位置运行。
可以看到,段寄存器值全为 0x1000,段内偏移为 0x0(eip),该位置对应的汇编代码为 start.S:5 jmp .

以上是关于自己动手写一个操作系统——loader的主要内容,如果未能解决你的问题,请参考以下文章

自己动手写一个操作系统——loader

自己动手写一个操作系统——loader

自己动手写一个操作系统——loader

自己动手写一个操作系统——MBR

自己动手写一个操作系统——MBR

自己动手写一个操作系统——MBR