summaryrefslogtreecommitdiff
path: root/asm
diff options
context:
space:
mode:
authorAnna (navi) Figueiredo Gomes <navi@vlhl.dev>2023-12-13 13:24:48 +0100
committerAnna (navi) Figueiredo Gomes <navi@vlhl.dev>2023-12-25 18:37:20 +0100
commit30d6e8f850d2fe26fffdeef0c38fc627ef8bab9a (patch)
treecd786b6a79d6a58590b2d35308746761c1e3d977 /asm
initial commit
currently with: booted via multiboot 1 protected mode to long mode boostrap code vga used for outputting gdt and idt set up identity paging for the whole memory reported by multiboot pic and ps/2 set up acpi code exists but is broken
Diffstat (limited to 'asm')
-rw-r--r--asm/boot.asm161
-rw-r--r--asm/long.asm171
2 files changed, 332 insertions, 0 deletions
diff --git a/asm/boot.asm b/asm/boot.asm
new file mode 100644
index 0000000..ae68761
--- /dev/null
+++ b/asm/boot.asm
@@ -0,0 +1,161 @@
+[BITS 32]
+
+; Declare constants for the multiboot header.
+MBALIGN equ 1 << 0 ; align loaded modules on page boundaries
+MEMINFO equ 1 << 1 ; provide memory map
+MBFLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
+MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
+CHECKSUM equ -(MAGIC + MBFLAGS) ; checksum of above, to prove we are multiboot
+
+; Declare a multiboot header that marks the program as a kernel. These are magic
+; values that are documented in the multiboot standard. The bootloader will
+; search for this signature in the first 8 KiB of the kernel file, aligned at a
+; 32-bit boundary. The signature is in its own section so the header can be
+; forced to be within the first 8 KiB of the kernel file.
+section .multiboot
+align 4
+ dd MAGIC
+ dd MBFLAGS
+ dd CHECKSUM
+
+section .bootstrap
+
+present equ 1 << 7
+not_sys equ 1 << 4
+exec equ 1 << 3
+cd equ 1 << 2
+rw equ 1 << 1
+access equ 1 << 0
+
+gran_4k equ 1 << 7
+sz_32 equ 1 << 6
+long_m equ 1 << 5
+
+global gdt.ptr
+gdt:
+.null: equ $ - gdt
+ dq 0
+.code: equ $ - gdt
+ dd 0xffff
+ db 0
+ db present | not_sys | exec | rw
+ db gran_4k | long_m | 0xf
+ db 0
+.data: equ $ - gdt
+ dd 0xffff
+ db 0
+ db present | not_sys | rw
+ db gran_4k | sz_32 | 0xf
+ db 0
+.ptr:
+ dw $ - gdt - 1
+ dq gdt
+
+global bootstrap
+bootstrap:
+
+ mov dword [multiboot_magic], eax
+ mov dword [multiboot_header], ebx
+
+ mov eax, cr0
+ and eax, ~(1 << 31)
+ mov cr0, eax
+
+ mov edi, pml4 ;0x1000
+ mov cr3, edi
+ xor eax, eax
+ mov ecx, 1024
+ rep stosd
+
+ mov edi, 0x2000
+ mov ecx, 4096
+ rep stosd
+
+ mov edi, cr3
+
+ mov dword [edi], 0x2003
+ mov edi, 0x2000
+ mov dword [edi], 0x3003
+ add edi, 0x1000
+ mov dword [edi], 0x4003
+ mov dword [edi + 8], 0x5003
+ add edi, 0x1000
+
+ mov ebx, 0x00000003
+ mov ecx, 512
+
+.set_entry:
+ mov dword [edi], ebx
+ add ebx, 0x1000
+ add edi, 8
+ loop .set_entry
+
+ mov edi, 0x5000
+ mov ecx, 512
+
+.set_entry_2:
+ mov dword [edi], ebx
+ add ebx, 0x1000
+ add edi, 8
+ loop .set_entry_2
+
+ mov eax, cr4
+ or eax, 1 << 5
+ mov cr4, eax
+
+ mov ecx, 0xc0000080
+ rdmsr
+ or eax, 1 << 8 | 1 << 0
+ wrmsr
+
+ mov eax, cr0
+ or eax, 1 << 31
+ mov cr0, eax
+
+ lgdt [gdt.ptr]
+
+ mov ax, gdt.data
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+
+ extern start
+ jmp gdt.code:start
+
+global set_pml4
+set_pml4:
+ mov ecx, 0xC000080
+ rdmsr
+ and eax, ~(1 << 8)
+ wrmsr
+
+ mov eax, cr0
+ and eax, ~(1 << 31)
+ mov cr0, eax
+
+ mov cr3, edi
+
+ mov ecx, 0xC000080
+ rdmsr
+ or eax, 1 << 8
+ wrmsr
+
+ mov eax, cr0
+ or eax, 1 << 31
+ mov cr0, eax
+
+ retf
+
+section .data
+align 16
+global multiboot_header
+multiboot_header: dq 0
+global multiboot_magic
+multiboot_magic: dq 0
+
+section .bss
+global pml4
+align 4096
+pml4: resq 512
diff --git a/asm/long.asm b/asm/long.asm
new file mode 100644
index 0000000..829d582
--- /dev/null
+++ b/asm/long.asm
@@ -0,0 +1,171 @@
+; The multiboot standard does not define the value of the stack pointer register
+; (esp) and it is up to the kernel to provide a stack. This allocates room for a
+; small stack by creating a symbol at the bottom of it, then allocating 16384
+; bytes for it, and finally creating a symbol at the top. The stack grows
+; downwards on x86. The stack is in its own section so it can be marked nobits,
+; which means the kernel file is smaller because it does not contain an
+; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
+; System V ABI standard and de-facto extensions. The compiler will assume the
+; stack is properly aligned and failure to align the stack will result in
+; undefined behavior.
+section .bss
+ align 16
+stack_bottom:
+ resb 16384 ; 16 KiB
+stack_top:
+
+; The linker script specifies _start as the entry point to the kernel and the
+; bootloader will jump to this position once the kernel has been loaded. It
+; doesn't make sense to return from this function as the bootloader is gone.
+; Declare _start as a function symbol with the given symbol size.
+section .text
+
+global idt_load
+extern idt_ptr
+idt_load:
+ lidt [idt_ptr]
+ ret
+
+%macro isr 1-*
+ %rep %0
+ isr_%+%1:
+ cli
+ push qword 0
+ push qword %1
+ jmp isr_common
+ %rotate 1
+ %endrep
+%endmacro
+
+%macro isr_err 1-*
+ %rep %0
+ isr_%+%1:
+ cli
+ push qword %1
+ jmp isr_common
+ %rotate 1
+ %endrep
+%endmacro
+
+isr 0, 1, 2, 3, 4, 5, 6, 7, 9, 15, 16, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 31
+isr_err 8, 10, 11, 12, 13, 14, 17, 21, 29, 30
+isr 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
+
+extern interrupt_handler
+isr_common:
+ push rdi
+ mov rdi, [rsp + 8]
+ push rsi
+ mov rsi, [rsp + 24]
+ push rax
+ push rdx
+ push rcx
+ push r8
+ push r9
+ push r10
+ push r11
+
+ call interrupt_handler
+
+ pop r11
+ pop r10
+ pop r9
+ pop r8
+ pop rcx
+ pop rdx
+ pop rax
+ pop rsi
+ pop rdi
+
+ add rsp, 16
+ iretq
+
+global isr_table
+isr_table:
+ %assign i 0
+ %rep 48
+ dq isr_%+i
+ %assign i i+1
+ %endrep
+
+global toggle_interrupts
+toggle_interrupts:
+ cmp rdi, 0x0
+ je .disable
+ sti
+ ret
+.disable
+ cli
+ ret
+
+global start:function (start.end - start)
+start:
+ ; The bootloader has loaded us into 32-bit protected mode on a x86
+ ; machine. Interrupts are disabled. Paging is disabled. The processor
+ ; state is as defined in the multiboot standard. The kernel has full
+ ; control of the CPU. The kernel can only make use of hardware features
+ ; and any code it provides as part of itself. There's no printf
+ ; function, unless the kernel provides its own <stdio.h> header and a
+ ; printf implementation. There are no security restrictions, no
+ ; safeguards, no debugging mechanisms, only what the kernel provides
+ ; itself. It has absolute and complete power over the
+ ; machine.
+ mov rsp, stack_top
+
+ ; To set up a stack, we set the esp register to point to the top of our
+ ; stack (as it grows downwards on x86 systems). This is necessarily done
+ ; in assembly as languages such as C cannot function without a stack.
+ ;mov rsp, stack_top
+
+ ; This is a good place to initialize crucial processor state before the
+ ; high-level kernel is entered. It's best to minimize the early
+ ; environment where crucial features are offline. Note that the
+ ; processor is not fully initialized yet: Features such as floating
+ ; point instructions and instruction set extensions are not initialized
+ ; yet. The GDT should be loaded here. Paging should be enabled here.
+ ; C++ features such as global constructors and exceptions will require
+ ; runtime support to work as well.
+
+ ; Enter the high-level kernel. The ABI requires the stack is 16-byte
+ ; aligned at the time of the call instruction (which afterwards pushes
+ ; the return pointer of size 4 bytes). The stack was originally 16-byte
+ ; aligned above and we've since pushed a multiple of 16 bytes to the
+ ; stack since (pushed 0 bytes so far) and the alignment is thus
+ ; preserved and the call is well defined.
+ ; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
+
+ mov rax, 0x1
+ cpuid
+ test edx, 1 << 25
+ jz .no_sse
+
+ mov rax, cr0
+ and ax, 0xfffb
+ or ax, 0x2
+ mov cr0, rax
+ mov rax, cr4
+ or ax, 3 << 9
+ mov cr4, rax
+
+.no_sse:
+ extern kernel_main
+ call kernel_main
+
+ ; If the system has nothing more to do, put the computer into an
+ ; infinite loop. To do that:
+ ; 1) Disable interrupts with cli (clear interrupt enable in eflags).
+ ; They are already disabled by the bootloader, so this is not needed.
+ ; Mind that you might later enable interrupts and return from
+ ; kernel_main (which is sort of nonsensical to do).
+ ; 2) Wait for the next interrupt to arrive with hlt (halt instruction).
+ ; Since they are disabled, this will lock up the computer.
+ ; 3) Jump to the hlt instruction if it ever wakes up due to a
+ ; non-maskable interrupt occurring or due to system management mode.
+ int 0
+.hang: hlt
+ jmp .hang
+.end:
+
+section .end
+global kernel_end
+kernel_end: