Orange'S 第三章第一节实验

来源:互联网 发布:淘宝老客户营销短信 编辑:程序博客网 时间:2024/06/01 10:21

这个实验主要包含三部分

第一部分是定义全局描述符表gdt,以及全局描述符表指针gdtr

第二部分是16位代码段,主要工作有:

  • 关中断
  • 开启A20
  • 开启保护模式
  • 跳到32位代码段

第三部分是32位代码段,往显存里写一个字母’p’

分析

首先第一部分是纯数据,可以用C语言来实现,代码如下:

desc.h

#include <stdint.h>// Type field#define TYPE_DATA_RO          0#define TYPE_DATA_RO_A        1#define TYPE_DATA_RW          2#define TYPE_DATA_RW_A        3#define TYPE_DATA_RO_E        4#define TYPE_DATA_RO_E_A      5#define TYPE_DATA_RW_E        6#define TYPE_DATA_RW_E_A      7#define TYPE_CODE_XO          8#define TYPE_CODE_XO_A        9#define TYPE_CODE_XR          10#define TYPE_CODE_XR_A        11#define TYPE_CODE_XO_C        12#define TYPE_CODE_XO_C_A      13#define TYPE_CODE_XR_C        14#define TYPE_CODE_XR_C_A      15#define TYPE_SYS_TSS_16_A     1#define TYPE_SYS_LDT          2#define TYPE_SYS_TSS_16_B     3#define TYPE_SYS_CALL_GATE_16 4#define TYPE_SYS_TASK_GATE    5#define TYPE_SYS_INT_GATE_16  6#define TYPE_SYS_TRAP_GATE_16 7#define TYPE_SYS_TSS_32_A     9#define TYPE_SYS_TSS_32_B     11#define TYPE_SYS_CALL_GATE_32 12#define TYPE_SYS_INT_GATE_32  14#define TYPE_SYS_TRAP_GATE_32 15// S(descripor type) flag#define DESC_TYPE_SYSTEM 0#define DESC_TYPE_CODE   1#define DESC_TYPE_DATA   1// DPL(descriptor privilege level) field#define DPL_0 0#define DPL_1 1#define DPL_2 2#define DPL_3 3// P(segment-present) flag#define SEG_PRESENT     1#define SEG_NOT_PRESENT 0// D/B flag#define CODE_32         1#define CODE_16         0#define DATA_32         1#define DATA_16         0// G(granularity) flag#define G_BYTE    0#define G_4K_BYTE 1typedef struct __attribute__((packed)) {    uint16_t limit;    uint32_t base;} gdtr_t;typedef struct {    uint64_t limit_15_0:16;    uint64_t base_23_0:24;    uint64_t type:4;    uint64_t s:1;    uint64_t dpl:2;    uint64_t p:1;    uint64_t limit_19_16:4;    uint64_t avl:1;    uint64_t res:1;    uint64_t db:1;    uint64_t g:1;    uint64_t base_31_24:8;} seg_desc_t;#define SEG_DESC(_base, _limit, _type, _s, _dpl, _p, _db, _g) { \        .base_23_0 = (_base) & 0xffffff,         \        .base_31_24 = ((_base) >> 24) & 0xff, \        .limit_15_0 = (_limit) & 0xffff, \            .limit_19_16 = ((_limit) >> 16) & 0xf, \            .type = (_type), \            .s = (_s), \            .dpl = (_dpl), \            .p = (_p), \            .avl = 0, \            .res = 0, \            .db = (_db), \            .g = (_g), \}

上述代码定义了两个结构,分别是:

  • gdtr_t 表示全局描述符指针
  • seg_desc_t 表示段描述符

boot1.c

#include "desc.h"seg_desc_t gdt[3] = {    [0] = SEG_DESC(0, 0, 0, 0, 0, 0, 0, 0),    [1] = SEG_DESC(0x7c80, 0xff, TYPE_CODE_XO, DESC_TYPE_CODE, DPL_0, SEG_PRESENT, CODE_32, G_BYTE),    [2] = SEG_DESC(0xb8000, 0xffff, TYPE_DATA_RW, DESC_TYPE_DATA, DPL_0, SEG_PRESENT, DATA_32, G_BYTE),};gdtr_t gdtr = {    .limit = sizeof(gdt)-1,    .base = (uint32_t)&gdt,};

上述代码定义了两个对象:

  • 全局描述符表gdt

    按要求全局描述符表的第一描述符是空描述符
    第二个描述符是代码段描述符,基地址是0x7c80,段大小(limit+1)是256字节,在这里够用了
    第三个描述符是数据段描述符,基地址是0xb8000,这个对应的是显存地址,段大小(limit+1)是64KB

  • 全局描述符表指针gdtr

    这个指针有两个字段,base指向全局描述符表,limit指定全局描述符表的界限

ok,第一部分数据定义完成了,下面看16位代码段和32位代码段

16位代码段以及32位代码

#define CODE_SELECTOR 0x8#define DATA_SELECTOR 0x10#define CURSOR_POS ((80 * 11 + 79) * 2)    .code16    .section text16    .globl _start_start:    cli    # load gdtr    lgdt gdtr    # enable a20    inb $0x92, %al    orb $0b10, %al    outb %al, $0x92    # enable pm    movl %cr0, %eax    orl $0x01, %eax    movl %eax, %cr0    # jump to pm    jmpl $CODE_SELECTOR, $0x0    hlt    .code32    .section text32    movw $DATA_SELECTOR, %ax    movw %ax, %gs    movl $CURSOR_POS, %edi    movb $0x0c, %ah    movb $0x70, %al    movw %ax, %gs:(%edi)    jmp .

在16位代码段中加载gdtr只需要用lgdt gdtr指令就可以,这个gdtr引用的就是在boot1.c中定义的gdtr。
跳转到32位代码段的指令jmpl $CODE_SELECTOR, $0x0,其中CODE_SELECTOR为8,就是1<<3,就是引用全局描述符表中的第1个描述符,即,代码段。
32位代码段中将指向第2个描述符(就是数据段描述符)的选择子存到gs寄存器中。

程序链接

先写一个链接脚本,如下:

OUTPUT_FORMAT(binary)ENTRY(_start)MEMORY {       boot_region (RX) : org = 0x7c00, len = 512}SECTIONS {     .text : {         boot0.o(text16)         . = 64;         boot1.o(.data*)         . = 128;         boot0.o(text32)         . = 510;         SHORT(0xaa55)     } >boot_region     /DISCARD/ : {           *(*)     }}

首先定义一个内存区,这个内存区是可读(R)可执行(X)的,起始地址为0x7c00,大小为512字节
注意: 32位代码段放到了0x7c00+128字节处,也就是0x7c80处,这就是为什么全局描述符表中的代码段描述符的基地址为0x7c80的原因

Makefile

a.img: boot.bin    -rm a.img    bximage -q -mode=create -fd=1.44M a.img    dd if=boot.bin of=a.img bs=512 count=1 conv=notruncboot.bin: boot0.o boot1.o boot.lds    ld -Tboot.lds boot0.o boot1.o -o boot.bin    ld -Tboot.lds boot0.o boot1.o --oformat=elf32-i386 -o boot.elf  # for objdumpboot0.o: boot0.S    gcc -c boot0.S -o boot0.oboot1.o: boot1.c desc.h    gcc -c boot1.c -o boot1.o.PHONY: cleanclean:    -rm boot0.o boot1.o boot.bin boot.elf a.img

这里链接了两次,分别输出了boot.bin和boot.elf,输出boot.elf是为了使用objdump工具查看反汇编的结果

下面是 objdump -dj .text boot.elf 的内容:

boot.elf:     file format elf32-i386Disassembly of section .text:00007c00 <_start>:    7c00:   fa                      cli        7c01:   0f 01 16                lgdtl  (%esi)    7c04:   58                      pop    %eax    7c05:   7c e4                   jl     7beb <_start-0x15>    7c07:   92                      xchg   %eax,%edx    7c08:   0c 02                   or     $0x2,%al    7c0a:   e6 92                   out    %al,$0x92    7c0c:   0f 20 c0                mov    %cr0,%eax    7c0f:   66 83 c8 01             or     $0x1,%ax    7c13:   0f 22 c0                mov    %eax,%cr0    7c16:   66 ea 00 00 00 00       ljmpw  $0x0,$0x0    7c1c:   08 00                   or     %al,(%eax)    7c1e:   f4                      hlt        ...00007c40 <gdt>:    ...    7c48:   ff 00 80 7c 00 98 40 00 ff ff 00 80 0b 92 40 00     ...|..@.......@.00007c58 <gdtr>:    7c58:   17 00 40 7c 00 00 00 00 00 00 00 00 00 00 00 00     ..@|............    ...    7c80:   66 b8 10 00 8e e8 bf 7e 07 00 00 b4 0c b0 70 65     f......~......pe    7c90:   66 89 07 eb fe 00 00 00 00 00 00 00 00 00 00 00     f...............    ...    7dfc:   00 00 55 aa                                         ..U.

可以看到:
16位代码段的起始地址为0x7c00
存放gdt和gdtr两个对象的数据段的起始地址为0x7c40
32位代码段的起始地址为0x7c80

运行效果

这里写图片描述

可以看到屏幕中央右侧有一个红色的字母p

原创粉丝点击