Как войти в 32-битный защищенный режим в сборке NASM?
Я изучаю сборку x86 и пытаюсь сделать игрушечную операционную систему в NASM, но кое-чего не понимаю.
Я сделал загрузчик, который успешно загружает мое ядро:
- Загружает 14 секторов с дискеты, которая содержит файл ядра;
- Поиск файла в этих секторах с пометкой
kernel.feo
; - Загружает этот файл в память в смещение
0x2000
; - Выполняет ядро, используя дальний переход
jmp 0x2000:0x0000
,
Так что у меня есть код ядра, расположенный на 0x2000:0
в памяти. CS
может быть правильно установлен, потому что использование прыжка в длину. В этом коде ядра я хочу войти в 32-битный защищенный режим, но я не уверен, как работают GDT. Когда я запускаю приведенный ниже код на виртуальной машине (QEMU)
это ничего не делать.
Я хочу порадовать вас, чтобы помочь мне войти в 32-битный защищенный режим!
Тем не менее, у вас есть следующие проблемы:
- Вы предполагаете, что код загружен в
0x7c00:0
из-заorg 0
, но это может быть не так. Гарантируется только физический адрес. Вы должны использовать дальний переход к точке входа, чтобыCS
правильно установлено.- Вы по каким-то причинам настраиваете
DS
в0x2000
поэтому ваш код не найдет никаких данных вообще. Вы должны установитьDS
соответствоватьCS
или используйтеCS
переопределить везде (не рекомендуется).- Код защищенного режима предполагает нулевой сегмент, что, в свою очередь, означает, что он ожидает
org 0x7c00
что, конечно, конфликтует с вашей настройкой. Вы должны переключиться наorg 0x7c00
и сегменты0
,- Сегмент текстового режима VGA находится на
0xb8000
не0xb80000
(на один меньше нуля).- У вас нет байтов подписи загрузки
0x55 0xaa
в конце загрузочного сектора.
Я исправил эти вещи в своем коде:
[org 0x0]
исправлен на[org 0x2000]
и сегменты установлены в0
;DS
исправлен на0
вместо0x2000
так что теперь он совпадает сCS
;- Сегмент текстового режима VGA исправлен на
0xb8000
;
Но код не будет работать с этими исправлениями, он должен напечатать две строки, но он ничего не делает!
Обратите внимание, что этот код ядра не должен заканчиваться загрузочной подписью 0x55 0xAA
потому что это не загрузочный сектор.
Вот исправленный код ядра (который не работает):
[bits 16]
[org 0x2000]
jmp 0:kernel_start
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 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
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
print:
mov ah, 14
mov bh, 0
lodsb
cmp al, 0
je .done
int 0x10
jmp print
.done:
ret
uzenet_real db 'uzenet16', 0
uzenet_prot db 'uzenet32', 0
kernel_start:
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov si, uzenet_real
call print
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x90000
mov esp, ebp
mov ebx, uzenet_prot
call print32
jmp $
1 ответ
Программирование ОС - сложная задача. Вы, по крайней мере, ожидаете, что сможете использовать отладчик, чтобы найти свои собственные ошибки и понять основные вещи. Возможно, вы захотите пересмотреть, есть ли у вас все предпосылки для этого усилия.
Тем не менее, у вас есть следующие проблемы:
- Вы предполагаете, что код загружен в
0x7c00:0
из-заorg 0
, но это может быть не так. Гарантируется только физический адрес. Вы должны использовать дальний переход к точке входа, чтобыCS
правильно установлено. - Вы по каким-то причинам настраиваете
DS
в0x2000
поэтому ваш код не найдет никаких данных вообще. Вы должны установитьDS
соответствоватьCS
или используйтеCS
переопределить везде (не рекомендуется). - Код защищенного режима предполагает нулевой сегмент, что, в свою очередь, означает, что он ожидает
org 0x7c00
что, конечно, конфликтует с вашей настройкой. Вы должны переключиться наorg 0x7c00
и сегменты0
, - Сегмент текстового режима VGA находится на
0xb8000
не0xb80000
(на один меньше нуля). - У вас нет байтов подписи загрузки
0x55 0xaa
в конце загрузочного сектора.
Фиксированный код:
[bits 16]
[org 0x7c00]
jmp 0:kernel_start
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 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
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
print:
pusha
mov ah, 14
mov bh, 0
.loop:
lodsb
cmp al, 0
je .done
int 0x10
jmp .loop
.done:
popa
ret
uzenet16 db 'uzenet16', 0
uzenet32 db 'uzenet32', 0
kernel_start:
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov si, uzenet16
call print
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x2000
mov esp, ebp
mov ebx, uzenet32
call print32
jmp $
[SECTION signature start=0x7dfe]
dw 0AA55h
Ваш обновленный вопрос все еще кажется запутанным по поводу того, где загружен код: вы говорите offset 0x2000
но тогда поговорим о Executes the kernel using a far jump jmp 0x2000:0x0000
что, конечно, неправильно, потому что у него есть еще один ноль в сегменте, и в любом случае он должен быть дальним прыжком с нулевого сегмента: jmp 0:0x2000
, Кроме этого, убедитесь, что ваш код действительно загружен в память в правильном месте. Научитесь пользоваться отладчиком.
Вот небольшой загрузочный сектор, который загружает вышеуказанный код из второго сектора по адресу 0x2000
, Он работает нормально, проблема не в GDT, особенно если вы даже не печатаете сообщение в реальном режиме (вы тоже не поняли).
[bits 16]
[org 0x7c00]
mov ax, 0201h
mov cx, 0002h
mov dh, 0
mov bx, 0
mov es, bx
mov bx, 2000h
int 13h
jmp 0:2000h
[SECTION signature start=0x7dfe]
dw 0AA55h