欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自己动手写操作系统(四)

程序员文章站 2024-03-24 11:51:16
...

    今天主要接着(二)来看一下stage2.asm。stage2.asm的功能主要有下面几个:

    =============================

    1.将系统从实模式切换到保护模式

    2.找到core.sys(就是kernel)的位置,并且将core.sys导入内存

    3.跳转到core.sys的入口函数处

    =============================

    首先看一下代码流程

    call GDT_INSTALL

    GDT全局描述符表[span]在英特尔x86[span]系列处理器的80286[span]起,为了定义的特点使用不同的存储区,在程序执行期间,包括基地址,大小和访问权限,如可执行可写。这些内存区域被称为(英特尔的术语)。内存中段所在的位置不需要写入特殊标记,段的信息(基地址、界限、属性等)保存通过段描述符表进行。GDT正是最重要的描述符表,进入保护模式,至少要准备GDT。GDT主要存放操作系统和各任务公用的描述符,如公用的数据和代码段描述符、各任务的TSS描述符和LDT描述符。每8个字节的条目在GDT是一个描述符。进入保护模式后我们会用到这个东东。     

    call ENABLE_PMODE
    jmp GDT_CODE_DESC:STAGE3    ; far jump to fix CS
    进入保护模式,然后跳转到StageE,可以看一下GDT_CODE_DESC就是之前我们定义在GDT中的段。

    关键来看一下stage3中的处理:

STAGE3:
    ; set segment registers
    cli
    mov ax, GDT_DATA_DESC   ; set data segments to data selectors (0x10)
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov esp, 0x90000    ; stack begins from 0x90000

    ; map PDE and enable paging
    ;call ENABLE_PAGING ;asido中会在这里进行内存第1M的分页,但是这样做会导致进入kernel后内存分页比较麻烦,所以这里直接不做分页

    ; calculate memory size in Kb from in 16-bit returned BIOS
    push DWORD [MemKBHigh]
    push DWORD [MemKBLow]
    call MEM16_TO_MEM32_SIZE
    mov DWORD [MemorySize], eax

    ; calculate kernel size in sectors returned from in 16-bit FAT12 driver
    push DWORD [KernelImgSizeHigh]
    push DWORD [KernelImgSizeLow]
    call KRNL_SIZE_TO_INT32
    mov DWORD [KernelImgSize], eax

    ; load the kernel
    push KERNEL_RMODE_BASE
    call LOAD_ELF ;这里是从core.sys中查找到入口函数地址,然后存放到eax中

    ; and execute it!
    cli
    push DWORD KERNEL_PMODE_BASE ;core.sys的加载起始地址入栈,这些数据会作为参数传入入口函数
    push DWORD [KernelImgSize]
    push DWORD [MemorySize]

;we push gdt addr to main start
    ;push DWORD [LGDT_VAL]
;we push gdt addr to main end

    call eax ;eax中存的就是入口函数地址,所以直接跳转。
    add esp, 0x4


这里还有一个技巧,就是我们在编译core.sys的时候先将起始的地址设为了0x100000(参看arale/core/linker.ld),然后将core.sys导入的内存地址也设定为从0x100000,这样,就能保证从core.sys中解析出来的入口函数地址和实际的物理地址是对应的。网上有很多其他的方法,例如在编译的时候加入

-Ttext $(ENTRYPOINT)来指定入口函数地址等,但都不是很方便。个人觉得还是这个上面介绍的方法比较方便。

从call eax开始,我们就进入了core的处理流程了(也就是大家常说的kernel)。后续主要的精力会先放在内存管理这块。主要也是因为性能优化关系较大的还是内存这块,哈哈~~~~。最关心的优先学~~。

谢谢