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
- Orange'S 第三章第一节实验
- Orange'S 第三章第二节实验
- Orange'S 第一章实验
- 第三章 --- 第一节
- orange's 一个操作系统的实现 实验环境搭建
- orange's 文件系统
- Orange's boot_loader_kernel
- Orange's 进程
- Orange's TTY
- Orange's笔记
- 利用bochs完成《orange's一个人的操作系统》引导扇区的实验
- orange's一个操作系统的实现实验遇到的问题及处理方法
- 《Orange's 一个操作系统的实现》学习笔记(一) 实验环境搭建
- [第三章、Java基础知识]第一节、数据类型
- 第三章第一节 apply族函数
- 学习Orange‘S笔记1
- Orange's - 工作环境搭建
- 01 orange‘S boot源码
- linux 更新nodejs到最新
- Java NIO 笔记
- C++文件操作——创建和删除文件夹
- web前端开发规范
- Minimum Snap轨迹规划详解(2)corridor与时间分配
- Orange'S 第三章第一节实验
- 观后总结:Head First设计模式(一)
- Css图文排布
- java多线程之线程组
- 设计模式C++版:第十九式解释器模式
- redis安装
- 喷水装置(二) 区间长度贪心
- Jboss与Tomcat的区别
- json转换成excel在线js小工具分享