1

I started creating an os entirely in assembly thinking I wanted to do it completely in real-mode, but as soon as I realized the limitations I had, I decided to create the bootloader in real-mode and the kernel in protected-mode I tried to set the gdt in the bootloader but it doesn't seem to work. Can you tell me why?

Here is the code

bootloader.asm:


bits 16

section .boot
global _start

KERNEL_ADDRESS equ 0x7E00

_start:
        cld
        xor  ax, ax
        mov  ds, ax
        mov  es, ax
        mov  ss, ax
        mov  sp, 9000h

        mov  di, 5        ; Allow 5 tries
    BOOT_TryNext:
        mov  dh, 0        ; CHS = (0,0,2), DL is drive number from BIOS
        mov  cx, 0002h
        mov  bx, 7E00h    ; ES:BX is buffer
        mov  ax, 0201h    ; BIOS.ReadDiskSector
        int  13h          ; -> AX CF
        jnc  BOOT_Loaded
        mov  ah, 00h      ; BIOS.ResetDiskSystem
        int  13h          ; -> AH CF
        dec  di
        jnz  BOOT_TryNext
    BOOT_Exit:
        
        call BOOT_notLoaded

        BOOT_Exit_Loop:
            cli
            hlt
            jmp  BOOT_Exit_Loop
    BOOT_Loaded:
        GDT_Start:
            null_descriptor:
                dd 0
                dd 0
            code_descriptor:
            dw 0xffff
            dw 0
            db 0
            db 10011010
            db 11001111
            db 0
            data_descriptor:
                dw 0xffff
                dw 0
                db 0
                db 10010010
                db 11001111
                db 0
        GDT_End:
        
        GDT_Descriptor:
            dw GDT_End - GDT_Start - 1
            dd GDT_Start

        CODE_SEG equ code_descriptor - GDT_Start
        DATA_SEG equ code_descriptor - GDT_Start

        cli
        lgdt [GDT_Descriptor]
        mov eax, cr0
        or eax, 1
        mov cr0, eax

        jmp CODE_SEG:boot_trampoline

        boot_trampoline:
            ; Setup dei segmenti per il kernel
            mov ax, DATA_SEG     ; Carica il segmento dati del kernel
            mov ds, ax
            mov es, ax
            mov fs, ax
            mov gs, ax

            ; Imposta lo stack pointer per il kernel
            mov ax, KERNEL_STACK_SIZE
            mov ss, ax
            mov sp, KERNEL_STACK_START

            ; Salta al kernel
            jmp KERNEL_ADDRESS

; Definisci le costanti per lo stack del kernel
KERNEL_STACK_SIZE equ 0x1000        ; Dimensione dello stack del kernel (4KB)
KERNEL_STACK_START equ 0x8000 - KERNEL_STACK_SIZE
    
    BOOT_notLoaded:
        pusha

        mov ax, 0x0700  ; function 07, AL=0 means scroll whole window
        mov bh, 0x07    ; character attribute = white on black
        mov cx, 0x0000  ; row = 0, col = 0
        mov dx, 0x184f  ; row = 24 (0x18), col = 79 (0x4f)
        int 10h         ; call BIOS video interrupt

        popa

        mov ah, 0x02
        xor bh, bh
        xor dx, dx
        int 10h

        mov si, boot_err
        mov ah, 0x0e
        boot_characterLoop2:
            lodsb
            or al, al
            jz BOOT_Exit_Loop
            int 10h

        jmp boot_characterLoop2

boot_msg: db "Il sistema operativo e' stato caricato correttamente!", 0
boot_err: db "Il sistema operativo non e' stato caricato correttamente!", 0

times 510-($-$$) db 0
dw 0xAA55

kernel.asm:

bits 32

section .kernel
global _start_kernel

_start_kernel:
    mov al, 'A'
    mov ah, 0x0f
    mov [0xb8000], ax

linker.ld:

OUTPUT_FORMAT(binary)
ENTRY(_start)

SECTIONS
{
    . = 0x7c00;

    bootloader :
    {
        bootloader.bin(.boot)
    }
    
    kernel :
    {
        kernel.bin(.kernel)
    }

    /DISCARD/ :
    {
        *(.bss*);
        *(COMMON*);
    }
}

Makefile:

boot = bootloader.asm
kernel = kernel.asm

boot_o = bootloader.bin
kernel_o = kernel.bin

bin = myos.bin

asm_compiler = nasm
qemu = qemu-system-i386

compile:
    $(asm_compiler) -f elf32 -g $(boot) -o $(boot_o)
    $(asm_compiler) -f elf32 -g $(kernel) -o $(kernel_o)
    ld -m elf_i386 -nmagic -T linker.ld -o $(bin) $(boot_o) $(kernel_o)

run:
    $(qemu) -fda $(bin)

clean:
    -rm $(boot_o)
    -rm $(kernel_o)
    -rm $(bin)
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 2
    One problem: You branch the control flow to `BOOT_Loaded` once the second sector has been read to 0:7E00h. But that label is where the data of the GDT lives! The machine will try to run your data by interpreting it as if it was code. This likely crashes the machine. – ecm Aug 01 '23 at 09:33
  • You did put the GDT in the execution flow. Plus you don't have an `ORG` directive so the assembler will start counting from 0 but that will be troublesome when the `lgdt` instruction (which expects a linear address) is used. Furthermore, the kernel stack initialization is clearly wrong (you are trying to set `ss` to 0x1000). Finally, your kernel abruptly ends. Try using bochs and its debugger. P.S. Either use English or Italian comments, don't mix and match. – Margaret Bloom Aug 01 '23 at 09:40
  • 1
    @MargaretBloom i cannot use org because i'm using elf32, the address is in linker file – Federico Occhiochiuso Aug 01 '23 at 10:15
  • 1
    As was mentioned `BOOT_Loaded:` is placed before the GDT.The processor will start executing the GDT as code which will cause undefined behaviour. Move `BOOT_Loaded:` label to just before the `cli`. The GDT is trying to use binary values but they are encoded as decimal so things like `10011010` should be `0b10011010` (`0b` prefix = binary). That occurs in 4 places in the GDT and are cause of the overflow warnings from NASM. Then `boot_trampoline:` is code running in 32-bit mode. So just before that label you need a `bits 32` directive to tell the assembler to generate 32-bit instructions – Michael Petch Aug 01 '23 at 16:29
  • 1
    `DATA_SEG equ code_descriptor - GDT_Start` needs to be `DATA_SEG equ data_descriptor - GDT_Start` (`code` is changed to `data`). You have stack issues. `mov ax, KERNEL_STACK_SIZE` `mov ss, ax` `mov sp, KERNEL_STACK_START`. `SS` should be loaded with `DATA_SEG` like other segment selectors (ie DS, ES etc). I don't know what you are doing with the SIZE. In 32-bit mode the stack pointer should be ESP and not SP. Put the stack somewhere where you won't clobber it. Maybe change `KERNEL_STACK_START` to something that grows down below the bootloader like 0x7c00 or from a memory address like 0x90000. – Michael Petch Aug 01 '23 at 16:36
  • 1
    Further to the last comment I am suggesting to you to remove `mov ax, KERNEL_STACK_SIZE`, then change `mov sp, KERNEL_STACK_START` to `mov esp, KERNEL_STACK_START` and then change `KERNEL_STACK_START` to a value of `0x7c00` (or `0x90000`) – Michael Petch Aug 01 '23 at 16:39
  • 1
    In kernel.asm you have code but you let it continuing executing whatever is in memory after `mov [0xb8000], ax`. After your last instruction you can put a `jmp $` to put the processor in an infinite loop to avoid that. – Michael Petch Aug 01 '23 at 16:41
  • I recommend debugging something like this with BOCHS. You can set a break point at 0x7c00 and step through the instructions from there to see where things are going wrong. Unlike QEMU, BOCHS has a debugger that understands real-mode and segment:offset addressing which makes it a better fit for debugging this kind of code, – Michael Petch Aug 01 '23 at 16:46

0 Answers0