Ontwerp en bouw een besturingssysteem/Hallo wereld/Assembler opstartcode

De eerste echte code die we gaan schrijven is bedoeld zodat de bootloader weet waar onze code moet beginnen met de uitvoering. We moeten daarna zelf zorgen dat we de beschikking krijgen over een stack.

Multiboot header

bewerken

Omdat de bootloader ervoor zorgt dat we in 32-bit protected mode zitten zodra onze kernel start, beginnen we de assemblercode met het [BITS 32] directief. Daarna volgt bij ons meteen de multiboot header, want die moet volledig in de eerste 8192 bytes van het kernelbestand voorkomen. De header moet beginnen op een veelvoud van 4 bytes (4-byte aligned) zijn, dus we beginnen met align 4. Daarna volgt nog een label met de naam multiboot.

Volgens de multiboot specificatie kunnen we door middel van flags aangeven wat we van de bootloader verwachten en wat de bootloader van ons verwacht. We verwachten dat de bootloader ons informatie over het geheugen geeft (MULTIBOOT_MEMORYINFO) en dat de bootloader de onderdelen van onze kernel op pagina-aligned adressen laadt (MULTIBOOT_PAGEALIGN). Ten slotte moeten we de bootloader vertellen waar onze code, data en BSS secties zijn, en waar de uitvoerbare code begint (MULTIBOOT_AOUT).

start.asm (deel 1)

[BITS 32]
section .text
; De multibootheader moet zo vroeg mogelijk in het bestand komen,
; en 4-byte aligned zijn.
align 4
multiboot:
    ; We gebruiken de volgende flags:
    MULTIBOOT_PAGEALIGN     equ 1<<0    ; Onderdelen pagina-aligned laden.
    MULTIBOOT_MEMORYINFO    equ 1<<1    ; Bootloader geeft geheugeninformatie.
    MULTIBOOT_AOUT          equ 1<<16   ; Wij zeggen waar alles zit en begint.
 
    ; De eerste drie velden van de multiboot header:
    MULTIBOOT_MAGIC         equ 0x1BADB002
    MULTIBOOT_FLAGS         equ MULTIBOOT_PAGEALIGN | MULTIBOOT_MEMORYINFO | MULTIBOOT_AOUT
    MULTIBOOT_CHECKSUM      equ -(MULTIBOOT_MAGIC + MULTIBOOT_FLAGS)
    dd MULTIBOOT_MAGIC
    dd MULTIBOOT_FLAGS
    dd MULTIBOOT_CHECKSUM
 
    extern code, bss, end
    ; Omdat we de MULTIBOOT_AOUT flag hebben opgegeven, moeten we hier
    ; de fysieke adressen van de verschillende onderdelen van onze
    ; hoofdexecutable invullen. Tenminste, ons link-script doet dat voor ons.
    dd multiboot
    dd code
    dd bss
    dd end
    dd start
    ...

Het equ commando maakt een macro met de opgegeven naam en waarde. Met het dd command wordt vervolgens die waarde als 32-bit waarde direct op dit plek in het uiteindelijke bestand geschreven. Ten slotte het extern commando, waarmee wordt aangegeven dat die symbolen (code, bss en end) ergens anders gedefinieerd zijn (in ons geval in het linkbestand uit het volgende hoofdstuk).

Uitvoerbare code

bewerken

Het volgende deel van de code is waar de kernel écht start. We zetten de stack (van maximaal 8 KiB) aan het einde van de kernel in de BSS sectie. Om de stack dan ook werkelijk te gebruiken moeten we een pointer ernaar toe in het ESP register laden. Daarna doen we voorlopig nog niks, dus zetten we daar een oneindige loop.

start.asm (deel 2)

    ...
; Onze startcode. Hier zetten we onze stack op
; en gaan we (met 'jmp $') een eindeloze loop in.
global start
start:
    mov esp, _kernel_stack
    jmp $
 
; In de BBS sectie komt de data die bij de uitvoering op 0 moet worden
; ingesteld. Dit is dan ook een goede plek voor onze stack. De stack groeit
; van het '_kernel_stack' label naar beneden (richting geheugenadres 0),
; en mag nu maximaal 8 KiB groot worden.
section .bss
    resb 0x2000
_kernel_stack:

Het global commando maakt het symbool start ook beschikbaar in de andere codebestanden die we nog gaan maken. Met het mov commando wordt het adres van het _stack symbool in het register ESP gezet. Dit register is speciaal voor dit doel bestemd, zie het voorgaande hoofdstuk Registers. Met jmp springt de uitvoering naar een andere plek. In dit geval springt de uitvoering naar $, wat de instructie zelf aangeeft. Dit geeft dus een oneindige loop.

De BSS sectie start bij section .bss, en met resb 4096 wordt er hier 4096 bytes aan ruimte gereserveerd. Dit gebruiken we voor de stack.

Voor de duidelijkheid volgt hier nog een keer het complete bestand.

start.asm (hele document)

[BITS 32]
section .text
; De multibootheader moet zo vroeg mogelijk in het bestand komen,
; en 4-byte aligned zijn.
align 4
multiboot:
    ; We gebruiken de volgende flags:
    MULTIBOOT_PAGEALIGN    equ 1<<0    ; Onderdelen pagina-aligned laden.
    MULTIBOOT_MEMORYINFO   equ 1<<1    ; Bootloader geeft geheugeninformatie.
    MULTIBOOT_AOUT         equ 1<<16   ; Wij zeggen waar alles zit en begint.
 
    ; De eerste drie velden van de multiboot header:
    MULTIBOOT_MAGIC        equ 0x1BADB002
    MULTIBOOT_FLAGS        equ MULTIBOOT_PAGEALIGN | MULTIBOOT_MEMORYINFO | MULTIBOOT_AOUT
    MULTIBOOT_CHECKSUM     equ -(MULTIBOOT_MAGIC + MULTIBOOT_FLAGS)
    dd MULTIBOOT_MAGIC
    dd MULTIBOOT_FLAGS
    dd MULTIBOOT_CHECKSUM
 
    extern code, bss, end
    ; Omdat we de MULTIBOOT_AOUT flag hebben opgegeven, moeten we hier
    ; de fysieke adressen van de verschillende onderdelen van onze
    ; hoofdexecutable invullen. Tenminste, ons link-script doet dat voor ons.
    dd multiboot
    dd code
    dd bss
    dd end
    dd start
; Onze startcode. Hier zetten we onze stack op
; en gaan we (met 'jmp $') een eindeloze loop in.
global start
start:
    mov esp, _kernel_stack
    jmp $
 
; In de BBS sectie komt de data die bij de uitvoering op 0 moet worden
; ingesteld. Dit is dan ook een goede plek voor onze stack. De stack groeit
; van het '_kernel_stack' label naar beneden (richting geheugenadres 0),
; en mag nu maximaal 8 KiB groot worden.
section .bss
    resb 0x2000
_kernel_stack:
Informatie afkomstig van https://nl.wikibooks.org Wikibooks NL.
Wikibooks NL is onderdeel van de wikimediafoundation.