以后代码越来越多,管理起来很不方便,下面我们就仿照linux的目录结构来整理一下:新建一个boot文件夹,将bootsect.S、head.S、setup.S此文件夹下;新建include文件夹,在此文件夹下新建asm和linux文件夹,asm文件夹下添加io.h和system.h,linux文件夹下添加head.h;新建init文件夹并将main.c放到此文件夹下;新建kernel文件夹并将asm.S和kliba.S放到此文件下,另外再新建一个Trap.c文件。
整理后目录结构如下:
io.h中放和io相关的代码,将mian.c中相关代码放到io.h中
#define outb(value,port) \ __asm__ ("outb %%al,%%dx"::"a" (value),"d" (port))
system.h文件
#define sti() __asm__ ("sti"::) #if 1 #define _set_gate(gate_addr,type,dpl,addr) \ __asm__ ("movw %%dx,%%ax\n\t" \ "movw %0,%%dx\n\t" \ "movl %%eax,%1\n\t" \ "movl %%edx,%2" \ : \ : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ "o" (*((char *) (gate_addr))), \ "o" (*(4+(char *) (gate_addr))), \ "d" ((char *) (addr)),"a" (0x00080000)) #else typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; typedef struct s_gate { u16 offset_low; u16 selector; u8 dcount; u8 attr; u16 offset_high; }GATE; void _set_gate(GATE *gate_addr,u8 type,u8 dpl,void (*addr)()) { gate_addr->offset_low = ((u32)addr) & 0xFFFF; gate_addr->selector = 0x08; gate_addr->dcount = 0; gate_addr->attr = type | (dpl << 5); gate_addr->offset_high = ((u32)addr >> 16) & 0xFFFF; } #endif #define set_intr_gate(n,addr) \ _set_gate(&idt[n],14,0,addr) #define set_trap_gate(n,addr) \ _set_gate(&idt[n],15,0,addr)
head.h
#ifndef _HEAD_H #define _HEAD_H typedef struct desc_struct { unsigned long a,b; } desc_table[256]; extern unsigned long pg_dir[1024]; extern desc_table idt,gdt; #endif
Trap.c
#include <asm/system.h> #include <asm/io.h> extern void divide_error(void); int i =0; void do_divide_error(void) { i++; disp_int(i); outb(0x20,0x20); }
整理后的main.c
#include <linux/head.h> #include <asm/system.h> #include <asm/io.h> extern void divide_error(void); void disp_str(char *info); void main(void) { disp_str("How old are you?\n"); set_intr_gate(32,÷_error); sti(); disp_int(0x12345678); while(1); }
为了和linux内核统一便于以后分析,我们在在head.S中添加idt和gdt标号,head.S
#define Descriptor(base,lim,attr)\ .word lim&0xffff;\ .word base&0xffff;\ .byte (base>>16)&0xff;\ .word ((lim>>8)&0xf00)|(attr&0x0f0ff);\ .byte ((base>>24)&0xff) #define Gate(Selector,Offset,PCount,Attr)\ .2byte (Offset&0xffff);\ .2byte (Selector);\ .2byte (PCount&0x1f)|((Attr<<8)&0xff00);\ .2byte ((Offset>>16)&0xffff) DA_32=0x4000 //32位模式 DA_LIMIT_4K=0x8000 //颗粒度为4096 DA_DRW=0x92 //数据段可读可写 DA_CR=0x9A //可读可执行 DA_C = 0x98 SETUPSEG = 0x9020 SETUPAddr = SETUPSEG<<4 DA_386CGate = 0x8c DA_386IGate = 0x8E DA_PL0 = 0x00 .text .globl start,idt,gdt/*程序从start处开始运行*/ .code32 _pg_dir://页目录将会存放在这里 start: movl $0x20,%eax mov %ax,%ds //因为ds是数据段寄存器,所以要加载一个数据段,否则会发生异常(可以试试0x08描述符) mov %ax,%es mov %ax,%ss mov $0xffff,%esp /*加载gdtr即将全局描述符表gdt的首地址和gdt的界限赋给gdtr寄存器*/ lgdt GdtPtr lidt IdtPtr movl $0x20,%eax mov %ax,%ds mov %ax,%es mov %ax,%ss mov $0xffff,%esp ljmp $0x8,$LABEL_SEG_CODE32 .align 4 LABEL_SEG_CODE32: .code32 movw $0x10,%ax movw %ax,%gs movl $((80*12+79)*2),%edi/*第12行,79列*/ movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/ movb $'S',%al/*显示的字符*/ movw %ax,%gs:(%edi) int $0x80 //sti jmp after_page_tables loop1:/*无限循环*/ jmp loop1 .align 4 INT_TEST: .code32 movl $((80*13+79)*2),%edi/*第11行,79列*/ movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/ movb $'I',%al/*显示的字符*/ movw %ax,%gs:(%edi) iret loop2:/*无限循环*/ jmp loop2 /* *每个页表长4kb字节,而每个页表项需要4个字节,因此一个页表可以存放1024个表项 */ .org 0x1000 pg0: .org 0x2000 pg1: .org 0x3000 pg2: .org 0x4000 pg3: .org 0x5000//定义下面的内存块从0x5000开始 //软盘缓冲区 _tmp_floppy_area: .fill 1024,1,0 //预留1024项,每项1字节,填充数值为0 after_page_tables: pushl $0 pushl $0 pushl $0 pushl $L6 pushl $main jmp setup_paging L6: jmp L6 int_msg: .asciz "Unknown interrupt\n\r" .align 4 ignore_int: .code32 movl $((80*13+79)*2),%edi/*第11行,79列*/ movb $0x0c,%ah/*高四位表示黑底,低四位表示红字*/ movb $'I',%al/*显示的字符*/ movw %ax,%gs:(%edi) iret loop21:/*无限循环*/ jmp loop21 .align 4 ClockHandler: movl $((80*14+79)*2),%edi movw %gs:(%edi),%ax add $1,%ax movw %ax,%gs:(%edi) movb $0x20,%al out %al,$0x20 iret loop3:/*无限循环*/ jmp loop3 ret .align 4 //按4字节方式对齐内存地址边界 setup_paging: //先对5页内存清零(1页目录+4页页表) movl $1024*5,%ecx xorl %eax,%eax xorl %edi,%edi //重复执行rep下面的语句,每次ecx减一,直到ecx为0 //页目录从0x00地址开始 cld;rep;stosl //eax内容存到es:edi所指内存位置,且edi增4 //STOSL指令相当于将EAX中的值保存到ES:EDI指向的地址中,若设置了EFLAGS中的方向位置位(即在STOSL指令前使用STD指令)则EDI自减4,否则(使用CLD指令)EDI自增4。 //设置页目录 movl $pg0+7,_pg_dir//7表示该页存在、用户可读写 movl $pg1+7,_pg_dir+4 movl $pg2+7,_pg_dir+8 movl $pg3+7,_pg_dir+12 //设置页表,共有4个页表,每个页表1024 movl $pg3+4092,%edi movl $0xfff007,%eax//16Mb - 4096 + 7 = 0xfff007 //cld,清方向标志,即(DF)=0,地址从低到高 //std,置方向标志1,DF=1,地址从高到低 std//std(set direction)置方向标志 1: stosl subl $0x1000,%eax jge 1b xorl %eax,%eax movl %eax,%cr3 movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 //jmp Page_test ret /**/ .align 4 .word 0 IdtPtr: .word (END_IDT-LABEL_IDT)-1 # so does gdt .long LABEL_IDT # This will be rewrite by code. .align 4 .word 0 GdtPtr: .word (GDT_END-GDT_START)-1 # so does gdt .long GDT_START # This will be rewrite by code. .align 8 idt: LABEL_IDT: .rep 32 Gate(0x08,(ignore_int - start),0,(DA_386IGate+DA_PL0)) .endr Gate(0x08,(ClockHandler - start),0,(DA_386IGate+DA_PL0)) .rep 222 Gate(0x08,(ignore_int - start),0,(DA_386IGate+DA_PL0)) .endr END_IDT: gdt: GDT_START: Descriptor_DUMMY:Descriptor(0x0,0x0,0x0) Descriptor_CODE32 :Descriptor(0x0,0xffffffff,DA_C+DA_32) Descriptor_VIDEO:Descriptor(0xb8000,0x0ffff,DA_DRW) Descriptor_SYSTM :Descriptor(0x00000,0xffffffff,DA_C+DA_32) Descriptor_SYSTM2 :Descriptor(0x00000,0xffffffff,DA_DRW) .fill 251,8,0 /* space for LDT's and TSS's etc */ GDT_END:
最后我们修改makefile
# Makefile for the simple example kernel. AS =as LD =ld LDFLAGS = --oformat binary -N -e start -Ttext 0x0 Image:bootsect setup system cat bootsect setup system >Image bootsect:boot/bootsect.s $(AS) -o bootsect.o -a boot/bootsect.s $(LD) $(LDFLAGS) -o bootsect bootsect.o setup:boot/setup.s $(AS) -o setup.o -a boot/setup.s $(LD) $(LDFLAGS) -o setup setup.o head.o:boot/head.s $(AS) -o head.o -a boot/head.s asm.o:kernel/asm.s $(AS) -o asm.o -a kernel/asm.s kliba.o:kernel/kliba.s $(AS) -o kliba.o -a kernel/kliba.s Traps.o:kernel/Traps.c gcc -Iinclude -c -o Traps.o kernel/Traps.c main.o: init/main.c gcc -Iinclude -c -o main.o init/main.c system: head.o kliba.o main.o asm.o Traps.o $(LD) $(LDFLAGS) -o system head.o kliba.o main.o asm.o Traps.o clean: rm -f Image setup bootsect system *.o *.s