操作系统与网络实现 之四

来源:互联网 发布:tcp端口号范围 编辑:程序博客网 时间:2024/05/29 14:21

实模式与保护模式互相跳转

从这里开始我们就要告别实模式,实现保护模式了。

首先了解一些相关知识。

段描述符的具体格式

段描述符长8个字节64位。

 

 

重点说明:
   空描述符:           ; 这是保护模式要求保留的,第一个段必须是空段,空描述符的64位全是0
        dd  0
        dd  0           
    对于代码段字节5:
        db  10011010b   ; 0x9A 属性描述位,P=1,DPL=0,DT1=1,TYPE=A,指明此是代码段、可读可执行
    对于数据段字节5:              
        db  10010010b   ;  0x92 属性描述位,P=1,DPL=0,DT1=1,TYPE=2,指明此是数据段,可读可写

虚拟地址转换物理地址

简单的说:虚拟地址的偏移量+base=线性地址

在没有使用分布机制情况下,线性地址就是物理地址。

这里,我们没有使用分布机制。

 

改变kernelloader.asm,让它从实模式到保护模式显示一个字母,再返回实模式并显示hello。

新的kernelloader.asm

[BITS 16]

jmp main

 

gdt_entries equ 5 ;共有五个段描述符:null,os code32,os data32,os code16,os data16

showbase equ 8900h

pe     equ 1 ;bit PE in CR0

null     equ 0h

os_code32_sel equ 8h ;1,gdt,rpl=00

os_data32_sel equ 10h     ;2,gdt,rpl=00

os_code16_sel equ 18h     ;3,gdt,rpl=00

os_data16_sel equ 20h     ;4,gdt,rpl=00

 

[SECTION .data]

ns db 0x48,0x65,0x6C,0x6C,0x6F,0x20,0x77,0x6F,0x72,0x6C,0x64,0x21, 'from ya' ;;hello world!from ya

gdt_table times (gdt_entries*8) db 0

pdescr     times 6 db 0

end_32     dd 0

     dw 0

hello: ;;hello子程序

push es

mov cx,19 ;;循环19次

mov bx,0 ;;从数组[0]开始

mov ah,0eh

next:

mov al,[ns+bx]

int 10h

inc bx

dec cx

jnz next ;;cx不为0则继续

pop es

ret

 

main:

mov ax,1000h

mov ds,ax

;打开 A 20 地址线

mov ax , 0x2401

int 0x15

mov word[end_32+0],end32

mov word[end_32+4],os_code16_sel     ;point to 0018:end32

;[1]built up GDT table

cli

mov eax,gdt_table

;item 0:null descriptor,

mov dword[eax],0

mov dword[eax+4],0

add eax,8

;item 1,OS code32 descriptor,

;Base=00010000h,limit=0fh,G=1,D=1,type=a,dpl=0

mov word[eax],0000fh

mov word[eax+2],0

mov byte[eax+4],01h

mov byte[eax+5],09ah

mov byte[eax+6],0c0h

mov byte[eax+7],00h

add eax,8

;item 2,OS data32 descriptor

;Base=00010000h,Limit=0fffh,G=1,D=1,Type=2,DPL=0

mov word[eax],0fffh

mov word[eax+2],0000h

mov byte[eax+4],01h

mov byte[eax+5],092h

mov byte[eax+6],0c0h

mov byte[eax+7],00h

add eax,8

;item 3,OS code16 descriptor,

;Base=00010000h,limit=0ffffh,G=0,D=0,type=a,dpl=0

mov word[eax],0ffffh

mov word[eax+2],0

mov byte[eax+4],01h

mov byte[eax+5],09ah

mov byte[eax+6],00h

mov byte[eax+7],00h

add eax,8

;item 4,OS data16 descriptor

;Base=00010000h,Limit=0ffffh,G=0,D=1,Type=2,DPL=0

mov word[eax],0ffffh

mov word[eax+2],0000h

mov byte[eax+4],01h

mov byte[eax+5],092h

mov byte[eax+6],40h

mov byte[eax+7],00h

;[2]built false GDT descriptor

mov word[pdescr+0],(gdt_entries*8)

mov dword[pdescr+2],gdt_table+00010000h

lgdt [pdescr]

;[3]enter into protected mode

;刷新CR0

mov eax,cr0

or eax,pe

mov cr0,eax

jmp flush

flush:

mov ax,os_data32_sel

mov ds,ax

mov es,ax

mov ss,ax

mov fs,ax

mov gs,ax

jmp dword os_code32_sel:start32

;[4]run in protected mode

start32:

cli

mov bx,showbase

dw 0ah         ;mov ebx,0a8900h

mov di,bx     ;es:edi=0010:000a8900=0b8900h

mov al,'A'

mov ah,34h

stosw

 

mov si,end_32

dw 00h      ;mov esi,offset end_32

 

db 0ffh,2eh         ;相当于jmp Fword Ptr [esi] ,jmp to 0018:end32

end32:

;[5]exit to dos

mov ax,os_data16_sel

mov ds,ax

mov es,ax

mov ss,ax

mov fs,ax

mov gs,ax

cli

mov eax,cr0

and al,0feh

mov cr0,eax     ;clear pe bit,exit of protected mode

mov cr3,eax

jmp realflush

realflush:

mov ax,01000h

mov ds,ax

mov es,ax

mov ss,ax

xor ax,ax

mov fs,ax

mov gs,ax

jmp 0x1000:real16

real16:

mov word[pdescr+0],0ffffh     ;limit

mov dword[pdescr+2],0 ;basement

lidt [pdescr]

sti

mov ah,4ch

int 21h

call hello ;返回实模式后显示hello

jmp $

 

程序中

;打开 A 20 地址线

mov ax , 0x2401

int 0x15

......

;刷新CR0

mov eax,cr0

or eax,pe

mov cr0,eax

jmp flush

flush:

这些语句都是按要求必须这么做的,没什么可说的,大家按套路来就行。

 

目前kernelloader.bin大小为444字节,占据1个扇区,相应的boot程序中读取扇区的参数值应为1

 

在bochs中运行效果如下:

 

16位模式下与32位模式下的编译结果(机器码)是有区别的:

(16位下)mov bx,8900h 机器码为 BB0089

(32位下)mov ebx,68900h 机器码为 BB00890600

两个相差0600

 

(16位下)mov di,bx 机器码为 89DF

(32位下)mov edi,ebx 机器码为 89DF

两个相同

 

(16位下)mov si,end_32 机器码为 BE4500

(32位下)mov esi,end_32 机器码为 BE45000000

两个相差0000

 

(16位下)jmp fword ptr [esi] 机器码为 FF2e

为什么要研究机器码?因为我们程序是在16位模式下编译,但是我们中间这段程序又将要跳转到保护模式,但是在保护模式下运行16位程序显然会出错,怎么解决这个问题?

我们可以将16位模式下与32位模式指令中相差的部分补充上,那么以下的语句就可以理解了:

mov bx,showbase

dw 06h         ;相当于mov ebx,68900h

mov di,bx     ;es:edi=0010:00068900=0b8900h

mov al,'A' ;16位与32位相同

mov ah,34h ;16位与32位相同

stosw ;16位与32位相同

 

mov si,end_32

dw 00h      ;mov esi,offset end_32

 

db 0ffh,2eh         ;相当于jmp Fword Ptr [esi] ,jmp to 0018:end32

 

0 0