assemblyprotected-modembrreal-modegdt

I failed in switching the cpu from real-mode to protected-mode


I do this according a book by Nick Blundell. I write a MBR program, which runs in real-mode firstly, and some instructions in the program will switch the cpu to protected-mode. First I set the GDT like this:

gdt_start:

gdt_null:
    dd 0x0
    dd 0x0

gdt_code:
    dw 0xffff
    dw 0x0
    db 10011010b
    db 11001111b
    db 0x0

gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0

gdt_end:

gdt_descriptor :
    dw gdt_end - gdt_start - 1
    dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

Then the cpu runs the following instructions:

   cli

    lgdt [gdt_descriptor]
    mov eax,cr0
    or eax,0x1
    mov cr0,eax ;this will set the cpu to protected-mode        

;jmp $  ;I use this instrction to find where is wrong
    jmp CODE_SEG:init_pm

jmp $
[bits 32]
init_pm:
jmp $
    mov ax,10
jmp $
    mov ds,eax
    mov ss,eax
jmp $
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ebp,0x90000
    mov esp,ebp
    call BEGIN_PM

The instruction jmp CODE_SEG:init_pm will cause the cpu to crash and restart. If I change this to jmp init_pm, the following instruction mov ax,10 will cause the cpu to crash and restart. And the book said the switching operation needs a long jump.

Could you please help me do the switching operation?


Solution

  • There are several problems in your code:

    1. You're missing lower byte of base´s high word in your gdt_code descriptor. Simply add db 0x0 after dw 0x0.
    2. Frank Kotler wrote that gdt_descriptor must contain linear address. Yes, that's true, but it's not the only place where linear address is required. You can use ORG directive before any code, or manually add origin to this field.
    3. Instruction lgdt still uses ds register for seg:off address calculation. You should have it set to zero when writing code under org.
    4. Far jump to the protected mode also needs linear address used as offset. Remember that this jump is executed in compatibility mode (as it's the first instruction executed after protected mode switch). It uses two bytes for segment selector (before colon) and only two bytes for offset (after colon). That means you shouldn't try to jump to address higher than 0xFFFF. Again, check origin of your code.
    5. The mov ax, 10 instruction about which you say that it will cause the cpu to crash and restart, uses the wrong value! The selector that we need is at offset 16, that is 0x10 in hexadecimal. And since you defined DATA_SEG equ gdt_data - gdt_start, you really should be writing mov ax, DATA_SEG.