您的位置:首页 > 其它

第4章 进入保护模式(设置GDT全局描述符表)

2011-09-04 16:14 162 查看
进入保护模式的步骤是:

1、关中断;
2、打开A20地址线,使之能访问1M以上的内存;
3、设置GDT; GDT知识可查看《Intel微处理器 第8版》p541 也可以查看 Intel开发手册
4、加载GDT;
5、进入保护模式,跳转到setup.s

boot.s的源代码如下所示
.text
.global  start
.include "kernel.inc"
.code16
start:
jmp $0x0, $code
msg:
.string "1kos booting......\x0"
code:
movw    $0xb800,%ax        #显存地址在0xb8000
movw    %ax,    %es
xorw    %di,    %di        #es:di = 0xb8000
xorw    %ax,    %ax
movw    %ax,    %ds        #ds:si = msg
movw    $msg,   %si
cld                #si和di自增
movb    $0x07,  %ah        #字符属性:黑底白字

#打印msg信息
print_c:
cmp        $0x0,   (%si)
je        load
lodsb            #ds:[si] -> al, ++si
stosw            #ax -> es:[di], di += 2  #al=字符 ah=属性
jmp        print_c

#读取kernel到0x7e00上
load:
#设置临时栈
xorw    %ax,    %ax
movw    %ax,    %ds        # ds = 0x0
movw    %ax,    %ss        # ss = 0x0,栈基址
movw    $KERNEL_STACK_BOT,%sp        # sp = 0x7c00,栈地址

#设置rd_kern的参数
#设置ES:BX参数
movw    $KERNEL_START_SEGMENT, %ax
movw    %ax,    %es        # es = 0x7e0
xorw    %bx,    %bx        # bx = 0x0,  es:bx = 0x7e00

#读取的起始扇区号(逻辑扇区起始扇区号是0)
movw    $0x1,   %si        # si = 0x1 ,是rd_sect的参数

#读取1400个扇区,共700K
movw    $KERNEL_SECT_NUMBER,  %cx

#调用rd_kern函数
#读取700K的kernel到0x7e00上
call    rd_kern

#关中断
cli

#探测内存情况,并把内存探测结果写入特定位置中
call    check_mem

#打开A20地址线
enable_a20:
inb        $0x64,  %al
testb   $0x2,   %al
jnz        enable_a20
movb    $0xdf,  %al
outb    %al,    $0x64

#设置GDT
lgdt    gdt_48

#进入保护模式
movl    %cr0,   %eax
orl        $0x1,   %eax
movl    %eax,   %cr0

ljmp    $CODE_SEL,  $0x7e00

#==============================================================
#函数名:rd_kern
#功能:读取软驱A上连续多个扇区
#参数1:  ES:BX    读取数据存放的目的地址
#参数2:  SI        指定起始逻辑扇区号
#参数3:  CX        欲读取的扇区数目
rd_kern:
pushw   %ax
rd_k1:
call    rd_sect
movw    %es,    %ax        #
addw    $0x20,  %ax        #
movw    %ax,    %es        # es:bx += 512
incw    %si            # ++si
loop    rd_k1        # if(cx != 0) goto rd_k1;
popw    %ax
ret

#==============================================================

#==============================================================
#函数名:rd_sect
#功能:读取软驱A上的1个扇区
#参数1:  ES:BX    读取数据存放的目的地址
#参数2:  SI        指定逻辑扇区号

rd_sect:
pushw   %ax
pushw   %dx
pushw   %cx
pushw   %bx

movw    %si,    %ax
movb    $18,    %bl
divb    %bl            # al <-- (ax)/(bl) 的商
# ah <-- (ax)/(bl)的余数
incb    %ah        # 因为软驱的物理起始扇区号从1开始,而逻辑扇区号从0开始,所以要增1
movb    %ah,    %cl        # 起始扇区号,(int 13H ah=02h 中断的参数)
xorb    %ah,    %ah
movb    $2,        %bl
divb    %bl
movb    %ah,    %dh        # 磁头号,(int 13H ah=02h 中断的参数)
movb    %al,    %ch        # 磁道号,(int 13H ah=02h 中断的参数)
popw    %bx

re_rd:
movb    $0x2,   %ah        # 读磁盘中断
movb    $1,        %al        # 读取1个扇区
movb    $0x0,   %dl        # 读取软驱A
int $0x13
jc re_rd

popw    %cx
popw    %dx
popw    %ax

ret

#==============================================================

#==============================================================
#函数名:check_mem
#功能:使用E820方法探测内存大小
#参数1: eax
#参数2: ebx
#参数3: ecx
#参数4: edx
#参数5: es:di

check_mem:
pushl   %eax
pushl   %ebx
pushl   %ecx
pushl   %edx
pushw   %es
pushw   %di

xorw    %ax,    %ax
movw    %ax,    %es
movl    $0x0,   %ebx
movw    $E820MAP, %di

1:
movl    $0x0E820,    %eax
movl    $20,    %ecx
movl    $0x534D4150,%edx
int        $0x15
jc        ck_fail
addw    $20,    %di
incl    E820NR(,1)
cmpl    $0,        %ebx
jne        1b
jmp        ck_ok
ck_fail:
movl    $0,        E820NR(,1)
ck_ok:
popw    %di
popw    %es
popl    %edx
popl    %ecx
popl    %ebx
popl    %eax

ret

#==============================================================
.org 0x100, 0xff
#==============================================================
#gdt在内存中的地址是0x7c00 + 0x100 = 0x7d00
gdt:
.quad   0x0000000000000000    #GDT表第一项为空

.word   0x0ffff        #边界限制为4GB
.word   0x0000        #基地址 = 0x00000000
.word   0x9a00        #代码段,可 读/执行
.word   0x00cf        # 4K边界粒度;386

.word   0x0ffff        #边界限制为4GB
.word   0x0000        #基地址 = 0x00000000
.word   0x9200        #数据段,可 读/写
.word   0x00cf        # 4K边界粒度;386

.quad   0x0000000000000000    #GDT表第四项备用
.quad   0x0000000000000000    #GDT表第五项备用
gdt_48:
.word   .-gdt-1
.long   gdt

#==============================================================
.org    0x1fe, 0x90
.word    0xaa55


kernel.inc的源代码如下
.set CODE_SEL,    0x08    #内核代码段选择符
.set DATA_SEL,    0x10    #内核数据段选择符
.set KERNEL_SECT_NUMBER,    1400  #内核长度,以扇区为单位
.set KERNEL_STACK_BOT,        0x7c00  #内核堆栈
.set KERNEL_START_SEGMENT,  0x7e0   #内核起始段,也就是内核会被拷贝到0x7e00上
.set GDT_ADDR,            0x7d00  #GDT在内存中的地址 0x7d00 = 0x7c00 + 0x100
.set NR_SYSTEM_CALLS,        11        #现在系统调用的数目
.set USER_CODE_SEL,        0x07    #用户代码段选择符
.set USER_DATA_SEL,        0x0f    #用户数据段选择符

.set E820NR ,            0x0FFC  #内存映射项个数,存放地址
.set E820MAP,            0x1000  #内存探测结果项,存放地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: