这个实验没有完全按照书上的来,这个实验对比第一小节只是增加了栈段,有了栈,就可以安全的使用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 1
typedef struct __attribute__((packed)) {
uint16_t limit;
uint32_t base;
} gdtr_t;
typedef struct {
uint16_t rpl:2;
uint16_t ti:1;
uint16_t index:13;
} seg_selector_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), \
}
desc.h
相对第一小节没有变化
gdt.c 这里文件定义了全局描述符表,即全局描述符表指针
#include "desc.h"
seg_desc_t gdt[] = {
// null
[0] = SEG_DESC(0, 0, 0, 0, 0, 0, 0, 0),
// code32
[1] = SEG_DESC(0x7c80, 0xff, TYPE_CODE_XO, DESC_TYPE_CODE, DPL_0, SEG_PRESENT, CODE_32, G_BYTE),
// stack
[2] = SEG_DESC(0x500000, 0xfffff, TYPE_DATA_RW, DESC_TYPE_DATA, DPL_0, SEG_PRESENT, DATA_32, G_BYTE),
// video
[3] = 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相对于第一小节,添加了一个栈段描述符,段基地址设为5M,为了演示突破1M的访问范围
boot.S 代码
#define CODE_SELECTOR (1 << 3)
#define STACK_SELECTOR (2 << 3)
#define VIDEO_SELECTOR (3 << 3)
.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 $STACK_SELECTOR, %ax
movw %ax, %ss
movw $VIDEO_SELECTOR, %ax
movw %ax, %gs
call main
boot.S 在进入保护模式后,只进行了栈段,和视频段的初始化,然后跳到main函数
main.c 代码
#include <stdint.h>
typedef union {
struct {
uint8_t c; // al
uint8_t attr; // ah
};
uint16_t value; // ax
} attr_char_t;
void display_char(attr_char_t ac, int pos)
{
asm volatile("movw %0, %%gs:(%1)\n\t"
:
: "a"(ac.value), "D"(pos));
}
void display_attr_string(const char *str, uint8_t attr, int pos)
{
for (; *str; ++str, pos+=2) {
attr_char_t c = { *str, attr };
display_char(c, pos);
}
}
int main()
{
display_attr_string("hello, world", 0x0c, 200);
}
c函数可以使用栈变量了,比如函数参数
#### boot.lds
OUTPUT_FORMAT(binary)
ENTRY(_start)
MEMORY {
boot_region (RX) : org = 0x7c00, len = 512
}
SECTIONS {
.text : {
boot.o(text16)
. = 64;
gdt.o(.data*)
. = 128;
boot.o(text32)
*(.text .rodata)
. = 510;
SHORT(0xaa55)
} >boot_region
/DISCARD/ : {
*(*)
}
}
Makefile
OBJECTS = boot.o gdt.o main.o
CFLAGS = -fno-pic -c
LDFLAGS = -Tboot.lds
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=notrunc
boot.bin: $(OBJECTS) boot.lds
ld $(LDFLAGS) $(OBJECTS) -o boot.bin
ld $(LDFLAGS) $(OBJECTS) --oformat=elf32-i386 -o boot.elf # for objdump
boot.o: boot.S
gcc $(CFLAGS) boot.S
gdt.o: gdt.c desc.h
gcc $(CFLAGS) gdt.c
main.o: main.c
gcc $(CFLAGS) main.c
.PHONY: clean
clean:
-rm $(OBJECTS) boot.bin boot.elf a.img