启动32位保护模式

时间:2018-06-19 18:03:06

标签: assembly x86 nasm dos protected-mode

我试图在汇编中启动32位保护模式,但是当我在dosbox中运行它时,出现错误:“ JMP非法解密器类型10”,而当我以真实dos身份运行时,整个PC都将重新启动。整个代码:

    org 100h
    cli

    lgdt [GdtDesc]

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

    jmp $+2
    jmp 0x8:Start32

GdtDesc:
    dw 0x18
    dd Gdt

Start32:
    mov ax,10h
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ss,ax

    mov esp,8000h

    mov eax,cr0
    xor eax,1
    mov cr0,eax

    ret

ALIGN 4 ; Global Descriptor Table (GDT) (faster accessed if aligned on 4).
Gdt:   ; GDT[0]: Null entry, never used.
    dd 0
    dd 0
    ; GDT[1]: Executable, read-only code, base address of 0, limit of FFFFFh, 
    ; granularity bit (G) set (making the limit 4GB)

    dw 0FFFFh ; Limit[15..0]
    dw 0000h ; Base[15..0]
    db 00h ; Base[23..16]
    db 10011010b ; P(1) DPL(00) S(1) 1 C(0) R(1) A(0)
    db 11001111b ; G(1) D(1) 0 0 Limit[19..16]
    db 00h ; Base[31..24]
    ; GDT[2]: Writable data segment, covering the save address space than GDT[1].

    dw 0FFFFh ; Limit[15..0]
    dw 0000h ; Base[15..0]
    db 00h ; Base[23..16]
    db 10010010b ; P(1) DPL(00) S(1) 0 E(0) W(1) A(0)
    db 11001111b ; G(1) B(1) 0 0 Limit[19..16]
    db 00h ; Base[31..24]

1 个答案:

答案 0 :(得分:3)

最佳答案来自Michael Petch(简洁明了的代码和注释):

  

有些时候回到IRC上,我写了一些类似的代码。我整理了一下并将其放在服务器上:capp-sysware.com/misc/ircasm/enterpm.asm。不确定它是否对您有用,但是您可能会得到一些启发。 –迈克尔·佩奇

; Assemble with NASM as
;     nasm -f bin enterpm.asm -o enterpm.com

STACK32_TOP EQU 0x200000
CODE32_REL  EQU 0x110000
VIDEOMEM    EQU 0x0b8000

use16
; COM program CS=DS=SS
org 100h

    call check_pmode    ; Check if we are already in protected mode
                        ;    This may be the case if we are in a VM8086 task.
                        ;    EMM386 and other expanded memory manager often
                        ;    run DOS in a VM8086 task. DOS extenders will have
                        ;    the same effect

    jz not_prot_mode    ; If not in protected mode proceed to switch
    mov dx, in_pmode_str;    otherwise print an error and exit back to DOS
    mov ah, 0x9
    int 0x21            ; Print Error
    ret

not_prot_mode:
    call a20_on         ; Enable A20 gate (uses Fast method as proof of concept)
    cli

    ; Compute linear address of label gdt_start
    ; Using (segment << 4) + offset
    mov eax,cs          ; EAX = CS
    shl eax,4           ; EAX = (CS << 4)
    mov ebx,eax         ; Make a copy of (CS << 4)
    add [gdtr+2],eax    ; Add base linear address to gdt_start address
                        ;     in the gdtr
    lgdt [gdtr]         ; Load gdt

    ; Compute linear address of label code_32bit
    ; Using (segment << 4) + offset
    add ebx,code_32bit  ; EBX = (CS << 4) + code_32bit

    push dword 0x08     ; CS Selector
    push ebx            ; Linear offset of code_32bit
    mov bp, sp          ; m16:32 address on top of stack, point BP to it

    mov eax,cr0
    or eax,1
    mov cr0,eax         ; Set protected mode flag

    jmp dword far [bp]  ; Indirect m16:32 FAR jmp with
                        ;    m16:32 constructed at top of stack
                        ;    DWORD allows us to use a 32-bit offset in 16-bit code

; 16-bit functions that run in real mode

; Check if protected mode is enabled, effectively checkign if we are
; in in a VM8086 task. Set ZF to 0 if in protected mode

check_pmode:
    smsw ax             ; Get lower 16 bits of control register in AX
    test ax, 0x1        ; Test the PE bit (bit 0) and set ZF flag accordingly
    ret 

; Enable a20 (fast method). This may not work on all hardware
a20_on:
    cli
    in al, 0x92         ; Read System Control Port A
    test al, 0x02       ; Test current a20 value (bit 1)
    jnz .skipfa20       ; If already 1 skip a20 enable
    or al, 0x02         ; Set a20 bit (bit 1) to 1
    and al, 0xfe        ; Always write a zero to bit 0 to avoid
                        ;     a fast reset into real mode
    out 0x92, al        ; Enable a20
.skipfa20:
    sti
    ret

in_pmode_str: db "Processor already in protected mode - exiting",0x0a,0x0d,"$"

align 4
gdtr:
    dw gdt_end-gdt_start-1
    dd gdt_start

gdt_start:
    ; First entry is always the Null Descriptor
    dd 0
    dd 0

gdt_code:
    ; 4gb flat r/w/executable code descriptor
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 0b10011010       ; access
    db 0b11001111       ; granularity
    db 0                ; base high

gdt_data:
    ; 4gb flat r/w data descriptor
    dw 0xFFFF           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 0b10010010       ; access
    db 0b11001111       ; granularity
    db 0                ; base high
gdt_end:

; Code that will run in 32-bit protected mode
; Align code to 4 byte boundary. code_32bit label is
; relative to the origin point 100h
align 4
code_32bit:
use32
; Set virtual memory address of pm code/data to CODE32_REL
; We will be relocating this section from low memory where DOS
; originally loaded it.
section protectedmode vstart=CODE32_REL, valign=4
start_32:
    cld                 ; Direction flag forward
    mov eax,0x10        ; 0x10 is flat selector for data
    mov ds,eax
    mov es,eax
    mov fs,eax
    mov gs,eax
    mov ss,eax
    mov esp,STACK32_TOP ; Should set ESP to a usable memory location
                        ; Stack will be grow down from this location

    mov edi,start_32    ; EDI = linear address where PM code will be copied
    mov esi,ebx         ; ESI = linear address of code_32bit
    mov ecx,PMSIZE_LONG ; ECX = number of DWORDs to copy
    rep movsd           ; Copy all code/data from code_32bit to CODE32_REL
    jmp 0x08:.relentry  ; Absolute jump to relocated code

.relentry:
    mov ah, 0x57        ; Attribute white on magenta

    ; Print a string to display
    mov esi,str         ; ESI = address of string to print
    mov edi,VIDEOMEM    ; EDI = base address of video memory
    call print_string_attr

    cli
endloop:
    hlt                 ; Halt CPU with infinite loop
    jmp endloop

print_string_attr:
    push ecx
    xor ecx,ecx         ; ECX = 0 current video offset
    jmp .loopentry
.printloop:
    mov [edi+ecx*2],ax  ; Copy attr and character to display
    inc ecx             ; Next word position
.loopentry:
    mov al,[esi+ecx]    ; Get next character to print
    test al,al
    jnz .printloop      ; If it's not NUL continue
.endprint:
    pop ecx
    ret

str: db "Protected Mode",0

PMSIZE_LONG equ ($-$$+3)>>2
                        ; Number of DWORDS that the protected mode
                        ;    code and data takes up (rounded up)