2016-04-12 20 views
4

Ich entwickle einen Bootloader, der nach dem Wechsel in den geschützten Modus in einen einfachen Kernel booten wird. Ich habe this paper als Tutorial irgendwo in Kapitel vier oder fünf verwendet. Theoretisch sollte es im 16-Bit-Real-Modus starten, den Kernel in den Speicher laden, in den 32-Bit-Protected-Modus wechseln und den Kernel-Code starten.Dreifacher Fehler beim Sprung in den geschützten Modus

Wenn ich jedoch in geschützten Modus und weit springen oder zu einem anderen Segment springen, es dreifach Fehler. Hier ist die Hauptbootsektor-Code:

[org 0x7c00] 

KERNEL_OFFSET equ 0x1000 

mov [BOOT_DRIVE], dl ;Get the current boot drive from the BIOS 

mov bp, 0x9000   ;Set up stack, with enough room to grow downwards 
mov sp, bp 

mov bx, REAL_MODE_MSG 
call print_string 

call load_kernel 

call switch_to_pm 

jmp $      ;Jump to current position and loop forever 

%include "boot/util/print_string.asm" 
%include "boot/util/disk.asm" 
%include "boot/gdt/gdt.asm" 
%include "boot/util/print_string_pm.asm" 
%include "boot/switch_to_pm.asm" 

[bits 16] 
load_kernel: 
    mov bx, LOAD_KERNEL_MSG ;Print a message saying we are loading the kernel 
    call print_string 
    mov bx, KERNEL_OFFSET  ;Set up disk_load routine parameters 
    mov dh, 15 
    mov dl, [BOOT_DRIVE] 
    call disk_load    ;Call disk_load 
    ret 

[bits 32] 
BEGIN_PM: 
    mov ebx, PROT_MODE_MSG 
    call print_string_pm 
    call KERNEL_OFFSET 

    jmp $ 

; Data 
BOOT_DRIVE: db 0 
REAL_MODE_MSG: db "Started in real mode.", 0 
PROT_MODE_MSG: db "Successfully entered 32-bit protected mode.", 0 
LOAD_KERNEL_MSG: db "Loading Kernel into memory", 0 

; Bootsector padding 
times 510-($-$$) db 0 
dw 0xaa55 

Hier ist die GDT:

;Global Descriptor Table 
gdt_start: 

gdt_null: ; We need a null descriptor at the start (8 bytes) 
    dd 0x0 
    dd 0x0 

gdt_code: ; 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 (0-15) 
    dw 0x0   ; Base (16-23) 
    db 10011010b ; 1st flags and type flags 
    db 11001111b ; 2nd flags and Limit (16-19) 
    db 0x0   ; Base (24-31) 

gdt_data: ; Data segment descriptor 
    ;Same as CSD except for type flags 
    ; (code)0 (expand down)0 (writable)1 (accessed)0 -> 0010b 
    dw 0xffff  ; Limit (bits 0-15) 
    dw 0x0   ; Base (0-15) 
    dw 0x0   ; Base (16-23) 
    db 10010010b ; 1st flags and type flags 
    db 11001111b ; 2nd flags and Limit (16-19) 
    db 0x0   ; Base (24-31) 

gdt_end: 


;GDT Descriptor 
gdt_descriptor: 
    dw gdt_end - gdt_start - 1 
    dd gdt_start 

;Some Constants 
CODE_SEG equ gdt_code - gdt_start 
DATA_SEG equ gdt_data - gdt_start 

Hier ist der Code für die in den geschützten Modus schalten, wo es triple Störungen:

[bits 16] 
switch_to_pm: 
    cli 
    lgdt [gdt_descriptor] ; load the gdt 
    mov eax, cr0   ; turn pm on 
    or eax, 0x1 
    mov cr0, eax 
    jmp CODE_SEG:init_pm ; THIS IS WHERE THE PROBLEM IS! 

[bits 32] 
init_pm: 
    mov ax, DATA_SEG ; Point segment registers to the data 
    mov ds, ax  ; selector defined in the gdt 
    mov ss, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 
    mov ebp, 0x90000 ; Update our stack 
    mov esp, ebp 
    call BEGIN_PM ;Move on 

Bei Ich setze eine jmp $ Anweisung in den Leerlauf an einer bestimmten Stelle, direkt vor der jmp CODE_SEG:init_pm Anweisung, es im Leerlauf und nicht dreifacher Fehler. Wenn ich es nach dieser Anweisung innerhalb des Labels init_pm platziere, macht es einen Dreifachfehler. Ich bin mir ziemlich sicher, dass dies der Grund ist. Ich bin nicht sicher, warum, vielleicht ist es ein Problem mit der GDT. Ich bin neu in der Entwicklung von Betriebssystemen und Bootloadern. Irgendwelche Vorschläge, wie man dieses Problem löst?

+3

Sind Sie sicher, dass Ihre GDT korrekt ist? Ich denke, die Sache, die bei einem flüchtigen Blick auffällt, ist, dass jeder Ihrer Einträge 9 Byte (72 Bit) ist.Ein GDT-Eintrag ist 8 Bytes (64 Bits). es scheint, dass Sie vielleicht 'db 0x0; Basis (16-23) 'anstelle von' dw 0x0; Basis (16-23) '? Beachten Sie, dass "dw" in "db" geändert wird. Falsche GDT-Einträge würden einen Dreifachfehler erzeugen. –

+1

Ich würde auch empfehlen, auf meine [allgemeine Bootloader-Tipps] (http://stackoverflow.com/questions/32701854/boot-loader-doesnt-jump-to-kernel-code/32705076#32705076) zu schauen. Sie nehmen an, dass das DS (Datensegment) -Register bei der Eingabe Null ist (da Sie org 0x7c00 verwenden). Sie sollten es explizit auf Null setzen. Sie legen den Stapel auch auf eine seltsame Weise fest. Sie setzen SP auf 9000, aber Sie setzen _SS_ nicht, was bedeutet, dass Sie nicht wirklich wissen, wo Sie den Stapel im Speicher ablegen. Sie sollten das Register _SS_ einstellen, gefolgt von der Einstellung des Registers _SP_. Meine Bootloader Tipps bieten ein Beispiel. –

+0

@Michael Petch: Stellt sich heraus, SS ist garantiert, um Null zu sein. – Joshua

Antwort

2

Michael Petch gab die richtige Antwort auf diese Frage in den Kommentaren. Leider scheint dies von mehreren Leuten verpasst worden zu sein, da jetzt drei falsche Antworten gepostet wurden, von denen zwei denselben Fehler machten. Hier wird dann seinen Kommentar als Antwort in der Hoffnung geschrieben, dass sie es besser sichtbar macht:

Sind Sie sicher, dass Ihr GDT ist richtig? Ich denke, die Sache, die bei einem flüchtigen Blick auffällt, ist, dass jeder Ihrer Einträge 9 Byte (72 Bit) ist. Ein GDT-Eintrag ist 8 Bytes (64 Bits). es scheint, dass Sie vielleicht db 0x0 ; Base (16-23) statt dw 0x0 ; Base (16-23) gemeint? Beachten Sie, dass der Unterschied darin besteht, dass dw in db geändert wird. Falsche GDT-Einträge würden einen Dreifachfehler erzeugen.

Michael Petch hat auch einen guten Followup Kommentar, andere Probleme mit dem Bootloader wies darauf hin:

ich auch an meinem general bootloader tips suchen empfehlen würde. Sie nehmen an, dass das DS (Datensegment) -Register bei der Eingabe Null ist (da Sie org 0x7c00 verwenden). Sie sollten es explizit auf Null setzen. Sie legen den Stapel auch auf eine seltsame Weise fest. Sie setzen SP auf 9000, aber Sie setzen SS nicht, was bedeutet, dass Sie nicht wirklich wissen, wo Sie den Stapel in den Speicher legen. Sie sollten das SS-Register einstellen, gefolgt von der Einstellung des SP-Registers. Meine Bootloader Tipps bieten ein Beispiel.

+0

Leider funktionierte keiner von denen alleine. Michael gab mir einen Code, den ich benutzte und es funktionierte. Ich weiß immer noch nicht genau, um welches Problem es sich handelt, abgesehen von denen, auf die er hingewiesen hat. – adotout1

2

Das Problem ist mit Ihnen jmp CODE_SEG:init_pm. Im 16-Bit-Modus ist es ein 4-Byte-Sprung zur 16-Bit-Adresse wie segment:offset. Aber Sie müssen 6-Byte Weitsprung zu einer 32-Bit-Adresse tun. In FASM Syntax wird es

seine
jmp fword CODE_SEG:init_pm 

Dies wird eine Operandengröße prefix 0x66 die Anweisung hinzuzufügen und zu behandeln init_pm als 32-Bit-Offset. Nicht sicher, wie man das gleiche in Nasm erreicht, aber Sie bekommen die Idee.

+2

OPs Die _JMP_-Anweisung funktioniert, da der Offset innerhalb der unteren 64 KB liegt (dies kann als 16-Bit-Offset ausgedrückt werden). Es verwendet den Selektor CODE_SEG und Null erweitert init_pm auf eine 32-Bit-Adresse. In diesem Fall ist ein 32-Bit-JMP nicht erforderlich. –

+0

Oh, aber, wenn es ein cs mit Nullbasis lädt, wo fügt es (offmode_cs << 4 ") zum Offset hinzu. – doug65536