summaryrefslogtreecommitdiff
path: root/src/asm/long.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/asm/long.asm')
-rw-r--r--src/asm/long.asm171
1 files changed, 171 insertions, 0 deletions
diff --git a/src/asm/long.asm b/src/asm/long.asm
new file mode 100644
index 0000000..829d582
--- /dev/null
+++ b/src/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: