Я не смог переключить процессор из реального режима в защищенный режим
Я делаю это по книге Ника Бланделла. Я пишу программу 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 ответа
В вашем коде есть несколько проблем:
- Вы пропускаете младший байт старшего слова базы в вашем
gdt_code
дескриптор. Просто добавьdb 0x0
послеdw 0x0
, - Фрэнк Котлер написал, что
gdt_descriptor
должен содержать линейный адрес. Да, это правда, но это не единственное место, где требуется линейный адрес. Ты можешь использоватьORG
Директива перед любым кодом или вручную добавьте источник в это поле. - инструкция
lgdt
все еще используетds
зарегистрироваться дляseg:off
расчет адреса. Вы должны установить его в ноль при написании кода подorg
, - Для перехода в защищенный режим также необходим линейный адрес, используемый в качестве смещения. Помните, что этот переход выполняется в режиме совместимости (так как это первая инструкция, выполняемая после переключения защищенного режима). Он использует два байта для селектора сегмента (перед двоеточием) и только два байта для смещения (после двоеточия). Это означает, что вы не должны пытаться перейти на адрес выше, чем
0xFFFF
, Опять же, проверьте происхождение вашего кода.
В вашем gdt_descriptor
У вас есть лимит и адрес. В отличие от большинства других адресов, это НЕ адрес сегмента: смещение. Это должен быть линейный адрес. Как MBR, вы, вероятно, переместили его с того места, где он изначально был загружен в 0x7C00. Адрес в вашем gdt_descriptor
(часто называется gdtr
) должен быть линейным адресом того, где вы сейчас находитесь. Вы не показываете достаточно кода, чтобы быть уверенным, но я подозреваю, что ваша проблема прямо там.