Я пишу код для загрузчика и пытаюсь заставить его перейти в 32-битный защищенный режим. Я попытался запустить код на QEMU, но он застрял с иконкой "ожидания".
Если я закомментирую «call switch_to_pm», он загружается и печатает сообщение «Запущен в 16-битном реальном режиме». Так что переключение в 32-битный защищенный режим, похоже, является проблемой.
Код указан ниже:
Имя файла : boot_loader.asm
; Commands quick reference
; dd if=boot_loader.bin of=kernel_dev_hd0.img bs=512 count=1 conv=notrunc
; nasm -f bin -o boot_loader.bin boot_loader.asm
[BITS 16]
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph (So 07c0h + 120h = 08e0h)
mov ss, ax ; Stack segment is 08e0h, so segment starts at address: 0x8e00
mov sp, 4096 ; Top of stack (ss:sp pair = 8e0h * 16 + 1000h = 9e00h)
mov ax, 07C0h ; Set data segment to where we're loaded the boot sector
mov ds, ax
mov si, MSG_REAL_MODE ; Put string position into SI
call print_string ; Call our string-printing routine
call switch_to_pm ; Note that we never return from here.
jmp $ ; Jump here - infinite loop!
%include "print_string.asm"
%include "gdt.asm"
%include "print_string_pm.asm"
%include "switch_to_pm.asm"
[bits 32] ; Switch to 32 bit mode
; This is where we arrive after switching to and initialising protected mode.
BEGIN_PM:
mov ebx, MSG_PROT_MODE
;call print_string_pm ; Use our 32-bit print routine.
jmp $ ; Hang.
; Global variables
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0
MSG_PROT_MODE db "Successfully landed in 32-bit Protected Mode", 0
; Boot sector padding
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
Имя файла : gdt.asm
; GDT
gdt_start:
gdt_null: ; the mandatory null descriptor
dd 0x0 ; ’dd’ means define double word (i.e. 4 bytes)
dd 0x0
gdt_code: ; the code segment descriptor
; base=0x0, limit=0xfffff ,
; 1st flags: (present )1 (privilege )00 (descriptor type)1 -> 1001b
; type flags: (code)1 (conforming )0 (readable )1 (accessed )0 -> 1010b
; 2nd flags: (granularity )1 (32-bit default )1 (64-bit seg)0 (AVL)0 -> 1100b
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16 -23)
db 10011010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24 -31)
gdt_data: ;the data segment descriptor
; Same as code segment except for the type flags:
; type flags: (code)0 (expand down)0 (writable )1 (accessed )0 -> 0010b
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16 -23)
db 10010010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit (bits 16-19)
db 0x0 ; Base (bits 24 -31)
gdt_end: ; The reason for putting a label at the end of the
; GDT is so we can have the assembler calculate
; the size of the GDT for the GDT decriptor (below)
; GDT descriptior
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one
; of the true size
dd gdt_start ; Start address of our GDT
; Define some handy constants for the GDT segment descriptor offsets , which
; are what segment registers must contain when in protected mode. For example ,
; when we set DS = 0x10 in PM , the CPU knows that we mean it to use the
; segment described at offset 0x10 (i.e. 16 bytes) in our GDT , which in our
; case is the DATA segment (0x0 -> NULL; 0x08 -> CODE; 0x10 -> DATA)
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
Имя файла : switch_to_pm.asm
[bits 16]
; Switch to protected mode
switch_to_pm:
cli ; We must switch off interrupts until we have
; set -up the protected mode interrupt vector
; otherwise interrupts will run riot.
lgdt [gdt_descriptor] ; Load our global descriptor table , which defines
; the protected mode segments (e.g. for code and data)
mov eax, cr0 ; To make the switch to protected mode , we set
or eax, 0x1 ; the first bit of CR0, a control register
mov cr0, eax
jmp CODE_SEG:init_pm ; Make a far jump (i.e. to a new segment) to our 32-bit
; code. This also forces the CPU to flush its cache of
; pre -fetched and real -mode decoded instructions , which can
; cause problems.
[bits 32]
; Initialise registers and the stack once in PM.
init_pm:
mov ax, DATA_SEG ; Now in PM, our old segments are meaningless ,
mov ds, ax ; so we point our segment registers to the
mov ss, ax ; data selector we defined in our GDT
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x9e00 ; Update our stack position so it is right
mov esp, ebp ; at the top of the free space.
call BEGIN_PM ; Finally , call some well -known label
Имя файла : print_string_pm.asm
[bits 32]
; Define some constants
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
; prints a null -terminated string pointed to by EDX
print_string_pm:
pusha
mov edx , VIDEO_MEMORY ; Set edx to the start of vid mem.
print_string_pm_loop:
mov al, [ebx] ; Store the char at EBX in AL
mov ah, WHITE_ON_BLACK ; Store the attributes in AH
cmp al, 0 ; if (al == 0), at end of string , so
je print_string_pm_done ; jump to done
mov [edx], ax ; Store char and attributes at current
; character cell.
add ebx , 1 ; Increment EBX to the next char in string.
add edx , 2 ; Move to next character cell in vid mem.
jmp print_string_pm_loop ; loop around to print the next char.
print_string_pm_done:
popa
ret ; Return from the function
Имя файла : print_string.asm
print_string: ; Routine: output string in SI to screen
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0 ; If char is zero, end of string
je .done
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
print_character: ; Routine: output character on to screen
mov ah, 0Eh ; int 10h 'print char' function
int 10h ; Otherwise, print it
ret