mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Implement rt0 assembly boot code for x86 arch
This commit is contained in:
parent
1a094f511e
commit
5b47048397
31
arch/x86/asm/gdt.inc
Normal file
31
arch/x86/asm/gdt.inc
Normal file
@ -0,0 +1,31 @@
|
||||
; vim: set ft=nasm :
|
||||
|
||||
%define SEG_NOEXEC (0 << 3)
|
||||
%define SEG_EXEC (1 << 3)
|
||||
|
||||
%define SEG_NORW (0 << 1)
|
||||
%define SEG_R (1 << 1)
|
||||
%define SEG_W (1 << 1)
|
||||
|
||||
%define SEG_GRAN_BYTE (0 << 7)
|
||||
%define SEG_GRAN_4K_PAGE (1 << 7)
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; GDT_ENTRY_32 creates a GDT entry for a 32-bit descriptor. It automatically sets
|
||||
; the following bits:
|
||||
; - Privl (ring) bits to 00 (ring 0)
|
||||
; - Pr (present) bit to 1
|
||||
; - Sz (size) bit to 1 (32-bit selector)
|
||||
; - L (long-mode) bit to 0
|
||||
;
|
||||
; Args: base, limit, access, flags
|
||||
;------------------------------------------------------------------------------
|
||||
%macro GDT_ENTRY_32 4
|
||||
dw (%2 & 0xFFFF) ; limit 0:15
|
||||
dw (%1 & 0xFFFF) ; base 0:15
|
||||
db ((%1 >> 16) & 0xFF) ; base 16:23
|
||||
db (0x90 | %3) ; set Pr = 1, bit 5 = 1 (required)
|
||||
; and apply access byte flags
|
||||
db 0x40 | (%4 & 0xC0) | ((%2 >> 16) & 0xF) ; set Sz and flags and limit bits 16:19
|
||||
db ((%1 >> 24) & 0xFF) ; base 24:31
|
||||
%endmacro
|
36
arch/x86/asm/multiboot_header.s
Normal file
36
arch/x86/asm/multiboot_header.s
Normal file
@ -0,0 +1,36 @@
|
||||
; vim: set ft=nasm :
|
||||
|
||||
section .multiboot_header
|
||||
|
||||
MAGIC equ 0xe85250d6
|
||||
ARCH equ 0x0
|
||||
|
||||
; Define the multiboot header (multiboot 1.6)
|
||||
; http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
|
||||
header_start:
|
||||
dd MAGIC ; magic number
|
||||
dd ARCH ; i386 protected mode
|
||||
dd header_end - header_start ; header length
|
||||
|
||||
; The field ‘checksum’ is a 32-bit unsigned value which, when added to the other
|
||||
; magic fields (i.e. ‘magic’, ‘architecture’ and ‘header_length’), must have a
|
||||
; 32-bit unsigned sum of zero.
|
||||
dd (1 << 32) - (MAGIC + ARCH + (header_end - header_start))
|
||||
|
||||
align 8 ; tags should be 64-bit aligned
|
||||
|
||||
; Define graphics mode tag
|
||||
;dw 5 ; type
|
||||
;dw 0 ; flags
|
||||
;dd 20 ; size
|
||||
;dd 80 ; width (pixels or chars)
|
||||
;dd 25 ; height (pixels or chars)
|
||||
;dd 0 ; bpp (0 for text mode
|
||||
|
||||
align 8 ; tags should be 64-bit aligned
|
||||
|
||||
; According to page 6 of the spec, the tag list is terminated by a tag with
|
||||
; type 0 and size 8
|
||||
dd 0 ; type & flag = 0
|
||||
dd 8 ; size
|
||||
header_end:
|
109
arch/x86/asm/rt0.s
Normal file
109
arch/x86/asm/rt0.s
Normal file
@ -0,0 +1,109 @@
|
||||
; vim: set ft=nasm :
|
||||
|
||||
section .bss
|
||||
align 4
|
||||
|
||||
; Reserve 16K for our stack. Stacks should be aligned to 16 byte boundaries.
|
||||
stack_bottom:
|
||||
resb 16384 ; 16 KiB
|
||||
stack_top:
|
||||
|
||||
; Reserve some extra space for our tls_0 block; GO functions expect the
|
||||
; GS segment register to point to the current TLS so we need to initialize this
|
||||
; first before invoking any go functions
|
||||
tls0:
|
||||
g0_ptr: resd 1 ; gs:0x00 is a pointer to the current g struct
|
||||
; in our case it should point to g0
|
||||
g0:
|
||||
g0_stack_lo: resd 1
|
||||
g0_stack_hi: resd 1
|
||||
g0_stackguard0: resd 1 ; sp compared to this value in go stack growth prologue
|
||||
g0_stackguard1: resd 1 ; sp compared to this value in C stack growth prologue
|
||||
|
||||
section .text
|
||||
bits 32
|
||||
align 4
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Kernel arch-specific entry point
|
||||
;
|
||||
; The boot loader will jump to this symbol after setting up the CPU according
|
||||
; to the multiboot standard. At this point:
|
||||
; - The CPU is using 32-bit protected mode
|
||||
; - Interrupts are disabled
|
||||
; - Paging is disabled
|
||||
;------------------------------------------------------------------------------
|
||||
global _rt0_entry
|
||||
_rt0_entry:
|
||||
; Initalize our stack by pointing ESP to the BSS-allocated stack. In x86,
|
||||
; stack grows downwards so we need to point ESP to stack_top
|
||||
mov esp, stack_top
|
||||
|
||||
; Load initial GDT
|
||||
call _rt0_load_gdt
|
||||
|
||||
; init g0 so we can invoke Go functions
|
||||
mov dword [gs:0x00], g0
|
||||
mov dword [g0_stack_hi], stack_top
|
||||
mov dword [g0_stack_lo], stack_bottom
|
||||
mov dword [g0_stackguard0], stack_bottom
|
||||
|
||||
extern main.main
|
||||
call main.main
|
||||
|
||||
; Main should never return; halt the CPU
|
||||
cli
|
||||
hlt
|
||||
.end:
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; Load GDT and flush CPU caches
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
_rt0_load_gdt:
|
||||
; Go code uses the GS register to access the TLS. Set the base address
|
||||
; for the GS descriptor to point to our tls0 table
|
||||
mov eax, tls0
|
||||
mov ebx, gdt0_gs_seg
|
||||
mov [ebx+2], al
|
||||
mov [ebx+3], ah
|
||||
shr eax, 16
|
||||
mov [ebx+4], al
|
||||
|
||||
lgdt [gdt0_desc]
|
||||
|
||||
; GDT has been loaded but the CPU still has the previous GDT data in cache.
|
||||
; We need to manually update the descriptors and use a JMP command to set
|
||||
; the CS segment descriptor
|
||||
jmp CS_SEG:update_descriptors
|
||||
update_descriptors:
|
||||
mov ax, DS_SEG
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov ax, GS_SEG
|
||||
mov gs, ax
|
||||
|
||||
ret
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; GDT definition
|
||||
;------------------------------------------------------------------------------
|
||||
%include "gdt.inc"
|
||||
|
||||
align 2
|
||||
gdt0:
|
||||
|
||||
gdt0_nil_seg: GDT_ENTRY_32 0x00, 0x0, 0x0, 0x0 ; nil descriptor (not used by CPU but required by some emulators)
|
||||
gdt0_cs_seg: GDT_ENTRY_32 0x00, 0xFFFFF, SEG_EXEC | SEG_R, SEG_GRAN_4K_PAGE ; code descriptor
|
||||
gdt0_ds_seg: GDT_ENTRY_32 0x00, 0xFFFFF, SEG_NOEXEC | SEG_W, SEG_GRAN_4K_PAGE ; data descriptor
|
||||
gdt0_gs_seg: GDT_ENTRY_32 0x00, 0x40, SEG_NOEXEC | SEG_W, SEG_GRAN_BYTE ; TLS descriptor (required in order to use go segmented stacks)
|
||||
|
||||
gdt0_desc:
|
||||
dw gdt0_desc - gdt0 - 1 ; gdt size should be 1 byte less than actual length
|
||||
dd gdt0
|
||||
|
||||
NULL_SEG equ gdt0_nil_seg - gdt0
|
||||
CS_SEG equ gdt0_cs_seg - gdt0
|
||||
DS_SEG equ gdt0_ds_seg - gdt0
|
||||
GS_SEG equ gdt0_gs_seg - gdt0
|
Loading…
x
Reference in New Issue
Block a user