summaryrefslogtreecommitdiff
path: root/kernel/src/boot
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/src/boot')
-rw-r--r--kernel/src/boot/boot.S181
-rw-r--r--kernel/src/boot/boot64.S19
-rw-r--r--kernel/src/boot/header.S40
-rw-r--r--kernel/src/boot/multiboot2.c41
4 files changed, 281 insertions, 0 deletions
diff --git a/kernel/src/boot/boot.S b/kernel/src/boot/boot.S
new file mode 100644
index 0000000..ee35eeb
--- /dev/null
+++ b/kernel/src/boot/boot.S
@@ -0,0 +1,181 @@
+.global _start
+.extern begin_long_mode
+
+.section .text
+.code32
+
+_start:
+ cli
+ mov $stack_top, %esp
+ pushl $0
+ pushl %eax
+ pushl $0
+ pushl %ebx
+
+ call check_multiboot
+ call check_cpuid
+ call check_long_mode
+
+ call setup_page_tables
+ call enable_paging
+
+ lgdt gdt_pointer
+ ljmp $0x08, $begin_long_mode
+
+
+check_multiboot:
+ cmp $0x36d76289, %eax
+ jne no_multiboot
+
+ ret
+
+check_cpuid:
+ pushfl
+ pop %eax
+ mov %eax, %ecx
+ xor $1<<21, %eax
+ push %eax
+ popfl
+ pushfl
+ pop %eax
+ push %ecx
+ popfl
+ cmp %eax, %ecx
+ je no_cpuid
+
+ ret
+
+check_long_mode:
+ mov $0x80000000, %eax
+ cpuid
+ cmp $0x80000001, %eax
+ jb no_long_mode
+
+ mov $0x80000001, %eax
+ cpuid
+ test $1<<29, %edx
+ jz no_long_mode
+
+ ret
+
+setup_page_tables:
+ mov $page_table_lvl3, %eax
+ or $0x3, %eax
+ mov %eax, page_table_lvl4
+
+ mov $page_table_lvl2, %eax
+ or $0x3, %eax
+ mov %eax, page_table_lvl3
+
+ mov $page_table_lvl2 + 4096, %eax
+ or $0x3, %eax
+ mov %eax, page_table_lvl3+8
+
+ mov $page_table_lvl2 + 8192, %eax
+ or $0x3, %eax
+ mov %eax, page_table_lvl3+16
+
+ mov $page_table_lvl2 + 12288, %eax
+ or $0x3, %eax
+ mov %eax, page_table_lvl3+24
+
+
+ movl $0, %ecx
+1:
+ movl $0x200000, %eax
+ mul %ecx
+ or $0b10000011, %eax
+ movl $page_table_lvl2, %edx
+ leal (%edx, %ecx, 8), %edx
+ movl %eax, (%edx)
+ inc %ecx
+ cmp $512*4, %ecx
+ jne 1b
+
+ ret
+
+enable_paging:
+ // enable PAE
+ mov %cr4, %edx
+ or $1<<5 ,%edx
+ mov %edx, %cr4
+
+ // set LME (long mode enable)
+ mov $0xC0000080, %ecx
+ rdmsr
+ or $1<<8, %eax
+ wrmsr
+
+ // page_table_lvl4
+ mov $page_table_lvl4, %eax
+ mov %eax, %cr3
+
+ // enable paging (+ protected mode if not already enabled)
+ mov %cr0, %eax
+ or $1<<31 + 1<<0, %eax
+ mov %eax, %cr0
+
+ ret
+
+no_multiboot:
+ hlt
+
+no_cpuid:
+ hlt
+
+no_long_mode:
+ hlt
+
+
+.section .bss
+.align 4096
+
+// stack
+stack_bottom:
+ .skip 4096*4
+stack_top:
+
+// page tables
+page_table_lvl4:
+ .skip 4096
+page_table_lvl3:
+ .skip 4096
+// map first 4GB
+page_table_lvl2:
+ .skip 4096*4
+
+// Access bits
+.section .rodata
+.set PRESENT, 1 << 7
+.set NOT_SYS, 1 << 4
+.set EXEC, 1 << 3
+.set DC, 1 << 2
+.set RW, 1 << 1
+.set ACCESSED, 1 << 0
+
+// Flags bits
+.set GRAN_4K, 1 << 7
+.set SZ_32, 1 << 6
+.set LONG_MODE, 1 << 5
+
+gdt:
+gdt_null = . - gdt
+ .quad 0
+gdt_code = . - gdt
+ .long 0xFFFF // Limit & Base (low, bits 0-15)
+ .byte 0 // Base (mid, bits 16-23)
+ .byte PRESENT | NOT_SYS | EXEC | RW // Access
+ .byte GRAN_4K | LONG_MODE | 0xF // Flags & Limit (high, bits 16-19)
+ .byte 0 // Base (high, bits 24-31)
+gdt_data = . - gdt
+ .long 0xFFFF
+ .byte 0
+ .byte PRESENT | NOT_SYS | RW
+ .byte GRAN_4K | SZ_32 | 0xF
+ .byte 0
+gdt_tss = . - gdt
+ .long 0x00000068
+ .long 0x00CF8900
+gdt_pointer:
+ .word . - gdt - 1
+ .quad gdt
diff --git a/kernel/src/boot/boot64.S b/kernel/src/boot/boot64.S
new file mode 100644
index 0000000..be87412
--- /dev/null
+++ b/kernel/src/boot/boot64.S
@@ -0,0 +1,19 @@
+.global begin_long_mode
+
+.section .text
+.code64
+
+begin_long_mode:
+ // reload segment registers
+ mov $0x10, %eax
+ mov %eax, %ss
+ mov %eax, %ds
+ mov %eax, %es
+ mov %eax, %fs
+ mov %eax, %gs
+
+ popq %rdi
+ popq %rsi
+
+ call kernel_main
+ hlt
diff --git a/kernel/src/boot/header.S b/kernel/src/boot/header.S
new file mode 100644
index 0000000..bbcb9be
--- /dev/null
+++ b/kernel/src/boot/header.S
@@ -0,0 +1,40 @@
+// multiboot tags
+.set TAG_END, 0
+.set TAG_FRAMEBUFFER, 5
+
+// multiboot flags
+.set TAG_REQUIRED, 0
+.set TAG_OPTIONAL, 1
+
+# multiboot2 header constants
+.set MAGIC, 0xe85250d6
+.set ARCH, 0
+.set HEADER_LENGTH, (header_end - header_start)
+.set CHECKSUM, 0x100000000 - (MAGIC + ARCH + HEADER_LENGTH)
+
+.section .multiboot_header, "a"
+.align 4
+header_start:
+ # magic
+ .align 8
+ .long MAGIC
+ .long ARCH
+ .long HEADER_LENGTH
+ .long CHECKSUM
+
+ # framebuffer
+ .align 8
+ .word TAG_FRAMEBUFFER
+ .word TAG_REQUIRED
+ .long 20
+ .long 1024
+ .long 768
+ .long 32
+
+ # end tag
+ .align 8
+ .word TAG_END
+ .word TAG_REQUIRED
+ .long 8
+
+header_end:
diff --git a/kernel/src/boot/multiboot2.c b/kernel/src/boot/multiboot2.c
new file mode 100644
index 0000000..e228ce1
--- /dev/null
+++ b/kernel/src/boot/multiboot2.c
@@ -0,0 +1,41 @@
+#include <stdint.h>
+#include <multiboot2.h>
+#include <graphics.h>
+
+fb_t fb;
+
+/* https://www.gnu.org/software/grub/manual/multiboot2/html_node/Boot-information-format.html */
+
+void init_fb(mb2_tag_header* multiboot_bootinfo, uint32_t multiboot_magic)
+{
+ if (multiboot_magic != MB2_MAGIC) {
+ // not loaded by multiboot2 bootloader
+ __asm__ volatile ("hlt;");
+ }
+
+ // we will store framebuffer information here
+ static mb2_tag_fb* tag_fb;
+
+ // skip first 8 bytes (total_size + reserved)
+ mb2_tag_header* tag_header = multiboot_bootinfo + 1;
+
+ while (tag_header->type != MB2_TAG_END) {
+ // process tag_type
+ switch(tag_header->type) {
+ case MB2_TAG_FB:
+ tag_fb = (mb2_tag_fb*)tag_header;
+ break;
+ default:
+ break;
+ }
+
+ // next mb2_tag
+ tag_header += tag_header->size / 8 + ((tag_header->size % 8) > 0);
+ }
+
+ fb.addr = tag_fb->framebuffer_addr;
+ fb.width = tag_fb->framebuffer_width;
+ fb.height = tag_fb->framebuffer_height;
+ fb.pitch = tag_fb->framebuffer_pitch;
+ fb.bpp = tag_fb->framebuffer_bpp;
+}