准备进入保护模式

一、准备GDT并加载

        1、了解GDT(32位系统)      

        之所以拆的这么乱,据传是因为要向前兼容8088等硬件的原因。所以设置起来有些麻烦。

        找一个GDT来看看,便于我们理解。windows内核调试可以很方便的找一个来看。先安装配置一个双机调试:

                1、主机安装wdk,快捷方式->属性->命令后输入这些参数: -b -k com:pipe,port=\\.\pipe\com_1,resets=0,reconnect -y,双击运行即可
                    也可以运行windbg,用菜单选择方式启动:
                    File->kernel debug->COM->115200,\\.pipe\com_1,勾选Pipe、Reconnect、Resets[0]
                2、虚拟机端:
            (1)安装串口:
                   a.选择使用管道命名:输入“\\.\pipe\com_1”(vmware17.5 不带双引号. com_1要和上面的对应。如果上面起的名字是abc,那么这里也应该是abc)
                   b.选择该端是服务器--因为是服务器,所以要早于客户机启动
                   c.选择另一端是应用程序
                   d.I/O模式:勾选轮询时主动放弃CPU (允许客户机操作系统在轮询模式下【而不是中断模式】使用此串口)
             (2)客户机操作系统:
                        修改boot.ini,添加一条启动debug条目:
            multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect /debug /debugport=COM1: /baudrate=115200
                        这个参数不用死记,打开win.ini,复制启动条目;
                        xp下运行msconfig,选到复制过的条目,选高级,勾选debug,选com1,选115200即可。
                       如果客户机是 windows10也可以采用这种方式。不过win10是64位系统,其描述符和32位系统不太一样。所以......

                (3)连接好以后,在windbg,kb>提示符下输入r gdtr,看一下寄存器里面存的地址,再用dd命令看一下,找到gdt的条目,对照上图分析一下加强感性认识。

gdt 条目

                

                        可以看到第一个8byte是全0。分析第二个看看:00cf9b00 0000ffff(由于高字节在后面,所以反过来才对。所以还是用dq一下看8字节比较好)

                        段基址24~31位:00

                        GDLA+段长度16~19位: c f = 1 1 0 0 | 1 1 1 1 -- G=1,粒度是4K;D=1,操作数是32位;L=0,非64位;AVL=0,没有用,这个可以随便。段长度16~19位=F。

                        P+DPL+S+TYPE: 9 b = 1 0 0 1 | 1 0 1 1 -- P=1,在内存中;DPL=0,特权级是0;S=1,普通段;TYPE.T=1,代码段;C=0,不可执行;R=1,可读;A=1,已被访问。

                        段基址0~23位:00 00 00

                        段长度0~15位:ff ff

                        总结:段基址=00 00 00 00,段长度= ff ff f(0~0xfffff,共1024,粒度是4096,所以长度是4G) 

                                   这个段还在内存中,是基址为0,长度4G,特权级为0,已被访问,可读不可执行的32位代码段。

6356554847403916150
段基址24~31位GDLA段长度:16~19位

P   

DPLSTYPE段基址:0~23位段长度:0~15位
1CRA
0EWA

                    00cff300`0000ffff:段基址:00 00 00;段长度:ff ff ff; GDLA全是1100;P=1,DPL=3,S=1;T=0,C=0,R=1,A=1。权限为3的数据段

                构造一个试试:段基址0x8000,长度4M,32位,用户态的可扩展可读写未访问的数据段:

                        段基址:4个字节--0x00 00 80 00, 分成两段,高8位00放在56~63位,剩下的24位放在16~39位。

                        段长度:4M,超过1M,粒度应该用4K,所以第55位G=1,32位段D=1并且L=0,AVL取0. GDLA=0xc

                                       4M=1K x 4K = 2^10 所以段长度第10位设置为1: 0000 0000 0100 0000 0000 = 0x0 40 00

                                        55~48位:GDLA+段长度高4位合并:0xc0 

                                        15~0位:0x 40 00

                        P+DPL+S+TYPE:1 11 1 0110  =f6,47~40位0xf6

                        合到一起:63~56 0x00

                                          55~48 0xc0

                                          47~40 0xf6

                                          39~16 0x00 80 00

                                          15~0  0x 40 00

                                           这个GDT条目:0x00 c0 f6 00 80 00 40 00 

二、打开A20

        这个简单且固定。原因是为了向前兼容8086系列硬件。

三、设置r0寄存器

        这个也简单且固定。

; boot.asm
[org 0x7c00]
[section .data]
LSA equ 0x500
M32 equ 0x800

[section .text]
[bits 16]
global _start
_start:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov sp, 0x1000

    mov ax, 3
    int 0x10

    mov si, M_ldSetup
    call print

    mov bl, 1  ;setup routine load to 2nd sector
    mov si, LSA;memory address
    call readHD

    mov bl, 2  ;mode32 routine load to 3rd sector
    mov si, M32;memory address
    call readHD

    jmp  LSA

readHD:
    mov dx, 0x1f2 ;count
    mov al, 2
    out dx, al

    inc dx        ;LBA address
    mov al, bl
    out dx, al

    inc dx
    xor al, al
    out dx, al

    inc dx
    out dx, al

    mov dx, 0x1f6
    mov al, 0xe0
    out dx, al

    mov dx, 0x1f7
.waitRDY:
    in al, dx
    test al, 0x40
    jz .waitRDY

    mov al, 0x20
    out dx, al

.waitDRQ:
    in al, dx
    test al, 0x08
    jz .waitDRQ

    mov cx, 0x100 ;256
    mov dx, 0x1f0
    mov di, si
.readDAT:
    in ax, dx
    push dx
    push ax
    mov dx, 0x1f7
    in al, dx
    test al, 0x01 ;ERR
    jnz .done
    pop ax
    pop dx
    mov word[di], ax
    add di, 2
    loop .readDAT
.done:
    ret

print:
    mov ah, 0xe
    mov bh, 0
    mov bl, 1
.loop:
    mov al, [si]
    cmp al, 0
    jz .done
    int 0x10
    inc si
    jmp .loop
.done:
    ret

M_ldSetup:
    db "Load setup routine ...", 13, 10, 0

times 510 - ($-$$) db 0
db 0x55, 0xaa
;setup.asm

[org 0x500]
[section .text]
[bits 16]

xchg bx, bx
    mov si, M_runSetup
    call print

    lgdt [gdt_entry]

    mov dx, 0x92
    in al, dx
    or al, 0x2
    out dx, al

    mov eax, cr0
    or  eax, 0x1
    mov cr0, eax

xchg bx, bx
    jmp 08:0


print:
    mov ax, 0xb800
    mov gs, ax
    mov di, 0xa0
    mov ah, 0x26
.loop:
    mov al, [si]
    cmp al, 0
    jz .done
    mov word[gs:di], ax
    inc si
    inc di
    inc di
    jmp .loop
.done:
    ret

GDT:
    dq 0
    dq 0x_00_40_9e_00_08_00_76_ff ; 32 code segment
    dq 0x_00_00_92_0b_80_00_7f_ff ; display memory area
END:

gdt_len equ END-GDT-1

gdt_entry:
    dw gdt_len
    dd GDT

M_runSetup:
    db "Setup routing running ...", 0
;mode32.asm

[org 0x800]
[section .text]
[bits 32]
xchg bx, bx
mode32:
    mov ax, cs
    mov gs, ax
    mov si, M_32
    call print32

    hlt

print32:
    mov ax, 0x10
    mov ds, ax
    mov di, 0x140
    mov ah, 0x62
.loop:
    ;mov al, [gs:si-0x800]
    mov al, [es:si]
    cmp al, 0
    jz .done
    mov word[ds:di], ax
    inc si
    inc di
    inc di
    jmp .loop
.done:
    ret
M_32:
    db "32 bit routine ...", 0

感受:

基础知识要尽量搞透彻。

遇到问题耐心一步一步把程序走一遍,注意看寄存器和内存的变化。汇编要把内存搞明白,GDT熟练掌握,另外要注意基址和偏移问题。容易踩坑。同时,原来看书说GDT 0条目没有用,通过这次学习发现,是有用的。比如上面程序寻找si的时候,默认是ds段,gs是代码段,都可以用,但要计算。在从0为基址开始时,es用GDT条目0即可实验成功(同gs - 0x800效果一样)。

另外Makefile文件把,mode32写入了第三个扇区。第三个扇区的内容被boot读到内存0x800处。

build:
	nasm boot.asm
	nasm setup.asm
	nasm mode32.asm
	dd if=/dev/zero of=a.img bs=512 count=2880
	dd if=boot of=a.img bs=512 count=1 seek=0 conv=notrunc
	dd if=setup of=a.img bs=512 count=1 seek=1 conv=notrunc
	dd if=mode32 of=a.img bs=512 count=1 seek=2 conv=notrunc
bochs:
	bochs -q -f bochsrc

clean:
	rm boot setup a.img

unlock:
	rm a.img.lock

考虑把16bit程序和32bit程序放在一个文件里应该也可以,试了一下还行

[org 0x500]
[section .text]
[bits 16]

    mov si, M_setupCome
    call print
xchg bx,bx
    lgdt [GDT_ENTRA]

    mov dx, 0x92
    in al, dx
    or al, 0x02
    out dx, al

    mov eax, cr0
    or  ax, 1
    mov cr0, eax

    jmp 8:entra32
print:
    mov ax, 0xb800
    mov gs, ax
    mov di, 0xa0
    mov ah, 0x26
.loop:
    mov al, [si]
    cmp al, 0
    jz .done
    mov word[gs:di], ax
    inc si
    add di,2
    jmp .loop
.done:
    ret

M_setupCome:
    db "Welcome setup routine comming ...", 0

; ------------GDT-----------------------------
GDT:
    dq 0
    dq 0x_00_cf_9e_00_00_00_ff_ff
    dq 0x_00_cf_96_00_00_00_ff_ff
    dq 0x_00_00_92_0b_80_00_7f_ff
END:

gdt_len equ END - GDT -1

GDT_ENTRA:
    dw gdt_len
    dq GDT
; -----------------------------------------------
; ------------32bit segment----------------------
[section .text]
[bits 32]
entra32:
    mov ax, cs
    mov ds, ax
    mov si, M_32
    call print32
    hlt

print32:
    mov eax, 0x18 ;显示存储区的描述符
    mov gs, ax
    mov di, 0x140 ;第三行开始显示
    mov ah, 0x62
.loop:
    mov al, [si]
    cmp al, 0
    jz .done
xchg bx, bx
    mov [gs:di], ax
    inc si
    add di,2
    jmp .loop
.done:
    ret

M_32:
      db "32 bits routine comming ...", 0
hlt

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值