cassemblymakefileoperating-systemqemu

How can I print a string on the Qemu screen when writing a simple OS?


I'm writing a simple OS. I'm having trouble with the output string in qemu screen, the output of the kernel.c file doesn't show on the qemu screen):

the screen of qemu

I have the following code:

bootloader.asm

BITS 16
org 0x7C00

start:
    ; Load the kernel from disk into memory
    mov ax, 0x1000
    mov es, ax
    mov bx, 0x0000
    mov ah, 0x02
    mov al, 3
    mov ch, 0
    mov cl, 2
    mov dh, 0
    int 0x13

    ; Set up protected mode
    cli
    lgdt [gdt_descriptor]
    mov eax, cr0
    or eax, 1
    mov cr0, eax
    jmp CODE_SEG:init_pm

[BITS 32]
init_pm:
    ; Setup segment registers
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    ; Jump to the kernel entry point
    call kernel_main

hang:
    hlt
    jmp hang

gdt:
    dw 0, 0, 0, 0    ; null descriptor
    dw 0xFFFF, 0, 0x9A00, 0x00CF    ; code segment
    dw 0xFFFF, 0, 0x9200, 0x00CF    ; data segment

gdt_descriptor:
    dw gdt - gdt_descriptor - 1
    dd gdt

CODE_SEG equ gdt - gdt_descriptor + 8
DATA_SEG equ gdt - gdt_descriptor + 16

kernel_main equ 0x00100000

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

kernel.c

void kernel_main() {
    char* video_memory = (char*)0xB8000;
    *video_memory = 'A';
}

linker.ld

ENTRY(kernel_main)
SECTIONS
{
    . = 0x00100000;

    .text :
    {
        *(.text)
    }

    .rodata :
    {
        *(.rodata)
    }

    .data :
    {
        *(.data)
    }

    .bss :
    {
        *(.bss)
    }
}

Makefile

# Makefile
CC = gcc
LD = ld
NASM = nasm
CFLAGS = -m32 -ffreestanding -fno-pie -fno-stack-protector -Wall -Wextra
LDFLAGS = -m elf_i386

all: os-image.bin

os-image.bin: bootloader.bin kernel.bin
    cat $^ > $@

bootloader.bin: bootloader.asm
    $(NASM) -f bin -o $@ $<

kernel.bin: kernel.o
    $(LD) $(LDFLAGS) -T linker.ld -o $@ $<

kernel.o: kernel.c
    $(CC) $(CFLAGS) -c -o $@ $<

clean:
    rm -f *.bin *.o os-image.bin

The output seems that the kernel.c file is not running, so how to solve this problem?


Solution

  • In the kernel.c file a kernel_entry.asm can be added, which will specify the kernel entry function and call the entry function. Then link the kernel_entry.asm file and kernel.c file. You may not need it in following codes, but you should keep the entry function at the top in the kernel.c file.

    By the following codes, it works:

    boot.asm

    BITS 16
    org 0x7C00
    
    start:
        mov bp, 0x9000
        mov sp, bp
        ; Load the kernel from disk into memory
        mov bx, 0x1000
    
        mov ah, 0x02
        mov al, 3
        mov ch, 0
        mov cl, 2
        mov dh, 0
        int 0x13
    
        ; Set up protected mode
        cli
        lgdt [gdt_descriptor]
        mov eax, cr0
        or eax, 0x1
        mov cr0, eax
        jmp 0x08:init_pm
    
    [BITS 32]
    init_pm:
        ; Setup segment registers
        mov ax, 0x10
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        mov ss, ax
    
        ; Jump to the kernel entry point
        call kernel_main
    
    hang:
        hlt
        jmp hang
    
    gdt:
        dw 0, 0, 0, 0                   ; null descriptor
        dw 0xFFFF, 0, 0x9A00, 0x00CF    ; code segment
        dw 0xFFFF, 0, 0x9200, 0x00CF    ; data segment
    
    gdt_descriptor:
        dw gdt - gdt_descriptor - 1
        dd gdt
    
    CODE_SEG equ gdt - gdt_descriptor + 8
    DATA_SEG equ gdt - gdt_descriptor + 16
    
    kernel_main equ 0x001000
    
    times 510-($-$$) db 0
    dw 0xAA55
    

    linker.ld

    ENTRY(kernel_main)
    SECTIONS
    {
      . = 0x1000;
      .text : { *(.text) }
      .data : { *(.data) }
      .bss : { *(.bss) }
    }
    

    kernel.c

    void kernel_main() {
        char* video_memory = (char*)0xB8000;
        char *str = "Hello OS!";
    
        for (int i = 0; *str != '\0'; ++ i) {
            video_memory[i] = *str++;
            video_memory[++i] = 0x0E;
        }
    }