您当前的位置:首页 > 计算机 > 编程开发 > Other

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

时间:11-22来源:作者:点击数:
CDSY,CDSY.XYZ

前言

上篇文章中,我们写了一个简单的 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
    # 加载 kernel 到内存
    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

    # 跳转到 kernel
    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 .

在这里插入图片描述
CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐