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; +} +