QEMU starts blinking with custom boot loader in x86

128 views Asked by At

I'm writing my own boot loader for i386 in NASM for education purposes. But when I'm running qemu with this it starts blinking and do anything but what I want. Here's my code:

; defs.asm
%define STACK_SEGMENT_REAL_MODE 0x9000
%define STACK_SEGMENT_PROT_MODE 0xFFFFFFFE
; head.asm
[bits 16]
[org 0x7C00]

%include "defs.asm"

start:
    cli

    xor ax, ax ; AX = 0
    mov ds, ax ; DS = 0
    mov es, ax ; ES = 0
    mov ss, ax ; SS = 0

    mov esp, STACK_SEGMENT_REAL_MODE
    mov ebp, esp

    sti

    call switch_real_to_prot

    ; at this point interrupts are diabled
  ; infinite loop
    jmp $

%include "protection.asm"

times 510-($-$$) db 0

db 0x55
db 0xAA
; protection.asm
; ----------------- Global Descriptor Table -----------------
;
;        Segment Descriptor
; 31              23               15                7               0
; +---------------+-+-+-+----------+-+-----+-+-----+-+---------------+
; |               | | |A|          | |     | |     | |               |
; |  BASE 31..24  |G|X|V|  LIMIT   |P| DPL |1| TYPE|A|  BASE 21..16  |
; |               | | |L|  19..16  | |     | |     | |               |
; +---------------+-+-+-+----------+-+-----+-+-----+-+---------------+
; |                                |                                 |
; |       SEGMENT BASE 15..0       |      SEGMENT LIMIT 15..0        |
; |                                |                                 |
; +--------------------------------+---------------------------------+

%include "defs.asm"

%define GDT_CS_PROT 0x08
%define GDT_DS_PROT 0x10

gdt:
    ; NULL Segment Descriptor
    dq 0

    ; Code Segment Descriptor
    ;
    ; BASE = 0x00000000
    ; LIMIT = 0xFFFFF (4GB), 4KiB GRANULARITY,
    ; PRESENT TYPE = 32 bit, EXECUTE/READ, DPL = 0
    dw 0xFFFF
    dw 0
    db 0
    db 0x9A
    db 0xCF
    db 0

    ; Data Segment Descriptor
    ;
    ; BASE = 0x00000000
    ; LIMIT = 0xFFFFF (4GB), 4KiB GRANULARITY,
    ; PRESENT TYPE = 32 bit, READ/WRITE, DPL = 0
    dw 0xFFFF
    dw 0
    db 0
    db 0x92
    db 0xCF
    db 0
gdt_end:

gdt_desc:          ;   Func  | Bits
                   ; --------+------
    db gdt_end - gdt ; Limit   | 0-15
    dw gdt           ; Address | 16-47

switch_real_to_prot:
    [bits 16]
    cli

    xor ax, ax
    mov ds, ax

    lgdt [gdt_desc]

    ; switch to protected mode
    mov eax, cr0
    or  eax, 1
    mov cr0, eax

    jmp GDT_CS_PROT:init_prot

[bits 32]
init_prot:
    ; Since this moment all data segment registers
    ; point to the same data segment in GDT:0x10
    mov ax, GDT_DS_PROT
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ds, ax
    mov ss, ax

    ; move return address to the real stack
    mov eax, [esp]
    mov [STACK_SEGMENT_REAL_MODE], eax

    ; get protected mode stack
    mov eax, [STACK_SEGMENT_PROT_MODE]
    mov esp, eax
    mov ebp, eax

    ; put the return address to the correct stack
    mov eax, [STACK_SEGMENT_REAL_MODE]
    mov [esp], eax

    xor eax, eax

    ret

I build it using nasm:

$ nasm -f bin head.asm -o boot

and run qemu like:

$ qemu-system-i386 boot

and then I see something weird. Can anybody explain what I'm doing wrong?

0

There are 0 answers