用GRUB引导内核--GDT的设置

在Multiboot手册中有这样一段

When the boot loader invokes the 32-bit operating system, the machine must have the
following state:
‘EAX’ Must contain the magic value ‘0x2BADB002’; the presence of this value indicates
to the operating system that it was loaded by a Multiboot-compliant boot loader
(e.g. as opposed to another type of boot loader that the operating system can
also be loaded from).
‘EBX’ Must contain the 32-bit physical address of the Multiboot information structure
provided by the boot loader (see Section 3.3 [Boot information format], page 8).
8 Multiboot Specification version 0.6.96
‘CS’ Must be a 32-bit read/execute code segment with an offset of ‘0’ and a limit of
0xFFFFFFFF’. The exact value is undefined.
‘DS’
‘ES’
‘FS’
‘GS’
‘SS’ Must be a 32-bit read/write data segment with an offset of ‘0’ and a limit of
0xFFFFFFFF’. The exact values are all undefined.
‘A20 gate’ Must be enabled.
‘CR0’ Bit 31 (PG) must be cleared. Bit 0 (PE) must be set. Other bits are all
undefined.
‘EFLAGS’ Bit 17 (VM) must be cleared. Bit 9 (IF) must be cleared. Other bits are all
undefined.
All other processor registers and flag bits are undefined. This includes, in particular:
‘ESP’ The OS image must create its own stack as soon as it needs one.
‘GDTR’ Even though the segment registers are set up as described above, the ‘GDTR’
may be invalid, so the OS image must not load any segment registers (even just
reloading the same values!) until it sets up its own ‘GDT’.
‘IDTR’ The OS image must leave interrupts disabled until it sets up its own IDT.

从中可知,引导内核后虽然进入保护模式(A20 gate打开,CR0 PE位置位),存在相应GDT,段寄存器也有所指向,但实际使用的GDT仍然需要自己定义,段寄存器仍然需要自己赋值。

用C语言定义一个GDT struct

typedef struct GDT{
u16 limit_low;
u16 base_low;
u8 base_middle;
u16 attr_plus_limit;
u8 base_high;
}__attribute__((packed))GDT;
__attribute__((packed))一定要有,其作用是取消字节对齐优化;若没有,则对GDT赋值会失败。
相应的GDT_PTR
typedef struct GDT_PTR{
u16 limit;
GDT *base;
}__attribute__((packed))GDT_PTR;

设置GDT内容:

   SetGdt((u8)0, 0, 0, 0);//empty
SetGdt((u8)1, 0x0, 0xfffff, DA_CR | DA_32 | DA_LIMIT_4K);//code
SetGdt((u8)2, 0x0, 0xfffff, DA_DRW | DA_32 | DA_LIMIT_4K);//data
SetGdt((u8)3, 0x0, 0x1ff, DA_DRWA | DA_32); //stack with the limit of 511 byte;
SetGdt((u8)4, 0xB8000, 0xffff, DA_DRW | DA_DPL3);//video

虽然设置了stack段,但实际没用到,因为实验时对ss赋值也会失败,而对寄存器赋值的失败会导致系统重启。

可以将ds,es, fs, ss 都赋以data段的Descriptor。

 

SELECTOR_CS EQU 0x08

SELECTOR_DS EQU 0x10

SELECTOR_SS EQU 0x18

SELECTOR_GS EQU 0x20



extern gdt_ptr

[SECTION .text]

ALIGN 32 

global _update_gdtr

_update_gdtr:

    lgdt [gdt_ptr]

    xor eax, eax

    mov ax,  SELECTOR_DS

    mov ds,  ax

    mov es,  ax

    mov fs,  ax

    

    pop bx ;ip

    pop dx ;cs

    

    mov ss, ax

    mov esp, 0xffff



    mov ax, SELECTOR_GS   

    mov gs, ax



    push dx

    push bx



    jmp SELECTOR_CS:RETURN

    nop

RETURN:

    ret

 

还有,要注意的是,对 esp 赋值时,用的是0xffff,不能是0xfffff。对 esp 赋值还要注意对原有 stack 所保存的返回地址加以备份,以便重新设置 esp 后能重新将返回地址压入新 stack。

"; 注意! 在使用 C 代码的时候一定要保证 ds, es, ss 这几个段寄存器的值是一样的
; 因为编译器有可能编译出使用它们的代码, 而编译器默认它们是一样的. 比如串拷贝操作会用到 ds 和 es."

 




你可能感兴趣的:(grub)