diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py
deleted file mode 100644
index eb964ec..0000000
--- a/.ycm_extra_conf.py
+++ /dev/null
@@ -1,147 +0,0 @@
-# Generated by YCM Generator at 2019-09-16 12:39:32.163870
-
-# This file is NOT licensed under the GPLv3, which is the license for the rest
-# of YouCompleteMe.
-#
-# Here's the license text for this file:
-#
-# This is free and unencumbered software released into the public domain.
-#
-# Anyone is free to copy, modify, publish, use, compile, sell, or
-# distribute this software, either in source code form or as a compiled
-# binary, for any purpose, commercial or non-commercial, and by any
-# means.
-#
-# In jurisdictions that recognize copyright laws, the author or authors
-# of this software dedicate any and all copyright interest in the
-# software to the public domain. We make this dedication for the benefit
-# of the public at large and to the detriment of our heirs and
-# successors. We intend this dedication to be an overt act of
-# relinquishment in perpetuity of all present and future rights to this
-# software under copyright law.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-# OTHER DEALINGS IN THE SOFTWARE.
-#
-# For more information, please refer to
-
-import os
-import ycm_core
-
-flags = [
- '-x',
- 'c',
- '-DARCH_AMD64',
- '-I.',
- '-Iinclude',
- '-Wall',
- '-Werror',
- '-Wextra',
- '-Wno-unused-parameter',
- '-Wpedantic',
- '-m64',
- '-nostdlib',
-]
-
-
-# Set this to the absolute path to the folder (NOT the file!) containing the
-# compile_commands.json file to use that instead of 'flags'. See here for
-# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
-#
-# You can get CMake to generate this file for you by adding:
-# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
-# to your CMakeLists.txt file.
-#
-# Most projects will NOT need to set this to anything; you can just change the
-# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
-compilation_database_folder = ''
-
-if os.path.exists( compilation_database_folder ):
- database = ycm_core.CompilationDatabase( compilation_database_folder )
-else:
- database = None
-
-SOURCE_EXTENSIONS = [ '.C', '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
-
-def DirectoryOfThisScript():
- return os.path.dirname( os.path.abspath( __file__ ) )
-
-
-def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
- if not working_directory:
- return list( flags )
- new_flags = []
- make_next_absolute = False
- path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
- for flag in flags:
- new_flag = flag
-
- if make_next_absolute:
- make_next_absolute = False
- if not flag.startswith( '/' ):
- new_flag = os.path.join( working_directory, flag )
-
- for path_flag in path_flags:
- if flag == path_flag:
- make_next_absolute = True
- break
-
- if flag.startswith( path_flag ):
- path = flag[ len( path_flag ): ]
- new_flag = path_flag + os.path.join( working_directory, path )
- break
-
- if new_flag:
- new_flags.append( new_flag )
- return new_flags
-
-
-def IsHeaderFile( filename ):
- extension = os.path.splitext( filename )[ 1 ]
- return extension in [ '.H', '.h', '.hxx', '.hpp', '.hh' ]
-
-
-def GetCompilationInfoForFile( filename ):
- # The compilation_commands.json file generated by CMake does not have entries
- # for header files. So we do our best by asking the db for flags for a
- # corresponding source file, if any. If one exists, the flags for that file
- # should be good enough.
- if IsHeaderFile( filename ):
- basename = os.path.splitext( filename )[ 0 ]
- for extension in SOURCE_EXTENSIONS:
- replacement_file = basename + extension
- if os.path.exists( replacement_file ):
- compilation_info = database.GetCompilationInfoForFile(
- replacement_file )
- if compilation_info.compiler_flags_:
- return compilation_info
- return None
- return database.GetCompilationInfoForFile( filename )
-
-
-def FlagsForFile( filename, **kwargs ):
- if database:
- # Bear in mind that compilation_info.compiler_flags_ does NOT return a
- # python list, but a "list-like" StringVec object
- compilation_info = GetCompilationInfoForFile( filename )
- if not compilation_info:
- return None
-
- final_flags = MakeRelativePathsInFlagsAbsolute(
- compilation_info.compiler_flags_,
- compilation_info.compiler_working_dir_ )
-
- else:
- relative_to = DirectoryOfThisScript()
- final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
-
- return {
- 'flags': final_flags,
- 'do_cache': True
- }
-
diff --git a/doc/x86/lapic.md b/doc/x86/lapic.md
new file mode 100644
index 0000000..1fc0bc6
--- /dev/null
+++ b/doc/x86/lapic.md
@@ -0,0 +1,31 @@
+Local APIC
+==========
+
+LAPIC Base MSR
+--------------
+
+`IA32_LAPIC_BASE = 0x1B` model-specific register stores information about Local APIC
+status and its base address.
+
+Notable LAPIC registers
+-----------------------
+
+| Base offset | Name | Description |
+|------------:|---------------------|-------------------------------------------|
+| 0x20 | IA32_LAPIC_REG_ID | Local APIC ID register |
+| 0x80 | IA32_LAPIC_REG_TPR | Task Priority Register |
+| 0xB0 | IA32_LAPIC_REG_EOI | End of interrupt register |
+| 0xF0 | IA32_LAPIC_REG_SVR | Spurious Interrupt Vector Register |
+| 0x300 | IA32_LAPIC_REG_ICR0 | Interrupt Control Register, 31 .. 0 |
+| 0x310 | IA32_LAPIC_REG_ICR1 | Interrupt Control Register, 63 .. 32 |
+|-------------|---------------------|-------------------------------------------|
+
+Local APIC configuration sequence
+---------------------------------
+
+1. Mask all the interrupts in i8259
+2. Remap all of them properly so spurious interrupts don't generate exceptions
+3. Setup `LINTx` registers for LAPIC-originated (internal) interrupts
+4. Set the IRQ number spurious interrupts should be mapped to and soft-enable the
+ LAPIC by writing `IA32_LAPIC_REG_SVR`
+5. Set the `IA32_LAPIC_REG_TPR` so that lower-priority interrupts don't get postponed
diff --git a/include/sys/amd64/hw/acpi.h b/include/sys/amd64/hw/acpi.h
new file mode 100644
index 0000000..c560669
--- /dev/null
+++ b/include/sys/amd64/hw/acpi.h
@@ -0,0 +1,47 @@
+#pragma once
+#include "sys/types.h"
+
+struct acpi_rsdp {
+ char signature[8];
+ uint8_t checksum;
+ char oemid[6];
+ uint8_t rev;
+ uint32_t rsdt_address;
+} __attribute__((packed));
+
+struct acpi_rsdp_ext {
+ struct acpi_rsdp rsdp;
+
+ uint32_t length;
+ uint64_t xsdt_address;
+ uint8_t ext_checksum;
+ uint8_t reserved[3];
+} __attribute__((packed));
+
+struct acpi_header {
+ char signature[4];
+ uint32_t length;
+ uint8_t rev;
+ uint8_t checksum;
+ char oemid[6];
+ char oem_table_id[8];
+ uint32_t oem_rev;
+ char asl_id[4];
+ uint32_t asl_rev;
+} __attribute__((packed));
+
+struct acpi_rsdt {
+ struct acpi_header hdr;
+
+ uint32_t entry[];
+} __attribute__((packed));
+
+struct acpi_madt {
+ struct acpi_header hdr;
+
+ uint32_t local_apic_addr;
+ uint32_t flags;
+ char entry[];
+} __attribute__((packed));
+
+void amd64_acpi_init(void);
diff --git a/include/sys/amd64/hw/apic.h b/include/sys/amd64/hw/apic.h
new file mode 100644
index 0000000..b861b0c
--- /dev/null
+++ b/include/sys/amd64/hw/apic.h
@@ -0,0 +1,4 @@
+#pragma once
+#include "sys/amd64/hw/acpi.h"
+
+void amd64_apic_init(struct acpi_madt *madt);
diff --git a/include/sys/amd64/hw/gdt.h b/include/sys/amd64/hw/gdt.h
new file mode 100644
index 0000000..5825881
--- /dev/null
+++ b/include/sys/amd64/hw/gdt.h
@@ -0,0 +1,37 @@
+#pragma once
+#include
+#include
+
+typedef struct {
+ uint16_t limit_lo;
+ uint16_t base_lo;
+ uint8_t base_mi;
+ uint8_t access;
+ uint8_t flags;
+ uint8_t base_hi;
+} amd64_gdt_entry_t;
+
+typedef struct {
+ uint32_t __res0;
+ uint64_t rsp0;
+ uint64_t rsp1;
+ uint64_t rsp2;
+ uint64_t __res1;
+ uint64_t ist1;
+ uint64_t ist2;
+ uint64_t ist3;
+ uint64_t ist4;
+ uint64_t ist5;
+ uint64_t ist6;
+ uint64_t ist7;
+ uint64_t __res2;
+ uint16_t __res3;
+ uint16_t iopb_base;
+}__attribute__((packed)) amd64_tss_t;
+
+typedef struct {
+ uint16_t size;
+ uintptr_t offset;
+} __attribute__((packed)) amd64_gdt_ptr_t;
+
+void amd64_gdt_init(void);
diff --git a/include/sys/amd64/hw/idt.h b/include/sys/amd64/hw/idt.h
new file mode 100644
index 0000000..b580ca3
--- /dev/null
+++ b/include/sys/amd64/hw/idt.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void amd64_idt_init(void);
diff --git a/include/sys/amd64/mm/mm.h b/include/sys/amd64/mm/mm.h
new file mode 100644
index 0000000..9961bd0
--- /dev/null
+++ b/include/sys/amd64/mm/mm.h
@@ -0,0 +1,34 @@
+/* vim: set ft=cpp.doxygen : */
+#pragma once
+#include
+#include "sys/amd64/loader/data.h"
+
+/// The place where the kernel pages are virtually mapped to
+#define KERNEL_VIRT_BASE 0xFFFFFF0000000000
+
+/// amd64 standard states that addresses' upper bits are copies of the 47th bit, so these need to be
+/// stripped down to only 48 bits
+#define AMD64_MM_STRIPSX(a) ((uintptr_t) (a) & 0xFFFFFFFFFFFF)
+#define AMD64_MM_ADDRSX(a) (((uintptr_t) (a) & (1ULL << 47)) ? \
+ (0xFFFFFF0000000000 | ((uintptr_t) (a))) : \
+ ((uintptr_t) (a)))
+
+#define MM_VIRTUALIZE(a) ((uintptr_t) (a) + 0xFFFFFF0000000000)
+#define MM_PHYS(a) ((uintptr_t) (a) - 0xFFFFFF0000000000)
+
+/// Page map level 4
+typedef uint64_t *mm_pml4_t;
+/// Page directory pointer table
+typedef uint64_t *mm_pdpt_t;
+/// Page directory
+typedef uint64_t *mm_pagedir_t;
+/// Page table
+typedef uint64_t *mm_pagetab_t;
+
+/// Virtual memory space
+typedef mm_pml4_t mm_space_t;
+
+/// Kernel memory space
+extern mm_space_t mm_kernel;
+
+void amd64_mm_init(struct amd64_loader_data *data);
diff --git a/include/sys/string.h b/include/sys/string.h
index 1ee306a..eaa08f5 100644
--- a/include/sys/string.h
+++ b/include/sys/string.h
@@ -7,6 +7,7 @@
void *memcpy(void *restrict dst, const void *restrict src, size_t cnt);
void *memmove(void *dst, const void *src, size_t cnt);
+void *memset(void *dst, int v, size_t count);
size_t strlen(const char *s);
int strncmp(const char *a, const char *b, size_t lim);
diff --git a/sys/amd64/conf.mk b/sys/amd64/conf.mk
index cb083f1..928ffcd 100644
--- a/sys/amd64/conf.mk
+++ b/sys/amd64/conf.mk
@@ -4,7 +4,15 @@ all:
### Kernel build
DEFINES+=-DARCH_AMD64
OBJS+=$(O)/sys/amd64/hw/rs232.o \
- $(O)/sys/amd64/string.o
+ $(O)/sys/amd64/kernel.o \
+ $(O)/sys/amd64/hw/gdt.o \
+ $(O)/sys/amd64/hw/gdt_s.o \
+ $(O)/sys/amd64/hw/acpi.o \
+ $(O)/sys/amd64/mm/mm.o \
+ $(O)/sys/amd64/hw/apic.o \
+ $(O)/sys/amd64/hw/idt.o \
+ $(O)/sys/amd64/hw/exc_s.o \
+ $(O)/sys/amd64/hw/irq0.o
kernel_OBJS=$(O)/sys/amd64/entry.o \
$(OBJS)
kernel_LINKER=sys/amd64/link.ld
@@ -33,7 +41,8 @@ kernel_CFLAGS=-ffreestanding \
-mno-sse2 \
-z max-page-size=0x1000
DIRS+=$(O)/sys/amd64/image/boot/grub \
- $(O)/sys/amd64/hw
+ $(O)/sys/amd64/hw \
+ $(O)/sys/amd64/mm
# add .inc includes for asm
HEADERS+=$(shell find include -name "*.inc")
@@ -94,7 +103,10 @@ $(O)/sys/amd64/initrd.img: amd64_mkstage
### Debugging and emulation
QEMU_BIN?=qemu-system-x86_64
QEMU_OPTS?=-serial mon:stdio \
- -m 512
+ -m 512 \
+ --accel tcg,thread=multi \
+ -cpu core2duo \
+ -smp 2
$(O)/sys/amd64/image.iso: $(O)/sys/amd64/kernel.elf \
$(O)/sys/amd64/loader.elf \
diff --git a/sys/amd64/entry.S b/sys/amd64/entry.S
index 28d4365..c7d9be2 100644
--- a/sys/amd64/entry.S
+++ b/sys/amd64/entry.S
@@ -4,21 +4,15 @@
.global _start
_start:
cli
- movabsq $kernel_stack_top, %rsp
+ leaq kernel_stack_top(%rip), %rsp
- leaq _msg_entry(%rip), %rsi
- xorq %rdi, %rdi
- call debugf
+ call kernel_main
1:
cli
hlt
jmp 1b
-.section .rodata
-_msg_entry:
- .string "Entered kernel\n"
-
.section .bss
kernel_stack_bottom:
.skip 65536
diff --git a/sys/amd64/hw/acpi.c b/sys/amd64/hw/acpi.c
new file mode 100644
index 0000000..5765c49
--- /dev/null
+++ b/sys/amd64/hw/acpi.c
@@ -0,0 +1,68 @@
+#include "sys/amd64/hw/acpi.h"
+#include "sys/amd64/hw/apic.h"
+#include "sys/string.h"
+#include "sys/debug.h"
+
+static uint8_t checksum(const void *ptr, size_t size) {
+ uint8_t v = 0;
+ for (size_t i = 0; i < size; ++i) {
+ v += ((const uint8_t *) ptr)[i];
+ }
+ return v;
+}
+
+void amd64_acpi_init(void) {
+ struct acpi_rsdp_ext *rsdp = NULL;
+ struct acpi_madt *madt = NULL;
+ struct acpi_rsdt *rsdt;
+
+ for (size_t i = 0xFFFFFF0000000000 + 0x000E0000; i < 0xFFFFFF0000000000 + 0x000FFFFF - 8; ++i) {
+ if (!strncmp((const char *) i, "RSD PTR ", 8)) {
+ rsdp = (struct acpi_rsdp_ext *) i;
+ break;
+ }
+ }
+
+ if (!rsdp) {
+ kdebug("Failed to find rsdp\n");
+ while (1);
+ }
+
+ kdebug("Found RSDP: %p\n", rsdp);
+
+ if (checksum(rsdp, sizeof(struct acpi_rsdp)) != 0) {
+ kdebug("RSDP is invalid\n");
+ while (1);
+ }
+
+ kdebug("RSDP revision %d\n", rsdp->rsdp.rev);
+ rsdt = (struct acpi_rsdt *) (0xFFFFFF0000000000 + rsdp->rsdp.rsdt_address);
+ kdebug("RSDT: %p\n", rsdt);
+
+ if (checksum(rsdt, rsdt->hdr.length) != 0) {
+ kdebug("RSDT is invalid\n");
+ while (1);
+ }
+
+ for (size_t i = 0; i < (rsdt->hdr.length - sizeof(struct acpi_header)) / sizeof(uint32_t); ++i) {
+ uintptr_t entry_addr = 0xFFFFFF0000000000 + rsdt->entry[i];
+ struct acpi_header *hdr = (struct acpi_header *) entry_addr;
+
+ if (!strncmp((const char *) hdr, "APIC", 4)) {
+ kdebug("Found MADT = %p\n", entry_addr);
+ if (checksum(hdr, hdr->length) != 0) {
+ kdebug("Entry is invalid\n");
+ while (1);
+ }
+
+ madt = (struct acpi_madt *) hdr;
+ }
+ }
+
+ if (!madt) {
+ kdebug("No MADT\n");
+ while (1);
+ }
+
+ amd64_apic_init(madt);
+}
diff --git a/sys/amd64/hw/apic.c b/sys/amd64/hw/apic.c
new file mode 100644
index 0000000..4ad4363
--- /dev/null
+++ b/sys/amd64/hw/apic.c
@@ -0,0 +1,110 @@
+#include "sys/amd64/hw/apic.h"
+#include "sys/debug.h"
+
+#define IA32_APIC_BASE_MSR 0x1B
+#define IA32_APIC_BASE_MSR_BSP 0x100
+#define IA32_APIC_BASE_MSR_ENABLE 0x800
+
+#define IA32_LAPIC_REG_ID 0x20
+#define IA32_LAPIC_REG_TPR 0x80
+#define IA32_LAPIC_REG_EOI 0xB0
+#define IA32_LAPIC_REG_SVR 0xF0
+
+#define IA32_LAPIC_REG_LVTT 0x320
+
+#define IA32_LAPIC_REG_TMRINITCNT 0x380
+#define IA32_LAPIC_REG_TMRCURRCNT 0x390
+#define IA32_LAPIC_REG_TMRDIV 0x3E0
+
+#define IA32_LAPIC_REG_CMD0 0x300
+#define IA32_LAPIC_REG_CMD1 0x310
+
+/////
+
+static uint64_t rdmsr(uint32_t addr) {
+ uint64_t v;
+ asm volatile ("rdmsr":"=A"(v):"c"(addr));
+ return v;
+}
+
+// static void wrmsr(uint32_t addr, uint64_t v) {
+// uint32_t low = v & 0xFFFFFFFF;
+// uint32_t high = v >> 32;
+// asm volatile ("wrmsr"::"c"(addr),"d"(high),"a"(low));
+// }
+
+/////
+
+struct acpi_apic_field_type {
+ uint8_t type;
+ uint8_t length;
+} __attribute__((packed));
+
+struct acpi_lapic_entry {
+ struct acpi_apic_field_type hdr;
+
+ uint8_t processor_id;
+ uint8_t apic_id;
+ uint32_t flags;
+} __attribute__((packed));
+
+struct acpi_ioapic_entry {
+ struct acpi_apic_field_type hdr;
+
+ uint8_t ioapic_id;
+ uint8_t res;
+ uint32_t ioapic_addr;
+ uint32_t gsi_base;
+} __attribute__((packed));
+
+/////
+
+// LAPIC ID of the BSP CPU
+static uint32_t bsp_lapic_id;
+// Local APIC base for every processor
+// (mapped to per-CPU LAPICs, while the address is the same)
+static uintptr_t lapic_base;
+
+/////
+
+static uintptr_t amd64_apic_base(void) {
+ uint64_t addr = rdmsr(IA32_APIC_BASE_MSR);
+ return addr & 0xFFFFFF000;
+}
+
+static int amd64_apic_status(void) {
+ uintptr_t v = rdmsr(IA32_APIC_BASE_MSR);
+ return v & IA32_APIC_BASE_MSR_ENABLE;
+}
+
+static void amd64_pic8259_disable(void) {
+ // Mask everything
+ asm volatile (
+ "mov $0xFF, %al \n"
+ "outb %al, $0xA1 \n"
+ "outb %al, $0x21"
+ );
+}
+
+void amd64_apic_init(struct acpi_madt *madt) {
+ // Get LAPIC base
+ lapic_base = amd64_apic_base() + 0xFFFFFF0000000000;
+ kdebug("APIC base is %p\n", lapic_base);
+ kdebug("APIC is %s\n", (amd64_apic_status() ? "enabled" : "disabled"));
+
+ kdebug("Disabling i8259 PIC\n");
+ amd64_pic8259_disable();
+
+ // Determine the BSP LAPIC ID
+ bsp_lapic_id = *(uint32_t *) (lapic_base + IA32_LAPIC_REG_ID);
+ kdebug("BSP Local APIC ID: %d\n", bsp_lapic_id);
+
+ // Enable LAPIC.SVR.SoftwareEnable bit
+ // And set spurious interrupt mapping to 0xFF
+ *(uint32_t *) (lapic_base + IA32_LAPIC_REG_SVR) |= (1 << 8) | (0xFF);
+
+ // Enable LAPIC timer
+ *(uint32_t *) (lapic_base + IA32_LAPIC_REG_LVTT) = 32 | 0x20000;
+ *(uint32_t *) (lapic_base + IA32_LAPIC_REG_TMRDIV) = 0x3;
+ *(uint32_t *) (lapic_base + IA32_LAPIC_REG_TMRINITCNT) = 100000;
+}
diff --git a/sys/amd64/hw/exc_s.S b/sys/amd64/hw/exc_s.S
new file mode 100644
index 0000000..a44a6fe
--- /dev/null
+++ b/sys/amd64/hw/exc_s.S
@@ -0,0 +1,96 @@
+.macro amd64_isr_nerr, n
+amd64_exc_isr_\n:
+ cli
+ pushq $0
+ pushq $\n
+ jmp amd64_exc_generic
+.endm
+
+.macro amd64_isr_yerr, n
+amd64_exc_isr_\n:
+ cli
+ pushq $\n
+ jmp amd64_exc_generic
+.endm
+
+.section .text
+amd64_exc_generic:
+ // TODO: exception handling
+ leaq _amd64_exc_str(%rip), %rsi
+ xorq %rdi, %rdi
+ call debugs
+1:
+ cli
+ hlt
+ jmp 1b
+_amd64_exc_str:
+ .string "Error: unhandled exception\n"
+
+amd64_isr_nerr 0
+amd64_isr_nerr 1
+amd64_isr_nerr 2
+amd64_isr_nerr 3
+amd64_isr_nerr 4
+amd64_isr_nerr 5
+amd64_isr_nerr 6
+amd64_isr_nerr 7
+amd64_isr_yerr 8
+amd64_isr_nerr 9
+amd64_isr_yerr 10
+amd64_isr_yerr 11
+amd64_isr_yerr 12
+amd64_isr_yerr 13
+amd64_isr_yerr 14
+amd64_isr_nerr 15
+amd64_isr_nerr 16
+amd64_isr_yerr 17
+amd64_isr_nerr 18
+amd64_isr_nerr 19
+amd64_isr_nerr 20
+amd64_isr_nerr 21
+amd64_isr_nerr 22
+amd64_isr_nerr 23
+amd64_isr_nerr 24
+amd64_isr_nerr 25
+amd64_isr_nerr 26
+amd64_isr_nerr 27
+amd64_isr_nerr 28
+amd64_isr_nerr 29
+amd64_isr_yerr 30
+amd64_isr_nerr 31
+
+.section .rodata
+.global amd64_exception_vectors
+amd64_exception_vectors:
+ .quad amd64_exc_isr_0
+ .quad amd64_exc_isr_1
+ .quad amd64_exc_isr_2
+ .quad amd64_exc_isr_3
+ .quad amd64_exc_isr_4
+ .quad amd64_exc_isr_5
+ .quad amd64_exc_isr_6
+ .quad amd64_exc_isr_7
+ .quad amd64_exc_isr_8
+ .quad amd64_exc_isr_9
+ .quad amd64_exc_isr_10
+ .quad amd64_exc_isr_11
+ .quad amd64_exc_isr_12
+ .quad amd64_exc_isr_13
+ .quad amd64_exc_isr_14
+ .quad amd64_exc_isr_15
+ .quad amd64_exc_isr_16
+ .quad amd64_exc_isr_17
+ .quad amd64_exc_isr_18
+ .quad amd64_exc_isr_19
+ .quad amd64_exc_isr_20
+ .quad amd64_exc_isr_21
+ .quad amd64_exc_isr_22
+ .quad amd64_exc_isr_23
+ .quad amd64_exc_isr_24
+ .quad amd64_exc_isr_25
+ .quad amd64_exc_isr_26
+ .quad amd64_exc_isr_27
+ .quad amd64_exc_isr_28
+ .quad amd64_exc_isr_29
+ .quad amd64_exc_isr_30
+ .quad amd64_exc_isr_31
diff --git a/sys/amd64/hw/gdt.c b/sys/amd64/hw/gdt.c
new file mode 100644
index 0000000..ebe0153
--- /dev/null
+++ b/sys/amd64/hw/gdt.c
@@ -0,0 +1,46 @@
+#include "sys/amd64/hw/gdt.h"
+#define GDT_SIZE 3
+
+extern void amd64_gdt_load(void *p);
+
+static amd64_gdt_entry_t gdt[GDT_SIZE] = { 0 };
+static amd64_gdt_ptr_t gdtr = {
+ sizeof(gdt) - 1,
+ (uintptr_t) gdt
+};
+
+#define GDT_ACC_AC (1 << 0)
+#define GDT_ACC_RW (1 << 1)
+#define GDT_ACC_DC (1 << 2)
+#define GDT_ACC_EX (1 << 3)
+#define GDT_ACC_S (1 << 4)
+#define GDT_ACC_R0 (0 << 5)
+#define GDT_ACC_R1 (1 << 5)
+#define GDT_ACC_R2 (2 << 5)
+#define GDT_ACC_R3 (3 << 5)
+#define GDT_ACC_PR (1 << 7)
+
+#define GDT_FLG_LONG (1 << 5)
+#define GDT_FLG_SZ (1 << 6)
+#define GDT_FLG_GR (1 << 7)
+
+static void amd64_gdt_set(int idx, uint32_t base, uint32_t limit, uint8_t flags, uint8_t access) {
+ gdt[idx].base_lo = base & 0xFFFF;
+ gdt[idx].base_mi = (base >> 16) & 0xFF;
+ gdt[idx].base_hi = (base >> 24) & 0xFF;
+ gdt[idx].access = access;
+ gdt[idx].flags = (flags & 0xF0) | ((limit >> 16) & 0xF);
+ gdt[idx].limit_lo = limit & 0xFFFF;
+}
+
+void amd64_gdt_init(void) {
+ amd64_gdt_set(0, 0, 0, 0, 0);
+ amd64_gdt_set(1, 0, 0,
+ GDT_FLG_LONG,
+ GDT_ACC_PR | GDT_ACC_S | GDT_ACC_EX);
+ amd64_gdt_set(2, 0, 0,
+ 0,
+ GDT_ACC_PR | GDT_ACC_S | GDT_ACC_RW);
+
+ amd64_gdt_load(&gdtr);
+}
diff --git a/sys/amd64/hw/gdt_s.S b/sys/amd64/hw/gdt_s.S
new file mode 100644
index 0000000..7cec8c2
--- /dev/null
+++ b/sys/amd64/hw/gdt_s.S
@@ -0,0 +1,6 @@
+.section .text
+.global amd64_gdt_load
+amd64_gdt_load:
+ lgdt (%rdi)
+ // Not sure if CS reload is needed
+ retq
diff --git a/sys/amd64/hw/idt.c b/sys/amd64/hw/idt.c
new file mode 100644
index 0000000..7a04ebf
--- /dev/null
+++ b/sys/amd64/hw/idt.c
@@ -0,0 +1,56 @@
+#include "sys/amd64/hw/idt.h"
+#include "sys/string.h"
+#include "sys/types.h"
+
+#define IDT_FLG_TASK32 0x5
+#define IDT_FLG_INT16 0x6
+#define IDT_FLG_TRAP16 0x7
+#define IDT_FLG_INT32 0xE
+#define IDT_FLG_TRAP32 0xF
+#define IDT_FLG_SS (1 << 4)
+#define IDT_FLG_R0 (0 << 5)
+#define IDT_FLG_R1 (1 << 5)
+#define IDT_FLG_R2 (2 << 5)
+#define IDT_FLG_R3 (3 << 5)
+#define IDT_FLG_P (1 << 7)
+
+extern uintptr_t amd64_exception_vectors[32];
+extern void amd64_irq0(void);
+
+static struct amd64_idt_entry {
+ uint16_t base_lo;
+ uint16_t selector;
+ uint8_t zero;
+ uint8_t flags;
+ uint16_t base_hi;
+ uint32_t base_ex;
+ uint32_t zero1;
+} __attribute__((packed)) idt[256] __attribute__((aligned(0x10)));
+
+static const struct {
+ uint16_t size;
+ uintptr_t offset;
+} __attribute__((packed)) idtr __attribute__((aligned(0x10))) = {
+ .size = sizeof(idt) - 1,
+ .offset = (uintptr_t) idt
+};
+
+static void amd64_idt_set(int idx, uintptr_t base, uint16_t selector, uint8_t flags) {
+ idt[idx].base_lo = base & 0xFFFF;
+ idt[idx].base_hi = (base >> 16) & 0xFFFF;
+ idt[idx].base_ex = (base >> 32) & 0xFFFFFFFF;
+ idt[idx].selector = selector;
+ idt[idx].flags = flags;
+ idt[idx].zero = 0;
+}
+
+void amd64_idt_init(void) {
+ memset(idt, 0, sizeof(idt));
+ // Exception vectors
+ for (size_t i = 0; i < 32; ++i) {
+ amd64_idt_set(i, amd64_exception_vectors[i], 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
+ }
+ amd64_idt_set(32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
+
+ asm volatile ("lidt idtr(%rip)");
+}
diff --git a/sys/amd64/hw/irq0.S b/sys/amd64/hw/irq0.S
new file mode 100644
index 0000000..dc2a965
--- /dev/null
+++ b/sys/amd64/hw/irq0.S
@@ -0,0 +1,32 @@
+.section .text
+.global amd64_irq0
+amd64_irq0:
+ pushq %rax
+ movq $0xFFFFFF00FEE000B0, %rax
+ movl $0, (%rax)
+ popq %rax
+
+ pushq %rax
+ pushq %rcx
+ pushq %rdx
+ pushq %rbx
+
+ pushq %rdi
+ pushq %rsi
+
+ xorq %rdi, %rdi
+ leaq _tmr_msg(%rip), %rsi
+ call debugs
+
+ popq %rsi
+ popq %rdi
+
+ popq %rbx
+ popq %rdx
+ popq %rcx
+ popq %rax
+
+ iretq
+
+_tmr_msg:
+ .string "T!\n"
diff --git a/sys/amd64/kernel.c b/sys/amd64/kernel.c
new file mode 100644
index 0000000..141b752
--- /dev/null
+++ b/sys/amd64/kernel.c
@@ -0,0 +1,16 @@
+#include "sys/debug.h"
+#include "sys/amd64/hw/gdt.h"
+#include "sys/amd64/hw/idt.h"
+#include "sys/amd64/mm/mm.h"
+#include "sys/amd64/hw/acpi.h"
+
+void kernel_main(struct amd64_loader_data *data) {
+ amd64_gdt_init();
+ amd64_idt_init();
+ amd64_mm_init(NULL);
+ amd64_acpi_init();
+
+ while (1) {
+ asm ("sti; hlt");
+ }
+}
diff --git a/sys/amd64/mm/mm.c b/sys/amd64/mm/mm.c
new file mode 100644
index 0000000..054066d
--- /dev/null
+++ b/sys/amd64/mm/mm.c
@@ -0,0 +1,52 @@
+// #include "sys/vmalloc.h"
+// #include "sys/assert.h"
+#include "sys/debug.h"
+#include "sys/string.h"
+#include "sys/amd64/mm/mm.h"
+// #include "sys/panic.h"
+// #include "sys/heap.h"
+// #include "sys/mem.h"
+// #include "sys/mm.h"
+
+mm_space_t mm_kernel;
+
+extern int _kernel_end;
+
+void amd64_mm_init(struct amd64_loader_data *data) {
+ kdebug("Memory manager init\n");
+
+ // Physical memory at this point:
+ // 0x000000 - 0x400000 - Used by kernel and info passed from the loader
+ // Virtual memory:
+ // Lower 1GiB mapped to itself for loader access
+ // 0xFFFFFF0000000000 (1GiB) is mapped to 0 for kernel access
+
+ // XXX: assuming nothing important is there
+ uint64_t *pml4 = (uint64_t *) (0x200000 - 0x1000);
+ uint64_t *pdpt = (uint64_t *) (0x200000 - 0x2000);
+
+ memset((void *) (0x200000 - 2 * 0x1000), 0, 0x1000 * 2);
+
+ // 0xFFFFFF0000000000 -> 0 (512GiB) mapping
+ pml4[AMD64_MM_STRIPSX(KERNEL_VIRT_BASE) >> 39] = ((uintptr_t) pdpt) | 1 | 2 | 4;
+ for (uint64_t i = 0; i < 4; ++i) {
+ kdebug("Mapping %p -> %p\n", KERNEL_VIRT_BASE | (i << 30), i << 30);
+ pdpt[((AMD64_MM_STRIPSX(KERNEL_VIRT_BASE) >> 30) + i) & 0x1FF] = (i << 30) | 1 | 2 | 4 | (1 << 7);
+ }
+
+ // Load the new table
+ asm volatile ("mov %0, %%cr3"::"a"(pml4):"memory");
+
+ // Create a pool located right after kernel image
+ // amd64_mm_pool_init((uintptr_t) &_kernel_end, MM_POOL_SIZE);
+
+ // mm_kernel = (mm_space_t) (MM_VIRTUALIZE(pml4));
+
+ // // Allocate some pages for kernel heap (base size: 16MiB)
+ // uintptr_t heap_base_phys = amd64_phys_alloc_contiguous(KERNEL_HEAP >> 12);
+ // assert(heap_base_phys != MM_NADDR, "Could not allocate %S of memory for kernel heap\n", KERNEL_HEAP);
+ // kdebug("Setting up kernel heap of %S @ %p\n", KERNEL_HEAP, heap_base_phys);
+ // amd64_heap_init(heap_global, heap_base_phys, KERNEL_HEAP);
+
+ // amd64_heap_dump(heap_global);
+}
diff --git a/sys/amd64/string.S b/sys/amd64/string.S
deleted file mode 100644
index 2aa6f34..0000000
--- a/sys/amd64/string.S
+++ /dev/null
@@ -1,14 +0,0 @@
-.section .text
-.global strlen
-strlen:
- // %rdi - str
- xorq %rax, %rax
-1:
- cmpq $0, (%rdi)
- je 1f
- incq %rax
- incq %rdi
- jmp 1b
-1:
-
- ret
diff --git a/sys/conf.mk b/sys/conf.mk
index c9574b7..8387357 100644
--- a/sys/conf.mk
+++ b/sys/conf.mk
@@ -42,4 +42,5 @@ endif
# $(O)/sys/tty.o \
# $(O)/sys/chr.o
-OBJS+=$(O)/sys/debug.o
+OBJS+=$(O)/sys/debug.o \
+ $(O)/sys/string.o
diff --git a/sys/string.c b/sys/string.c
new file mode 100644
index 0000000..e54bc2b
--- /dev/null
+++ b/sys/string.c
@@ -0,0 +1,144 @@
+#include "sys/string.h"
+
+size_t strlen(const char *a) {
+ size_t s = 0;
+ while (*a++) {
+ ++s;
+ }
+ return s;
+}
+
+int strncmp(const char *a, const char *b, size_t n) {
+ size_t c = 0;
+ for (; c < n && (*a || *b); ++c, ++a, ++b) {
+ if (*a != *b) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int strcmp(const char *a, const char *b) {
+ if (a == b) {
+ return 0;
+ }
+ if (a == NULL || b == NULL) {
+ return -1;
+ }
+ for (; *a || *b; ++a, ++b) {
+ if (*a != *b) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+char *strcpy(char *dst, const char *src) {
+ size_t i;
+ for (i = 0; src[i]; ++i) {
+ dst[i] = src[i];
+ }
+ dst[i] = 0;
+ return dst;
+}
+
+char *strncpy(char *dst, const char *src, size_t lim) {
+ size_t i = 0;
+ while (i < lim) {
+ if (!src[i]) {
+ dst[i] = 0;
+ return dst;
+ }
+ dst[i] = src[i];
+ ++i;
+ }
+ return dst;
+}
+
+char *strchr(const char *s, int c) {
+ while (*s) {
+ if (*s == (char) c) {
+ return (char *) s;
+ }
+ ++s;
+ }
+ return NULL;
+}
+
+char *strrchr(const char *s, int c) {
+ ssize_t l = (ssize_t) strlen(s);
+ if (!l) {
+ return NULL;
+ }
+ while (l >= 0) {
+ if (s[l] == (char) c) {
+ return (char *) &s[l];
+ }
+ --l;
+ }
+ return NULL;
+}
+
+void *memmove(void *dest, const void *src, size_t n) {
+ char *d = dest;
+ const char *s = src;
+
+ if (d == s) {
+ return d;
+ }
+
+ if ((s + n) <= d || (d + n) <= s) {
+ return memcpy(d, s, n);
+ }
+
+ if (d < s) {
+ if (((uintptr_t) s) % sizeof(size_t) == ((uintptr_t) d) % sizeof(size_t)) {
+ while (((uintptr_t) d) % sizeof(size_t)) {
+ if (!n--) {
+ return dest;
+ }
+ *d++ = *s++;
+ }
+ for (; n >= sizeof(size_t); n -= sizeof(size_t), d += sizeof(size_t), s += sizeof(size_t)) {
+ *((size_t *) d) = *((size_t *) s);
+ }
+ }
+ for (; n; n--) {
+ *d++ = *s++;
+ }
+ } else {
+ if (((uintptr_t) s) % sizeof(size_t) == ((uintptr_t) d) % sizeof(size_t)) {
+ while (((uintptr_t) (d + n)) % sizeof(size_t)) {
+ if (!n--) {
+ return dest;
+ }
+ d[n] = s[n];
+ }
+ while (n >= sizeof(size_t)) {
+ n -= sizeof(size_t);
+ *((size_t *) (d + n)) = *((size_t *) (s + n));
+ }
+ }
+ while (n) {
+ n--;
+ d[n] = s[n];
+ }
+ }
+
+ return dest;
+}
+
+void *memset(void *blk, int v, size_t sz) {
+ for (size_t i = 0; i < sz; ++i) {
+ ((char *) blk)[i] = v;
+ }
+ return blk;
+}
+
+void *memcpy(void *dst, const void *src, size_t sz) {
+ for (size_t i = 0; i < sz; ++i) {
+ ((char *) dst)[i] = ((const char *) src)[i];
+ }
+ return dst;
+}
+