commit a9d4aa30ae54e293a085786cef37d1215c547c88 Author: op52 Date: Tue Dec 4 01:29:45 2018 -0500 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28d4520 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/ +cmake-build-debug/ +out/ +user/out/ +.vscode/ +CMakeLists.txt \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d5ea261 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Op52 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b6dcbef --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# CurrOS +CurrOS is a simple x86-64 kernel that supports interrupt, memory management, processes and threads, userspace and system calls. + +## Toolchain +clang, lld, xorriso, grub-pc-bin, nasm + +## Build +Run make inside "user" directory to build the test user program. +Run make in root dir and boot from out/curros.iso + +## Clean +make clean +cd user +make clean diff --git a/grub.cfg b/grub.cfg new file mode 100644 index 0000000..29349bc --- /dev/null +++ b/grub.cfg @@ -0,0 +1,7 @@ +set timeout=0 +set default=0 + +menuentry "curros" { + multiboot2 /kernel.elf + module2 /hello.elf +} diff --git a/inc/asm.inc b/inc/asm.inc new file mode 100644 index 0000000..740e1bb --- /dev/null +++ b/inc/asm.inc @@ -0,0 +1,47 @@ +%macro PUSH_REGS 0 + push rax ;save current rax + push rbx ;save current rbx + push rcx ;save current rcx + push rdx ;save current rdx + push rbp ;save current rbp + push rdi ;save current rdi + push rsi ;save current rsi + push r8 ;save current r8 + push r9 ;save current r9 + push r10 ;save current r10 + push r11 ;save current r11 + push r12 ;save current r12 + push r13 ;save current r13 + push r14 ;save current r14 + push r15 ;save current r15 + mov rax, ds + push rax + mov rax, es + push rax + push fs + push gs +%endmacro + +%macro POP_REGS 0 + pop gs + pop fs + pop rax + mov es, rax + pop rax + mov ds, rax + pop r15 ;restore current r15 + pop r14 ;restore current r14 + pop r13 ;restore current r13 + pop r12 ;restore current r12 + pop r11 ;restore current r11 + pop r10 ;restore current r10 + pop r9 ;restore current r9 + pop r8 ;restore current r8 + pop rsi ;restore current rsi + pop rdi ;restore current rdi + pop rbp ;restore current rbp + pop rdx ;restore current rdx + pop rcx ;restore current rcx + pop rbx ;restore current rbx + pop rax ;restore current rax +%endmacro \ No newline at end of file diff --git a/inc/cdef.h b/inc/cdef.h new file mode 100644 index 0000000..5ce4273 --- /dev/null +++ b/inc/cdef.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +typedef uint32_t uint32; +typedef int32_t int32; +typedef uint64_t uint64; +typedef int64_t int64; +typedef uintptr_t uintptr; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint8_t uint8; +typedef int8_t int8; +typedef size_t usize; + +typedef _Bool bool; + +#define TRUE (1) +#define FALSE (0) +#define ASM_F __attribute__((cdecl)) +#define PACKED __attribute__((packed)) + +#define PRAGMA_PACKED __attribute__((packed)) + +#define ALIGN(type, num, align) (((type)(num) + ((type)align - 1)) & ~((type)align - 1)) + +#define UNREFERENCED(x) {(x) = (x);} + + diff --git a/inc/clib.h b/inc/clib.h new file mode 100644 index 0000000..993a788 --- /dev/null +++ b/inc/clib.h @@ -0,0 +1,46 @@ +#pragma once + +#include "cdef.h" + +/** + * Common macros, etc + */ + +#define OBTAIN_STRUCT_ADDR(member_addr, struct_name, member_name) ((struct_name*)((uintptr)(member_addr) - (uintptr)(&(((struct_name*)0)->member_name)))) + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define SWAP(a, b, T) do { T temp = *(a); *(a) = *(b); *(b) = temp; } while(0); + +uint64 +str_len(char const *str); + + +uint64 +str_cmp(char const *str1, char const *str2); + + +void +mem_cpy(void *src, void *dst, uint64 size); + + +void +mem_mv(void *src, void *dst, uint64 size); + +#define KASSERT(expr) kassert_ex(#expr, __FILE__, __LINE__, expr) + +void +kassert_ex(const char *expr_str, const char *file, int32 line, int32 expr); + +void +mem_set(void *src, uint8 val, uint64 size); + + +static inline uint64 +bit_field_mask(uint32 low, uint32 high) +{ + return ~((uint64)-1 << (high - low + 1)) << low; +} + +void +poor_sleep(uint32 dat); diff --git a/inc/cpu.h b/inc/cpu.h new file mode 100644 index 0000000..3b88568 --- /dev/null +++ b/inc/cpu.h @@ -0,0 +1,44 @@ +#pragma once + +#include "cdef.h" + +void ASM_F out_8(uint16 port, uint8 data); +void ASM_F out_16(uint16 port, uint16 data); +void ASM_F out_32(uint16 port, uint32 data); + +int32 ASM_F cmpxchg_32(int32 *dst, int32 old_val, int32 new_val); +int32 ASM_F xinc_32(int32* dst, int32 val); + +uint8 ASM_F in_8(uint16 port); +uint16 ASM_F in_16(uint16 port); +uint32 ASM_F in_32(uint16 port); + +void ASM_F flush_gdt(void *gdt_ptr, uint16 code_slct, uint16 data_slct); + +void ASM_F flush_idt(void *idt_ptr); + +void ASM_F flush_tss(uint16 tss_slct); + +void ASM_F cpuid(uint32 *eax, uint32 *ebx, uint32 *ecx, uint32 *edx); + +void ASM_F read_msr(uint32 *ecx, uint32 *edx, uint32 *eax); + +void ASM_F write_msr(uint32 *ecx, uint32 *edx, uint32 *eax); + +void ASM_F sti(); + +void ASM_F cli(); + +void ASM_F hlt(); + +uint64 ASM_F read_cr8(); + +void ASM_F write_cr8(uint64 val); + +uint64 ASM_F read_cr3(); + +void ASM_F write_cr3(uint64 val); + +void ASM_F flush_tlb(); + +#define BOCHS_BREAK __asm__("xchg %bx,%bx"); diff --git a/inc/elf64.h b/inc/elf64.h new file mode 100644 index 0000000..7e1409f --- /dev/null +++ b/inc/elf64.h @@ -0,0 +1,129 @@ +/* ELF64 Object representation and parsing header. */ + +#include "cdef.h" +#include "proc.h" + +typedef uint64 Elf64_Addr; +typedef uint64 Elf64_Off; +typedef uint16 Elf64_Half; +typedef uint32 Elf64_Word; +typedef uint32 Elf64_Sword; +typedef uint64 Elf64_Xword; +typedef uint64 Elf64_Sxword; +// This is custom +typedef uint8 uchar; + + +/* HEADER */ + +typedef struct +{ + uchar e_ident[16]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} elf64_hdr; + +#define EI_MAG0 0 // should be \x7f +#define EI_MAG1 1 // E +#define EI_MAG2 2 // L +#define EI_MAG3 3 // F + +#define ELFMAG0 '\x7f' +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_ABIVERSION 8 +#define EI_PAD 9 +#define EI_NIDENT 16 + +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define ELFOSABI_SYSV 0 +#define ELFOSABI_HPUX 1 +#define ELFOSABI_STANDALONE 255 + +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 + +#define ET_LOOS 0xFE00 +#define ET_HIOS 0XFEFF +#define ET_LOPROC 0XFF00 +#define ET_HIPROC 0XFFFF + +#define EV_CURRENT 1 + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} elf64_phdr; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + + +int32 elf_load_file(struct pcb *proc, void *file, void **entry); + diff --git a/inc/error.h b/inc/error.h new file mode 100644 index 0000000..62a948a --- /dev/null +++ b/inc/error.h @@ -0,0 +1,11 @@ +#pragma once + +#include "cdef.h" + +enum +{ + ESUCCESS = 0, + ENOSUPPORT = -1, + ENOMEM = -2, + EINVARG = -3, +}; diff --git a/inc/intr.h b/inc/intr.h new file mode 100644 index 0000000..a230aa6 --- /dev/null +++ b/inc/intr.h @@ -0,0 +1,113 @@ +#pragma once + +#include "cdef.h" +#include "cpu.h" + +#define READ_IRQ() read_cr8() +#define WRITE_IRQ(x) write_cr8(x) + +#define NUM_IDT_DESC (256) +#define NUM_GDT_DESC (7) +#define IDT_DESC_SIZE (16) +#define GDT_DESC_SIZE (8) +// + 8 because TSS descriptor is 16 bytes instead +#define GDT_SIZE ((NUM_GDT_DESC) * (GDT_DESC_SIZE)) +#define IDT_SIZE ((NUM_IDT_DESC) * (IDT_DESC_SIZE)) + +#define GDT_K_CODE (1) +#define GDT_NULL (0) +#define GDT_K_DATA (2) +#define GDT_U_CODE (3) +#define GDT_U_DATA (4) +#define GDT_U_TSS (5) +#define SEL(idx, TI, RPL) ((((uint16)idx) << 3) | (((uint16) TI) << 2) | ((uint16) RPL)) + +struct PACKED gdtr +{ + uint16 size; + uint64 offset; +}; + +struct PACKED idtr +{ + uint16 size; + uint64 offset; +}; + +struct PACKED gdt_desc +{ + uint16 seg_l; + uint16 base_l; + uint8 base_m; + uint8 attr1; + uint8 attr2; + uint8 base_h; +}; + +struct PACKED idt_desc +{ + uint16 offset_l; + uint16 seg_sel; + uint16 attr; + uint16 offset_m; + uint32 offset_h; + uint32 reserved; +}; + +struct PACKED intr_frame +{ + uint64 gs; + uint64 fs; + uint64 es; + uint64 ds; + uint64 r15; + uint64 r14; + uint64 r13; + uint64 r12; + uint64 r11; + uint64 r10; + uint64 r9; + uint64 r8; + uint64 rsi; + uint64 rdi; + uint64 rbp; + uint64 rdx; + uint64 rcx; + uint64 rbx; + uint64 rax; + uint64 error_code; + uint64 rip; + uint64 cs; + uint64 rflags; + uint64 rsp; + uint64 ss; +}; + +struct PACKED tss +{ + uint32 unused0; + uint64 rsp0; + uint64 rsp1; + uint64 rsp2; + uint32 unused1[19]; +}; + +#define INTR_VEC_TIMER (50) +#define INTR_VEC_SPURIOUS (255) +#define INTR_VEC_SYSCALL (51) +#define INTR_LIMIT_INTEL (31) + + +// returns the new exception frame pointer +// interrupt handlers should EOI +typedef void* (*intr_handler)(struct intr_frame *frame); + +int32 intr_init(); + +void* ASM_F intr_dispatcher(uint32 vec, struct intr_frame *frame); + +void set_intr_handler(uint32 vec, intr_handler handler); + +void send_ipi(uint32 vec); + +void stop_cpu(); diff --git a/inc/llist.h b/inc/llist.h new file mode 100644 index 0000000..2a83230 --- /dev/null +++ b/inc/llist.h @@ -0,0 +1,70 @@ +#pragma once + +#include "cdef.h" + +struct llist_node +{ + struct llist_node *prev; + struct llist_node *next; + void * data; +}; + +struct llist +{ + struct llist_node *head; + struct llist_node *tail; + uint32 size; +}; + +void +lb_llist_init(struct llist *list); + +uint32 +lb_llist_size(struct llist *list); + +void +lb_llist_push_front(struct llist *list, struct llist_node *node); + +void +lb_llist_push_back(struct llist *list, struct llist_node *node); + +struct llist_node * +lb_llist_pop_front(struct llist *list); + + +struct llist_node * +lb_llist_pop_back(struct llist *list); + +void +lb_llist_insert_by_idx(struct llist *list, uint32 index, struct llist_node *node); + +struct llist_node * +lb_llist_remove_by_idx(struct llist *list, uint32 index); + + +struct llist_node * +lb_llist_get(struct llist *list, uint32 index); + + +void +lb_llist_insert_by_ref(struct llist *list, struct llist_node *cur_node, struct llist_node *new_node); + + +struct llist_node * +lb_llist_remove_by_ref(struct llist *list, struct llist_node *node); + + +struct llist_node * +lb_llist_next(struct llist_node *node); + + +struct llist_node * +lb_llist_prev(struct llist_node *node); + + +struct llist_node * +lb_llist_first(struct llist *list); + + +struct llist_node * +lb_llist_last(struct llist *list); diff --git a/inc/memory_layout.h b/inc/memory_layout.h new file mode 100644 index 0000000..5f09326 --- /dev/null +++ b/inc/memory_layout.h @@ -0,0 +1,18 @@ +/* System memory layout */ +#pragma once +#define U_STACK_VADDR 0x90000000 + +#define K_START 0xFFFF800000000000 +#define K_PMAP_VADDR 0xFFFF800000000000 +#define K_DYNAMIC 0xFFFFFFFF00000000 +#define K_DYN_END 0xFFFFFFFF70000000 +#define K_IMAGE 0xFFFFFFFF80000000 + +#define PAGE_SIZE (0x1000) + +#define K_IMAGE_PADDR (0x1000000) +// reserve 16MB +#define K_IMAGE_PRESRV (16 * 1024 * 1024) + +#define R_PADDR(paddr) (void*)((paddr) + K_PMAP_VADDR) +#define IS_KERN_SPACE(vaddr) (((uintptr)vaddr) >= K_START) diff --git a/inc/multiboot2.h b/inc/multiboot2.h new file mode 100644 index 0000000..d23daa0 --- /dev/null +++ b/inc/multiboot2.h @@ -0,0 +1,434 @@ + /* multiboot2.h - Multiboot 2 header file. */ + /* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 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 ANY + * DEVELOPER OR DISTRIBUTOR 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. + */ + + #ifndef MULTIBOOT_HEADER + #define MULTIBOOT_HEADER 1 + + /* How many bytes from the start of the file we search for the header. */ + #define MULTIBOOT_SEARCH 32768 + #define MULTIBOOT_HEADER_ALIGN 8 + + /* The magic field should contain this. */ + #define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 + + /* This should be in %eax. */ + #define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 + + /* Alignment of multiboot modules. */ + #define MULTIBOOT_MOD_ALIGN 0x00001000 + + /* Alignment of the multiboot info structure. */ + #define MULTIBOOT_INFO_ALIGN 0x00000008 + + /* Flags set in the 'flags' member of the multiboot header. */ + + #define MULTIBOOT_TAG_ALIGN 8 + #define MULTIBOOT_TAG_TYPE_END 0 + #define MULTIBOOT_TAG_TYPE_CMDLINE 1 + #define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 + #define MULTIBOOT_TAG_TYPE_MODULE 3 + #define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 + #define MULTIBOOT_TAG_TYPE_BOOTDEV 5 + #define MULTIBOOT_TAG_TYPE_MMAP 6 + #define MULTIBOOT_TAG_TYPE_VBE 7 + #define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 + #define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 + #define MULTIBOOT_TAG_TYPE_APM 10 + #define MULTIBOOT_TAG_TYPE_EFI32 11 + #define MULTIBOOT_TAG_TYPE_EFI64 12 + #define MULTIBOOT_TAG_TYPE_SMBIOS 13 + #define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 + #define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 + #define MULTIBOOT_TAG_TYPE_NETWORK 16 + #define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 + #define MULTIBOOT_TAG_TYPE_EFI_BS 18 + #define MULTIBOOT_TAG_TYPE_EFI32_IH 19 + #define MULTIBOOT_TAG_TYPE_EFI64_IH 20 + #define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + + #define MULTIBOOT_HEADER_TAG_END 0 + #define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 + #define MULTIBOOT_HEADER_TAG_ADDRESS 2 + #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 + #define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 + #define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 + #define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 + #define MULTIBOOT_HEADER_TAG_EFI_BS 7 + #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8 + #define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 + #define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 + + #define MULTIBOOT_ARCHITECTURE_I386 0 + #define MULTIBOOT_ARCHITECTURE_MIPS32 4 + #define MULTIBOOT_HEADER_TAG_OPTIONAL 1 + + #define MULTIBOOT_LOAD_PREFERENCE_NONE 0 + #define MULTIBOOT_LOAD_PREFERENCE_LOW 1 + #define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 + + #define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 + #define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 + + #ifndef ASM_FILE + + /* CurrOS */ + #include "cdef.h" + /* CurrOS */ + + typedef unsigned char multiboot_uint8_t; + typedef unsigned short multiboot_uint16_t; + typedef unsigned int multiboot_uint32_t; + typedef unsigned long long multiboot_uint64_t; + + struct multiboot_header + { + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* ISA */ + multiboot_uint32_t architecture; + + /* Total header length. */ + multiboot_uint32_t header_length; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; + }; + + struct multiboot_header_tag + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + }; + + struct multiboot_header_tag_information_request + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t requests[0]; + }; + + struct multiboot_header_tag_address + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; + }; + + struct multiboot_header_tag_entry_address + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t entry_addr; + }; + + struct multiboot_header_tag_console_flags + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t console_flags; + }; + + struct multiboot_header_tag_framebuffer + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; + }; + + struct multiboot_header_tag_module_align + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + }; + + struct multiboot_header_tag_relocatable + { + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t min_addr; + multiboot_uint32_t max_addr; + multiboot_uint32_t align; + multiboot_uint32_t preference; + }; + + struct multiboot_color + { + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; + }; + + struct multiboot_mmap_entry + { + multiboot_uint64_t addr; + multiboot_uint64_t len; + #define MULTIBOOT_MEMORY_AVAILABLE 1 + #define MULTIBOOT_MEMORY_RESERVED 2 + #define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 + #define MULTIBOOT_MEMORY_NVS 4 + #define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; + multiboot_uint32_t zero; + }; + typedef struct multiboot_mmap_entry multiboot_memory_map_t; + + struct multiboot_tag + { + multiboot_uint32_t type; + multiboot_uint32_t size; + }; + + struct multiboot_tag_string + { + multiboot_uint32_t type; + multiboot_uint32_t size; + char string[0]; + }; + + struct multiboot_tag_module + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + char cmdline[0]; + }; + + struct multiboot_tag_basic_meminfo + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + }; + + struct multiboot_tag_bootdev + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t biosdev; + multiboot_uint32_t slice; + multiboot_uint32_t part; + }; + + struct multiboot_tag_mmap + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t entry_size; + multiboot_uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; + }; + + struct multiboot_vbe_info_block + { + multiboot_uint8_t external_specification[512]; + }; + + struct multiboot_vbe_mode_info_block + { + multiboot_uint8_t external_specification[256]; + }; + + struct multiboot_tag_vbe + { + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; + }; + + struct multiboot_tag_framebuffer_common + { + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; + #define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 + #define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 + #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + multiboot_uint16_t reserved; + }; + + struct multiboot_tag_framebuffer + { + struct multiboot_tag_framebuffer_common common; + + union + { + struct + { + multiboot_uint16_t framebuffer_palette_num_colors; + struct multiboot_color framebuffer_palette[0]; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; + }; + + struct multiboot_tag_elf_sections + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t num; + multiboot_uint32_t entsize; + multiboot_uint32_t shndx; + char sections[0]; + }; + + struct multiboot_tag_apm + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; + }; + + struct multiboot_tag_efi32 + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; + }; + + struct multiboot_tag_efi64 + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; + }; + + struct multiboot_tag_smbios + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t major; + multiboot_uint8_t minor; + multiboot_uint8_t reserved[6]; + multiboot_uint8_t tables[0]; + }; + + struct multiboot_tag_old_acpi + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; + }; + + struct multiboot_tag_new_acpi + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; + }; + + struct multiboot_tag_network + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t dhcpack[0]; + }; + + struct multiboot_tag_efi_mmap + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t descr_size; + multiboot_uint32_t descr_vers; + multiboot_uint8_t efi_mmap[0]; + }; + + struct multiboot_tag_efi32_ih + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; + }; + + struct multiboot_tag_efi64_ih + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; + }; + + struct multiboot_tag_load_base_addr + { + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t load_base_addr; + }; + + /* CurrOS */ + typedef struct multiboot2_entry + { + uint32 total_size; + uint32 reserved; + } mbentry; + + typedef struct multiboot_header mheader; + typedef struct multiboot_header_tag htag; + typedef struct multiboot_tag_module mod_tag; + void parse_mb2(mbentry * mb, void ** module, char ** ld_name, uint64*, uint64*); + /* CurrOS */ + + #endif /* ! ASM_FILE */ + + #endif /* ! MULTIBOOT_HEADER */ diff --git a/inc/multiboot2.inc b/inc/multiboot2.inc new file mode 100644 index 0000000..eee369b --- /dev/null +++ b/inc/multiboot2.inc @@ -0,0 +1,43 @@ +MULTIBOOT_SEARCH equ 32768 +MULTIBOOT_HEADER_ALIGN equ 8 +MULTIBOOT2_HEADER_MAGIC equ 0xe85250d6 +MULTIBOOT2_BOOTLOADER_MAGIC equ 0x36d76289 +MULTIBOOT_MOD_ALIGN equ 0x00001000 +MULTIBOOT_INFO_ALIGN equ 0x00000008 + +MULTIBOOT_TAG_ALIGN equ 8 +MULTIBOOT_TAG_TYPE_END equ 0 +MULTIBOOT_TAG_TYPE_CMDLINE equ 1 +MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME equ 2 +MULTIBOOT_TAG_TYPE_MODULE equ 3 +MULTIBOOT_TAG_TYPE_BASIC_MEMINFO equ 4 +MULTIBOOT_TAG_TYPE_BOOTDEV equ 5 +MULTIBOOT_TAG_TYPE_MMAP equ 6 +MULTIBOOT_TAG_TYPE_VBE equ 7 +MULTIBOOT_TAG_TYPE_FRAMEBUFFER equ 8 +MULTIBOOT_TAG_TYPE_ELF_SECTIONS equ 9 +MULTIBOOT_TAG_TYPE_APM equ 10 +MULTIBOOT_TAG_TYPE_EFI32 equ 11 +MULTIBOOT_TAG_TYPE_EFI64 equ 12 +MULTIBOOT_TAG_TYPE_SMBIOS equ 13 +MULTIBOOT_TAG_TYPE_ACPI_OLD equ 14 +MULTIBOOT_TAG_TYPE_ACPI_NEW equ 15 +MULTIBOOT_TAG_TYPE_NETWORK equ 16 +MULTIBOOT_TAG_TYPE_EFI_MMAP equ 17 +MULTIBOOT_TAG_TYPE_EFI_BS equ 18 + +MULTIBOOT_HEADER_TAG_END equ 0 +MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST equ 1 +MULTIBOOT_HEADER_TAG_ADDRESS equ 2 +MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS equ 3 +MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS equ 4 +MULTIBOOT_HEADER_TAG_FRAMEBUFFER equ 5 +MULTIBOOT_HEADER_TAG_MODULE_ALIGN equ 6 +MULTIBOOT_HEADER_TAG_EFI_BS equ 7 + +MULTIBOOT_ARCHITECTURE_I386 equ 0 +MULTIBOOT_ARCHITECTURE_MIPS32 equ 4 +MULTIBOOT_HEADER_TAG_OPTIONAL equ 1 + +MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED equ 1 +MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED equ 2 \ No newline at end of file diff --git a/inc/paging.h b/inc/paging.h new file mode 100644 index 0000000..705d819 --- /dev/null +++ b/inc/paging.h @@ -0,0 +1,15 @@ +#pragma once + +#include "cdef.h" +#include "memory_layout.h" + +#define PML4_ENTRY(vaddr) ((vaddr >> 39) & 0x1FF) +#define PDPT_ENTRY(vaddr) ((vaddr >> 30) & 0x1FF) +#define PD_ENTRY(vaddr) ((vaddr >> 21) & 0x1FF) +#define PT_ENTRY(vaddr) ((vaddr >> 12) & 0x1FF) + +#define KERNEL_PAGE_SIZE (PAGE_SIZE) + +uintptr get_paddr(uint64 cr3, uintptr vaddr); + +int32 map_vmem(uint64 cr3, uintptr virt_addr, uintptr phys_addr); diff --git a/inc/pmm.h b/inc/pmm.h new file mode 100644 index 0000000..0a95665 --- /dev/null +++ b/inc/pmm.h @@ -0,0 +1,28 @@ +/* Header file for physical memory management */ +#pragma once + +#include "cdef.h" +#include "llist.h" +#include "spin_lock.h" + +typedef uintptr paddr; + +typedef struct llist memlist; +typedef struct llist_node memnode; + +typedef struct allocation_unit { + paddr head; + uint64 size; + uint8 status; +} allocu; + +void * pmalloc(uint32 size); // Allocate a physical page +void pfree(paddr p); // Free physical page + +// TODO : Setup methods +void pmm_init(uint64 low, uint64 high); +uint8 get_mem_phase(); +void set_mmap_low(uint64 low, uint64 size); +void set_mmap_high(uint64 high, uint64 size); + + diff --git a/inc/print.h b/inc/print.h new file mode 100644 index 0000000..36fe868 --- /dev/null +++ b/inc/print.h @@ -0,0 +1,9 @@ +#pragma once + +#include "cdef.h" + +void clear_screen(void); + +void kprintf(char const *format, ...); + +void print_init(); diff --git a/inc/proc.h b/inc/proc.h new file mode 100644 index 0000000..1330453 --- /dev/null +++ b/inc/proc.h @@ -0,0 +1,22 @@ +#pragma once + +#include "cdef.h" +#include "llist.h" +#include "spin_lock.h" + +struct pcb +{ + uint64 cr3; + uint32 proc_id; + struct llist threads; + struct llist_node list_node; + struct spin_lock lock; +}; + +// procs now are simply cr3 holders +int32 proc_create(void* elf64, uint32* proc_id); + + +// proc init also makes the current address space process 0 +// and creates a thread to run k_routine +int32 proc_init(void* k_routine); diff --git a/inc/spin_lock.h b/inc/spin_lock.h new file mode 100644 index 0000000..4c8dc7b --- /dev/null +++ b/inc/spin_lock.h @@ -0,0 +1,18 @@ +#pragma once + +#include "cdef.h" + +struct spin_lock +{ + int32 val; +}; + +void spin_init(struct spin_lock *lock); + +void spin_lock(struct spin_lock *lock); + +void spin_unlock(struct spin_lock *lock); + +uint64 spin_lock_irq_save(struct spin_lock* lock); + +void spin_unlock_irq_restore(struct spin_lock* lock, uint64 irq); diff --git a/inc/syscall.h b/inc/syscall.h new file mode 100644 index 0000000..af0f694 --- /dev/null +++ b/inc/syscall.h @@ -0,0 +1,5 @@ +#pragma once + +#include "cdef.h" + +void syscall_init(); diff --git a/inc/thread.h b/inc/thread.h new file mode 100644 index 0000000..a8e9046 --- /dev/null +++ b/inc/thread.h @@ -0,0 +1,42 @@ +#pragma once + +#include "proc.h" + +struct tcb +{ + struct pcb *proc; + uint32 tid; + int32 exit_code; + uint32 state; + + uintptr ustack; + usize ustack_sz; + void* kstack; + usize kstack_sz; + + uint64 rsp0; // kernel stack pointer for the stack, has context information + struct spin_lock lock; + struct llist_node list_node; +}; + +struct tcb* get_cur_thread(); + +int32 thread_stop(uint32 tid, int32 code); + +void list_threads(); + +int32 thread_create(struct pcb* proc, void* entry, void* args, uint32* tid); + +void thread_init(); + +void thread_schedule(); + +int32 thread_get_exit_code(uint32 tid, int32* exit_code); + +void thread_yield(); + +int32 thread_resume(uint32 tid); + +void thread_exit(int32 code); + +int32 thread_block(uint32 tid); diff --git a/inc/vmm.h b/inc/vmm.h new file mode 100644 index 0000000..d87b0f0 --- /dev/null +++ b/inc/vmm.h @@ -0,0 +1,64 @@ +#pragma once +/* Virtual memory management header */ + +#include "cdef.h" +#include "memory_layout.h" +#include "llist.h" +#include "pmm.h" +#include "spin_lock.h" + +typedef uintptr vaddr; +typedef struct llist vmll; +typedef struct llist_node vm_node; + +typedef struct virtual_page +{ + vaddr address; // Init to 0x0 + vaddr table_ptr; + vmll allocs; // Linked list of vm_atom + uint64 free; + uint8 status; +} vm_object; + +typedef struct allocation +{ + usize offset; + usize size; + vm_object *page_ptr; +} vm_atom; + +typedef struct page_table +{ + uint64 id; + usize size; + usize free; + vm_atom *map; +} vm_unit; + +typedef struct allocation_sector +{ + uint64 start; + uint64 end; + vm_object **actbck; + vm_object **resbck; + vm_object *largest_free; + uint16 dirty; +} vm_sector; + +typedef struct page_table_entries +{ + vm_unit *groups; + vm_sector sectors[3]; + vm_object *pages; + + vmll *global_alloc; + usize free_frames; +} vmem; + +void init_vm(); + +void vm_issue_unit(uint64 id, usize size); + +void *kalloc(usize size); + +void kfree(void *ptr); diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..3f6a427 --- /dev/null +++ b/linker.ld @@ -0,0 +1,32 @@ +ENTRY(sys_entry) + +KERNEL_IMAGE_VADDR = 0xFFFFFFFF80000000; +KERNEL_IMAGE_PADDR = 0x1000000; +KERNEL_PAGE_SIZE = 0x1000; + +SECTIONS +{ + . = KERNEL_IMAGE_VADDR + KERNEL_IMAGE_PADDR; + + .multiboot_header ALIGN(KERNEL_PAGE_SIZE) : AT(ADDR(.multiboot_header) - KERNEL_IMAGE_VADDR) + { + *(.multiboot_header) + } + + .text ALIGN(KERNEL_PAGE_SIZE) : AT(ADDR(.text) - KERNEL_IMAGE_VADDR) + { + *(.text) + } + + .data ALIGN(KERNEL_PAGE_SIZE) : AT(ADDR(.data) - KERNEL_IMAGE_VADDR) + { + *(.data) + *(.rodata*) + } + + .bss ALIGN(KERNEL_PAGE_SIZE) : AT(ADDR(.bss) - KERNEL_IMAGE_VADDR) + { + *(.bss) + *(COMMON) + } +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..9c6936a --- /dev/null +++ b/makefile @@ -0,0 +1,137 @@ + +AS := nasm +CC := clang-6.0 +LD := lld-6.0 +DAS := llvm-objdump-6.0 + +.DEFAULT_GOAL := all + +C_FLAGS_ARCH_X86_64 := -mcmodel=kernel \ + -target x86_64-pc-none-elf \ + -mno-red-zone \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 \ + -mno-sse3 \ + -mno-3dnow + +C_FLAGS = -x c \ + -g \ + -c \ + -O0 \ + -std=c17 \ + -Wall \ + -Werror \ + -Wextra \ + -Wpedantic \ + -ffreestanding \ + -fno-pic \ + -fno-stack-protector \ + -Wno-int-to-pointer-cast \ + -Wno-zero-length-array \ + $(C_FLAGS_ARCH_X86_64) \ + -I$(INC)/ \ + $(C_EFLAGS) + +AS_FLAGS = -w+all \ + -w+error \ + -f elf64 \ + -F dwarf \ + -g \ + -I$(INC)/ \ + $(AS_FLAGS_$(MOD)) + +DUMP_FLAGS = -x86-asm-syntax=intel \ + -disassemble \ + -r \ + -t \ + -triple=x86_64-pc-none-elf \ + -print-imm-hex + +LD_FLAGS = -fuse-ld=$(LD) \ + -nostdlib \ + -Wl,-T,$(LD_SCRIPT) \ + -Wl,--fatal-warnings + +# =============================== +# HERE COMES file definitions +# =============================== + +SRC := src +INC := inc +OUT := out +LD_SCRIPT := linker.ld +GRUB_CFG := grub.cfg + +TGT := $(OUT)/kernel.elf +ISO := $(OUT)/curros.iso +DMP := $(OUT)/kernel.dmp + +# =============================== +# Add additional c source files here +# =============================== +C_SRC := kmain.c \ + llist.c \ + intr.c \ + clib.c \ + print.c \ + multiboot2.c \ + pmm.c \ + elf64.c \ + vmm.c \ + spin_lock.c \ + thread.c \ + proc.c \ + paging.c \ + syscall.c + +# =============================== +# Add additional ASM source files here +# =============================== +ASM_SRC := boot.asm \ + cpu.asm \ + intr.asm + + +# =============================== +# Compilation rules +# =============================== +C_OBJ := $(addsuffix .o, $(addprefix $(OUT)/,$(C_SRC))) + +ASM_OBJ := $(addsuffix .o, $(addprefix $(OUT)/,$(ASM_SRC))) + +$(C_OBJ): $(OUT)/%.c.o : $(SRC)/%.c + $(CC) $(C_FLAGS) -o $@ $< + +$(ASM_OBJ): $(OUT)/%.asm.o : $(SRC)/%.asm + $(AS) $(AS_FLAGS) -o $@ $< + +$(TGT): $(C_OBJ) $(ASM_OBJ) $(LD_SCRIPT) + $(CC) $(LD_FLAGS) -o $@ $^ + +$(DMP): $(TGT) + $(DAS) $(DUMP_FLAGS) $< > $@ + +$(ISO): $(TGT) $(GRUB_CFG) + mkdir -p $(OUT)/temp/boot/grub + cp $(GRUB_CFG) $(OUT)/temp/boot/grub/ + cp $(TGT) $(OUT)/temp/ + cp ./user/out/hello.elf $(OUT)/temp/ + grub-mkrescue -d /usr/lib/grub/i386-pc -o $(ISO) $(OUT)/temp + +.PHONY: mkdir +mkdir: + mkdir -p out + +.PHONY: clean +clean: + rm -rf $(OUT) + +.PHONY: all +all: mkdir $(TGT) $(DMP) $(ISO) + +.PHONY: debug +debug: + qemu-system-x86_64 -boot d -cdrom $(ISO) + + diff --git a/paper/curros.tex b/paper/curros.tex new file mode 100644 index 0000000..f5f3820 --- /dev/null +++ b/paper/curros.tex @@ -0,0 +1,14 @@ +\title{CurrOS: A Work In Progress} +\author{ + Mohammad Yazdani +} +\date{\today} + +\documentclass[12pt]{article} + +\begin{document} +\maketitle + +\end{document} + + diff --git a/src/boot.asm b/src/boot.asm new file mode 100644 index 0000000..efc7bcb --- /dev/null +++ b/src/boot.asm @@ -0,0 +1,269 @@ +%include "multiboot2.inc" + +KERNEL_PAGE_SIZE equ 0x1000 +KERNEL_IMAGE_VADDR equ 0xFFFFFFFF80000000 +KERNEL_PMAP_VADDR equ 0xFFFF800000000000 + +%define GET_PADDR(x) ((x) - KERNEL_IMAGE_VADDR) +%define GET_PML4(vaddr) (((vaddr) >> 39 ) & 0x1FF) +%define GET_PDPT(vaddr) (((vaddr) >> 30 ) & 0x1FF) + +global sys_entry +extern kmain + +section .multiboot_header + +ASM_MULTIBOOT_CHECK_SUM equ (0xFFFFFFFF - (MULTIBOOT2_HEADER_MAGIC + ASM_MULTIBOOT_HEADER_SIZE + MULTIBOOT_ARCHITECTURE_I386) + 1) + +section .multiboot_header +bits 32 +align KERNEL_PAGE_SIZE +;==================== +align MULTIBOOT_HEADER_ALIGN +start_hdr: + dd MULTIBOOT2_HEADER_MAGIC + dd MULTIBOOT_ARCHITECTURE_I386 + dd ASM_MULTIBOOT_HEADER_SIZE + dd ASM_MULTIBOOT_CHECK_SUM +;==================== +align MULTIBOOT_INFO_ALIGN + dw MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST + dw 0 ; flag + dd (8+4*3) ; size + dd MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME + dd MULTIBOOT_TAG_TYPE_MMAP + dd MULTIBOOT_TAG_TYPE_ACPI_NEW +;==================== +align MULTIBOOT_INFO_ALIGN + dw MULTIBOOT_HEADER_TAG_MODULE_ALIGN; type=6 + dw 0 ; flag + dd 8 ; size +;==================== +align MULTIBOOT_INFO_ALIGN + dw MULTIBOOT_HEADER_TAG_END + dw 0 ; flag + dd 8 ; size +;==================== +ASM_MULTIBOOT_HEADER_SIZE equ ($ - start_hdr) + +section .text +bits 32 +sys_entry: + cli + cld + cmp eax, MULTIBOOT2_BOOTLOADER_MAGIC + jne .end + + ; save multiboot info + mov dword [GET_PADDR(multiboot_info_ptr)], ebx + + ; setup stack + call check_long_mode ; check support for long mode + cmp eax, 1 + jne .end + + ; disable paging first + mov eax, cr0 ; Set the A-register to control register 0. + and eax, ~(1 << 31) & 0xFFFFFFFF ; Clear the PG-bit, which is bit 31, and hack to get rid of warning + mov cr0, eax ; Set control register 0 to the A-register. + + + ; point the first PML4 entry to the identity pdpt + mov eax, GET_PADDR(init_pml4) + mov dword [eax], GET_PADDR(init_pdpt_iden) + 11b ; write the lower bits, higher = 0 + + ; point the nth PML4 entry to the kernel pdpt + mov eax, GET_PADDR(init_pml4) + GET_PML4(KERNEL_IMAGE_VADDR) * 8 + mov dword [eax], GET_PADDR(init_pdpt_kern) + 11b + + ; point the nth PML4 entry to the kernel pmap pdpt + mov eax, GET_PADDR(init_pml4) + GET_PML4(KERNEL_PMAP_VADDR) * 8 + mov dword [eax], GET_PADDR(init_pdpt_pmap) + 11b + + ; identity map the first 4GB and to kernel pmap region + mov eax, GET_PADDR(init_pdpt_iden) + mov edx, GET_PADDR(init_pdpt_pmap) + mov ebx, 10000011b ; R/W + SU + 1G page + mov ecx, 4 ; loop 4 times +.l0: + mov dword [eax], ebx + mov dword [edx], ebx + add ebx, 1*1024*1024*1024 ; 1G + add eax, 8 + add edx, 8 + loop .l0 + + ; map the first 1 GB, which contains the kernel, to KERNEL_BASE_VADDR + mov eax, GET_PADDR(init_pdpt_kern) + ; extract the PML4 entry + add eax, GET_PDPT(KERNEL_IMAGE_VADDR) * 8 + mov ebx, 10000011b ; R/W + SU + 1G page + mov dword [eax], ebx + + + ; enable PAE + mov eax, cr4 ; Set the A-register to control register 4. + or eax, 1 << 5 ; Set the PAE-bit, which is the 6th bit (bit 5). + mov cr4, eax ; Set control register 4 to the A-register. + + ; enable long mode + mov ecx, 0xC0000080 ; Set the C-register to 0xC0000080, which is the EFER MSR. + rdmsr ; Read from the model-specific register. + or eax, 1 << 8 ; Set the LM-bit which is the 9th bit (bit 8). + wrmsr ; Write to the model-specific register. + + + ; let cr3 point at page table + mov eax, GET_PADDR(init_pml4) + mov cr3, eax + + ; enable paging, enter compatibility mode + mov eax, cr0 ; Set the A-register to control register 0. + or eax, 1 << 31 ; Set the PG-bit, which is bit 31. + mov cr0, eax ; Set control register 0 to the A-register. + + ; now we are in compat mode + + ; load the long mode GDT + lgdt [GET_PADDR(init_gdt.ptr)] + + ; switch to long mode + jmp init_gdt.code:GET_PADDR(sys_entry_64) +.end: + hlt + +check_long_mode: + push ebp + mov ebp,esp + pushfd + pop eax + mov ecx, eax + xor eax, 1 << 21 + push eax + popfd + pushfd + pop eax + push ecx + popfd + xor eax, ecx + jz .not_supported + mov eax, 0x80000000 ; Set the A-register to 0x80000000. + cpuid ; CPU identification. + cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. + jb .not_supported ; It is less, there is no long mode. + mov eax, 0x80000001 ; Set the A-register to 0x80000001. + cpuid ; CPU identification. + test edx, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register. + jz .not_supported ; They arent, there is no long mode. + mov eax,1 + jmp .end +.not_supported: + xor eax,eax +.end: + mov esp,ebp + pop ebp + ret + +section .data +bits 32 +multiboot_info_ptr: + dd 0 + +section .text +bits 64 +sys_entry_64: + ; note that we are in long mode but rip is still lower + ; switch to high address + mov rax, .high + jmp rax +.high: + + ; set ds segment + mov rax,init_gdt.data + mov ds, rax + + ; map GDT into virtual address space + mov qword [init_gdt.ptr + 2], init_gdt + lgdt [init_gdt.ptr] + + ;reload cs + push qword init_gdt.data ; ss + push qword init_stack ; rsp + pushfq + push qword init_gdt.code ; cs + push qword .reload ; rip + iretq +.reload: + mov rax, ss + mov ds, rax + mov es, rax + mov fs, rax + mov gs, rax + + ; unmap the first 4GB because we don't need them anymore + mov rax, GET_PADDR(init_pml4) + mov qword [rax], 0 ; + + ; flush TLB + mov rax, cr3 + mov cr3, rax + + ; kernel is now in only -2GB mode + xor rdi, rdi + mov edi, dword [multiboot_info_ptr] + mov rax, KERNEL_PMAP_VADDR + add rdi, rax + + call kmain +.end: + hlt + + +section .data +bits 64 +align KERNEL_PAGE_SIZE + times KERNEL_PAGE_SIZE db 0 +init_stack: + +init_pml4: +align KERNEL_PAGE_SIZE + times KERNEL_PAGE_SIZE db 0 + +init_pdpt_iden: +align KERNEL_PAGE_SIZE + times KERNEL_PAGE_SIZE db 0 + +init_pdpt_pmap: +align KERNEL_PAGE_SIZE + times KERNEL_PAGE_SIZE db 0 + +init_pdpt_kern: +align KERNEL_PAGE_SIZE + times KERNEL_PAGE_SIZE db 0 + +init_gdt: ; Global Descriptor Table (long mode). +.null: equ $ - init_gdt ; The null descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 0 ; Access. + db 0 ; Granularity. + db 0 ; Base (high). +.code: equ $ - init_gdt ; The code descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 10011010b ; Access (exec/read). + db 00100000b ; Granularity. + db 0 ; Base (high). +.data: equ $ - init_gdt ; The data descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 10010010b ; Access (read/write). + db 00100000b ; Granularity. + db 0 ; Base (high). +.ptr: + ; GDT PTR + dw $ - init_gdt - 1 ; Limit. + dq GET_PADDR(init_gdt) ; Base. diff --git a/src/clib.c b/src/clib.c new file mode 100644 index 0000000..0f04f5c --- /dev/null +++ b/src/clib.c @@ -0,0 +1,89 @@ +#include "clib.h" +#include "print.h" +#include "intr.h" + +void +mem_cpy(void *src, void *dst, uint64 size) +{ + char *cSrc = (char *) src; + char *cDst = (char *) dst; + while (size--) + { + *(cDst++) = *(cSrc++); + } +} + +void +mem_set(void *src, uint8 val, uint64 size) +{ + while (size--) + { + *(uint8 *) src = val; + src = (void *) ((uintptr) src + 1); + } +} + +void +mem_mv(void *src, void *dst, uint64 size) +{ + if (src >= dst) + { + mem_cpy(src, dst, size); + return; + } + src = (void *) ((uintptr) src + size - 1); + dst = (void *) ((uintptr) dst + size - 1); + while (size--) + { + *(char *) dst = *(char *) src; + dst = (void *) ((uintptr) dst - 1); + src = (void *) ((uintptr) src - 1); + } +} + +uint64 +str_len(char const *str) +{ + uint64 length = 0; + while (*str != 0) + { + str++; + length++; + } + return length; +} + +uint64 +str_cmp(char const *str1, char const *str2) +{ + uint64 length = str_len(str1); + if (length != str_len(str2)) + { + return 0; + } + while (length--) + { + if (*(str1 + length) != *(str2 + length)) + { + return 0; + } + } + return 1; +} + +void +poor_sleep(uint32 dat) +{ + for(uint32 i = 0; i < dat; i++) + { + } +} + +void kassert_ex(const char *expr_str, const char *file, int32 line, int32 expr) +{ + if (!expr) + { + kprintf("Assertion \"%s\" failed at %s:%d.\n", expr_str, file, line); + stop_cpu(); + } +} diff --git a/src/cpu.asm b/src/cpu.asm new file mode 100644 index 0000000..38cabe2 --- /dev/null +++ b/src/cpu.asm @@ -0,0 +1,201 @@ +section .text +bits 64 + +global out_8 +global out_16 +global out_32 +global in_8 +global in_16 +global in_32 +global read_msr +global read_cr8 +global write_cr8 +global write_msr +global cpuid +global flush_gdt +global flush_idt +global cmpxchg_32 +global xinc_32 +global cli +global sti +global read_cr3 +global write_cr3 +global flush_tlb +global hlt +global flush_tss + +flush_tss: +mov ax, di +ltr ax +ret + +hlt: +hlt +ret + +flush_tlb: +mov rax, cr3 +mov cr3, rax +ret + +read_cr3: +mov rax, cr3 +ret + +write_cr3: +mov cr3, rdi +ret + +read_cr8: +mov rax, cr8 +ret + +write_cr8: +mov cr8, rdi +ret + +cli: +cli +ret + +sti: +sti +ret + +out_32: +mov rdx,rdi +mov rax,rsi +out dx,eax +nop +nop +nop +ret + + +out_16: +mov rdx,rdi +mov rax,rsi +out dx,ax +nop +nop +nop +ret + + +out_8: +mov rdx,rdi +mov rax,rsi +out dx,al +nop +nop +nop +ret + + +in_8: +mov rdx,rdi +xor rax,rax +in al,dx +nop +nop +nop +ret + +in_16: +mov rdx,rdi +xor rax,rax +in ax,dx +nop +nop +nop +ret + + +in_32: +mov rdx,rdi +xor rax,rax +in eax,dx +nop +nop +nop +ret + +read_msr: +; preserve rdx +push rdx +mov ecx, dword [rdi] +rdmsr +mov dword [rdi], ecx +mov dword [rsi], edx +pop r11 +mov dword [r11], eax +ret + +write_msr: +mov ecx, dword [rdi] +mov eax, dword [rdx] +mov edx, dword [rsi] +wrmsr +ret + + +cpuid: +push rbp +mov rbp,rsp +; preserve rbx,rcx,rdx +push rbx +push rcx +push rdx +; cpuid parameters eax,ecx +mov eax, dword [rdi] +mov ecx, dword [rdx] +cpuid +; write results back to memory +mov dword [rdi], eax +mov dword [rsi], ebx +pop r11 +mov dword [r11], ecx +pop r11 +mov dword [r11], edx +pop rbx +mov rsp,rbp +pop rbp +ret + +flush_gdt: +push rbp +mov rbp, rsp +lgdt [rdi] + +;reload cs +push rdx ; data_slct : ss +push rbp ; rsp +pushfq +push rsi ; cs +mov rax, .reload +push rax ;rip +iretq +.reload: +mov es,rdx +mov fs,rdx +mov gs,rdx +mov ds,rdx +pop rbp +ret + +flush_idt: +lidt [rdi] +ret + +; ============================ +cmpxchg_32: +mov eax, esi; eax = test_node_compare +lock cmpxchg dword [rdi], edx ; edx = val, rdi = ptr to dst +ret + +; ============================ +xinc_32: +lock xadd dword [rdi], esi ; [rdi] = [rdi] + esi, esi = old [rdi] +xor rax, rax +mov eax, esi +ret \ No newline at end of file diff --git a/src/elf64.c b/src/elf64.c new file mode 100644 index 0000000..5ecd334 --- /dev/null +++ b/src/elf64.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include + +bool +elf_check_file(elf64_hdr *hdr) +{ + if (!hdr) + { return 0; } + if (hdr->e_ident[EI_MAG0] != ELFMAG0) + { return 0; } + if (hdr->e_ident[EI_MAG1] != ELFMAG1) + { return 0; } + if (hdr->e_ident[EI_MAG2] != ELFMAG2) + { return 0; } + if (hdr->e_ident[EI_MAG3] != ELFMAG3) + { return 0; } + + return 1; +} + +bool +elf_check_supported(elf64_hdr *hdr) +{ + if (!elf_check_file(hdr)) + { return 0; } + if (hdr->e_ident[EI_CLASS] != ELFCLASS64) + { return 0; } + if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) + { return 0; } // TODO : May not be true + //if (hdr->e_machine != EM_386) return 0; // TODO : CHECK + if (hdr->e_ident[EI_VERSION] != EV_CURRENT) + { return 0; } + if (hdr->e_type != ET_EXEC) + { return 0; } + + return 1; +} + +static int32 elf_load_seg(struct pcb *proc, void* file, elf64_phdr* hdr) +{ + void *seg_start = (void*)((uintptr)file + hdr->p_offset); + void *vaddr = (void*)hdr->p_vaddr; + usize file_size = hdr->p_filesz; + usize mem_size = hdr->p_memsz; + + int32 ret = ESUCCESS; + if (((uintptr) vaddr & 0xfff) != 0) + { + // alignment + ret = EINVARG; + } + + if (ret == ESUCCESS) + { + uint32 num_pages = ((uint32)mem_size + KERNEL_PAGE_SIZE - 1) / KERNEL_PAGE_SIZE; + + // allocate memory + for (uint32 i = 0; i < num_pages; i++) + { + // map pages + uintptr page = (uintptr) pmalloc(KERNEL_PAGE_SIZE); + if (page == (uintptr) NULL) + { + ret = ENOMEM; + } + + if (ret == ESUCCESS) + { + ret = map_vmem(proc->cr3, (uintptr) vaddr, page); + } + + if (ret == ESUCCESS) + { + usize cpy_size = file_size > KERNEL_PAGE_SIZE ? KERNEL_PAGE_SIZE : (file_size % KERNEL_PAGE_SIZE); + // copy the current segment + mem_cpy(seg_start, R_PADDR(page), cpy_size); + } + + if (ret != ESUCCESS) + { + break; + } + + vaddr = (void *) ((uintptr) vaddr + KERNEL_PAGE_SIZE); + seg_start = (void *) ((uintptr) seg_start + KERNEL_PAGE_SIZE); + } + } + + if (ret != ESUCCESS) + { + // cleanup + } + + return ret; +} + +int32 +elf_load_file(struct pcb *proc, void *file, void **entry) +{ + int32 ret = ESUCCESS; + elf64_hdr *hdr = (elf64_hdr *) file; + if (!elf_check_supported(hdr)) + { + ret = EINVARG; + } + + if (ret == ESUCCESS) + { + elf64_phdr *phdr_start = (elf64_phdr *) ((uintptr) file + hdr->e_phoff); + // load program segments + for (int i = 0; i < hdr->e_phnum; i++) + { + elf64_phdr *each_seg = (elf64_phdr *) ((uintptr) phdr_start + i * hdr->e_phentsize); + if (each_seg->p_type == PT_LOAD) + { + // load segment + ret = elf_load_seg(proc, file, each_seg); +#ifdef KDBG + if (ret == ESUCCESS) + { + kprintf("Loaded segment offset 0x%x to vaddr 0x%x, fs: %d, ms: %d\n", (uint64) each_seg->p_offset, + (uint64) each_seg->p_vaddr, (uint64) each_seg->p_filesz, (uint64) each_seg->p_memsz); + } +#endif + } + if (ret != ESUCCESS) + { + break; + } + } + } + + if (ret == ESUCCESS) + { + // write back entry + *entry = (void *) hdr->e_entry; + } + + return ret; +} diff --git a/src/intr.asm b/src/intr.asm new file mode 100644 index 0000000..337323c --- /dev/null +++ b/src/intr.asm @@ -0,0 +1,566 @@ +%include "asm.inc" + +extern intr_dispatcher +global intr_stub_array + +section .data +bits 64 +intr_stub_array: +dq intr_stub_0 +dq intr_stub_1 +dq intr_stub_2 +dq intr_stub_3 +dq intr_stub_4 +dq intr_stub_5 +dq intr_stub_6 +dq intr_stub_7 +dq intr_stub_8 +dq intr_stub_9 +dq intr_stub_10 +dq intr_stub_11 +dq intr_stub_12 +dq intr_stub_13 +dq intr_stub_14 +dq intr_stub_15 +dq intr_stub_16 +dq intr_stub_17 +dq intr_stub_18 +dq intr_stub_19 +dq intr_stub_20 +dq intr_stub_21 +dq intr_stub_22 +dq intr_stub_23 +dq intr_stub_24 +dq intr_stub_25 +dq intr_stub_26 +dq intr_stub_27 +dq intr_stub_28 +dq intr_stub_29 +dq intr_stub_30 +dq intr_stub_31 +dq intr_stub_32 +dq intr_stub_33 +dq intr_stub_34 +dq intr_stub_35 +dq intr_stub_36 +dq intr_stub_37 +dq intr_stub_38 +dq intr_stub_39 +dq intr_stub_40 +dq intr_stub_41 +dq intr_stub_42 +dq intr_stub_43 +dq intr_stub_44 +dq intr_stub_45 +dq intr_stub_46 +dq intr_stub_47 +dq intr_stub_48 +dq intr_stub_49 +dq intr_stub_50 +dq intr_stub_51 +dq intr_stub_52 +dq intr_stub_53 +dq intr_stub_54 +dq intr_stub_55 +dq intr_stub_56 +dq intr_stub_57 +dq intr_stub_58 +dq intr_stub_59 +dq intr_stub_60 +dq intr_stub_61 +dq intr_stub_62 +dq intr_stub_63 +dq intr_stub_64 +dq intr_stub_65 +dq intr_stub_66 +dq intr_stub_67 +dq intr_stub_68 +dq intr_stub_69 +dq intr_stub_70 +dq intr_stub_71 +dq intr_stub_72 +dq intr_stub_73 +dq intr_stub_74 +dq intr_stub_75 +dq intr_stub_76 +dq intr_stub_77 +dq intr_stub_78 +dq intr_stub_79 +dq intr_stub_80 +dq intr_stub_81 +dq intr_stub_82 +dq intr_stub_83 +dq intr_stub_84 +dq intr_stub_85 +dq intr_stub_86 +dq intr_stub_87 +dq intr_stub_88 +dq intr_stub_89 +dq intr_stub_90 +dq intr_stub_91 +dq intr_stub_92 +dq intr_stub_93 +dq intr_stub_94 +dq intr_stub_95 +dq intr_stub_96 +dq intr_stub_97 +dq intr_stub_98 +dq intr_stub_99 +dq intr_stub_100 +dq intr_stub_101 +dq intr_stub_102 +dq intr_stub_103 +dq intr_stub_104 +dq intr_stub_105 +dq intr_stub_106 +dq intr_stub_107 +dq intr_stub_108 +dq intr_stub_109 +dq intr_stub_110 +dq intr_stub_111 +dq intr_stub_112 +dq intr_stub_113 +dq intr_stub_114 +dq intr_stub_115 +dq intr_stub_116 +dq intr_stub_117 +dq intr_stub_118 +dq intr_stub_119 +dq intr_stub_120 +dq intr_stub_121 +dq intr_stub_122 +dq intr_stub_123 +dq intr_stub_124 +dq intr_stub_125 +dq intr_stub_126 +dq intr_stub_127 +dq intr_stub_128 +dq intr_stub_129 +dq intr_stub_130 +dq intr_stub_131 +dq intr_stub_132 +dq intr_stub_133 +dq intr_stub_134 +dq intr_stub_135 +dq intr_stub_136 +dq intr_stub_137 +dq intr_stub_138 +dq intr_stub_139 +dq intr_stub_140 +dq intr_stub_141 +dq intr_stub_142 +dq intr_stub_143 +dq intr_stub_144 +dq intr_stub_145 +dq intr_stub_146 +dq intr_stub_147 +dq intr_stub_148 +dq intr_stub_149 +dq intr_stub_150 +dq intr_stub_151 +dq intr_stub_152 +dq intr_stub_153 +dq intr_stub_154 +dq intr_stub_155 +dq intr_stub_156 +dq intr_stub_157 +dq intr_stub_158 +dq intr_stub_159 +dq intr_stub_160 +dq intr_stub_161 +dq intr_stub_162 +dq intr_stub_163 +dq intr_stub_164 +dq intr_stub_165 +dq intr_stub_166 +dq intr_stub_167 +dq intr_stub_168 +dq intr_stub_169 +dq intr_stub_170 +dq intr_stub_171 +dq intr_stub_172 +dq intr_stub_173 +dq intr_stub_174 +dq intr_stub_175 +dq intr_stub_176 +dq intr_stub_177 +dq intr_stub_178 +dq intr_stub_179 +dq intr_stub_180 +dq intr_stub_181 +dq intr_stub_182 +dq intr_stub_183 +dq intr_stub_184 +dq intr_stub_185 +dq intr_stub_186 +dq intr_stub_187 +dq intr_stub_188 +dq intr_stub_189 +dq intr_stub_190 +dq intr_stub_191 +dq intr_stub_192 +dq intr_stub_193 +dq intr_stub_194 +dq intr_stub_195 +dq intr_stub_196 +dq intr_stub_197 +dq intr_stub_198 +dq intr_stub_199 +dq intr_stub_200 +dq intr_stub_201 +dq intr_stub_202 +dq intr_stub_203 +dq intr_stub_204 +dq intr_stub_205 +dq intr_stub_206 +dq intr_stub_207 +dq intr_stub_208 +dq intr_stub_209 +dq intr_stub_210 +dq intr_stub_211 +dq intr_stub_212 +dq intr_stub_213 +dq intr_stub_214 +dq intr_stub_215 +dq intr_stub_216 +dq intr_stub_217 +dq intr_stub_218 +dq intr_stub_219 +dq intr_stub_220 +dq intr_stub_221 +dq intr_stub_222 +dq intr_stub_223 +dq intr_stub_224 +dq intr_stub_225 +dq intr_stub_226 +dq intr_stub_227 +dq intr_stub_228 +dq intr_stub_229 +dq intr_stub_230 +dq intr_stub_231 +dq intr_stub_232 +dq intr_stub_233 +dq intr_stub_234 +dq intr_stub_235 +dq intr_stub_236 +dq intr_stub_237 +dq intr_stub_238 +dq intr_stub_239 +dq intr_stub_240 +dq intr_stub_241 +dq intr_stub_242 +dq intr_stub_243 +dq intr_stub_244 +dq intr_stub_245 +dq intr_stub_246 +dq intr_stub_247 +dq intr_stub_248 +dq intr_stub_249 +dq intr_stub_250 +dq intr_stub_251 +dq intr_stub_252 +dq intr_stub_253 +dq intr_stub_254 +dq intr_stub_255 + +section .text +bits 64 + +%macro decl_err_intr_stub 1 +intr_stub_%1: +; save context +PUSH_REGS +push qword %1 +jmp intr_stub +%endmacro + +%macro decl_intr_stub 1 +intr_stub_%1: +; push dummy error code to have consistent frame +push qword 0 +PUSH_REGS +push qword %1 +jmp intr_stub +%endmacro + +intr_stub: +pop rdi +mov rsi, rsp +call intr_dispatcher +; restore registers +POP_REGS +; skip error code +add rsp, 8 +iretq + +intr_stub_start: +decl_intr_stub 0 +decl_intr_stub 1 +decl_intr_stub 2 +decl_intr_stub 3 +decl_intr_stub 4 +decl_intr_stub 5 +decl_intr_stub 6 +decl_intr_stub 7 +decl_err_intr_stub 8 +decl_intr_stub 9 +decl_err_intr_stub 10 +decl_err_intr_stub 11 +decl_err_intr_stub 12 +decl_err_intr_stub 13 +decl_err_intr_stub 14 +decl_intr_stub 15 +decl_intr_stub 16 +decl_err_intr_stub 17 +decl_intr_stub 18 +decl_intr_stub 19 +decl_intr_stub 20 +decl_intr_stub 21 +decl_intr_stub 22 +decl_intr_stub 23 +decl_intr_stub 24 +decl_intr_stub 25 +decl_intr_stub 26 +decl_intr_stub 27 +decl_intr_stub 28 +decl_intr_stub 29 +decl_intr_stub 30 +decl_intr_stub 31 +decl_intr_stub 32 +decl_intr_stub 33 +decl_intr_stub 34 +decl_intr_stub 35 +decl_intr_stub 36 +decl_intr_stub 37 +decl_intr_stub 38 +decl_intr_stub 39 +decl_intr_stub 40 +decl_intr_stub 41 +decl_intr_stub 42 +decl_intr_stub 43 +decl_intr_stub 44 +decl_intr_stub 45 +decl_intr_stub 46 +decl_intr_stub 47 +decl_intr_stub 48 +decl_intr_stub 49 + +intr_stub_50: +; push dummy error code to have consistent frame +push qword 0 +PUSH_REGS +mov rdi, 50 +mov rsi, rsp +call intr_dispatcher +xchg bx,bx + ; now rax hows the next thread's rsp0 +mov rsp, rax ; switch stack +POP_REGS ; restore registers +add rsp, 8 ; skip error code +iretq + +decl_intr_stub 51 +decl_intr_stub 52 +decl_intr_stub 53 +decl_intr_stub 54 +decl_intr_stub 55 +decl_intr_stub 56 +decl_intr_stub 57 +decl_intr_stub 58 +decl_intr_stub 59 +decl_intr_stub 60 +decl_intr_stub 61 +decl_intr_stub 62 +decl_intr_stub 63 +decl_intr_stub 64 +decl_intr_stub 65 +decl_intr_stub 66 +decl_intr_stub 67 +decl_intr_stub 68 +decl_intr_stub 69 +decl_intr_stub 70 +decl_intr_stub 71 +decl_intr_stub 72 +decl_intr_stub 73 +decl_intr_stub 74 +decl_intr_stub 75 +decl_intr_stub 76 +decl_intr_stub 77 +decl_intr_stub 78 +decl_intr_stub 79 +decl_intr_stub 80 +decl_intr_stub 81 +decl_intr_stub 82 +decl_intr_stub 83 +decl_intr_stub 84 +decl_intr_stub 85 +decl_intr_stub 86 +decl_intr_stub 87 +decl_intr_stub 88 +decl_intr_stub 89 +decl_intr_stub 90 +decl_intr_stub 91 +decl_intr_stub 92 +decl_intr_stub 93 +decl_intr_stub 94 +decl_intr_stub 95 +decl_intr_stub 96 +decl_intr_stub 97 +decl_intr_stub 98 +decl_intr_stub 99 +decl_intr_stub 100 +decl_intr_stub 101 +decl_intr_stub 102 +decl_intr_stub 103 +decl_intr_stub 104 +decl_intr_stub 105 +decl_intr_stub 106 +decl_intr_stub 107 +decl_intr_stub 108 +decl_intr_stub 109 +decl_intr_stub 110 +decl_intr_stub 111 +decl_intr_stub 112 +decl_intr_stub 113 +decl_intr_stub 114 +decl_intr_stub 115 +decl_intr_stub 116 +decl_intr_stub 117 +decl_intr_stub 118 +decl_intr_stub 119 +decl_intr_stub 120 +decl_intr_stub 121 +decl_intr_stub 122 +decl_intr_stub 123 +decl_intr_stub 124 +decl_intr_stub 125 +decl_intr_stub 126 +decl_intr_stub 127 +decl_intr_stub 128 +decl_intr_stub 129 +decl_intr_stub 130 +decl_intr_stub 131 +decl_intr_stub 132 +decl_intr_stub 133 +decl_intr_stub 134 +decl_intr_stub 135 +decl_intr_stub 136 +decl_intr_stub 137 +decl_intr_stub 138 +decl_intr_stub 139 +decl_intr_stub 140 +decl_intr_stub 141 +decl_intr_stub 142 +decl_intr_stub 143 +decl_intr_stub 144 +decl_intr_stub 145 +decl_intr_stub 146 +decl_intr_stub 147 +decl_intr_stub 148 +decl_intr_stub 149 +decl_intr_stub 150 +decl_intr_stub 151 +decl_intr_stub 152 +decl_intr_stub 153 +decl_intr_stub 154 +decl_intr_stub 155 +decl_intr_stub 156 +decl_intr_stub 157 +decl_intr_stub 158 +decl_intr_stub 159 +decl_intr_stub 160 +decl_intr_stub 161 +decl_intr_stub 162 +decl_intr_stub 163 +decl_intr_stub 164 +decl_intr_stub 165 +decl_intr_stub 166 +decl_intr_stub 167 +decl_intr_stub 168 +decl_intr_stub 169 +decl_intr_stub 170 +decl_intr_stub 171 +decl_intr_stub 172 +decl_intr_stub 173 +decl_intr_stub 174 +decl_intr_stub 175 +decl_intr_stub 176 +decl_intr_stub 177 +decl_intr_stub 178 +decl_intr_stub 179 +decl_intr_stub 180 +decl_intr_stub 181 +decl_intr_stub 182 +decl_intr_stub 183 +decl_intr_stub 184 +decl_intr_stub 185 +decl_intr_stub 186 +decl_intr_stub 187 +decl_intr_stub 188 +decl_intr_stub 189 +decl_intr_stub 190 +decl_intr_stub 191 +decl_intr_stub 192 +decl_intr_stub 193 +decl_intr_stub 194 +decl_intr_stub 195 +decl_intr_stub 196 +decl_intr_stub 197 +decl_intr_stub 198 +decl_intr_stub 199 +decl_intr_stub 200 +decl_intr_stub 201 +decl_intr_stub 202 +decl_intr_stub 203 +decl_intr_stub 204 +decl_intr_stub 205 +decl_intr_stub 206 +decl_intr_stub 207 +decl_intr_stub 208 +decl_intr_stub 209 +decl_intr_stub 210 +decl_intr_stub 211 +decl_intr_stub 212 +decl_intr_stub 213 +decl_intr_stub 214 +decl_intr_stub 215 +decl_intr_stub 216 +decl_intr_stub 217 +decl_intr_stub 218 +decl_intr_stub 219 +decl_intr_stub 220 +decl_intr_stub 221 +decl_intr_stub 222 +decl_intr_stub 223 +decl_intr_stub 224 +decl_intr_stub 225 +decl_intr_stub 226 +decl_intr_stub 227 +decl_intr_stub 228 +decl_intr_stub 229 +decl_intr_stub 230 +decl_intr_stub 231 +decl_intr_stub 232 +decl_intr_stub 233 +decl_intr_stub 234 +decl_intr_stub 235 +decl_intr_stub 236 +decl_intr_stub 237 +decl_intr_stub 238 +decl_intr_stub 239 +decl_intr_stub 240 +decl_intr_stub 241 +decl_intr_stub 242 +decl_intr_stub 243 +decl_intr_stub 244 +decl_intr_stub 245 +decl_intr_stub 246 +decl_intr_stub 247 +decl_intr_stub 248 +decl_intr_stub 249 +decl_intr_stub 250 +decl_intr_stub 251 +decl_intr_stub 252 +decl_intr_stub 253 +decl_intr_stub 254 +decl_intr_stub 255 \ No newline at end of file diff --git a/src/intr.c b/src/intr.c new file mode 100644 index 0000000..64024d6 --- /dev/null +++ b/src/intr.c @@ -0,0 +1,353 @@ +#include "intr.h" +#include "cpu.h" +#include "clib.h" +#include "memory_layout.h" +#include "print.h" +#include "paging.h" +#include "thread.h" +#include "error.h" + +/** + * IDT Defns + */ +#define GATE_DPL_0 (0ull << 13) +#define GATE_DPL_1 (1ull << 13) +#define GATE_DPL_2 (2ull << 13) +#define GATE_DPL_3 (3ull << 13) +#define GATE_PRESENT (1ull << 15) +#define GATE_TYPE_CALL (12ull << 8) +#define GATE_TYPE_INTERRUPT (14ull << 8) +#define GATE_TYPE_TRAP (15ull << 8) + +/** + GDT Defns +**/ +#define SEG_GRANULARITY (1ull << 55) +#define SEG_LONG (1ull << 53) +#define SEG_DPL_0 (0ull << 45) +#define SEG_DPL_1 (1ull << 45) +#define SEG_DPL_2 (2ull << 45) +#define SEG_DPL_3 (3ull << 45) +#define SEG_PRESENT (1ull << 47) +#define SEG_CODE_DATA (1ull << 44) +#define SEG_TYPE_DATA_RW (2ull << 40) +#define SEG_TYPE_DATA_R (0ull << 40) +#define SEG_TYPE_CODE_X (8ull << 40) +#define SEG_TYPE_CODE_XR (10ull << 40) +#define SEG_TYPE_CODE_XC (12ull << 40) +#define SEG_TYPE_CODE_XRC (14ull << 40) +#define SEG_AVAILABLE (1ull << 52) +#define SEG_32_BITS (1ull << 54) + +// APIC Interrupt vectors +#define EDX_MASK_APIC (1 << 9) +#define MSR_APIC_BASE (0x1b) +#define MSR_MASK_APIC (1 << 11) + +// PIC defs +#define PIC1_COMMAND (0x20) +#define PIC1_DATA (PIC1_COMMAND+1) +#define PIC2_COMMAND (0xa0) +#define PIC2_DATA (PIC2_COMMAND+1) + +#define APIC_REG_SPURIOUS (0xF0) +#define APIC_REG_TIMER (0x320) +#define APIC_REG_EOI (0xB0) +#define APIC_REG_TIMER_DIV (0x3E0) +#define APIC_REG_TIMER_INIT_CNT (0x380) +#define APIC_REG_TIMER_CUR_CNT (0x390) +#define APIC_REG_ID (0x20) +#define APIC_REG_LINT0 (0x350) +#define APIC_REG_LINT1 (0x360) +#define APIC_REG_ICR_L (0x300) +#define APIC_REG_ICR_H (0x310) + +#define IPI_DLM_FIXED (0 << 8) +#define IPI_DLM_START_UP (6 << 8) + +#define IPI_DSM_PHYS (0 << 11) +#define IPI_DSM_LOGI (1 << 11) +#define IPI_STATUS_MASK (1 << 12) + +#define IPI_LVL_ASS (1 << 15) +#define IPI_LVL_DEA (0 << 15) + +// base on 1Ghz CPU +#define APIC_TIMER_INIT_CNT (10000000) +#define REG_SPURIOUS_APIC_ENABLE (1 << 8) + +extern void *intr_stub_array[NUM_IDT_DESC]; + +static void *apic_base; +static struct tss utss; +static struct gdtr gdtptr; +static struct idtr idtptr; +static struct gdt_desc gdt[NUM_GDT_DESC]; +static struct idt_desc idt[NUM_IDT_DESC]; +static intr_handler intr_disp_tbl[NUM_IDT_DESC]; + +static void write_idt_desc(void *gate, uint64 offset, uint32 selector, uint32 attr) +{ + ((uint8 *) gate)[0] = (uint8) (offset & 0xFF); + ((uint8 *) gate)[1] = (uint8) ((offset >> 8) & 0xFF); + ((uint8 *) gate)[2] = (uint8) (selector & 0xFF); + ((uint8 *) gate)[3] = (uint8) ((selector >> 8) & 0xFF); + ((uint8 *) gate)[4] = (uint8) (attr & 0xFF); + ((uint8 *) gate)[5] = (uint8) ((attr >> 8) & 0xFF); + ((uint8 *) gate)[6] = (uint8) ((offset >> 16) & 0xFF); + ((uint8 *) gate)[7] = (uint8) ((offset >> 24) & 0xFF); + ((uint8 *) gate)[8] = (uint8) ((offset >> 32) & 0xFF); + ((uint8 *) gate)[9] = (uint8) ((offset >> 40) & 0xFF); + ((uint8 *) gate)[10] = (uint8) ((offset >> 48) & 0xFF); + ((uint8 *) gate)[11] = (uint8) ((offset >> 56) & 0xFF); + ((uint8 *) gate)[12] = 0; + ((uint8 *) gate)[13] = 0; + ((uint8 *) gate)[14] = 0; + ((uint8 *) gate)[15] = 0; +} + +static void write_gdt_desc(void *gdt, uint32 base, uint32 limit, uint64 attr) +{ + uint64 seg_desc = (((uint64) base & 0xFFFF) << 16) | ((((uint64) base >> 16) & 0xFF) << 32) | + ((((uint64) base >> 24) & 0xFF) << 56) | ((uint64) limit & 0xFFFF) | + ((((uint64) limit >> 16) & 0xF) << 48) | attr; + ((uint8 *) gdt)[0] = (uint8) (seg_desc & 0xFF); + ((uint8 *) gdt)[1] = (uint8) ((seg_desc >> 8) & 0xFF); + ((uint8 *) gdt)[2] = (uint8) ((seg_desc >> 16) & 0xFF); + ((uint8 *) gdt)[3] = (uint8) ((seg_desc >> 24) & 0xFF); + ((uint8 *) gdt)[4] = (uint8) ((seg_desc >> 32) & 0xFF); + ((uint8 *) gdt)[5] = (uint8) ((seg_desc >> 40) & 0xFF); + ((uint8 *) gdt)[6] = (uint8) ((seg_desc >> 48) & 0xFF); + ((uint8 *) gdt)[7] = (uint8) ((seg_desc >> 56) & 0xFF); +} + +static void disable_pic() +{ + out_8(PIC1_COMMAND, 0x11); // starts the initialization sequence (in cascade mode) + out_8(PIC2_COMMAND, 0x11); + + out_8(PIC1_DATA, 32); // ICW2: Master PIC vector offset + out_8(PIC2_DATA, 40); // ICW2: Slave PIC vector offset + + out_8(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) + out_8(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) + + out_8(PIC1_DATA, 0x1); // set their modes to 8086 + out_8(PIC2_DATA, 0x1); + + out_8(PIC1_DATA, 0xff); + out_8(PIC2_DATA, 0xff); +} + +static void write_apic_reg(uint32 reg, uint32 val) +{ + *(uint32 *) ((uintptr) apic_base + reg) = val; +} + +static uint32 read_apic_reg(uint32 reg) +{ + return *(uint32 *) ((uintptr) apic_base + reg); +} + +static int32 init_apic() +{ + uint32 eax = 1, ebx, ecx, edx; + cpuid(&eax, &ebx, &ecx, &edx); + + if (!(edx & EDX_MASK_APIC)) + { + return ENOSUPPORT; + } + + disable_pic(); + + // hardware enable APIC + ecx = MSR_APIC_BASE; + read_msr(&ecx, &edx, &eax); + apic_base = R_PADDR(eax & 0xFFFF0000); + eax |= MSR_MASK_APIC; + write_msr(&ecx, &edx, &eax); +#ifdef KDBG + kprintf("APIC base address: 0x%x\n", (uint64) apic_base); +#endif + + // map spurious interrupt and software enable APIC + uint32 reg = read_apic_reg(APIC_REG_SPURIOUS); + write_apic_reg(APIC_REG_SPURIOUS, reg | REG_SPURIOUS_APIC_ENABLE); + + // Mask LINT0 LINT1 + reg = read_apic_reg(APIC_REG_LINT0); + write_apic_reg(APIC_REG_LINT0, reg | (1 << 16)); + reg = read_apic_reg(APIC_REG_LINT1); + write_apic_reg(APIC_REG_LINT1, reg | (1 << 16)); + + // configure APIC timer + write_apic_reg(APIC_REG_TIMER_DIV, 0xB); // 0x1011, divide by 1 + write_apic_reg(APIC_REG_TIMER_INIT_CNT, APIC_TIMER_INIT_CNT); + write_apic_reg(APIC_REG_TIMER_CUR_CNT, APIC_REG_TIMER_INIT_CNT); + write_apic_reg(APIC_REG_TIMER, 0xFF020000 | INTR_VEC_TIMER); // periodic + + // unblock all interrupts + write_cr8(0); + + return ESUCCESS; +} + +static void *timer_intr_handler(struct intr_frame *frame) +{ + struct tcb *cur = get_cur_thread(); + + if (cur != NULL) + { + // hack to get away first scheduling 1st time + // save the stack pointer of the current thread + cur->rsp0 = (uint64) frame; + } + + // run the scheduler + thread_schedule(); + + cur = get_cur_thread(); + // now cur is the next thread to run + // swap address space + // which shouldn't page fault as we are in kernel + write_cr3(cur->proc->cr3); + flush_tlb(); + + // write tss segment + utss.rsp0 = (uint64)((uintptr)cur->kstack + cur->kstack_sz); + + // now we are in the target address space + // the only thing to do is to switch stack, which will be done in ASM handler + return (void *) cur->rsp0; +} + +extern char init_stack[]; + +int32 intr_init() +{ + int32 ret; + // init GDT + write_gdt_desc(&gdt[GDT_NULL], 0, 0, 0); // empty desc + write_gdt_desc(&gdt[GDT_K_CODE], 0, 0xFFFFFFFF, + SEG_LONG | SEG_DPL_0 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA | + SEG_TYPE_CODE_XR); // kernel code + write_gdt_desc(&gdt[GDT_K_DATA], 0, 0xFFFFFFFF, + SEG_LONG | SEG_DPL_0 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA | + SEG_TYPE_DATA_RW); // kernel data + write_gdt_desc(&gdt[GDT_U_CODE], 0, 0xFFFFFFFF, + SEG_LONG | SEG_DPL_3 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA | + SEG_TYPE_CODE_XR); // user code + write_gdt_desc(&gdt[GDT_U_DATA], 0, 0xFFFFFFFF, + SEG_LONG | SEG_DPL_3 | SEG_PRESENT | SEG_GRANULARITY | SEG_CODE_DATA | + SEG_TYPE_DATA_RW); // user data + // write tss + write_gdt_desc(&gdt[GDT_U_TSS], (uint32) &utss, sizeof(struct tss), + SEG_PRESENT | SEG_DPL_3 | ((uint64)9 << 40)); + // write high bits + *(uint64 *) &gdt[GDT_U_TSS + 1] = ((uintptr)&utss) >> 32; + + // init tss + mem_set(&utss, 0, sizeof(struct tss)); + + gdtptr.size = GDT_SIZE - 1; + gdtptr.offset = (uintptr) gdt; + flush_gdt(&gdtptr, SEL(GDT_K_CODE, 0, 0), SEL(GDT_K_DATA, 0, 0)); + flush_tss(SEL(GDT_U_TSS, 0, 3)); + + idtptr.offset = (uintptr) idt; + idtptr.size = IDT_SIZE - 1; + for (int i = 0; i < NUM_IDT_DESC; i++) + { + write_idt_desc(&idt[i], (uintptr) intr_stub_array[i], + SEL(GDT_K_CODE, 0, 0), + GATE_DPL_3 | GATE_TYPE_INTERRUPT | GATE_PRESENT); + } + mem_set(intr_disp_tbl, 0, sizeof(intr_disp_tbl)); + flush_idt(&idtptr); + ret = init_apic(); + + if (ret == ESUCCESS) + { + // enable interrupt flag but mask all external interrupts + WRITE_IRQ(0xf); + sti(); + + set_intr_handler(INTR_VEC_TIMER, timer_intr_handler); + } + + return ret; +} + +void set_intr_handler(uint32 vec, intr_handler handler) +{ + intr_disp_tbl[vec] = handler; +} + +void stop_cpu() +{ + // stop interrupt and halt + cli(); + hlt(); +} + +// this returns info returned by intr_disp_tbl +// in case the assembly stub needs them (e.g. ctx swap) +void *intr_dispatcher(uint32 vec, struct intr_frame *frame) +{ + void *ret = NULL; + + if (intr_disp_tbl[vec] != NULL) + { + ret = intr_disp_tbl[vec](frame); + } + else + { + if (vec <= INTR_LIMIT_INTEL) + { + kprintf("[PANIC] Exception %d has no handler. RIP: 0x%x ERR:0x%x\n", (uint64) vec, (uint64) frame->rip, + (uint64) frame->error_code); + stop_cpu(); + } + else + { + kprintf("[WARN] Interrupt %d has no handler. RIP: 0x%x\n", (uint64) vec, (uint64) frame->rip); + } + } + + if (vec >= INTR_VEC_TIMER && vec != INTR_VEC_SPURIOUS) + { + // if it's delivered by local APIC, signal EOI + write_apic_reg(APIC_REG_EOI, 0); + } + + return ret; +} + +static uint32 get_core() +{ + return read_apic_reg(APIC_REG_ID) >> 24; +} + + +void send_ipi(uint32 vec) +{ + // we decide to not support multicore for now. So hardcode to the current core + // used to emulate IO stuff + uint64 reg = ((uint64) get_core() << 56) | IPI_DLM_FIXED | IPI_DSM_PHYS | IPI_LVL_ASS | (vec & 0xff); + + // block all interrupts because there is no context + uint64 irq = READ_IRQ(); + WRITE_IRQ(0xf); + + write_apic_reg(APIC_REG_ICR_L, (uint32)reg); + write_apic_reg(APIC_REG_ICR_H, (uint32)(reg >> 32)); + // block until successfully delivered + while (reg & IPI_STATUS_MASK) + { + reg = read_apic_reg(APIC_REG_ICR_L); + } + + WRITE_IRQ(irq); +} + diff --git a/src/kmain.c b/src/kmain.c new file mode 100644 index 0000000..85f3db6 --- /dev/null +++ b/src/kmain.c @@ -0,0 +1,138 @@ +#include "multiboot2.h" +#include "intr.h" +#include "print.h" +#include "clib.h" +#include "cpu.h" +#include "thread.h" +#include "proc.h" +#include "pmm.h" +#include "syscall.h" +#include "error.h" +#include "vmm.h" +#include "elf64.h" + +void kthread1(void *arg); + +void kthread2(void *arg); + +void kproc(void *arg); + +void *_module; + +void kmain(mbentry *mb) +{ + char *ld_name = 0x0; + mod_tag *module = 0x0; + uint64 mem_low; + uint64 mem_high; + int32 status; + print_init(); + + parse_mb2(mb, (void **) &module, &ld_name, &mem_low, &mem_high); + + kprintf("Bootloader: %s\n", ld_name); + kprintf("User executable loaded at 0x%x size %d.\n", (uint64) R_PADDR(module->mod_start), (uint64) (module->mod_end - module->mod_start)); + + kprintf("Initializing interrupt...\n"); + status = intr_init(); + kprintf("Initializing system call...\n"); + syscall_init(); + KASSERT(status == ESUCCESS); + + _module = R_PADDR(module->mod_start); + + kprintf("Initializing PMM...\n"); + pmm_init(mem_low, mem_high); + kprintf("Initializing VMM...\n"); + init_vm(); + + kprintf("Initializing threads...\n"); + thread_init(); + kprintf("Initializing processes...\n"); + status = proc_init((void *) kproc); + KASSERT(status == ESUCCESS); + + // unmask all interrupts + WRITE_IRQ(0x0); + + send_ipi(INTR_VEC_TIMER); + KASSERT(FALSE); +} + +#define THREAD_DELAY (5000000*4) + +static uint32 kt1id; +static uint32 kt2id; + +void kthread1(void *arg) +{ + UNREFERENCED(arg); + uint64 i = 0; + while (i != 0xFFFFFFFFFF) + { + poor_sleep(THREAD_DELAY); + kprintf("[KThread1] %d\n", i); + + if (i == 10) + { + kprintf("[KThread1] Blocking KThread 2... \n", i); + thread_block(kt2id); + } + + if (i == 20) + { + kprintf("[KThread1] Resuming KThread 2... \n", i); + thread_resume(kt2id); + } + + i++; + } +} + +void kthread2(void *arg) +{ + UNREFERENCED(arg); + bool thread1exited = FALSE; + uint64 i = 0; + int32 ex; + while (i != 0xFFFFFFFFFF) + { + poor_sleep(THREAD_DELAY); + + kprintf("[KThread2] %d\n", i); + + if (i == 40) + { + kprintf("[KThread2] Stopping KThread1 with exit code 0xdeadbeef\n", i); + thread_stop(kt1id, 0xDEADBEEF); + } + + if (!thread1exited && thread_get_exit_code(kt1id, &ex) == ESUCCESS) + { + kprintf("[KThread2] KThread1 exited with 0x%x\n", (uint64) ex); + thread1exited = TRUE; + } + i++; + } +} + +void kproc(void *arg) +{ + // the idle thread + UNREFERENCED(arg); + int32 status; + status = thread_create(get_cur_thread()->proc, (void *) kthread1, NULL, &kt1id); + kprintf("[KIdle] Created KThread1... 0x%x\n", (uint64) status); + status = thread_create(get_cur_thread()->proc, (void *) kthread2, NULL, &kt2id); + kprintf("[KIdle] Created KThread2... 0x%x\n", (uint64) status); + uint32 id; + status = proc_create(_module, &id); + kprintf("[KIdle] Created process from module... 0x%x\n", (uint64) status); + while (1) + { +#ifdef KDBG + kprintf("[KIdle] Yielding...\n"); +#endif + thread_yield(); + } +} diff --git a/src/llist.c b/src/llist.c new file mode 100644 index 0000000..7c57dc1 --- /dev/null +++ b/src/llist.c @@ -0,0 +1,250 @@ +#include "llist.h" + +static void +llist_node_init(struct llist_node *node) +{ + node->next = NULL; + node->prev = NULL; +} + + +void +lb_llist_init(struct llist *list) +{ + list->head = NULL; + list->tail = NULL; + list->size = 0; +} + + +uint32 +lb_llist_size(struct llist *list) +{ + return list->size; +} + + +void +lb_llist_push_front(struct llist *list, struct llist_node *node) +{ + llist_node_init(node); + lb_llist_insert_by_ref(list, NULL, node); +} + + +void +lb_llist_push_back(struct llist *list, struct llist_node *node) +{ + llist_node_init(node); + lb_llist_insert_by_ref(list, list->tail, node); +} + + +struct llist_node * +lb_llist_pop_front(struct llist *list) +{ + struct llist_node *ret; + + ret = list->head; + lb_llist_remove_by_ref(list, list->head); + + return ret; +} + + +struct llist_node * +lb_llist_pop_back(struct llist *list) +{ + struct llist_node *ret; + + ret = list->tail; + lb_llist_remove_by_ref(list, list->tail); + + return ret; +} + + +void +lb_llist_insert_by_ref(struct llist *list, struct llist_node *cur_node, struct llist_node *new_node) +{ + struct llist_node *left_node; + struct llist_node *right_node; + + if (list == NULL || new_node == NULL) + { + return; + } + + llist_node_init(new_node); + + /* + * adjust the current node + */ + if (cur_node == NULL) + { + new_node->next = list->head; + new_node->prev = NULL; + } + else + { + new_node->prev = cur_node; + new_node->next = cur_node->next; + } + + /* + * assign left and treenode node + */ + if (cur_node == NULL) + { + left_node = NULL; + right_node = list->head == NULL ? NULL : list->head; + } + else + { + left_node = cur_node; + right_node = cur_node->next; + } + + /* + * adjust left and treenode node accordingly + */ + if (left_node != NULL) + { + left_node->next = new_node; + } + else + { + list->head = new_node; + } + + if (right_node != NULL) + { + right_node->prev = new_node; + } + else + { + list->tail = new_node; + } + + list->size++; +} + + +void +lb_llist_insert_by_idx(struct llist *list, uint32 index, struct llist_node *node) +{ + struct llist_node *prev_node; + + prev_node = lb_llist_get(list, index - 1); + llist_node_init(node); + + if (prev_node == NULL) + { + if (index == 0) + { + lb_llist_insert_by_ref(list, NULL, node); + } + } + else + { + lb_llist_insert_by_ref(list, prev_node, node); + } +} + + +struct llist_node * +lb_llist_remove_by_idx(struct llist *list, uint32 index) +{ + struct llist_node *cur_node; + + cur_node = lb_llist_get(list, index); + + if (cur_node == NULL) + { + return NULL; + } + + return lb_llist_remove_by_ref(list, cur_node); +} + + +/** + * returns the next node + */ +struct llist_node * +lb_llist_remove_by_ref(struct llist *list, struct llist_node *node) +{ + struct llist_node *ret; + + /* + * Adjust the left and treenode node + */ + if (node->prev != NULL) + { + node->prev->next = node->next; + } + else + { + list->head = node->next; + } + + if (node->next != NULL) + { + node->next->prev = node->prev; + } + else + { + list->tail = node->prev; + } + + ret = node->next; + + llist_node_init(node); + + list->size--; + + return ret; +} + + +struct llist_node * +lb_llist_get(struct llist *list, uint32 index) +{ + if (list->head == NULL) + { + return NULL; + } + struct llist_node *cur_node = list->head; + while (index-- && (cur_node = cur_node->next) != NULL) + {} + return cur_node; +} + + +struct llist_node * +lb_llist_next(struct llist_node *node) +{ + return node->next; +} + + +struct llist_node * +lb_llist_prev(struct llist_node *node) +{ + return node->prev; +} + + +struct llist_node * +lb_llist_first(struct llist *list) +{ + return list->head; +} + + +struct llist_node * +lb_llist_last(struct llist *list) +{ + return list->tail; +} + diff --git a/src/multiboot2.c b/src/multiboot2.c new file mode 100644 index 0000000..7d3cf28 --- /dev/null +++ b/src/multiboot2.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include + + +// Putting multiboot device info here +static char *loader_name = 0x0; +static void *file_mod = 0x0; + +// OH BOY DON"T I FUCKING LOVE THIS SHIT +static uint64 _low = 0; +static uint64 _high = 0; + + +void +process_mmap(struct multiboot_tag_mmap *mmap) +{ + struct multiboot_mmap_entry entry; + + uint16 num_entries = (mmap->size - sizeof(*mmap)) / mmap->entry_size; + for (uint16 i = 0; i < num_entries; i++) + { + entry = mmap->entries[i]; + if ((entry.type == 1) && (entry.len > _high - _low)) + { +#ifdef KDBG + kprintf("Update low: 0x%x high: 0x%x\n", (uint64) entry.addr, (uint64) (entry.addr + entry.len)); +#endif + _high = entry.addr + entry.len; + _low = entry.addr; + } + } +} + + +void +process_tag(htag *tag) +{ + struct multiboot_tag_mmap *mmap_tag; + struct multiboot_tag_string *strtag; + struct multiboot_tag_module *modtag; + while(1) + { + if(tag->type == MULTIBOOT_HEADER_TAG_END) + { + break; + } + + switch (tag->type) + { + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + strtag = (struct multiboot_tag_string *) tag; + loader_name = &strtag->string[0]; + tag = (void*)ALIGN(uintptr, (uintptr)tag + tag->size, MULTIBOOT_TAG_ALIGN); + break; + case MULTIBOOT_TAG_TYPE_MMAP: + mmap_tag = (struct multiboot_tag_mmap *) tag; + process_mmap(mmap_tag); + tag = (void*)ALIGN(uintptr, (uintptr)tag + tag->size, MULTIBOOT_TAG_ALIGN); + break; + case MULTIBOOT_TAG_TYPE_MODULE: + modtag = (struct multiboot_tag_module *)tag; + file_mod = modtag; + tag = (void*)ALIGN(uintptr, (uintptr)tag + tag->size, MULTIBOOT_TAG_ALIGN); + break; + default: + tag = (void*)ALIGN(uintptr, (uintptr)tag + tag->size, MULTIBOOT_TAG_ALIGN); + break; + } + } +} + +void parse_mb2(mbentry *mb, void **module, char **ld_name, uint64 *low, uint64 *high) +{ + uint64 tag_base0 = (uint64) mb; + uint64 next_entry; + uint64 last_entry; + + next_entry = tag_base0 + 8; + last_entry = tag_base0 + mb->total_size - 8; + + htag *last_tag = (htag *) last_entry; + if (!(last_tag->type == 0 && last_tag->size == 8)) + { return; } + + htag *tag = (htag *) next_entry; + process_tag(tag); + + *module = file_mod; + *ld_name = loader_name; + *low = _low; + *high = _high; +} + diff --git a/src/paging.c b/src/paging.c new file mode 100644 index 0000000..f0f2cad --- /dev/null +++ b/src/paging.c @@ -0,0 +1,159 @@ +#include +#include +#include "paging.h" +#include "pmm.h" +#include "cpu.h" +#include "clib.h" +#include "error.h" + +#define ENTRY_SIZE (8) + +#define NEXT_LEVEL_MASK bit_field_mask(12,51) + +#define BIT_PRESENT (1ull) +#define BIT_WRITEABLE (1ull << 1) +#define BIT_USER (1ull << 2) +#define BIT_WRITE_THROUGH (1ull << 3) +#define BIT_CACHE_DISABLED (1ull << 4) +#define BIT_ACCESSED (1ull << 5) +#define BIT_PS (1ull << 7) +#define BIT_EXECUTION_DISABLED (1ull << 63) + +static void +write_page_table(void *const base, uintptr const p_addr, uint64 const attr) +{ + uint64 entry = (p_addr & 0xFFFFFFFFFF000) | attr; + ((uint8 *) base)[0] = (uint8) (entry & 0xFF); + ((uint8 *) base)[1] = (uint8) ((entry >> 8) & 0xFF); + ((uint8 *) base)[2] = (uint8) ((entry >> 16) & 0xFF); + ((uint8 *) base)[3] = (uint8) ((entry >> 24) & 0xFF); + ((uint8 *) base)[4] = (uint8) ((entry >> 32) & 0xFF); + ((uint8 *) base)[5] = (uint8) ((entry >> 40) & 0xFF); + ((uint8 *) base)[6] = (uint8) ((entry >> 48) & 0xFF); + ((uint8 *) base)[7] = (uint8) ((entry >> 56) & 0xFF); +} + +static int32 ensure_present(uint64 *entry, uint64 attr, uintptr *alloc) +{ + uint32 ret = ESUCCESS; + + uintptr p_alloc = (uintptr) NULL; + + if (!(*entry & BIT_PRESENT)) + { + p_alloc = (uintptr) pmalloc(KERNEL_PAGE_SIZE); + if (p_alloc == (uintptr) NULL) + { + ret = ENOMEM; + } + else + { + mem_set(R_PADDR(p_alloc), 0, KERNEL_PAGE_SIZE); + write_page_table(entry, p_alloc, attr); + } + } + + if (ret == ESUCCESS) + { + *alloc = p_alloc; + } + + return ret; +} + +int32 map_vmem(uint64 cr3, uintptr vaddr, uintptr paddr) +{ + int32 ret; + + vaddr &= ~(uintptr)0xfff; + paddr &= ~(uintptr)0xfff; + + uint64 *pml4_e = R_PADDR(cr3 + ENTRY_SIZE * PML4_ENTRY(vaddr)); + uintptr pdpt_alloc = (uintptr) NULL; + ret = ensure_present(pml4_e, BIT_PRESENT | BIT_WRITEABLE | (IS_KERN_SPACE(vaddr) ? 0 : BIT_USER), &pdpt_alloc); + + uint64 *pdpt_e = NULL; + uintptr pd_alloc = (uintptr) NULL; + if (ret == ESUCCESS) + { + pdpt_e = R_PADDR((*pml4_e & NEXT_LEVEL_MASK) + ENTRY_SIZE * PDPT_ENTRY(vaddr)); + ret = ensure_present(pdpt_e, BIT_PRESENT | BIT_WRITEABLE | (IS_KERN_SPACE(vaddr) ? 0 : BIT_USER), &pd_alloc); + } + + uint64 *pd_e = NULL; + uintptr pt_alloc = (uintptr) NULL; + if (ret == ESUCCESS) + { + pd_e = R_PADDR((*pdpt_e & NEXT_LEVEL_MASK) + ENTRY_SIZE * PD_ENTRY(vaddr)); + ret = ensure_present(pd_e, BIT_PRESENT | BIT_WRITEABLE | (IS_KERN_SPACE(vaddr) ? 0 : BIT_USER), &pt_alloc); + } + + if (ret == ESUCCESS) + { + uint64 *pt_e = R_PADDR((*pd_e & NEXT_LEVEL_MASK) + ENTRY_SIZE * PT_ENTRY(vaddr)); + write_page_table(pt_e, paddr, BIT_PRESENT | BIT_WRITEABLE | (IS_KERN_SPACE(vaddr) ? 0 : BIT_USER)); +#ifdef KDBG + kprintf("Mapped 0x%x to 0x%x\n", vaddr, paddr); +#endif + } + + if (ret != ESUCCESS) + { + if (pdpt_alloc != (uintptr) NULL) + { + pfree(pdpt_alloc); + } + + if (pd_alloc != (uintptr) NULL) + { + pfree(pd_alloc); + } + + if (pt_alloc != (uintptr) NULL) + { + pfree(pt_alloc); + } + } + + return ret; +} + +uintptr get_paddr(uint64 cr3, uintptr vaddr) +{ + uint64 pml4_e = *(uint64 *) R_PADDR(cr3 + ENTRY_SIZE * PML4_ENTRY(vaddr)); + + if (!(pml4_e & BIT_PRESENT)) + { + // not present in PML4 + return (uintptr) NULL; + } + uint64 pdpt_e = *(uint64 *) R_PADDR((pml4_e & NEXT_LEVEL_MASK) + ENTRY_SIZE * PDPT_ENTRY((uintptr)vaddr)); + + if (!(pdpt_e & BIT_PRESENT)) + { + return (uintptr) NULL; + } + + if (pdpt_e & BIT_PS) + { + // 1GB page + return (pdpt_e & bit_field_mask(30, 51)) + ((uintptr)vaddr & (0x3FFFFFFF)); + } + + uint64 pd_e = *(uint64 *) R_PADDR((pdpt_e & NEXT_LEVEL_MASK) + ENTRY_SIZE * PD_ENTRY((uintptr)vaddr)); + + if (!(pdpt_e & BIT_PRESENT) || (pdpt_e & BIT_PS)) + { + // don't support 2MB pages yet + return (uintptr) NULL; + } + + uint64 pt_e = *(uint64 *) R_PADDR((pd_e & NEXT_LEVEL_MASK) + ENTRY_SIZE * PT_ENTRY(vaddr)); + + if (!(pt_e & BIT_PRESENT)) + { + return (uintptr) NULL; + } + + return (pt_e & bit_field_mask(12, 51)) + (uintptr)vaddr & 0xFFF; +} diff --git a/src/pmm.c b/src/pmm.c new file mode 100644 index 0000000..417cd4d --- /dev/null +++ b/src/pmm.c @@ -0,0 +1,48 @@ +#include +#include +#include "memory_layout.h" +#include "pmm.h" +#include "clib.h" + +static uint64 mem_low; +static uint64 mem_high; +static uint64 ptr; +static struct spin_lock pm_global_lock; + +void +pfree(paddr p) +{ + UNREFERENCED(p); +} + +void* pmalloc(uint32 size) +{ + void* ret = NULL; + + spin_lock(&pm_global_lock); + if((size > KERNEL_PAGE_SIZE) || (ptr >= mem_high)) + { + ret = NULL; + } + else + { + ret = (void *) ptr; + ptr = ptr + KERNEL_PAGE_SIZE; + } + + spin_unlock(&pm_global_lock); + +#ifdef KDBG + kprintf("Allocated physical: 0x%x, size: %d\n", (uint64)ptr, (uint64)size); +#endif + + return ret; +} + +void pmm_init(uint64 low, uint64 high) +{ + mem_low = MAX(K_IMAGE_PADDR + K_IMAGE_PRESRV, low); + ptr = mem_low; + mem_high = high; + spin_init(&pm_global_lock); +} diff --git a/src/print.c b/src/print.c new file mode 100644 index 0000000..5368212 --- /dev/null +++ b/src/print.c @@ -0,0 +1,212 @@ +#include "print.h" +#include "clib.h" +#include "memory_layout.h" +#include "spin_lock.h" + +#define FBUF (uintptr)R_PADDR(0xb8000) +#define GET_ROW(pos) ((pos) / 80) +#define GET_POS(row, col) ((row) * 80 + (col)) + +static struct spin_lock lock; +static uint64 text_pos; + +void print_init() +{ + spin_init(&lock); + text_pos = 0; + clear_screen(); +} + +static void +print_scroll(void) +{ + mem_mv((void*)(FBUF + GET_POS(1, 0) * 2), (void *) (FBUF + GET_POS(0, 0) * 2), (80 * 24) * 2); +} + +static void +print_str(char const *str) +{ + if (str == NULL) + { + return; + } + while (*str != 0) + { + if (*str == '\n') + { + text_pos = 80 * (GET_ROW(text_pos) + 1); + if (text_pos > 80 * 25 - 1) + { + //can't hold + print_scroll(); + mem_set((void *) (FBUF + 80 * 24 * 2), 0, 80 * 2); // clear last row + text_pos = 80 * 24; + } + str++; + } + else + { + if (text_pos > 80 * 25 - 1) + { + //can't hold + print_scroll(); + text_pos = 80 * 24; + } + *((char *) (FBUF) + text_pos * 2) = *str; + *((char *) (FBUF) + text_pos * 2 + 1) = 7; + str++; + text_pos++; + } + } +} + +static void +print_uint(uint64 number) +{ + char arr[21]; // do not need to initialize + arr[20] = 0; //zero-terminated + uint32 index = 19; + uint32 const div = 10; + while (1) + { + uint64 quo = number / div; + uint64 rmd = number % div; + number = quo; + arr[index--] = (char) ('0' + rmd); + if (number == 0) + { + break; + } + } + print_str(&(arr[index + 1])); +} + +static void +print_int(int64 number) +{ + char arr[21]; // do not need to initialize + arr[20] = 0; //zero-terminated + uint32 index = 19; + uint32 isNegative = 0; + uint32 const div = 10; + if (number < 0) + { + isNegative = 1; + number *= -1; + } + while (1) + { + int64 quo = number / div; + int64 rmd = number % div; + number = quo; + arr[index--] = (char) ('0' + rmd); + if (number == 0) + { + break; + } + } + if (isNegative) + { + arr[index--] = '-'; + } + print_str(&(arr[index + 1])); +} + +static void +print_hex(uint64 number, uint64 capital) +{ + char const lookup_table_cap[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + char const lookup_table[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + char const *const look_up = capital == 1 ? &lookup_table_cap[0] : &lookup_table[0]; + char arr[17]; + arr[16] = 0; //zero-terminated + uint32 index = 15; + uint32 const div = 16; + while (1) + { + uint64 quo = number / div; + uint64 rmd = number % div; + number = quo; + arr[index--] = look_up[rmd]; + if (number == 0) + { + break; + } + } + print_str(&(arr[index + 1])); +} + +void +clear_screen(void) +{ + uint64 irq; + irq = spin_lock_irq_save(&lock); + text_pos = 0; // reset text_pos + mem_set((void *) FBUF, 0, 25 * 80 * 2); + spin_unlock_irq_restore(&lock, irq); +} + +static void +kvprintf(char const *format, va_list args) +{ + char buf[2]; + int64 d; + uint64 u; + char *s; + + buf[1] = '\0'; + for (; *format != '\0'; format++) + { + if (*format != '%') + { + buf[0] = *format; + print_str(buf); + continue; + } + format++; + switch (*format) + { + case 'd': + d = va_arg(args, int64); + print_int(d); + break; + case 'u': + u = va_arg(args, uint64); + print_uint(u); + break; + case 's': + s = va_arg(args, char *); + print_str(s); + break; + case 'x': + u = va_arg(args, uint64); + print_hex(u, 0); + break; + case 'X': + u = va_arg(args, uint64); + print_hex(u, 1); + break; + case '%': + buf[0] = '%'; + print_str(buf); + break; + default: + buf[0] = '%'; + print_str(buf); + format--; + break; + } + } +} + +void +kprintf(char const *format, ...) +{ + va_list args; + va_start(args, format); + uint64 irq; + irq = spin_lock_irq_save(&lock); + kvprintf(format, args); + spin_unlock_irq_restore(&lock, irq); + va_end(args); +} diff --git a/src/proc.c b/src/proc.c new file mode 100644 index 0000000..7de8bc7 --- /dev/null +++ b/src/proc.c @@ -0,0 +1,132 @@ +#include "error.h" +#include "proc.h" +#include "pmm.h" +#include "vmm.h" +#include "thread.h" +#include "elf64.h" +#include "cpu.h" +#include "paging.h" +#include "memory_layout.h" + +static struct spin_lock proc_list_lock; +static struct llist proc_list; +static uint32 proc_id; + +int32 proc_init(void* k_routine) +{ + uint32 tid; + int32 ret = ESUCCESS; + spin_init(&proc_list_lock); + lb_llist_init(&proc_list); + proc_id = 0; + + // hack to make the first process + struct pcb *pcb = kalloc(sizeof(struct pcb)); + if (pcb == NULL) + { + ret = ENOMEM; + } + + if (ret == ESUCCESS) + { + pcb->cr3 = read_cr3(); + pcb->proc_id = (uint32) xinc_32((int32 *) &proc_id, 1); + spin_init(&pcb->lock); + lb_llist_init(&pcb->threads); + + ret = thread_create(pcb, k_routine, NULL, &tid); + } + + if (ret != ESUCCESS) + { + kfree(pcb); + } + + return ret; +} + +int32 proc_create(void* elf64, uint32 *pid) +{ + int32 ret = ESUCCESS; + + // allocate struct pcb + struct pcb *pcb = kalloc(sizeof(struct pcb)); + if (pcb == NULL) + { + ret = ENOMEM; + } + + // allocate pml4 + uintptr cr3 = (uintptr) NULL; + if (ret == ESUCCESS) + { + cr3 = (uintptr) pmalloc(PAGE_SIZE); + if (cr3 == (uintptr) NULL) + { + ret = ENOMEM; + } + } + + void* entry = NULL; + bool pushed = FALSE; + if (ret == ESUCCESS) + { + // init pcb + pcb->cr3 = cr3; + pcb->proc_id = (uint32) xinc_32((int32 *) &proc_id, 1); + spin_init(&pcb->lock); + lb_llist_init(&pcb->threads); + + // write page tables + uintptr cur_cr3 = read_cr3(); + uint64 *cur_pml4 = R_PADDR(cur_cr3); + + uint64 *pml4 = R_PADDR(cr3); + // identity map the kernel space + for (uint32 i = PML4_ENTRY(K_START); i < 512; i++) + { + pml4[i] = cur_pml4[i]; + } + + // register the process + spin_lock(&proc_list_lock); + lb_llist_push_front(&proc_list, &pcb->list_node); + spin_unlock(&proc_list_lock); + pushed = TRUE; + + ret = elf_load_file(pcb, elf64, &entry); + } + + if (ret == ESUCCESS) + { + // create the thread + uint32 tid; + ret = thread_create(pcb, entry, (void*)0x13141516, &tid); + } + + if (ret == ESUCCESS) + { + *pid = pcb->proc_id; + } + else + { + if (pcb != NULL) + { + if (pushed) + { + spin_lock(&proc_list_lock); + lb_llist_remove_by_ref(&proc_list, &pcb->list_node); + spin_unlock(&proc_list_lock); + } + kfree(pcb); + } + + // need to properly unmap everything + if (cr3 != (uintptr) NULL) + { + pfree(cr3); + } + } + + return ret; +} diff --git a/src/spin_lock.c b/src/spin_lock.c new file mode 100644 index 0000000..d564868 --- /dev/null +++ b/src/spin_lock.c @@ -0,0 +1,47 @@ +#include "spin_lock.h" +#include "cpu.h" +#include "intr.h" + +void +spin_init(struct spin_lock *lock) +{ + if (lock != NULL) + { + lock->val = 0; + } +} + + +void +spin_lock(struct spin_lock *lock) +{ + if (lock != NULL) + { + while (cmpxchg_32(&lock->val, 0, 1) != 0) + {} + } +} + + +void +spin_unlock(struct spin_lock *lock) +{ + if (lock != NULL) + { + lock->val = 0; + } +} + +uint64 spin_lock_irq_save(struct spin_lock* lock) +{ + uint64 ret = READ_IRQ(); + WRITE_IRQ(0xf); + spin_lock(lock); + return ret; +} + +void spin_unlock_irq_restore(struct spin_lock* lock, uint64 irq) +{ + spin_unlock(lock); + WRITE_IRQ(irq); +} diff --git a/src/syscall.c b/src/syscall.c new file mode 100644 index 0000000..049d2b2 --- /dev/null +++ b/src/syscall.c @@ -0,0 +1,28 @@ +#include +#include "syscall.h" +#include "thread.h" +#include "intr.h" + +void *syscall_handler(struct intr_frame *frame) +{ + uint64 vec = (uint32)frame->rdi; + if (vec == 0) + { + // print + kprintf("%s", (char*)frame->rsi); + } + else + { + kprintf("[WARN] Unrecognized syscall from proc %d, thread %d, function %d\n", (uint64) get_cur_thread()->proc->proc_id, + (uint64) get_cur_thread()->tid, + vec); + } + + // nothing for the handler + return NULL; +} + +void syscall_init() +{ + set_intr_handler(INTR_VEC_SYSCALL, syscall_handler); +} diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000..59c58c1 --- /dev/null +++ b/src/thread.c @@ -0,0 +1,420 @@ +#include +#include +#include "thread.h" +#include "error.h" +#include "proc.h" +#include "clib.h" +#include "vmm.h" +#include "cpu.h" +#include "intr.h" +#include "memory_layout.h" + +#define THREAD_STACK_SIZE (PAGE_SIZE) + +enum +{ + THREAD_STATE_RDY = 0, + THREAD_STATE_BLK = 1, + THREAD_STATE_ZOM = 2, +}; + +static uint32 thread_id; +static struct tcb *cur_thread; +static struct llist thread_list; +static struct spin_lock thread_list_lock; + +void list_threads() +{ + struct llist_node *node = lb_llist_first(&thread_list); + struct tcb *tcb = NULL; + while (node != NULL) + { + tcb = OBTAIN_STRUCT_ADDR(node, struct tcb, list_node); + // reading no need to lock thread lock + kprintf("Thread %d\n", (uint64) tcb->tid); + node = lb_llist_next(node); + } +} + +static struct tcb *get_tcb_by_id(uint32 tid) +{ + // the thread list lock must be held + struct llist_node *node = lb_llist_first(&thread_list); + struct tcb *tcb = NULL; + while (node != NULL) + { + tcb = OBTAIN_STRUCT_ADDR(node, struct tcb, list_node); + // reading no need to lock thread lock + if (tcb->tid == tid) + { + break; + } + node = lb_llist_next(node); + } + + return tcb; +} + +int32 thread_get_exit_code(uint32 tid, int32 *exit_code) +{ + int32 ret = ESUCCESS; + + struct tcb *tcb; + uint64 irq = spin_lock_irq_save(&thread_list_lock); + tcb = get_tcb_by_id(tid); + spin_unlock_irq_restore(&thread_list_lock, irq); + + if (tcb == NULL) + { + ret = EINVARG; + } + + if (ret == ESUCCESS) + { + if (tcb->state == THREAD_STATE_ZOM) + { + *exit_code = tcb->exit_code; + + // this is unfair but do it for now + thread_yield(); + } + else + { + ret = EINVARG; + } + } + + return ret; +} + +int32 thread_stop(uint32 tid, int32 code) +{ + int32 ret = ESUCCESS; + + struct tcb *tcb; + uint64 irq = spin_lock_irq_save(&thread_list_lock); + tcb = get_tcb_by_id(tid); + spin_unlock_irq_restore(&thread_list_lock, irq); + + if (tcb == NULL) + { + ret = EINVARG; + } + + if (ret == ESUCCESS) + { + spin_lock(&tcb->lock); + if (tcb->state != THREAD_STATE_ZOM) + { + tcb->state = THREAD_STATE_ZOM; + tcb->exit_code = code; + } + else + { + ret = EINVARG; + } + spin_unlock(&tcb->lock); + } + + if (ret == ESUCCESS) + { + // well this is unfair + // but do this for now + thread_yield(); + } + + return ret; +} + +void set_cur_thread(struct tcb *t) +{ + // first time setting + KASSERT(cur_thread == NULL); + cur_thread = t; +} + +// this guy picks the next thread to run +// by updating cur_thread +// timer interrupt always +// the actual context swap is done elsewhere +void thread_schedule() +{ + // only timer interrupt context + spin_lock(&thread_list_lock); + + if (cur_thread == NULL) + { + // first thread hack + cur_thread = OBTAIN_STRUCT_ADDR(lb_llist_first(&thread_list), struct tcb, list_node); + } + + KASSERT(cur_thread != NULL); + + struct llist_node *next_node = &cur_thread->list_node; + // since there must be a null thread, node cannot be null + while (1) + { + // get the next tcb in a round robbin fashion + next_node = lb_llist_next(next_node); + if (next_node == NULL) + { + next_node = lb_llist_first(&thread_list); + } + + struct tcb *next = OBTAIN_STRUCT_ADDR(next_node, struct tcb, list_node); + + // we are only checking states here so no need to worry + // since thread_block always fires an timer interrupt, which will be served immediately after this returns + // so even if someone else from another core blocks this thread during this window + // this thread will get descheduled right after the current timer interrupt deasserts + // the worst case is someone unblocked a thread right after we checked it, but it doesn't break integrity + if (next->state == THREAD_STATE_RDY) + { + cur_thread = next; + // stop looping, scheduler runs fast, although o(n) + break; + } + else if (next->state == THREAD_STATE_ZOM) + { + // zombie thread, should use ref count to properly deallocate stuff + // TODO: clean up zombie threads + } + } + + spin_unlock(&thread_list_lock); +#ifdef KDBG + kprintf("[Scheduler] Next Proc: %d. Next Thread: %d\n", (uint64)cur_thread->proc->proc_id, (uint64)cur_thread->tid); +#endif +} + +void thread_exit(int32 code) +{ + spin_lock(&cur_thread->lock); + KASSERT(cur_thread->state == THREAD_STATE_RDY); + cur_thread->exit_code = code; + cur_thread->state = THREAD_STATE_ZOM; + spin_unlock(&cur_thread->lock); + thread_yield(); + // shouldn't get here + KASSERT(FALSE); +} + +int32 thread_resume(uint32 tid) +{ + int32 ret = ESUCCESS; + + struct tcb *tcb; + uint64 irq = spin_lock_irq_save(&thread_list_lock); + tcb = get_tcb_by_id(tid); + spin_unlock_irq_restore(&thread_list_lock, irq); + + if (tcb == NULL) + { + ret = EINVARG; + } + + if (ret == ESUCCESS) + { + spin_lock(&tcb->lock); + if (tcb->state == THREAD_STATE_BLK) + { + tcb->state = THREAD_STATE_RDY; + } + else + { + ret = EINVARG; + } + spin_unlock(&tcb->lock); + } + + return ret; +} + +int32 thread_block(uint32 tid) +{ + int32 ret = ESUCCESS; + + struct tcb *tcb; + uint64 irq = spin_lock_irq_save(&thread_list_lock); + tcb = get_tcb_by_id(tid); + spin_unlock_irq_restore(&thread_list_lock, irq); + + if (tcb == NULL) + { + ret = EINVARG; + } + + if (ret == ESUCCESS) + { + spin_lock(&tcb->lock); + if (tcb->state == THREAD_STATE_RDY) + { + tcb->state = THREAD_STATE_BLK; + } + else + { + ret = EINVARG; + } + spin_unlock(&tcb->lock); + } + + if (ret == ESUCCESS) + { + // this is unfair but do it for now + thread_yield(); + } + + return ret; +} + +void thread_yield() +{ + // emulate a timer interrupt on target core + send_ipi(INTR_VEC_TIMER); +} + +#define MODE_K (0) +#define MODE_U (1) + +static void write_intr_frame(struct intr_frame *frame, uint32 mode, uint64 iret_addr, uint64 iret_stack, uint64 arg) +{ + uint64 dsel = mode == MODE_K ? SEL(GDT_K_DATA, 0, 0) : SEL(GDT_U_DATA, 0, 3); + uint64 csel = mode == MODE_K ? SEL(GDT_K_CODE, 0, 0) : SEL(GDT_U_CODE, 0, 3); + frame->ss = dsel; + frame->ds = dsel; + frame->es = dsel; + frame->fs = dsel; + frame->gs = dsel; + frame->cs = csel; + frame->rip = (uint64) iret_addr; + frame->rdi = arg; + frame->rsp = iret_stack; + + // only set interrupt enable flag + frame->rflags = (0x200); +} + +int32 thread_create(struct pcb *proc, void *entry, void *args, uint32 *tid) +{ + int32 ret = ESUCCESS; + + struct tcb *tcb = kalloc(sizeof(struct tcb)); + + if (tcb == NULL) + { + ret = ENOMEM; + } + + // allocate thread kernel stack + // 4k stack for now + // no stack overflow + uintptr ustack = (uintptr) NULL; + uint64 *kstack = NULL; + uint64 *kstack_top = NULL; + if (ret == ESUCCESS) + { + kstack = kalloc(THREAD_STACK_SIZE); + + if (kstack == NULL) + { + ret = ENOMEM; + } + else + { + kstack_top = (uint64 *) ((uintptr) kstack + THREAD_STACK_SIZE); + } + } + + // allocate ustack always + if (ret == ESUCCESS) + { + ustack = (uintptr) pmalloc(THREAD_STACK_SIZE); + + if (ustack == (uintptr) NULL) + { + ret = ENOMEM; + } + } + + struct intr_frame *frame = NULL; + if (ret == ESUCCESS) + { + tcb->tid = (uint32) xinc_32((int32 *) &thread_id, 1); + spin_init(&tcb->lock); + tcb->exit_code = 0; + tcb->state = THREAD_STATE_RDY; + tcb->proc = proc; + tcb->kstack = kstack; + tcb->kstack_sz = THREAD_STACK_SIZE; + tcb->ustack_sz = THREAD_STACK_SIZE; + tcb->ustack = ustack; + + // write initial context information on the kernel stack + frame = (struct intr_frame *) ((uintptr) kstack_top - sizeof(struct intr_frame)); + mem_set(frame, 0, sizeof(struct intr_frame)); + + // here we wanna check if the entrance is user mode + if (IS_KERN_SPACE(entry)) + { + write_intr_frame(frame, MODE_K, (uint64) entry, (uint64) kstack_top, (uint64) args); + } + else + { + // map ustack to the user address space + ret = map_vmem(proc->cr3, U_STACK_VADDR, ustack); + + if (ret == ESUCCESS) + { + write_intr_frame(frame, MODE_U, (uint64) entry, (uint64) (U_STACK_VADDR + THREAD_STACK_SIZE), + (uint64) args); + } + } + } + + if (ret == ESUCCESS) + { + // update interrupt stack pointer + tcb->rsp0 = (uint64) frame; + + // add to thread list + uint64 irq; + irq = spin_lock_irq_save(&thread_list_lock); + lb_llist_push_back(&thread_list, &tcb->list_node); + spin_unlock_irq_restore(&thread_list_lock, irq); + *tid = tcb->tid; + } + + if (ret != ESUCCESS) + { + // clean up + if (tcb != NULL) + { + kfree(tcb); + } + + if (ustack != (uintptr) NULL) + { + pfree(ustack); + } + + if (kstack != NULL) + { + kfree(kstack); + } + } + + return ret; +} + +void thread_init() +{ + cur_thread = NULL; + lb_llist_init(&thread_list); + spin_init(&thread_list_lock); + thread_id = 0; +} + +struct tcb *get_cur_thread() +{ + return cur_thread; +} diff --git a/src/vmm.c b/src/vmm.c new file mode 100644 index 0000000..1dee09b --- /dev/null +++ b/src/vmm.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static void *pointer; +static struct spin_lock lock; + +void *kalloc(usize size) +{ + spin_lock(&lock); + + void *ret; + if ((size > KERNEL_PAGE_SIZE) || ((uintptr) pointer + size >= K_DYN_END)) + { + ret = NULL; + } + else + { + ret = pointer; + pointer = (void *) ((uintptr) pointer + KERNEL_PAGE_SIZE); + } + + spin_unlock(&lock); + + /* Hack */ + int32 status = ESUCCESS; + if (ret != NULL) + { + // check if the target page is already mapped + // ret val does not cross page boundries + if (get_paddr(read_cr3(), (uintptr) ret) == (uintptr) NULL) + { + uintptr frame = (uintptr) pmalloc(PAGE_SIZE); + status = (frame != (uintptr) NULL) && (map_vmem(read_cr3(), (uintptr) ret, frame) != ESUCCESS); + } + } + + if (status != ESUCCESS) + { + ret = NULL; + } + else + { + flush_tlb(); + } + +#ifdef KDBG + kprintf("Allocated Virtual: 0x%x Size: %d\n", ret, (uint64) size); +#endif + + return ret; +} + +void init_vm() +{ + pointer = (void *) K_DYNAMIC; + spin_init(&lock); +} + +void +kfree(void *ptr) +{ + UNREFERENCED(ptr); +} diff --git a/test/bochsrc b/test/bochsrc new file mode 100644 index 0000000..03de350 --- /dev/null +++ b/test/bochsrc @@ -0,0 +1,6 @@ +display_library: x, options="gui_debug" +cpuid: 1g_pages=1 +ata0-master: type=cdrom, path="out/curros.iso", status=inserted +boot: cdrom +magic_break: enabled=1 +memory: guest=512, host=512 \ No newline at end of file diff --git a/test/qemu.sh b/test/qemu.sh new file mode 100755 index 0000000..bfdf80b --- /dev/null +++ b/test/qemu.sh @@ -0,0 +1,4 @@ +#!/bin/bash +qemu-system-x86_64 -m 128 -monitor stdio -cdrom out/curros.iso -s -S -d cpu_reset + + diff --git a/user/curros.asm b/user/curros.asm new file mode 100644 index 0000000..7990037 --- /dev/null +++ b/user/curros.asm @@ -0,0 +1,12 @@ +SYSCALL_VEC equ 51 + +SECTION .text +BITS 64 + +GLOBAL syscall + +syscall: +; rdi = function number +; rsi = args +int SYSCALL_VEC +ret \ No newline at end of file diff --git a/user/hello.c b/user/hello.c new file mode 100644 index 0000000..b6aefc2 --- /dev/null +++ b/user/hello.c @@ -0,0 +1,36 @@ +#include +#include +#include + +#define THREAD_DELAY (5000000*4) + +void +main(void *dat) +{ + char *msg_ok = "[UThread3] Magic number OK.\n"; + char *msg_bad = "[UThread3] Magic number FAILED.\n"; + char msg[] = "[UThread3] \n"; + + uint32 success = 0; + uint32 magic = 0x13141516; + if ((uint32) dat == magic) + { + syscall(SYSCALL_FUNC_PRINT, msg_ok); + success = 1; + } + else + { + syscall(SYSCALL_FUNC_PRINT, msg_bad); + success = 0; + } + int i = 0; + while (1) + { + poor_sleep(THREAD_DELAY); + i = i % 10; + msg[11] = i + 48; + syscall(SYSCALL_FUNC_PRINT, msg); + i++; + } + // don't return here, need to call thread exit +} diff --git a/user/inc/cdef.h b/user/inc/cdef.h new file mode 100644 index 0000000..df4d23b --- /dev/null +++ b/user/inc/cdef.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +typedef uint32_t uint32; +typedef int32_t int32; +typedef uint64_t uint64; +typedef int64_t int64; +typedef uintptr_t uintptr; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint8_t uint8; +typedef int8_t int8; +typedef size_t usize; +typedef _Bool bool; + +#define TRUE (1) +#define FALSE (0) + + diff --git a/user/inc/curros.h b/user/inc/curros.h new file mode 100644 index 0000000..640cac9 --- /dev/null +++ b/user/inc/curros.h @@ -0,0 +1,14 @@ +#pragma once + +#include "cdef.h" + +#define SYSCALL_FUNC_PRINT (0) + +void syscall(uint32 func, void* args); + +static inline void poor_sleep(uint32 dat) +{ + for(uint32 i = 0; i < dat; i++) + { + } +} diff --git a/user/linker.ld b/user/linker.ld new file mode 100644 index 0000000..6d9a481 --- /dev/null +++ b/user/linker.ld @@ -0,0 +1,26 @@ +ENTRY(main) + +KERNEL_PAGE_SIZE = 0x1000; +USER_APPLICATION_VADDR = 0x1000000; + +SECTIONS +{ + . = USER_APPLICATION_VADDR; + + .text ALIGN(KERNEL_PAGE_SIZE): + { + *(.text) + } + + .data ALIGN(KERNEL_PAGE_SIZE): + { + *(.data) + *(.rodata*) + } + + .bss ALIGN(KERNEL_PAGE_SIZE): + { + *(.bss) + *(COMMON) + } +} diff --git a/user/makefile b/user/makefile new file mode 100644 index 0000000..3bb16b3 --- /dev/null +++ b/user/makefile @@ -0,0 +1,91 @@ + +AS := nasm +CC := clang-6.0 +LD := lld-6.0 +DAS := llvm-objdump-6.0 + +.DEFAULT_GOAL := all + +C_FLAGS_ARCH_X86_64 := -target x86_64-pc-none-elf \ + -mno-red-zone \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 \ + -mno-sse3 \ + -mno-3dnow + +C_FLAGS = -x c \ + -g \ + -c \ + -O0 \ + -std=c17 \ + -Wall \ + -Werror \ + -Wextra \ + -Wpedantic \ + -ffreestanding \ + -fno-pic \ + -fno-stack-protector \ + -Wno-error=int-to-pointer-cast \ + -Wno-error=zero-length-array \ + $(C_FLAGS_ARCH_X86_64) \ + -I$(INC)/ + +AS_FLAGS = -w+all \ + -w+error \ + -f elf64 \ + -F dwarf \ + -g \ + -I$(INC)/ \ + +LD_FLAGS = -fuse-ld=$(LD) \ + -nostdlib \ + -Wl, linker.ld \ + -Wl,--fatal-warnings + +# =============================== +# HERE COMES file definitions +# =============================== + +INC := inc +SRC := . +OUT := out +TGT := $(OUT)/hello.elf + +# =============================== +# Add additional c source files here +# =============================== +C_SRC := hello.c + +# =============================== +# Add additional ASM source files here +# =============================== +ASM_SRC := curros.asm + +# =============================== +# Compilation rules +# =============================== +C_OBJ := $(addsuffix .o, $(addprefix $(OUT)/,$(C_SRC))) + +ASM_OBJ := $(addsuffix .o, $(addprefix $(OUT)/,$(ASM_SRC))) + +$(C_OBJ): $(OUT)/%.c.o : %.c + $(CC) $(C_FLAGS) -o $@ $< + +$(ASM_OBJ): $(OUT)/%.asm.o : %.asm + $(AS) $(AS_FLAGS) -o $@ $< + +$(TGT): $(C_OBJ) $(ASM_OBJ) + $(CC) $(LD_FLAGS) -o $@ $^ + +.PHONY: mkdir +mkdir: + mkdir -p out + +.PHONY: clean +clean: + rm -rf $(OUT) + +.PHONY: all +all: mkdir $(TGT) +