Проблема с переключением в 32-битный защищенный режим - PullRequest
0 голосов
/ 11 июня 2019

Я пишу код для загрузчика и пытаюсь заставить его перейти в 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
...