Я не смог переключить процессор из реального режима в защищенный режим

Я делаю это по книге Ника Бланделла. Я пишу программу MBR, которая сначала запускается в реальном режиме, и некоторые инструкции в программе переведут процессор в защищенный режим. Сначала я установил GDT так:

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

Затем процессор выполняет следующие инструкции:

   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

Инструкция jmp CODE_SEG:init_pm вызовет сбой и перезагрузку процессора. Если я изменю это на jmp init_pm, следующая инструкция mov ax,10 вызовет сбой и перезагрузку процессора. И в книге сказано, что операция переключения требует длинного прыжка.

Не могли бы вы помочь мне сделать операцию переключения?

2 ответа

Решение

В вашем коде есть несколько проблем:

  1. Вы пропускаете младший байт старшего слова базы в вашем gdt_code дескриптор. Просто добавь db 0x0 после dw 0x0,
  2. Фрэнк Котлер написал, что gdt_descriptor должен содержать линейный адрес. Да, это правда, но это не единственное место, где требуется линейный адрес. Ты можешь использовать ORG Директива перед любым кодом или вручную добавьте источник в это поле.
  3. инструкция lgdt все еще использует ds зарегистрироваться для seg:off расчет адреса. Вы должны установить его в ноль при написании кода под org,
  4. Для перехода в защищенный режим также необходим линейный адрес, используемый в качестве смещения. Помните, что этот переход выполняется в режиме совместимости (так как это первая инструкция, выполняемая после переключения защищенного режима). Он использует два байта для селектора сегмента (перед двоеточием) и только два байта для смещения (после двоеточия). Это означает, что вы не должны пытаться перейти на адрес выше, чем 0xFFFF, Опять же, проверьте происхождение вашего кода.

В вашем gdt_descriptorУ вас есть лимит и адрес. В отличие от большинства других адресов, это НЕ адрес сегмента: смещение. Это должен быть линейный адрес. Как MBR, вы, вероятно, переместили его с того места, где он изначально был загружен в 0x7C00. Адрес в вашем gdt_descriptor (часто называется gdtr) должен быть линейным адресом того, где вы сейчас находитесь. Вы не показываете достаточно кода, чтобы быть уверенным, но я подозреваю, что ваша проблема прямо там.

Другие вопросы по тегам