Ver código fonte

Merge branch 'paging'

greatbridf 2 anos atrás
pai
commit
7e5a6e89c7

+ 2 - 0
.gitignore

@@ -7,3 +7,5 @@ test/
 bx_enh_dbg.ini
 
 .cache/
+
+compile_commands.json

+ 21 - 6
CMakeLists.txt

@@ -3,6 +3,8 @@ project(my_os_bootloader ASM)
 
 set(CMAKE_ASM_FLAGS "-m32")
 
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
 include_directories(${PROJECT_SOURCE_DIR}/include)
 
 set(EXTRACT_DIR ${PROJECT_BINARY_DIR}/extract)
@@ -12,19 +14,20 @@ set(BOOTLOADER_SOURCES src/boot.s
                        src/asm/a20.s
                        src/asm/interrupt.s
                        src/asm/port_io.s
+                       src/asm/sys.s
                        )
 
 add_library(bootloader STATIC ${BOOTLOADER_SOURCES})
 
 add_custom_command(OUTPUT extracted_bootloader
     DEPENDS bootloader
-    COMMAND ar xf ${PROJECT_BINARY_DIR}/libbootloader.a --output=${EXTRACT_DIR}
+    COMMAND ${CMAKE_AR} xf ${PROJECT_BINARY_DIR}/libbootloader.a --output=${EXTRACT_DIR}
 )
 
 project(kernel_main)
 
-set(CMAKE_C_FLAGS "-nostdinc -m32 -nostdlib -Wall -Wextra -fverbose-asm -fno-exceptions -fno-pic -fno-stack-protector")
-set(CMAKE_CXX_FLAGS "-nostdinc -m32 -nostdlib -Wall -Wextra -fverbose-asm -fno-use-cxa-atexit -fno-exceptions -fno-pic -fno-stack-protector")
+set(CMAKE_C_FLAGS "-nostdinc -m32 -nostdlib -Wall -Wextra -Wno-builtin-declaration-mismatch -Wno-format -fverbose-asm -fno-exceptions -fno-pic -fno-stack-protector")
+set(CMAKE_CXX_FLAGS "-nostdinc -m32 -nostdlib -Wall -Wextra -Wno-builtin-declaration-mismatch -Wno-format -fverbose-asm -fno-use-cxa-atexit -fno-exceptions -fno-pic -fno-stack-protector")
 
 if (CMAKE_BUILD_TYPE STREQUAL "Debug")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g")
@@ -39,29 +42,40 @@ include_directories(${PROJECT_SOURCE_DIR}/include)
 set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         src/kernel/errno.c
                         src/kernel/interrupt.c
+                        src/kernel/process.c
+                        src/kernel/tty.c
                         src/kernel/stdio.c
                         src/kernel/mem.c
                         src/kernel/vga.c
                         src/kernel/hw/keyboard.cpp
+                        src/kernel/hw/serial.c
                         src/kernel/hw/timer.c
                         src/kernel/event/event.cpp
+                        src/types/bitmap.c
                         src/types/buffer.c
                         include/asm/boot.h
                         include/asm/port_io.h
+                        include/asm/sys.h
                         include/kernel/event/event.h
                         include/kernel/errno.h
+                        include/kernel/tty.h
                         include/kernel/interrupt.h
+                        include/kernel/process.h
                         include/kernel/stdio.h
                         include/kernel/mem.h
                         include/kernel/vga.h
                         include/kernel/hw/keyboard.h
+                        include/kernel/hw/serial.h
                         include/kernel/hw/timer.h
                         include/kernel/input/keycodes.h
                         include/kernel/input/input_event.h
+                        include/types/bitmap.h
                         include/types/buffer.h
                         include/types/types.h
                         include/types/size.h
+                        include/types/status.h
                         include/types/stdint.h
+                        include/types/list.h
                         include/types/list.hpp
                         include/kernel_main.h
                         )
@@ -69,19 +83,20 @@ add_library(kernel_main STATIC ${KERNEL_MAIN_SOURCES})
 
 add_custom_command(OUTPUT extracted_kernel_main
     DEPENDS kernel_main
-    COMMAND ar xf ${PROJECT_BINARY_DIR}/libkernel_main.a --output=${EXTRACT_DIR}
+    COMMAND ${CMAKE_AR} xf ${PROJECT_BINARY_DIR}/libkernel_main.a --output=${EXTRACT_DIR}
 )
 
 add_custom_target(kernel.out
     DEPENDS extracted_bootloader
     DEPENDS extracted_kernel_main
-    COMMAND ld -T ${CMAKE_SOURCE_DIR}/ldscript.ld ${EXTRACT_DIR}/*.o
+    DEPENDS ${CMAKE_SOURCE_DIR}/ldscript.ld
+    COMMAND ${CMAKE_LINKER} -T ${CMAKE_SOURCE_DIR}/ldscript.ld ${EXTRACT_DIR}/*.o
     -melf_i386 -o ${CMAKE_BINARY_DIR}/kernel.out
 )
 
 add_custom_target(boot.img
     DEPENDS kernel.out
-    COMMAND objcopy --strip-debug -O binary
+    COMMAND ${CMAKE_OBJCOPY} --strip-debug -O binary
     ${CMAKE_BINARY_DIR}/kernel.out boot.img)
 
 add_custom_command(OUTPUT run

+ 28 - 3
Makefile

@@ -1,7 +1,32 @@
+# disable kvm to debug triple faults
+QEMU_ARGS=-drive file=build/boot.img,format=raw -no-reboot -no-shutdown -enable-kvm #-d cpu_reset,int
 .PHONY: run
-run:
-	-rm build/boot.img.lock
-	-bochs -f bochs.conf
+run: build
+	qemu-system-i386 $(QEMU_ARGS) -display curses -S -s
+.PHONY: srun
+srun: build
+	qemu-system-i386 $(QEMU_ARGS) -display none -S -s -serial mon:stdio
+.PHONY: nativerun
+nativerun: build
+	qemu-system-i386 $(QEMU_ARGS) -display none -serial mon:stdio
+
+.PHONY: configure
+configure:
+	cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug
+	cp build/compile_commands.json .
+
+.PHONY: build
+build:
+	cmake --build build --target boot.img
+
+.PHONY: clean
+clean:
+	-rm -rf build
+	-rm compile_commands.json
+
+.PHONY: debug
+debug:
+	gdb --symbols=build/kernel.out --init-eval-command 'set pagination off' --init-eval-command 'target remote:1234' --eval-command 'hbr kernel_main' --eval-command 'c'
 
 build/boot.vdi: build/boot.img
 	-rm build/boot.vdi

+ 14 - 0
cross-compile.cmake

@@ -0,0 +1,14 @@
+set(CMAKE_SYSTEM_NAME Linux)
+
+set(TOOLCHAIN_PATH_AND_PREFIX "/usr/local/x86_64-elf-gcc/bin/x86_64-elf-")
+
+set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}gcc)
+set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}g++)
+set(CMAKE_AR ${TOOLCHAIN_PATH_AND_PREFIX}ar)
+set(CMAKE_LINKER ${TOOLCHAIN_PATH_AND_PREFIX}ld)
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
+SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+# for libraries and headers in the target directories
+SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

+ 42 - 0
doc/malloc.md

@@ -1,3 +1,45 @@
 [参考链接1](https://blog.codinglabs.org/articles/a-malloc-tutorial.html#212-%E9%A1%B5%E4%B8%8E%E5%9C%B0%E5%9D%80%E6%9E%84%E6%88%90)
 [malloc的glibc实现](https://repo.or.cz/glibc.git/blob/HEAD:/malloc/malloc.c)
 [How the kernel manage your memory](https://manybutfinite.com/post/how-the-kernel-manages-your-memory/)
+
+## 地址空间分区
+从0x00000000到0x3fffffff为内核空间
+
+从0x40000000到0xffffffff为用户空间
+
+### 内核空间:
+0x00000000到0x2fffffff为动态映射区
+0x30000000到0x3fffffff为永久映射区,这个区域的内存在与物理页进行映射后不会被交换出地址空间
+
+## 物理内存分配
+0x00000000-0x00000fff:内核页目录
+0x00001000-0x00001fff:空白页
+
+## 大致思路:
+每个进程(包括内核)拥有一个struct mm,用于记录自身的虚拟地址映射状况
+
+struct mm拥有struct page的链表,对应虚拟地址实际映射的物理页
+
+struct mm的项目还可包括copy on write的页
+
+发生缺页中断时,内核通过该触发中断的进程的struct mm检查页权限、是否需要复制页等
+
+若因权限不足而触发中断,则内核中断用户进程执行,或内核panic
+
+若因页已被交换出内存,则内核将页换回内存,继续进程执行
+
+若因页为copy on write页,写入时触发中断,则将页复制一份,继续进程执行
+
+### 内核提供几个接口
+1. alloc_page从页位图中找出未被使用的物理页返回
+2. p_map用于将物理页映射到指定的虚拟地址
+3. kmap用于给出物理页,将其映射到一个未被该进程使用的虚拟地址
+
+### 分配内存
+通过kmap将空白页映射到某虚拟地址上,并开启copy on write,
+随后则可以直接对该页进行读写,写入时内核中断自动进行页的分配
+
+因此,换页中断的处理非常重要
+
+## 注
+分页相关的内存数据结构应始终被映射到永久映射区,并且该映射应在页表创建时被完成

+ 12 - 0
global_find.sh

@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# $1: text to find
+# $2: file extension
+do_find()
+{
+    for ext in $2; do
+        find src include -name "*.$ext" -exec grep -n -H -T -i "$1" {} \;
+    done
+}
+
+do_find "$1" "c h cpp hpp s"

+ 6 - 13
include/asm/boot.h

@@ -2,7 +2,10 @@
 
 #include <types/stdint.h>
 
-struct gdt_descriptor {
+#define KERNEL_EARLY_STACK_ADDR ((phys_ptr_t)0x01000000)
+#define KERNEL_EARLY_STACK_SIZE ((size_t)0x100000)
+
+struct __attribute__((__packed__)) gdt_descriptor {
     uint16_t size;
     uint32_t address;
 };
@@ -11,20 +14,10 @@ extern struct gdt_descriptor asm_gdt_descriptor;
 
 extern uint32_t check_a20_on(void);
 
-struct e820_mem_map_entry_20 {
-    uint64_t base;
-    uint64_t len;
-    uint32_t type;
-};
-
-struct e820_mem_map_entry_24 {
-    struct e820_mem_map_entry_20 in;
-    uint32_t acpi_extension_attr;
-};
+extern struct mem_size_info asm_mem_size_info;
 
 extern uint8_t asm_e820_mem_map[1024];
 extern uint32_t asm_e820_mem_map_count;
 extern uint32_t asm_e820_mem_map_entry_size;
 
-#define e820_mem_map_20 ((struct e820_mem_map_entry_20*)asm_e820_mem_map)
-#define e820_mem_map_24 ((struct e820_mem_map_entry_24*)asm_e820_mem_map)
+extern uint32_t asm_kernel_size;

+ 5 - 2
include/asm/port_io.h

@@ -2,6 +2,8 @@
 
 #include <types/types.h>
 
+typedef uint16_t port_id_t;
+
 #define PORT_PIC1 (0x20)
 #define PORT_PIC2 (0xa0)
 #define PORT_PIC1_COMMAND (PORT_PIC1)
@@ -21,12 +23,13 @@
 extern "C" {
 #endif
 
-extern void asm_outb(uint16_t port_number, uint8_t data);
-extern uint8_t asm_inb(uint16_t port_number);
+extern void asm_outb(port_id_t port_number, uint8_t data);
+extern uint8_t asm_inb(port_id_t port_number);
 
 extern void asm_hlt(void);
 extern void asm_cli(void);
 extern void asm_sti(void);
+extern void asm_enable_sse(void);
 
 #ifdef __cplusplus
 }

+ 27 - 0
include/asm/sys.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include <kernel/mem.h>
+#include <types/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void asm_enable_paging(page_directory_entry* pd_addr);
+
+phys_ptr_t current_pd(void);
+
+// the limit should be set on the higher 16bit
+// e.g. (n * sizeof(segment_descriptor) - 1) << 16
+// the lower bit off the limit is either 0 or 1
+// indicating whether or not to enable interrupt
+// after loading gdt
+void asm_load_gdt(uint32_t limit, phys_ptr_t addr);
+
+void asm_load_tr(uint16_t index);
+
+extern void* __real_kernel_end;
+
+#ifdef __cplusplus
+}
+#endif

+ 1 - 0
include/kernel/errno.h

@@ -8,3 +8,4 @@ extern uint32_t* _get_errno(void);
 
 #define ENOMEM 0
 #define ENOTFOUND 1
+#define EEXIST (1 << 1)

+ 13 - 0
include/kernel/hw/serial.h

@@ -0,0 +1,13 @@
+#pragma once
+#include <asm/port_io.h>
+
+#define PORT_SERIAL0 (0x3f8)
+#define PORT_SERIAL1 (0x2f8)
+
+int32_t init_serial_port(port_id_t port);
+
+int32_t is_serial_has_data(port_id_t port);
+uint8_t serial_read_data(port_id_t port);
+
+int32_t is_serial_ready_for_transmition(port_id_t port);
+void serial_send_data(port_id_t port, uint8_t data);

+ 29 - 2
include/kernel/interrupt.h

@@ -17,6 +17,24 @@ struct regs_32 {
     uint32_t eax;
 };
 
+// present: When set, the page fault was caused by a page-protection violation.
+//          When not set, it was caused by a non-present page.
+// write:   When set, the page fault was caused by a write access.
+//          When not set, it was caused by a read access.
+// user:    When set, the page fault was caused while CPL = 3.
+//          This does not necessarily mean that the page fault was a privilege violation.
+// from https://wiki.osdev.org/Exceptions#Page_Fault
+struct page_fault_error_code {
+    uint32_t present : 1;
+    uint32_t write : 1;
+    uint32_t user : 1;
+    uint32_t reserved_write : 1;
+    uint32_t instruction_fetch : 1;
+    uint32_t protection_key : 1;
+    uint32_t shadow_stack : 1;
+    uint32_t software_guard_extensions : 1;
+};
+
 // external interrupt handler function
 // stub in assembly MUST be called irqN
 #define SET_UP_IRQ(N, SELECTOR)        \
@@ -48,12 +66,13 @@ struct IDT_entry {
 extern struct IDT_entry IDT[256];
 #endif
 
-void init_idt();
+void init_idt(void);
+void init_pic(void);
 
 // idt_descriptor: uint16_t[3]
 // [0] bit 0 :15 => limit
 // [1] bit 16:47 => address
-extern void asm_load_idt(uint16_t idt_descriptor[3]);
+extern void asm_load_idt(uint16_t idt_descriptor[3], int sti);
 
 void int13_handler(
     struct regs_32 s_regs,
@@ -62,6 +81,14 @@ void int13_handler(
     uint16_t cs,
     uint32_t eflags);
 
+void int14_handler(
+    linr_ptr_t l_addr,
+    struct regs_32 s_regs,
+    struct page_fault_error_code error_code,
+    void* v_eip,
+    uint16_t cs,
+    uint32_t eflags);
+
 void irq0_handler(void);
 void irq1_handler(void);
 void irq2_handler(void);

+ 251 - 5
include/kernel/mem.h

@@ -1,22 +1,137 @@
 #pragma once
 
+#include <types/size.h>
 #include <types/stdint.h>
 
+#define PAGE_SIZE (4096)
+#define KERNEL_IDENTICALLY_MAPPED_AREA_LIMIT ((void*)0x30000000)
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+// in mem.c
+extern struct mm* kernel_mm_head;
+
 // don't forget to add the initial 1m to the total
 struct mem_size_info {
     uint16_t n_1k_blks; // memory between 1m and 16m in 1k blocks
     uint16_t n_64k_blks; // memory above 16m in 64k blocks
 };
 
-extern struct mem_size_info asm_mem_size_info;
+struct e820_mem_map_entry_20 {
+    uint64_t base;
+    uint64_t len;
+    uint32_t type;
+};
+
+struct e820_mem_map_entry_24 {
+    struct e820_mem_map_entry_20 in;
+    uint32_t acpi_extension_attr;
+};
+
+/*
+ * page directory entry
+ *
+ * p   : present (1)
+ * rw  : allow write (1)
+ * us  : allow user access (1)
+ * pwt : todo
+ * pcd : todo
+ * a   : accessed for linear address translation (1)
+ * d   : dirty (1) (ignored)
+ * ps  : use 4MiB pages (ignored)
+ * addr: page table address
+ */
+struct page_directory_entry_in {
+    uint32_t p : 1;
+    uint32_t rw : 1;
+    uint32_t us : 1;
+    uint32_t pwt : 1;
+    uint32_t pcd : 1;
+    uint32_t a : 1;
+    uint32_t d : 1;
+    uint32_t ps : 1;
+    uint32_t ignored : 4;
+    page_t pt_page : 20;
+};
+
+typedef union page_directory_entry {
+    uint32_t v;
+    struct page_directory_entry_in in;
+} page_directory_entry;
+
+/*
+ * page table entry
+ *
+ * p   : present (1)
+ * rw  : allow write (1)
+ * us  : allow user access (1)
+ * pwt : todo
+ * pcd : todo
+ * a   : accessed for linear address translation (1)
+ * d   : dirty (1)
+ * pat : todo (ignored)
+ * g   : used in cr4 mode (ignored)
+ * addr: physical memory address
+ */
+struct page_table_entry_in {
+    uint32_t p : 1;
+    uint32_t rw : 1;
+    uint32_t us : 1;
+    uint32_t pwt : 1;
+    uint32_t pcd : 1;
+    uint32_t a : 1;
+    uint32_t d : 1;
+    uint32_t pat : 1;
+    uint32_t g : 1;
+    uint32_t ignored : 3;
+    page_t page : 20;
+};
+
+typedef union page_table_entry {
+    uint32_t v;
+    struct page_table_entry_in in;
+} page_table_entry;
+
+struct page_attr {
+    uint32_t read : 1;
+    uint32_t write : 1;
+    uint32_t system : 1;
+    uint32_t cow : 1;
+};
+
+struct page {
+    page_t phys_page_id;
+    size_t* ref_count;
+    struct page_attr attr;
+    struct page* next;
+};
+
+struct mm_attr {
+    uint32_t read : 1;
+    uint32_t write : 1;
+    uint32_t system : 1;
+};
+
+struct mm {
+    linr_ptr_t start;
+    size_t len;
+    struct mm_attr attr;
+    struct page* pgs;
+    struct mm* next;
+    page_directory_entry* pd;
+};
+
+// in kernel_main.c
+extern uint8_t e820_mem_map[1024];
+extern uint32_t e820_mem_map_count;
+extern uint32_t e820_mem_map_entry_size;
+extern uint32_t kernel_size;
+extern struct mem_size_info mem_size_info;
 
-// TODO: decide heap start address according
-//   to user's memory size
-#define HEAP_START ((void*)0x01000000)
+#define KERNEL_HEAP_START ((void*)0x30000000)
+#define KERNEL_HEAP_LIMIT ((void*)0x40000000)
 
 struct mem_blk_flags {
     uint8_t is_free;
@@ -33,12 +148,143 @@ struct mem_blk {
     uint8_t data[4];
 };
 
-void init_heap(void);
+int init_heap(void);
 
 void* k_malloc(size_t size);
 
 void k_free(void* ptr);
 
+// translate physical address to virtual(mapped) address
+void* p_ptr_to_v_ptr(phys_ptr_t p_ptr);
+
+// translate linear address to physical address
+phys_ptr_t l_ptr_to_p_ptr(struct mm* mm_area, linr_ptr_t v_ptr);
+
+// translate virtual(mapped) address to physical address
+phys_ptr_t v_ptr_to_p_ptr(void* v_ptr);
+
+// check if the l_ptr is contained in the area
+// @return GB_OK if l_ptr is in the area
+//         GB_FAILED if not
+int is_l_ptr_valid(struct mm* mm_area, linr_ptr_t l_ptr);
+
+// find the corresponding page the l_ptr pointing to
+// @return the pointer to the struct if found, NULL if not found
+struct page* find_page_by_l_ptr(struct mm* mm_area, linr_ptr_t l_ptr);
+
+static inline page_t phys_addr_to_page(phys_ptr_t ptr)
+{
+    return ptr >> 12;
+}
+
+static inline pd_i_t page_to_pd_i(page_t p)
+{
+    return p >> 10;
+}
+
+static inline pt_i_t page_to_pt_i(page_t p)
+{
+    return p & (1024 - 1);
+}
+
+static inline phys_ptr_t page_to_phys_addr(page_t p)
+{
+    return p << 12;
+}
+
+static inline pd_i_t linr_addr_to_pd_i(linr_ptr_t ptr)
+{
+    return page_to_pd_i(phys_addr_to_page(ptr));
+}
+
+static inline pd_i_t linr_addr_to_pt_i(linr_ptr_t ptr)
+{
+    return page_to_pt_i(phys_addr_to_page(ptr));
+}
+
+static inline page_directory_entry* lptr_to_pde(struct mm* mm, linr_ptr_t l_ptr)
+{
+    return mm->pd + linr_addr_to_pd_i((phys_ptr_t)l_ptr);
+}
+
+static inline page_table_entry* lptr_to_pte(struct mm* mm, linr_ptr_t l_ptr)
+{
+    page_directory_entry* pde = lptr_to_pde(mm, l_ptr);
+    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
+    return pte + linr_addr_to_pt_i((phys_ptr_t)l_ptr);
+}
+
+static inline page_directory_entry* lp_to_pde(struct mm* mm, linr_ptr_t l_ptr)
+{
+    phys_ptr_t p_ptr = l_ptr_to_p_ptr(mm, l_ptr);
+    page_directory_entry* pde = mm->pd + linr_addr_to_pd_i(p_ptr);
+    return pde;
+}
+
+// get the corresponding pte for the linear address
+// for example: l_ptr = 0x30001000 will return the pte including the page it is mapped to
+static inline page_table_entry* lp_to_pte(struct mm* mm, linr_ptr_t l_ptr)
+{
+    phys_ptr_t p_ptr = l_ptr_to_p_ptr(mm, l_ptr);
+
+    page_directory_entry* pde = lp_to_pde(mm, l_ptr);
+    phys_ptr_t p_pt = page_to_phys_addr(pde->in.pt_page);
+
+    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(p_pt);
+    pte += linr_addr_to_pt_i(p_ptr);
+
+    return pte;
+}
+
+// map the page to the end of the mm_area in pd
+int k_map(
+    struct mm* mm_area,
+    struct page* page,
+    int read,
+    int write,
+    int priv,
+    int cow);
+
+// allocate a raw page
+page_t alloc_raw_page(void);
+
+// allocate a struct page together with the raw page
+struct page* allocate_page(void);
+
+#define KERNEL_PAGE_DIRECTORY_ADDR ((page_directory_entry*)0x00000000)
+
+void init_mem(void);
+
+#define KERNEL_CODE_SEGMENT (0x08)
+#define KERNEL_DATA_SEGMENT (0x10)
+#define USER_CODE_SEGMENT (0x18)
+#define USER_DATA_SEGMENT (0x20)
+
+#define SD_TYPE_CODE_SYSTEM (0x9a)
+#define SD_TYPE_DATA_SYSTEM (0x92)
+
+#define SD_TYPE_CODE_USER (0xfa)
+#define SD_TYPE_DATA_USER (0xf2)
+
+#define SD_TYPE_TSS (0x89)
+
+typedef struct segment_descriptor_struct {
+    uint64_t limit_low : 16;
+    uint64_t base_low : 16;
+    uint64_t base_mid : 8;
+    uint64_t access : 8;
+    uint64_t limit_high : 4;
+    uint64_t flags : 4;
+    uint64_t base_high : 8;
+} segment_descriptor;
+
+void create_segment_descriptor(
+    segment_descriptor* sd,
+    uint32_t base,
+    uint32_t limit,
+    uint32_t flags,
+    uint32_t access);
+
 #ifdef __cplusplus
 }
 #endif

+ 15 - 0
include/kernel/process.h

@@ -0,0 +1,15 @@
+#pragma once
+
+#include <kernel/mem.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct process {
+    struct mm* mm;
+};
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 0
include/kernel/stdio.h

@@ -14,6 +14,11 @@
 extern "C" {
 #endif
 
+void* memcpy(void* dst, const void* src, size_t n);
+void* memset(void* dst, int c, size_t n);
+size_t strlen(const char* str);
+char* strncpy(char* dst, const char* src, size_t max_n);
+
 ssize_t
 snprint_decimal(
     char* buf,

+ 16 - 0
include/kernel/task.h

@@ -0,0 +1,16 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tss32_t {
+    uint32_t backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
+    uint32_t eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
+    uint32_t es, cs, ss, ds, fs, gs;
+    uint32_t ldtr, iomap;
+};
+
+#ifdef __cplusplus
+}
+#endif

+ 34 - 0
include/kernel/tty.h

@@ -0,0 +1,34 @@
+#pragma once
+#include <asm/port_io.h>
+
+#define STRUCT_TTY_NAME_LEN (32)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tty;
+
+struct tty_operations
+{
+    void (*put_char)(struct tty* p_tty, char c);
+};
+
+struct tty
+{
+    char name[STRUCT_TTY_NAME_LEN];
+    struct tty_operations* ops;
+    char data[12];
+};
+
+// in kernel_main.c
+extern struct tty* console;
+
+void tty_print(struct tty* p_tty, const char* str);
+
+int make_serial_tty(struct tty* p_tty, int id);
+int make_vga_tty(struct tty* p_tty);
+
+#ifdef __cplusplus
+}
+#endif

+ 3 - 1
include/kernel/vga.h

@@ -8,6 +8,8 @@
 extern "C" {
 #endif
 
+#define VGA_CHAR_COLOR_WHITE (0x0fU)
+
 struct vga_char {
     int8_t c;
     uint8_t color;
@@ -18,7 +20,7 @@ struct vga_char {
 #define VGA_SCREEN_HEIGHT_IN_CHARS (25U)
 
 void vga_put_char(struct vga_char* c);
-void vga_printk(const char* str, uint8_t color);
+void vga_print(const char* str, uint8_t color);
 
 #ifdef __cplusplus
 }

+ 11 - 1
include/kernel_main.h

@@ -1,5 +1,15 @@
 #pragma once
 
-#define MAKE_BREAK_POINT() asm volatile("xchgw %bx, %bx")
+static inline void __break_point(void)
+{
+    asm volatile("xchgw %bx, %bx");
+}
+
+#define MAKE_BREAK_POINT() __break_point()
+
+#define KERNEL_STACK_SIZE (16 * 1024)
+#define KERNEL_STACK_SEGMENT (0x10)
+
+#define KERNEL_START_ADDR (0x00100000)
 
 void kernel_main(void);

+ 7 - 0
include/types/bitmap.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#include <types/stdint.h>
+
+int bm_test(char* bm, size_t n);
+void bm_set(char* bm, size_t n);
+void bm_clear(char* bm, size_t n);

+ 9 - 0
include/types/list.h

@@ -0,0 +1,9 @@
+#pragma once
+
+#define LIST_LIKE_AT(type, list_like, pos, result_name) \
+    type* result_name = list_like; \
+    {                   \
+        size_t _tmp_pos = (pos); \
+        while (_tmp_pos--) \
+            result_name = result_name->next; \
+    }

+ 13 - 0
include/types/size.h

@@ -2,5 +2,18 @@
 
 #include "stdint.h"
 
+#define __32bit_system
+
+#ifdef __32bit_system
 typedef uint32_t ptr_t;
 typedef int32_t diff_t;
+#elif
+typedef uint64_t ptr_t;
+typedef int64_t diff_t;
+#endif
+
+typedef ptr_t phys_ptr_t;
+typedef ptr_t linr_ptr_t;
+typedef size_t page_t;
+typedef size_t pd_i_t;
+typedef size_t pt_i_t;

+ 4 - 0
include/types/status.h

@@ -0,0 +1,4 @@
+#pragma once
+
+#define GB_OK (0)
+#define GB_FAILED (1)

+ 3 - 1
include/types/stdint.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#define NULL ((void*)0)
+
 typedef __INT8_TYPE__ int8_t;
 typedef __INT16_TYPE__ int16_t;
 typedef __INT32_TYPE__ int32_t;
@@ -10,7 +12,7 @@ typedef __UINT16_TYPE__ uint16_t;
 typedef __UINT32_TYPE__ uint32_t;
 typedef __UINT64_TYPE__ uint64_t;
 
-typedef uint32_t size_t;
+typedef __SIZE_TYPE__ size_t;
 typedef int32_t ssize_t;
 
 typedef uint32_t time_t;

+ 2 - 0
include/types/types.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#include "bitmap.h"
 #include "buffer.h"
 #include "size.h"
+#include "status.h"
 #include "stdint.h"

+ 21 - 4
ldscript.ld

@@ -19,15 +19,26 @@ SECTIONS
         BYTE(0xaa);
     } > WHOLE
 
-    .text 0x600 : AT(0x200)
+    .text.loader 0x7e00 : AT(0x200)
     {
         *(.text.loader)
+        __kernel_size_offset = .;
+        LONG(LOADADDR(.data) - LOADADDR(.text));
+        __loader_end = .;
+    } > WHOLE
+
+    .text 0x100000 : AT(LOADADDR(.text.loader) + SIZEOF(.text.loader))
+    {
+        __real_kernel_start = .;
         *(.text*)
         *(.rodata*)
     } > WHOLE
 
-    .data :
+    .data : AT(LOADADDR(.text) + ADDR(.data) - ADDR(.text))
     {
+        asm_kernel_size = .;
+        LONG(__real_kernel_end - ADDR(.text));
+
         start_ctors = .;
         KEEP(*(.init_array));
         KEEP(*(SORT_BY_INIT_PRIORITY(.init_array*)));
@@ -43,7 +54,12 @@ SECTIONS
         *(.bss*)
     } > WHOLE
 
-	/* Stabs debugging sections.  */
+    .kernel_end :
+    {
+        __real_kernel_end = .;
+    } > WHOLE
+
+    /* Stabs debugging sections.  */
     .stab          0 : { *(.stab) }
     .stabstr       0 : { *(.stabstr) }
     .stab.excl     0 : { *(.stab.excl) }
@@ -84,6 +100,7 @@ SECTIONS
 
     /DISCARD/ :
     {
-        *(.comment)
+        *(.fini_array*)
+        *(.eh_frame*)
     }
 }

+ 40 - 1
src/asm/interrupt.s

@@ -2,12 +2,47 @@
 
 .text
 
+.globl int6
+.type  int6 @function
+int6:
+    xchgw %bx, %bx
+    pushal
+    call int6_handler
+    popal
+
+    iret
+
+.globl int8
+.type  int8 @function
+int8:
+    nop
+    iret
+
 .globl int13
 .type  int13 @function
 int13:
+    xchgw %bx, %bx
     pushal
     call int13_handler
     popal
+
+# remove the 32bit error code from stack
+    addl $4, %esp
+    iret
+
+.globl int14
+.type  int14 @function
+int14:
+    xchgw %bx, %bx
+    pushal
+    movl %cr2, %eax
+    pushl %eax
+    call int14_handler
+    popl %eax
+    popal
+
+# remove the 32bit error code from stack
+    addl $4, %esp
     iret
 
 .globl irq0
@@ -25,7 +60,7 @@ irq1:
     call irq1_handler
     popal
     iret
-    
+
 .globl irq2
 .type  irq2 @function
 irq2:
@@ -130,5 +165,9 @@ irq15:
 asm_load_idt:
     movl 4(%esp), %edx
     lidt (%edx)
+    movl 8(%esp), %edx
+    cmpl $0, %edx
+    je asm_load_idt_skip
     sti
+asm_load_idt_skip:
     ret

+ 11 - 0
src/asm/port_io.s

@@ -40,3 +40,14 @@ asm_cli:
 asm_sti:
     sti
     ret
+
+.globl asm_enable_sse
+.type  asm_enable_sse @function
+asm_enable_sse:
+	movl %cr0, %eax
+	orl $0b10, %eax
+	movl %eax, %cr0
+	movl %cr4, %eax
+	orl $0b11000000000, %eax
+	movl %eax, %cr4
+	ret

+ 76 - 0
src/asm/sys.s

@@ -0,0 +1,76 @@
+.code32
+
+.text
+
+.global asm_enable_paging
+.type   asm_enable_paging @function
+asm_enable_paging:
+    cli
+    // page directory address
+    movl 4(%esp), %eax
+    movl %eax, %cr3
+
+    movl %cr0, %eax
+    // SET PE, WP, PG
+    orl $0x80010001, %eax
+    movl %eax, %cr0
+
+    ret
+
+.global current_pd
+.type   current_pd @function
+current_pd:
+    movl %cr3, %eax
+    ret
+
+.global asm_load_gdt
+.type   asm_load_gdt @function
+asm_load_gdt:
+    cli
+    leal 6(%esp), %eax
+    lgdt (%eax)
+    ljmp $0x08, $_asm_load_gdt_fin
+_asm_load_gdt_fin:
+	movw 4(%esp), %ax
+	cmpw $0, %ax
+	je _asm_load_gdt_fin_ret
+    sti
+_asm_load_gdt_fin_ret:
+    ret
+
+.global asm_load_tr
+.type   asm_load_tr @function
+asm_load_tr:
+    cli
+    movl 4(%esp), %eax
+    orl $0, %eax
+    ltr %ax
+    sti
+    ret
+
+
+# examples for going ring 3
+_test_user_space_program:
+    movl $0x1919810, %eax
+    movl $0xc48c, %ecx
+_reap:
+    cmpl $1000, (%ecx)
+    jl _reap
+_fault:
+    cli
+
+go_user_space_example:
+    movl $((4 * 8) | 3), %eax
+    movw %ax, %ds
+    movw %ax, %es
+    movw %ax, %fs
+    movw %ax, %gs
+
+    movl %esp, %eax
+    pushl $((4 * 8) | 3)
+    pushl %eax
+    pushf
+    pushl $((3 * 8) | 3)
+    pushl $_test_user_space_program
+
+    iret

+ 116 - 18
src/boot.s

@@ -21,7 +21,7 @@ real_start:
 
     call read_data
 
-    ljmp $0x0060, $(loader_start-loader_start)
+    ljmp $0x07e0, $(loader_start-loader_start)
 
 die:
     hlt
@@ -39,9 +39,11 @@ string_hello:
 
 read_data_pack:
     .byte 0x10, 0
-    .word 32     # block count (read 16k)
+# TODO!!!
+# read more!
+    .word 64     # block count (read 32k)
     .word 0x0000 # offset address
-    .word 0x0060 # segment address
+    .word 0x07e0 # segment address
     .long 1      # LBA to read
 
 stack_edge:
@@ -143,7 +145,7 @@ _load_gdt:
     orl $1, %eax
     movl %eax, %cr0
 
-    ljmp $0x08, $0x0600 + (start_32bit-loader_start)
+    ljmp $0x08, $0x7e00 + (start_32bit-loader_start)
 
 .code32
 
@@ -155,12 +157,92 @@ start_32bit:
     movw %ax, %gs
     movw %ax, %ss
 
-# set up stack
-# in order to align 16 byte
-# set stack base address at
-# 0x003ffff0
-    movl $0x03fffff0, %ebp
-    movl $0x03fffff0, %esp
+# set up early stack at 0x001000000
+    movl $0x01000000, %ebp
+    movl $0x01000000, %esp
+
+setup_early_kernel_page_table:
+# set up early kernel page table
+
+# the early kernel page directory is located at physical
+# address 0x00000000, size 4k, and the empty page is at
+# 0x5000-0x5fff, so we fill the first 6KiB
+    movl $0x00000000, %eax
+    movl $0x6000, %ecx
+    call _fill_zero
+
+# map the first 16MiB identically
+# 0x0000-0x0fff: early kernel pd
+# 0x1000-0x4fff: pde 0 - 4
+    movl $0x00000000, %eax
+    movl $0x00001003, %ebx
+_fill_pde_loop:
+    movl %ebx, (%eax)
+    addl $4, %eax
+    addl $0x1000, %ebx
+    cmpl $0x5003, %ebx
+    jne _fill_pde_loop
+
+# then, create page tables
+    movl $0x00000003, %eax
+    movl $0x00001000, %ecx
+
+_create_page_table_loop1:
+    movl %eax, (%ecx)
+    addl $4, %ecx
+    addl $0x1000, %eax
+    cmpl $0x4ffc, %ecx
+    jle _create_page_table_loop1
+
+load_early_kernel_page_table:
+    movl $0x00000000, %eax
+    movl %eax, %cr3
+
+    movl %cr0, %eax
+    // SET PE, WP, PG
+    orl $0x80010001, %eax
+    movl %eax, %cr0
+
+    jmp start_move_kernel
+
+# quick call
+# %eax: address to fill
+# %ecx: byte count to fill
+_fill_zero:
+    movl %ecx, -4(%esp)
+    movl %eax, -8(%esp)
+
+_fill_zero_loop:
+    cmpl $0, %ecx
+    jz _fill_zero_end
+    subl $4, %ecx
+    movl $0, (%eax)
+    addl $4, %eax
+    jmp _fill_zero_loop
+
+_fill_zero_end:
+    movl -8(%esp), %eax
+    movl -4(%esp), %ecx
+    ret
+
+start_move_kernel:
+# move the kernel to 0x100000
+    movl $__loader_end, %eax
+    movl $__real_kernel_start, %ebx
+
+    movl $__kernel_size_offset, %ecx
+    movl (%ecx), %ecx
+    addl $__loader_end, %ecx
+    movl (%ecx), %ecx
+
+_move_kernel:
+    movl (%eax), %edx
+    movl %edx, (%ebx)
+    addl $4, %eax
+    addl $4, %ebx
+    subl $4, %ecx
+    cmpl $0, %ecx
+    jge _move_kernel
 
     call kernel_main
 
@@ -169,8 +251,8 @@ loader_halt:
     jmp loader_halt
 
 asm_gdt_descriptor:
-    .word (3 * 8) - 1 # size
-    .long 0x0600+(asm_gdt_table-loader_start)  # address
+    .word (5 * 8) - 1 # size
+    .long 0x7e00+(asm_gdt_table-loader_start)  # address
 
 .globl asm_gdt_descriptor
 .type asm_gdt_descriptor @object
@@ -179,20 +261,36 @@ asm_gdt_descriptor:
 asm_gdt_table:
     .8byte 0         # null descriptor
 
-    # code segment
-    .word 0x3fff     # limit 0 :15
+    # kernel code segment
+    .word 0xffff     # limit 0 :15
     .word 0x0000     # base  0 :15
     .byte 0x00       # base  16:23
     .byte 0x9a       # access
-    .byte 0b11000000 # flag and limit 16:20
+    .byte 0b11001111 # flag and limit 16:20
     .byte 0x00       # base 24:31
 
-    # data segment
-    .word 0x3fff     # limit 0 :15
+    # kernel data segment
+    .word 0xffff     # limit 0 :15
     .word 0x0000     # base  0 :15
     .byte 0x00       # base  16:23
     .byte 0x92       # access
-    .byte 0b11000000 # flag and limit 16:20
+    .byte 0b11001111 # flag and limit 16:20
+    .byte 0x00       # base 24:31
+
+    # user code segment
+    .word 0xffff     # limit 0 :15
+    .word 0x0000     # base  0 :15
+    .byte 0x00       # base  16:23
+    .byte 0xfa       # access
+    .byte 0b11001111 # flag and limit 16:20
+    .byte 0x00       # base 24:31
+
+    # user data segment
+    .word 0xffff     # limit 0 :15
+    .word 0x0000     # base  0 :15
+    .byte 0x00       # base  16:23
+    .byte 0xf2       # access
+    .byte 0b11001111 # flag and limit 16:20
     .byte 0x00       # base 24:31
 
 asm_mem_size_info:

+ 5 - 2
src/kernel/event/event.cpp

@@ -1,4 +1,4 @@
-#include "kernel/vga.h"
+#include <kernel/tty.h>
 #include <asm/port_io.h>
 #include <kernel/event/event.h>
 #include <kernel/input/input_event.h>
@@ -24,11 +24,14 @@ void dispatch_event(void)
     char buf[1024];
     auto& input_event_queue = event::input_event_queue();
 
+    char* ptr = (char*)0x8000000;
+    *ptr = 0xff;
+
     while (!input_event_queue.empty()) {
         for (auto iter = input_event_queue.begin(); iter != input_event_queue.end(); ++iter) {
             const auto& item = *iter;
             snprintf(buf, 1024, "\rinput event: type%x, data%x, code%x\r", item.type, item.data, item.code);
-            vga_printk(buf, 0x0fu);
+            tty_print(console, buf);
             input_event_queue.erase(iter);
         }
     }

+ 53 - 0
src/kernel/hw/serial.c

@@ -0,0 +1,53 @@
+#include <kernel/hw/serial.h>
+#include <asm/port_io.h>
+
+int32_t init_serial_port(port_id_t port)
+{
+    // taken from osdev.org
+
+   asm_outb(port + 1, 0x00);    // Disable all interrupts
+   asm_outb(port + 3, 0x80);    // Enable DLAB (set baud rate divisor)
+   // TODO: set baud rate
+   asm_outb(port + 0, 0x00);    // Set divisor to 0 -3- (lo byte) 115200 -38400- baud
+   asm_outb(port + 1, 0x00);    //                  (hi byte)
+   asm_outb(port + 3, 0x03);    // 8 bits, no parity, one stop bit
+   asm_outb(port + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold
+   // TODO: IRQ disabled
+   // asm_outb(port + 4, 0x0B);    // IRQs enabled, RTS/DSR set
+   asm_outb(port + 4, 0x1E);    // Set in loopback mode, test the serial chip
+   asm_outb(port + 0, 0xAE);    // Test serial chip (send byte 0xAE and check if serial returns same byte)
+
+   // Check if serial is faulty (i.e: not same byte as sent)
+   if(asm_inb(port + 0) != 0xAE) {
+      return 1;
+   }
+
+   // If serial is not faulty set it in normal operation mode
+   // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
+   asm_outb(port + 4, 0x0F);
+   return 0;
+}
+
+int32_t is_serial_has_data(port_id_t port)
+{
+    return asm_inb(port + 5) & 1;
+}
+
+uint8_t serial_read_data(port_id_t port)
+{
+    while (is_serial_has_data(port) == 0)
+        ;
+    return asm_inb(port);
+}
+
+int32_t is_serial_ready_for_transmition(port_id_t port)
+{
+    return asm_inb(port + 5) & 0x20;
+}
+
+void serial_send_data(port_id_t port, uint8_t data)
+{
+    while (is_serial_ready_for_transmition(port) == 0)
+        ;
+    return asm_outb(port, data);
+}

+ 1 - 0
src/kernel/hw/timer.c

@@ -1,4 +1,5 @@
 #include <asm/port_io.h>
+#include <kernel/tty.h>
 #include <kernel/hw/timer.h>
 
 static time_t _current_ticks = 0;

+ 127 - 12
src/kernel/interrupt.c

@@ -4,7 +4,9 @@
 #include <kernel/hw/keyboard.h>
 #include <kernel/hw/timer.h>
 #include <kernel/interrupt.h>
+#include <kernel/mem.h>
 #include <kernel/stdio.h>
+#include <kernel/tty.h>
 #include <kernel/vga.h>
 #include <kernel_main.h>
 
@@ -14,6 +16,28 @@ void init_idt()
 {
     asm_cli();
 
+    memset(IDT, 0x00, sizeof(IDT));
+
+    // invalid opcode
+    SET_IDT_ENTRY_FN(6, int6, 0x08);
+    // double fault
+    SET_IDT_ENTRY_FN(8, int8, 0x08);
+    // general protection
+    SET_IDT_ENTRY_FN(13, int13, 0x08);
+    // page fault
+    SET_IDT_ENTRY_FN(14, int14, 0x08);
+
+    uint16_t idt_descriptor[3];
+    idt_descriptor[0] = sizeof(struct IDT_entry) * 256;
+    *((uint32_t*)(idt_descriptor + 1)) = (ptr_t)IDT;
+
+    asm_load_idt(idt_descriptor, 0);
+}
+
+void init_pic(void)
+{
+    asm_cli();
+
     asm_outb(PORT_PIC1_COMMAND, 0x11); // edge trigger mode
     asm_outb(PORT_PIC1_DATA, 0x20); // start from int 0x20
     asm_outb(PORT_PIC1_DATA, 0x04); // PIC1 is connected to IRQ2 (1 << 2)
@@ -28,10 +52,6 @@ void init_idt()
     asm_outb(PORT_PIC1_DATA, 0x00);
     asm_outb(PORT_PIC2_DATA, 0x00);
 
-    // handle general protection fault (handle segmentation fault)
-    SET_IDT_ENTRY_FN(13, int13, 0x08);
-    // SET_IDT_ENTRY(0x0c, /* addr */ 0, 0x08);
-
     // 0x08 stands for kernel code segment
     SET_UP_IRQ(0, 0x08);
     SET_UP_IRQ(1, 0x08);
@@ -50,13 +70,37 @@ void init_idt()
     SET_UP_IRQ(14, 0x08);
     SET_UP_IRQ(15, 0x08);
 
-    uint16_t idt_descriptor[3];
-    idt_descriptor[0] = sizeof(struct IDT_entry) * 256;
-    *((uint32_t*)(idt_descriptor + 1)) = (ptr_t)IDT;
+    asm_sti();
+}
+
+void int6_handler(
+    struct regs_32 s_regs,
+    uint32_t error_code,
+    ptr_t eip,
+    uint16_t cs)
+{
+    char buf[512];
 
-    asm_load_idt(idt_descriptor);
+    tty_print(console, "---- INVALID OPCODE ----\n");
+
+    snprintf(
+        buf, 512,
+        "eax: %x, ebx: %x, ecx: %x, edx: %x\n"
+        "esp: %x, ebp: %x, esi: %x, edi: %x\n"
+        "eip: %x, cs: %x, error_code: %x   \n",
+        s_regs.eax, s_regs.ebx, s_regs.ecx,
+        s_regs.edx, s_regs.esp, s_regs.ebp,
+        s_regs.esi, s_regs.edi, eip,
+        cs, error_code);
+    tty_print(console, buf);
+
+    tty_print(console, "----   HALTING SYSTEM   ----");
+
+    asm_cli();
+    asm_hlt();
 }
 
+// general protection
 void int13_handler(
     struct regs_32 s_regs,
     uint32_t error_code,
@@ -64,9 +108,9 @@ void int13_handler(
     uint16_t cs,
     uint32_t eflags)
 {
-    char buf[512] = { 0 };
+    char buf[512];
 
-    vga_printk("---- SEGMENTATION FAULT ----\n", 0x0fu);
+    tty_print(console, "---- SEGMENTATION FAULT ----\n");
 
     snprintf(
         buf, 512,
@@ -78,14 +122,85 @@ void int13_handler(
         s_regs.edx, s_regs.esp, s_regs.ebp,
         s_regs.esi, s_regs.edi, eip,
         cs, error_code, eflags);
-    vga_printk(buf, 0x0fu);
+    tty_print(console, buf);
 
-    vga_printk("----   HALTING SYSTEM   ----", 0x0fu);
+    tty_print(console, "----   HALTING SYSTEM   ----");
 
     asm_cli();
     asm_hlt();
 }
 
+static size_t page_fault_times;
+
+// page fault
+void int14_handler(
+    linr_ptr_t l_addr,
+    struct regs_32 s_regs,
+    struct page_fault_error_code error_code,
+    void* v_eip,
+    uint16_t cs,
+    uint32_t eflags)
+{
+    MAKE_BREAK_POINT();
+    char buf[512];
+
+    ++page_fault_times;
+
+    // not present page, possibly mapped but not loaded
+    // or invalid address or just invalid address
+    // TODO: mmapping and swapping
+    if (error_code.present == 0) {
+        goto kill;
+    }
+
+    // kernel code
+    if (cs == KERNEL_CODE_SEGMENT) {
+        if (is_l_ptr_valid(kernel_mm_head, l_addr) != GB_OK) {
+            goto kill;
+        }
+        struct page* page = find_page_by_l_ptr(kernel_mm_head, l_addr);
+
+        // copy on write
+        if (error_code.write == 1 && page->attr.cow == 1) {
+            page_directory_entry* pde = kernel_mm_head->pd + linr_addr_to_pd_i(l_addr);
+            page_table_entry* pte = p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
+            pte += linr_addr_to_pt_i(l_addr);
+
+            // if it is a dying page
+            if (*page->ref_count == 1) {
+                page->attr.cow = 0;
+                pte->in.a = 0;
+                pte->in.rw = 1;
+                return;
+            }
+            // duplicate the page
+            page_t new_page = alloc_raw_page();
+            void* new_page_data = p_ptr_to_v_ptr(page_to_phys_addr(new_page));
+            memcpy(new_page_data, p_ptr_to_v_ptr(page_to_phys_addr(page->phys_page_id)), PAGE_SIZE);
+
+            pte->in.page = new_page;
+            pte->in.rw = 1;
+            pte->in.a = 0;
+
+            --*page->ref_count;
+
+            page->ref_count = (size_t*)k_malloc(sizeof(size_t));
+            *page->ref_count = 1;
+            page->attr.cow = 0;
+            page->phys_page_id = new_page;
+            return;
+        }
+    }
+
+kill:
+    snprintf(
+        buf, 512,
+        "killed: segmentation fault (eip: %x, cr2: %x, error_code: %x)", v_eip, l_addr, error_code);
+    tty_print(console, buf);
+    asm_cli();
+    asm_hlt();
+}
+
 void irq0_handler(void)
 {
     inc_tick();

+ 401 - 10
src/kernel/mem.c

@@ -1,12 +1,35 @@
+#include <asm/boot.h>
 #include <asm/port_io.h>
+#include <asm/sys.h>
 #include <kernel/errno.h>
 #include <kernel/mem.h>
+#include <kernel/stdio.h>
+#include <kernel/task.h>
 #include <kernel/vga.h>
 #include <kernel_main.h>
+#include <types/bitmap.h>
+#include <types/list.h>
+
+// static variables
+
+struct mm kernel_mm;
+struct mm* kernel_mm_head;
+
+// ---------------------
+
+// constant values
+
+#define EMPTY_PAGE_ADDR ((phys_ptr_t)0x5000)
+#define EMPTY_PAGE_END ((phys_ptr_t)0x6000)
+
+// ---------------------
 
 static void* p_start;
 static void* p_break;
 
+static size_t mem_size;
+static char mem_bitmap[1024 * 1024 / 8];
+
 static int32_t set_heap_start(void* start_addr)
 {
     p_start = start_addr;
@@ -15,6 +38,9 @@ static int32_t set_heap_start(void* start_addr)
 
 static int32_t brk(void* addr)
 {
+    if (addr >= KERNEL_HEAP_LIMIT) {
+        return GB_FAILED;
+    }
     p_break = addr;
     return 0;
 }
@@ -31,23 +57,18 @@ static void* sbrk(size_t increment)
     }
 }
 
-void init_heap(void)
+int init_heap(void)
 {
-    // start of the available address space
-    // TODO: adjust heap start address
-    //   according to user's memory size
-    set_heap_start(HEAP_START);
+    set_heap_start(KERNEL_HEAP_START);
 
-    if (brk(HEAP_START) != 0) {
-        vga_printk("Failed to initialize heap, halting...", 0x0fu);
-        MAKE_BREAK_POINT();
-        asm_cli();
-        asm_hlt();
+    if (brk(KERNEL_HEAP_START) != 0) {
+        return GB_FAILED;
     }
     struct mem_blk* p_blk = sbrk(0);
     p_blk->size = 4;
     p_blk->flags.has_next = 0;
     p_blk->flags.is_free = 1;
+    return GB_OK;
 }
 
 // @param start_pos position where to start finding
@@ -152,3 +173,373 @@ void k_free(void* ptr)
     blk->flags.is_free = 1;
     // TODO: fusion free blocks nearby
 }
+
+void* p_ptr_to_v_ptr(phys_ptr_t p_ptr)
+{
+    if (p_ptr <= 0x30000000) {
+        // memory below 768MiB is identically mapped
+        return (void*)p_ptr;
+    } else {
+        // TODO: address translation
+        MAKE_BREAK_POINT();
+        return (void*)0xffffffff;
+    }
+}
+
+phys_ptr_t l_ptr_to_p_ptr(struct mm* mm, linr_ptr_t v_ptr)
+{
+    if (mm == kernel_mm_head && v_ptr < (linr_ptr_t)KERNEL_IDENTICALLY_MAPPED_AREA_LIMIT) {
+        return (phys_ptr_t)v_ptr;
+    }
+    while (mm != NULL) {
+        if (v_ptr < mm->start || v_ptr >= mm->start + mm->len * 4096) {
+            goto next;
+        }
+        size_t offset = (size_t)(v_ptr - mm->start);
+        LIST_LIKE_AT(struct page, mm->pgs, offset / PAGE_SIZE, result);
+        return page_to_phys_addr(result->phys_page_id) + (offset % 4096);
+    next:
+        mm = mm->next;
+    }
+
+    // TODO: handle error
+    return 0xffffffff;
+}
+
+phys_ptr_t v_ptr_to_p_ptr(void* v_ptr)
+{
+    return l_ptr_to_p_ptr(kernel_mm_head, (linr_ptr_t)v_ptr);
+}
+
+static inline void mark_page(page_t n)
+{
+    bm_set(mem_bitmap, n);
+}
+
+static inline void free_page(page_t n)
+{
+    bm_clear(mem_bitmap, n);
+}
+
+static void mark_addr_len(phys_ptr_t start, size_t n)
+{
+    if (n == 0)
+        return;
+    page_t start_page = phys_addr_to_page(start);
+    page_t end_page = phys_addr_to_page(start + n + 4095);
+    for (page_t i = start_page; i < end_page; ++i)
+        mark_page(i);
+}
+
+static void free_addr_len(phys_ptr_t start, size_t n)
+{
+    if (n == 0)
+        return;
+    page_t start_page = phys_addr_to_page(start);
+    page_t end_page = phys_addr_to_page(start + n + 4095);
+    for (page_t i = start_page; i < end_page; ++i)
+        free_page(i);
+}
+
+static inline void mark_addr_range(phys_ptr_t start, phys_ptr_t end)
+{
+    mark_addr_len(start, end - start);
+}
+
+static inline void free_addr_range(phys_ptr_t start, phys_ptr_t end)
+{
+    free_addr_len(start, end - start);
+}
+
+page_t alloc_raw_page(void)
+{
+    for (page_t i = 0; i < 1024 * 1024; ++i) {
+        if (bm_test(mem_bitmap, i) == 0) {
+            mark_page(i);
+            return i;
+        }
+    }
+    return GB_FAILED;
+}
+
+struct page* allocate_page(void)
+{
+    // TODO: allocate memory on identically mapped area
+    struct page* p = (struct page*)k_malloc(sizeof(struct page));
+    memset(p, 0x00, sizeof(struct page));
+    p->phys_page_id = alloc_raw_page();
+    p->ref_count = (size_t*)k_malloc(sizeof(size_t));
+    return p;
+}
+
+static inline void make_page_table(page_table_entry* pt)
+{
+    memset(pt, 0x00, sizeof(page_table_entry) * 1024);
+}
+
+static inline void init_mem_layout(void)
+{
+    mem_size = 1024 * mem_size_info.n_1k_blks;
+    mem_size += 64 * 1024 * mem_size_info.n_64k_blks;
+
+    // mark kernel page directory
+    mark_addr_range(0x00000000, 0x00005000);
+    // mark empty page
+    mark_addr_range(EMPTY_PAGE_ADDR, EMPTY_PAGE_END);
+    // mark EBDA and upper memory as allocated
+    mark_addr_range(0x80000, 0xfffff);
+    // mark kernel
+    mark_addr_len(0x00100000, kernel_size);
+
+    if (e820_mem_map_entry_size == 20) {
+        struct e820_mem_map_entry_20* entry = (struct e820_mem_map_entry_20*)e820_mem_map;
+        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
+            if (entry->type != 1) {
+                mark_addr_len(entry->base, entry->len);
+            }
+        }
+    } else {
+        struct e820_mem_map_entry_24* entry = (struct e820_mem_map_entry_24*)e820_mem_map;
+        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
+            if (entry->in.type != 1) {
+                mark_addr_len(entry->in.base, entry->in.len);
+            }
+        }
+    }
+}
+
+int is_l_ptr_valid(struct mm* mm_area, linr_ptr_t l_ptr)
+{
+    while (mm_area != NULL) {
+        if (l_ptr >= mm_area->start && l_ptr < mm_area->start + mm_area->len * PAGE_SIZE) {
+            return GB_OK;
+        }
+        mm_area = mm_area->next;
+    }
+    return GB_FAILED;
+}
+
+struct page* find_page_by_l_ptr(struct mm* mm, linr_ptr_t l_ptr)
+{
+    if (mm == kernel_mm_head && l_ptr < (linr_ptr_t)KERNEL_IDENTICALLY_MAPPED_AREA_LIMIT) {
+        // TODO: make mm for identically mapped area
+        MAKE_BREAK_POINT();
+        return (struct page*)0xffffffff;
+    }
+    while (mm != NULL) {
+        if (l_ptr >= mm->start && l_ptr < mm->start + mm->len * 4096) {
+            size_t offset = (size_t)(l_ptr - mm->start);
+            LIST_LIKE_AT(struct page, mm->pgs, offset / PAGE_SIZE, result);
+            return result;
+        }
+        mm = mm->next;
+    }
+
+    // TODO: error handling
+    return NULL;
+}
+
+void map_raw_page_to_pte(
+    page_table_entry* pte,
+    page_t page,
+    int rw,
+    int priv)
+{
+    // set P bit
+    pte->v = 0x00000001;
+    pte->in.rw = (rw == 1);
+    pte->in.us = (priv == 1);
+    pte->in.page = page;
+}
+
+static void _map_raw_page_to_addr(
+    struct mm* mm_area,
+    page_t page,
+    int rw,
+    int priv)
+{
+    linr_ptr_t addr = (linr_ptr_t)mm_area->start + mm_area->len * 4096;
+    page_directory_entry* pde = mm_area->pd + linr_addr_to_pd_i(addr);
+    // page table not exist
+    if (!pde->in.p) {
+        // allocate a page for the page table
+        pde->in.p = 1;
+        pde->in.rw = 1;
+        pde->in.us = 0;
+        pde->in.pt_page = alloc_raw_page();
+
+        make_page_table((page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page)));
+    }
+
+    // map the page in the page table
+    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
+    pte += linr_addr_to_pt_i(addr);
+    map_raw_page_to_pte(pte, page, rw, priv);
+}
+
+// map page to the end of mm_area in pd
+int k_map(
+    struct mm* mm_area,
+    struct page* page,
+    int read,
+    int write,
+    int priv,
+    int cow)
+{
+    struct page* p_page_end = mm_area->pgs;
+    while (p_page_end != NULL && p_page_end->next != NULL)
+        p_page_end = p_page_end->next;
+
+    if (cow) {
+        // find its ancestor
+        while (page->attr.cow)
+            page = page->next;
+
+        // create a new page node
+        struct page* new_page = k_malloc(sizeof(struct page));
+
+        new_page->attr.read = (read == 1);
+        new_page->attr.write = (write == 1);
+        new_page->attr.system = (priv == 1);
+        new_page->attr.cow = 1;
+        // TODO: move *next out of struct page
+        new_page->next = NULL;
+
+        new_page->phys_page_id = page->phys_page_id;
+        new_page->ref_count = page->ref_count;
+
+        if (p_page_end != NULL)
+            p_page_end->next = new_page;
+        else
+            mm_area->pgs = new_page;
+    } else {
+        page->attr.read = (read == 1);
+        page->attr.write = (write == 1);
+        page->attr.system = (priv == 1);
+        page->attr.cow = 0;
+        // TODO: move *next out of struct page
+        page->next = NULL;
+
+        if (p_page_end != NULL)
+            p_page_end->next = page;
+        else
+            mm_area->pgs = page;
+    }
+    _map_raw_page_to_addr(
+        mm_area,
+        page->phys_page_id,
+        (write && !cow),
+        priv);
+
+    ++mm_area->len;
+    ++*page->ref_count;
+    return GB_OK;
+}
+
+// map a page identically
+// this function is only meant to be used in the initialization process
+// it checks the pde's P bit so you need to make sure it's already set
+// to avoid dead loops
+static inline void _init_map_page_identically(page_t page)
+{
+    page_directory_entry* pde = KERNEL_PAGE_DIRECTORY_ADDR + page_to_pd_i(page);
+    // page table not exist
+    if (!pde->in.p) {
+        // allocate a page for the page table
+        // set the P bit of the pde in advance
+        pde->in.p = 1;
+        pde->in.rw = 1;
+        pde->in.us = 0;
+        pde->in.pt_page = alloc_raw_page();
+        _init_map_page_identically(pde->in.pt_page);
+
+        make_page_table((page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page)));
+    }
+
+    // map the page in the page table
+    page_table_entry* pt = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
+    pt += page_to_pt_i(page);
+    pt->v = 0x00000003;
+    pt->in.page = page;
+}
+
+static inline void init_paging_map_low_mem_identically(void)
+{
+    for (phys_ptr_t addr = 0x01000000; addr < 0x30000000; addr += 0x1000) {
+        // check if the address is valid and not mapped
+        if (bm_test(mem_bitmap, phys_addr_to_page(addr)))
+            continue;
+        _init_map_page_identically(phys_addr_to_page(addr));
+    }
+}
+
+static struct page empty_page;
+static struct page heap_first_page;
+static size_t heap_first_page_ref_count;
+
+void init_mem(void)
+{
+    init_mem_layout();
+
+    // map the 16MiB-768MiB identically
+    init_paging_map_low_mem_identically();
+
+    kernel_mm_head = &kernel_mm;
+
+    kernel_mm.attr.read = 1;
+    kernel_mm.attr.write = 1;
+    kernel_mm.attr.system = 1;
+    kernel_mm.len = 0;
+    kernel_mm.next = NULL;
+    kernel_mm.pd = KERNEL_PAGE_DIRECTORY_ADDR;
+    kernel_mm.pgs = NULL;
+    kernel_mm.start = (linr_ptr_t)KERNEL_HEAP_START;
+
+    heap_first_page.attr.cow = 0;
+    heap_first_page.attr.read = 1;
+    heap_first_page.attr.write = 1;
+    heap_first_page.attr.system = 1;
+    heap_first_page.next = NULL;
+    heap_first_page.phys_page_id = alloc_raw_page();
+    heap_first_page.ref_count = &heap_first_page_ref_count;
+
+    *heap_first_page.ref_count = 0;
+
+    k_map(kernel_mm_head, &heap_first_page, 1, 1, 1, 0);
+
+    init_heap();
+
+    // create empty_page struct
+    empty_page.attr.cow = 0;
+    empty_page.attr.read = 1;
+    empty_page.attr.write = 0;
+    empty_page.attr.system = 0;
+    empty_page.next = NULL;
+    empty_page.phys_page_id = phys_addr_to_page(EMPTY_PAGE_ADDR);
+    empty_page.ref_count = (size_t*)k_malloc(sizeof(size_t));
+    *empty_page.ref_count = 1;
+
+    // TODO: improve the algorithm SO FREAKING SLOW
+    // while (kernel_mm_head->len < 256 * 1024 * 1024 / PAGE_SIZE) {
+    while (kernel_mm_head->len < 16 * 1024 * 1024 / PAGE_SIZE) {
+        k_map(
+            kernel_mm_head, &empty_page,
+            1, 1, 1, 1);
+    }
+}
+
+void create_segment_descriptor(
+    segment_descriptor* sd,
+    uint32_t base,
+    uint32_t limit,
+    uint32_t flags,
+    uint32_t access)
+{
+    sd->base_low = base & 0x0000ffff;
+    sd->base_mid = ((base & 0x00ff0000) >> 16);
+    sd->base_high = ((base & 0xff000000) >> 24);
+    sd->limit_low = limit & 0x0000ffff;
+    sd->limit_high = ((limit & 0x000f0000) >> 16);
+    sd->access = access;
+    sd->flags = flags;
+}

+ 0 - 0
src/kernel/process.c


+ 113 - 6
src/kernel/stdio.c

@@ -187,7 +187,7 @@ snprint_decimal64(
 }
 
 ssize_t
-snprint_hex(
+snprint_hex32(
     char* buf,
     size_t buf_size,
     uint32_t num,
@@ -234,6 +234,54 @@ snprint_hex(
     return n_write;
 }
 
+ssize_t
+snprint_hex64(
+    char* buf,
+    size_t buf_size,
+    uint64_t num,
+    int32_t capitalized)
+{
+    ssize_t n_write = 0;
+
+    do_write_if_free(buf, buf_size, '0');
+    if (capitalized) {
+        do_write_if_free(buf, buf_size, 'X');
+    } else {
+        do_write_if_free(buf, buf_size, 'x');
+    }
+    n_write += 2;
+
+    char* orig_buf = buf;
+
+    do {
+        if (capitalized) {
+            do_write_if_free(buf, buf_size, X_to_c(num % 16));
+        } else {
+            do_write_if_free(buf, buf_size, x_to_c(num % 16));
+        }
+        num /= 16;
+        ++n_write;
+    } while (num != 0);
+
+    // prepend trailing '\0'
+    if (buf_size > 0)
+        *buf = 0x00;
+
+    // move buf pointer to the last digit of number
+    --buf;
+
+    // reverse output
+    while (orig_buf < buf) {
+        char c = *buf;
+        *buf = *orig_buf;
+        *orig_buf = c;
+        --buf;
+        ++orig_buf;
+    }
+
+    return n_write;
+}
+
 static inline ssize_t
 snprint_char(
     char* buf,
@@ -268,12 +316,12 @@ snprintf(
                 break;
 
             case 'x':
-                n_tmp_write = snprint_hex(buf, buf_size, *(uint32_t*)arg_ptr, 0);
+                n_tmp_write = snprint_hex32(buf, buf_size, *(uint32_t*)arg_ptr, 0);
                 arg_ptr += sizeof(uint32_t);
                 break;
 
             case 'X':
-                n_tmp_write = snprint_hex(buf, buf_size, *(uint32_t*)arg_ptr, 1);
+                n_tmp_write = snprint_hex32(buf, buf_size, *(uint32_t*)arg_ptr, 1);
                 arg_ptr += sizeof(uint32_t);
                 break;
 
@@ -282,8 +330,17 @@ snprintf(
                 switch (*(++fmt)) {
                 // long long aka int64
                 case 'l':
-                    ++fmt;
-                    n_tmp_write = snprint_decimal64(buf, buf_size, *(int64_t*)arg_ptr);
+                    switch (*(++fmt)) {
+                    case 'd':
+                        n_tmp_write = snprint_decimal64(buf, buf_size, *(int64_t*)arg_ptr);
+                        break;
+                    case 'x':
+                        n_tmp_write = snprint_hex64(buf, buf_size, *(int64_t*)arg_ptr, 0);
+                        break;
+                    case 'X':
+                        n_tmp_write = snprint_hex64(buf, buf_size, *(int64_t*)arg_ptr, 1);
+                        break;
+                    }
                     arg_ptr += sizeof(int64_t);
                     break;
                 // long int aka int32
@@ -291,6 +348,15 @@ snprintf(
                     n_tmp_write = snprint_decimal32(buf, buf_size, *(int32_t*)arg_ptr);
                     arg_ptr += sizeof(int32_t);
                     break;
+                case 'x':
+                    n_tmp_write = snprint_hex32(buf, buf_size, *(uint32_t*)arg_ptr, 0);
+                    arg_ptr += sizeof(uint32_t);
+                    break;
+
+                case 'X':
+                    n_tmp_write = snprint_hex32(buf, buf_size, *(uint32_t*)arg_ptr, 1);
+                    arg_ptr += sizeof(uint32_t);
+                    break;
                 }
                 break;
 
@@ -308,7 +374,11 @@ snprintf(
 
             // pointer
             case 'p':
-                n_tmp_write = snprint_hex(buf, buf_size, *(ptr_t*)arg_ptr, 0);
+#ifdef __32bit_system
+                n_tmp_write = snprint_hex32(buf, buf_size, *(ptr_t*)arg_ptr, 0);
+#else
+                n_tmp_write = snprint_hex64(buf, buf_size, *(ptr_t*)arg_ptr, 0);
+#endif
                 arg_ptr += sizeof(ptr_t);
                 break;
 
@@ -341,3 +411,40 @@ snprintf(
 
     return n_write;
 }
+
+#define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t)/sizeof(uint8_t))
+void* memcpy(void* dst, const void* src, size_t n)
+{
+    void* orig_dst = dst;
+    for (size_t i = 0; i < n / BYTES_PER_MAX_COPY_UNIT; ++i) {
+        *(uint32_t*)dst = *(uint32_t*)src;
+        dst += BYTES_PER_MAX_COPY_UNIT;
+        src += BYTES_PER_MAX_COPY_UNIT;
+    }
+    for (size_t i = 0; i < (n % BYTES_PER_MAX_COPY_UNIT); ++i) {
+        *((char*)dst++) = *((char*)src++);
+    }
+    return orig_dst;
+}
+
+void* memset(void* dst, int c, size_t n)
+{
+    c &= 0xff;
+    int cc = (c + (c << 8) + (c << 16) + (c << 24));
+    for (size_t i = 0; i < n / BYTES_PER_MAX_COPY_UNIT; ++i) {
+        *(uint32_t*)dst = cc;
+        dst += BYTES_PER_MAX_COPY_UNIT;
+    }
+    for (size_t i = 0; i < (n % BYTES_PER_MAX_COPY_UNIT); ++i) {
+        *((char*)dst++) = c;
+    }
+    return dst;
+}
+
+size_t strlen(const char* str)
+{
+    size_t n = 0;
+    while (*(str++) != '\0')
+        ++n;
+    return n;
+}

+ 49 - 0
src/kernel/tty.c

@@ -0,0 +1,49 @@
+#include <asm/port_io.h>
+#include <kernel/stdio.h>
+#include <kernel/tty.h>
+#include <kernel/mem.h>
+#include <kernel/vga.h>
+#include <kernel/hw/serial.h>
+
+static void serial_tty_put_char(struct tty* p_tty, char c)
+{
+    serial_send_data(*(port_id_t*)&p_tty->data, c);
+}
+
+static void vga_tty_put_char(struct tty* _unused, char c)
+{
+    static struct vga_char vc = { .c = '\0', .color = VGA_CHAR_COLOR_WHITE };
+    vc.c = c;
+    vga_put_char(&vc);
+}
+
+static struct tty_operations serial_tty_ops = {
+    .put_char = serial_tty_put_char,
+};
+
+static struct tty_operations vga_tty_ops = {
+    .put_char = vga_tty_put_char,
+};
+
+void tty_print(struct tty* p_tty, const char* str)
+{
+    while (*str != '\0') {
+        p_tty->ops->put_char(p_tty, *str);
+        ++str;
+    }
+}
+
+int make_serial_tty(struct tty* p_tty, int id)
+{
+    *(port_id_t*)&p_tty->data = id;
+    snprintf(p_tty->name, sizeof(p_tty->name), "ttyS%x", id);
+    p_tty->ops = &serial_tty_ops;
+    return GB_OK;
+}
+
+int make_vga_tty(struct tty* p_tty)
+{
+    snprintf(p_tty->name, sizeof(p_tty->name), "ttyVGA");
+    p_tty->ops = &vga_tty_ops;
+    return GB_OK;
+}

+ 28 - 23
src/kernel/vga.c

@@ -6,22 +6,13 @@
 
 static struct vga_char* p_vga_head = VGA_MEM;
 
-void vga_put_char(struct vga_char* c)
-{
-    *p_vga_head = *c;
-    ++p_vga_head;
-    if ((p_vga_head - VGA_MEM) == 80 * 25) {
-        p_vga_head = VGA_MEM;
-    }
-}
-
-void vga_return()
+static inline void vga_return()
 {
     const int32_t offset = p_vga_head - VGA_MEM;
     p_vga_head -= (offset % VGA_SCREEN_WIDTH_IN_CHARS);
 }
 
-void vga_new_line()
+static inline void vga_new_line()
 {
     int32_t offset = p_vga_head - VGA_MEM;
     offset %= VGA_SCREEN_WIDTH_IN_CHARS;
@@ -31,21 +22,35 @@ void vga_new_line()
     }
 }
 
-void vga_printk(const char* str, uint8_t color)
+static inline void real_vga_put_char(struct vga_char* c)
+{
+    *p_vga_head = *c;
+    ++p_vga_head;
+    if ((p_vga_head - VGA_MEM) == 80 * 25) {
+        p_vga_head = VGA_MEM;
+    }
+}
+
+void vga_put_char(struct vga_char* c)
+{
+    switch (c->c) {
+    case CR:
+        vga_return();
+        break;
+    case LF:
+        vga_new_line();
+        break;
+    default:
+        real_vga_put_char(c);
+        break;
+    }
+}
+
+void vga_print(const char* str, uint8_t color)
 {
     struct vga_char s_c;
     s_c.color = color;
     while ((s_c.c = *(str++)) != 0x00) {
-        switch (s_c.c) {
-        case CR:
-            vga_return();
-            break;
-        case LF:
-            vga_new_line();
-            break;
-        default:
-            vga_put_char(&s_c);
-            break;
-        }
+        vga_put_char(&s_c);
     }
 }

+ 107 - 37
src/kernel_main.c

@@ -1,14 +1,18 @@
-#include <kernel_main.h>
+#include "kernel_main.h"
 
 #include <asm/boot.h>
 #include <asm/port_io.h>
+#include <asm/sys.h>
 #include <kernel/event/event.h>
 #include <kernel/hw/keyboard.h>
+#include <kernel/hw/serial.h>
 #include <kernel/hw/timer.h>
 #include <kernel/interrupt.h>
 #include <kernel/mem.h>
 #include <kernel/stdio.h>
+#include <kernel/tty.h>
 #include <kernel/vga.h>
+#include <types/bitmap.h>
 
 typedef void (*constructor)(void);
 extern constructor start_ctors;
@@ -22,87 +26,153 @@ void call_constructors_for_cpp(void)
 
 #define KERNEL_MAIN_BUF_SIZE (128)
 
+struct tty* console = NULL;
 #define printkf(x...)                       \
     snprintf(buf, KERNEL_MAIN_BUF_SIZE, x); \
-    vga_printk(buf, 0x0fu);
+    tty_print(console, buf)
 
-void kernel_main(void)
-{
-    MAKE_BREAK_POINT();
+#define EVE_START(x) printkf(x "... ")
+#define INIT_START(x) EVE_START("initializing " x)
+#define INIT_OK() printkf("ok\n")
+#define INIT_FAILED() printkf("failed\n")
 
+static inline void check_a20_status(void)
+{
     uint32_t result;
     result = check_a20_on();
 
     if (result) {
-        vga_printk("A20 is ON\n", 0x0fU);
+        tty_print(console, "a20 is on");
     } else {
-        vga_printk("A20 is NOT ON\n", 0x0fU);
+        tty_print(console, "a20 is NOT on");
     }
+}
 
+static inline void halt_on_init_error(void)
+{
+    MAKE_BREAK_POINT();
+    asm_cli();
+    while (1)
+        asm_hlt();
+}
+
+uint8_t e820_mem_map[1024];
+uint32_t e820_mem_map_count;
+uint32_t e820_mem_map_entry_size;
+size_t kernel_size;
+struct mem_size_info mem_size_info;
+
+static inline void save_loader_data(void)
+{
+    memcpy(e820_mem_map, asm_e820_mem_map, sizeof(e820_mem_map));
+    e820_mem_map_count = asm_e820_mem_map_count;
+    e820_mem_map_entry_size = asm_e820_mem_map_entry_size;
+    kernel_size = asm_kernel_size;
+    memcpy(&mem_size_info, &asm_mem_size_info, sizeof(struct mem_size_info));
+}
+
+static inline void show_mem_info(char* buf)
+{
     uint32_t mem_size = 0;
-    mem_size += 1024 * asm_mem_size_info.n_1k_blks;
-    mem_size += 64 * 1024 * asm_mem_size_info.n_64k_blks;
+    mem_size += 1024 * mem_size_info.n_1k_blks;
+    mem_size += 64 * 1024 * mem_size_info.n_64k_blks;
 
-    char buf[KERNEL_MAIN_BUF_SIZE] = { 0 };
     printkf(
         "Memory size: %d bytes (%d MB), 16k blocks: %d, 64k blocks: %d\n",
         mem_size,
         mem_size / 1024 / 1024,
-        (int32_t)asm_mem_size_info.n_1k_blks,
-        (int32_t)asm_mem_size_info.n_64k_blks);
+        (int32_t)mem_size_info.n_1k_blks,
+        (int32_t)mem_size_info.n_64k_blks);
 
     printkf(
         "mem_map_entry_count: %d , mem_map_entry_size: %d \n",
-        asm_e820_mem_map_count,
-        asm_e820_mem_map_entry_size);
+        e820_mem_map_count,
+        e820_mem_map_entry_size);
 
-    if (asm_e820_mem_map_entry_size == 20) {
-        for (uint32_t i = 0; i < asm_e820_mem_map_count; ++i) {
+    if (e820_mem_map_entry_size == 20) {
+        struct e820_mem_map_entry_20* entry = (struct e820_mem_map_entry_20*)e820_mem_map;
+        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
             printkf(
-                "[mem] entry %d: %lld ~ %lld, type: %d\n",
+                "[mem] entry %d: %llx ~ %llx, type: %d\n",
                 i,
-                e820_mem_map_20[i].base,
-                e820_mem_map_20[i].base + e820_mem_map_20[i].len,
-                e820_mem_map_20[i].type);
+                entry->base,
+                entry->base + entry->len,
+                entry->type);
         }
     } else {
-        for (uint32_t i = 0; i < asm_e820_mem_map_count; ++i) {
+        struct e820_mem_map_entry_24* entry = (struct e820_mem_map_entry_24*)e820_mem_map;
+        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
             printkf(
                 "[mem] entry %d: %lld ~ %lld, type: %d, acpi_attr: %d\n",
                 i,
-                e820_mem_map_24[i].in.base,
-                e820_mem_map_24[i].in.base + e820_mem_map_24[i].in.len,
-                e820_mem_map_24[i].in.type,
-                e820_mem_map_24[i].acpi_extension_attr);
+                entry->in.base,
+                entry->in.base + entry->in.len,
+                entry->in.type,
+                entry->acpi_extension_attr);
         }
     }
+    printkf("kernel size: %x\n", kernel_size);
+}
 
-    printkf("Initializing interrupt descriptor table...\n");
+static segment_descriptor new_gdt[5];
 
-    init_idt();
+void load_new_gdt(void)
+{
+    create_segment_descriptor(new_gdt + 0, 0, 0, 0, 0);
+    create_segment_descriptor(new_gdt + 1, 0, ~0, 0b1100, SD_TYPE_CODE_SYSTEM);
+    create_segment_descriptor(new_gdt + 2, 0, ~0, 0b1100, SD_TYPE_DATA_SYSTEM);
+    create_segment_descriptor(new_gdt + 3, 0, ~0, 0b1100, SD_TYPE_CODE_USER);
+    create_segment_descriptor(new_gdt + 4, 0, ~0, 0b1100, SD_TYPE_DATA_USER);
+    asm_load_gdt((5 * 8 - 1) << 16, (phys_ptr_t)new_gdt);
+    asm_cli();
+}
 
-    init_pit();
+void kernel_main(void)
+{
+    // MAKE_BREAK_POINT();
+    asm_enable_sse();
 
-    printkf("Interrupt descriptor table initialized!\n");
+    save_loader_data();
 
-    printkf("Initializing heap space\n");
+    load_new_gdt();
 
-    init_heap();
+    char buf[KERNEL_MAIN_BUF_SIZE];
 
-    printkf("Heap space initialized!\n");
+    init_serial_port(PORT_SERIAL0);
 
-    printkf("Constructing c++ global objects\n");
+    struct tty early_console;
+    if (make_serial_tty(&early_console, PORT_SERIAL0) != GB_OK) {
+        halt_on_init_error();
+    }
+    console = &early_console;
+
+    show_mem_info(buf);
+
+    INIT_START("exception handlers");
+    init_idt();
+    INIT_OK();
 
+    INIT_START("memory allocation");
+    init_mem();
+    INIT_OK();
+
+    INIT_START("C++ global objects");
     call_constructors_for_cpp();
+    INIT_OK();
 
-    printkf("Cpp global objects constructed\n");
+    INIT_START("programmable interrupt controller and timer");
+    init_pic();
+    init_pit();
+    INIT_OK();
 
     printkf("Testing k_malloc...\n");
-    char* k_malloc_buf = (char*)k_malloc(sizeof(char) * 128);
-    snprintf(k_malloc_buf, 128, "This text is printed on the heap!\n");
-    vga_printk(k_malloc_buf, 0x0fu);
+    char* k_malloc_buf = (char*)k_malloc(sizeof(char) * 4097);
+    snprintf(k_malloc_buf, 4097, "This text is printed on the heap!\n");
+    tty_print(console, k_malloc_buf);
     k_free(k_malloc_buf);
 
+    k_malloc_buf[4096] = '\x89';
+
     printkf("No work to do, halting...\n");
 
     while (1) {

+ 22 - 0
src/types/bitmap.c

@@ -0,0 +1,22 @@
+#include <types/bitmap.h>
+
+#define SZ (8)
+
+size_t make_bm_size(size_t n)
+{
+    return sizeof(size_t) + (n / SZ) + ((n % SZ) ? 1 : 0);
+}
+
+int bm_test(char* bm, size_t n)
+{
+    return (bm[n / SZ] & (1 << (n % SZ))) != 0;
+}
+
+void bm_set(char* bm, size_t n)
+{
+    bm[n / SZ] |= (1 << (n % SZ));
+}
+void bm_clear(char* bm, size_t n)
+{
+    bm[n / SZ] &= (~(1 << (n % SZ)));
+}