summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-30 22:12:14 +0100
committerAnton Kling <anton@kling.gg>2023-10-31 00:18:38 +0100
commit8a9208612eec8ddae4c418485d848ecfa0613699 (patch)
tree2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel
parentca76600acc8bf7a02346efa5bd8f17072210ec01 (diff)
Meta: Move kernel and userland to their own folders.
This is to allow both the kernel and the userland to share certain header files and to make the folder structure a bit more clear.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile33
-rw-r--r--kernel/arch/i386/boot.s128
-rw-r--r--kernel/arch/i386/mmu.c565
-rw-r--r--kernel/cpu/gdt.c72
-rw-r--r--kernel/cpu/gdt.h76
-rw-r--r--kernel/cpu/idt.c195
-rw-r--r--kernel/cpu/idt.h70
-rw-r--r--kernel/cpu/int_syscall.s20
-rw-r--r--kernel/cpu/io.h15
-rw-r--r--kernel/cpu/io.s147
-rw-r--r--kernel/cpu/reload_gdt.s17
-rw-r--r--kernel/cpu/spinlock.c2
-rw-r--r--kernel/cpu/spinlock.h5
-rw-r--r--kernel/cpu/syscall.c183
-rw-r--r--kernel/cpu/syscall.h59
-rw-r--r--kernel/crypto/ChaCha20/chacha20.c29
-rw-r--r--kernel/crypto/ChaCha20/chacha20.h15
m---------kernel/crypto/SHA10
-rw-r--r--kernel/drivers/ata.c253
-rw-r--r--kernel/drivers/ata.h16
-rw-r--r--kernel/drivers/keyboard.c188
-rw-r--r--kernel/drivers/keyboard.h11
-rw-r--r--kernel/drivers/mouse.c144
-rw-r--r--kernel/drivers/mouse.h5
-rw-r--r--kernel/drivers/pci.c69
-rw-r--r--kernel/drivers/pci.h26
-rw-r--r--kernel/drivers/pit.c55
-rw-r--r--kernel/drivers/pit.h12
-rw-r--r--kernel/drivers/pst.c17
-rw-r--r--kernel/drivers/pst.h7
-rw-r--r--kernel/drivers/rtl8139.c182
-rw-r--r--kernel/drivers/rtl8139.h4
-rw-r--r--kernel/drivers/serial.c35
-rw-r--r--kernel/drivers/serial.h2
-rw-r--r--kernel/drivers/vbe.c73
-rw-r--r--kernel/drivers/vbe.h7
-rw-r--r--kernel/elf.c73
-rw-r--r--kernel/elf.h99
-rw-r--r--kernel/fs/devfs.c91
-rw-r--r--kernel/fs/devfs.h26
-rw-r--r--kernel/fs/ext2.c763
-rw-r--r--kernel/fs/ext2.h140
-rw-r--r--kernel/fs/fifo.c97
-rw-r--r--kernel/fs/fifo.h27
-rw-r--r--kernel/fs/shm.c94
-rw-r--r--kernel/fs/shm.h13
-rw-r--r--kernel/fs/tmpfs.c96
-rw-r--r--kernel/fs/tmpfs.h16
-rw-r--r--kernel/fs/vfs.c318
-rw-r--r--kernel/fs/vfs.h100
-rw-r--r--kernel/halts.c102
-rw-r--r--kernel/halts.h21
-rw-r--r--kernel/hashmap/hashmap.c212
-rw-r--r--kernel/hashmap/hashmap.h39
-rw-r--r--kernel/includes/defs.h5
-rw-r--r--kernel/includes/math.h2
-rw-r--r--kernel/includes/mmu.h61
-rw-r--r--kernel/includes/signal.h46
-rw-r--r--kernel/includes/sys/types.h2
-rw-r--r--kernel/init/kernel.c103
-rw-r--r--kernel/isodir/boot/grub/grub.cfg3
-rw-r--r--kernel/kmalloc.c230
-rw-r--r--kernel/kmalloc.h21
-rw-r--r--kernel/ksbrk.c40
-rw-r--r--kernel/ksbrk.h8
-rw-r--r--kernel/kubsan.c58
-rw-r--r--kernel/kubsan.h79
-rw-r--r--kernel/libc/exit/assert.c12
-rw-r--r--kernel/libc/include/assert.h19
-rw-r--r--kernel/libc/include/errno.h85
-rw-r--r--kernel/libc/include/limits.h1
-rw-r--r--kernel/libc/include/stdio.h9
-rw-r--r--kernel/libc/include/stdlib.h0
-rw-r--r--kernel/libc/include/string.h19
-rw-r--r--kernel/libc/include/time.h6
-rw-r--r--kernel/libc/include/types.h27
-rw-r--r--kernel/libc/stdio/print.c97
-rw-r--r--kernel/libc/string/copy.c26
-rw-r--r--kernel/libc/string/isequal.c15
-rw-r--r--kernel/libc/string/memcmp.c12
-rw-r--r--kernel/libc/string/memcpy.c20
-rw-r--r--kernel/libc/string/memset.c9
-rw-r--r--kernel/libc/string/strcat.c6
-rw-r--r--kernel/libc/string/strcmp.c8
-rw-r--r--kernel/libc/string/strcpy.c8
-rw-r--r--kernel/libc/string/strlcpy.c21
-rw-r--r--kernel/libc/string/strlen.c8
-rw-r--r--kernel/libc/string/strncpy.c11
-rw-r--r--kernel/linker.ld41
-rw-r--r--kernel/log.c40
-rw-r--r--kernel/log.h10
-rw-r--r--kernel/math.c3
l---------kernel/mount/cat1
l---------kernel/mount/init1
-rwxr-xr-xkernel/mount/pid1bin0 -> 2396 bytes
-rwxr-xr-xkernel/mount/programbin0 -> 1744 bytes
-rwxr-xr-xkernel/mount/shbin0 -> 45660 bytes
-rw-r--r--kernel/multiboot.h243
-rw-r--r--kernel/network/arp.c153
-rw-r--r--kernel/network/arp.h4
-rw-r--r--kernel/network/bytes.c10
-rw-r--r--kernel/network/bytes.h5
-rw-r--r--kernel/network/ethernet.c93
-rw-r--r--kernel/network/ethernet.h5
-rw-r--r--kernel/network/ipv4.c98
-rw-r--r--kernel/network/ipv4.h5
-rw-r--r--kernel/network/udp.c64
-rw-r--r--kernel/network/udp.h7
-rw-r--r--kernel/poll.c54
-rw-r--r--kernel/poll.h15
-rw-r--r--kernel/process.s33
-rw-r--r--kernel/random.c142
-rw-r--r--kernel/random.h7
-rw-r--r--kernel/read_eip.s5
-rw-r--r--kernel/scalls/accept.c5
-rw-r--r--kernel/scalls/accept.h9
-rw-r--r--kernel/scalls/bind.c5
-rw-r--r--kernel/scalls/bind.h9
-rw-r--r--kernel/scalls/clock_gettime.c10
-rw-r--r--kernel/scalls/clock_gettime.h8
-rw-r--r--kernel/scalls/ftruncate.c6
-rw-r--r--kernel/scalls/ftruncate.h2
-rw-r--r--kernel/scalls/kill.c5
-rw-r--r--kernel/scalls/kill.h2
-rw-r--r--kernel/scalls/mkdir.c5
-rw-r--r--kernel/scalls/mkdir.h2
-rw-r--r--kernel/scalls/mmap.c7
-rw-r--r--kernel/scalls/mmap.h13
-rw-r--r--kernel/scalls/msleep.c9
-rw-r--r--kernel/scalls/msleep.h5
-rw-r--r--kernel/scalls/ppoll.c11
-rw-r--r--kernel/scalls/ppoll.h9
-rw-r--r--kernel/scalls/recvfrom.c59
-rw-r--r--kernel/scalls/recvfrom.h11
-rw-r--r--kernel/scalls/sendto.c23
-rw-r--r--kernel/scalls/sendto.h11
-rw-r--r--kernel/scalls/shm.c6
-rw-r--r--kernel/scalls/shm.h14
-rw-r--r--kernel/scalls/socket.c5
-rw-r--r--kernel/scalls/socket.h9
-rw-r--r--kernel/scalls/stat.c13
-rw-r--r--kernel/scalls/stat.h33
-rw-r--r--kernel/scalls/uptime.c4
-rw-r--r--kernel/scalls/uptime.h2
-rw-r--r--kernel/sched/scheduler.c449
-rw-r--r--kernel/sched/scheduler.h65
-rw-r--r--kernel/signal.s18
-rw-r--r--kernel/socket.c180
-rw-r--r--kernel/socket.h72
-rw-r--r--kernel/time.h10
150 files changed, 8286 insertions, 0 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644
index 0000000..5098944
--- /dev/null
+++ b/kernel/Makefile
@@ -0,0 +1,33 @@
+CC="i686-sb-gcc"
+AS="i686-sb-as"
+OBJ = arch/i386/boot.o init/kernel.o cpu/gdt.o cpu/reload_gdt.o cpu/idt.o cpu/io.o libc/stdio/print.o drivers/keyboard.o log.o drivers/pit.o libc/string/memcpy.o libc/string/strlen.o libc/string/memcmp.o drivers/ata.o libc/string/memset.o cpu/syscall.o read_eip.o libc/exit/assert.o process.o cpu/int_syscall.o libc/string/strcpy.o arch/i386/mmu.o kmalloc.o fs/ext2.o fs/vfs.o fs/devfs.o cpu/spinlock.o random.o libc/string/strcmp.o crypto/ChaCha20/chacha20.o crypto/SHA1/sha1.o fs/tmpfs.o libc/string/isequal.o drivers/pst.o halts.o scalls/ppoll.o scalls/ftruncate.o kubsan.o scalls/mmap.o drivers/serial.o scalls/accept.o scalls/bind.o scalls/socket.o socket.o poll.o fs/fifo.o hashmap/hashmap.o fs/shm.o scalls/shm.o elf.o ksbrk.o sched/scheduler.o scalls/stat.o libc/string/copy.o libc/string/strncpy.o drivers/mouse.o libc/string/strlcpy.o libc/string/strcat.o drivers/vbe.o scalls/msleep.o scalls/uptime.o scalls/mkdir.o drivers/pci.o drivers/rtl8139.o network/ethernet.o network/arp.o network/bytes.o network/ipv4.o network/udp.o scalls/recvfrom.o math.o scalls/sendto.o signal.o scalls/kill.o scalls/sigaction.o
+CFLAGS = -O2 -fsanitize=vla-bound,shift-exponent,pointer-overflow,shift,signed-integer-overflow,bounds -ggdb -ffreestanding -Wall -Werror -mgeneral-regs-only -Wimplicit-fallthrough -I./libc/include/ -I.
+INCLUDE=-I./includes/ -I./libc/include/
+
+all: myos.iso
+
+%.o: %.c
+ $(CC) $(INCLUDE) -c -o $@ $< $(CFLAGS)
+
+%.o: %.s
+ $(AS) $< -o $@
+
+myos.bin: $(OBJ)
+ $(CC) $(INCLUDE) -shared -T linker.ld -o myos.bin -ffreestanding -nostdlib $(CFLAGS) $^ -lgcc
+
+debug:
+ qemu-system-i386 -no-reboot -no-shutdown -serial file:./serial.log -hda ext2.img -m 1G -cdrom myos.iso -s -S &
+ gdb -x .gdbinit
+
+nk:
+ qemu-system-i386 -d int -netdev user,id=n0,hostfwd=udp:127.0.0.1:6001-:6000 -device rtl8139,netdev=n0 -no-reboot -no-shutdown -serial file:./serial.log -hda ext2.img -m 1G -cdrom myos.iso -s
+
+run:
+ qemu-system-i386 -enable-kvm -netdev user,id=n0,hostfwd=udp:127.0.0.1:6001-:6000 -device rtl8139,netdev=n0 -object filter-dump,id=id,netdev=n0,file=netout -d int -no-reboot -no-shutdown -chardev stdio,id=char0,logfile=serial.log,signal=off -serial chardev:char0 -hda ext2.img -m 1G -cdrom myos.iso -s
+
+myos.iso: myos.bin
+ cp myos.bin isodir/boot
+ grub-mkrescue -o myos.iso isodir
+
+clean:
+ rm myos.bin myos.iso $(OBJ)
diff --git a/kernel/arch/i386/boot.s b/kernel/arch/i386/boot.s
new file mode 100644
index 0000000..61bbfbb
--- /dev/null
+++ b/kernel/arch/i386/boot.s
@@ -0,0 +1,128 @@
+.set ALIGN, 1<<0
+.set MEMINFO, 1<<1
+.set VIDEO_MODE, 1<<2
+.set FLAGS, VIDEO_MODE|ALIGN | MEMINFO
+.set MAGIC, 0x1BADB002
+.set CHECKSUM, -(MAGIC + FLAGS)
+
+#.section .multiboot
+.align 16, 0
+.section .multiboot.data, "aw"
+ .align 4
+ .long MAGIC
+ .long FLAGS
+ .long CHECKSUM
+ # unused(padding)
+ .long 0,0,0,0,0
+ # Video mode
+ .long 0 # Linear graphics
+ .long 0 # Preferred width
+ .long 0 # Preferred height
+ .long 32 # Preferred pixel depth
+
+# Allocate the initial stack.
+.section .bootstrap_stack, "aw", @nobits
+stack_bottom:
+.skip 16384 # 16 KiB
+stack_top:
+
+.global boot_page_directory
+.global boot_page_table1
+.section .bss, "aw", @nobits
+ .align 4096
+boot_page_directory:
+ .skip 4096
+boot_page_table1:
+ .skip 4096
+
+.section .multiboot.text, "a"
+.global _start
+.extern _kernel_end
+.type _start, @function
+# Kernel entry
+_start:
+ # edi contains the buffer we wish to modify
+ movl $(boot_page_table1 - 0xC0000000), %edi
+
+ # for loop
+ movl $0, %esi
+ movl $1024, %ecx
+1:
+ # esi = address
+ # If we are in kernel range jump to "label 2"
+ cmpl $_kernel_start, %esi
+ jl 2f
+
+ # If we are past the kernel jump to the final stage
+ # "label 3"
+ cmpl $(_kernel_end - 0xC0000000), %esi
+ jge 3f
+
+2:
+ # Add permission to the pages
+ movl %esi, %edx
+ orl $0x003, %edx
+ movl %edx, (%edi)
+
+ # Size of page is 4096 bytes.
+ addl $4096, %esi
+ # Size of entries in boot_page_table1 is 4 bytes.
+ addl $4, %edi
+ # Loop to the next entry if we haven't finished.
+ loop 1b
+
+3:
+ # Map the page table to both virtual addresses 0x00000000 and 0xC0000000.
+ movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 0
+ movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 768 * 4
+
+ # Set cr3 to the address of the boot_page_directory.
+ movl $(boot_page_directory - 0xC0000000), %ecx
+ movl %ecx, %cr3
+
+ # Enable paging
+ movl %cr0, %ecx
+ orl $0x80000000, %ecx
+ movl %ecx, %cr0
+
+ # Jump to higher half with an absolute jump.
+ lea 4f, %ecx
+ jmp *%ecx
+
+.size _start, . - _start
+
+.section .text
+
+4:
+ # At this point, paging is fully set up and enabled.
+
+ # Unmap the identity mapping as it is now unnecessary.
+ movl $0, boot_page_directory + 0
+
+ # Reload crc3 to force a TLB flush so the changes to take effect.
+ movl %cr3, %ecx
+ movl %ecx, %cr3
+
+ # Set up the stack.
+ mov $stack_top, %esp
+
+ # Reset EFLAGS.
+ pushl $0
+ popf
+
+ pushl %esp
+
+ /* Push the pointer to the Multiboot information structure. */
+ pushl %ebx
+ /* Push the magic value. */
+ pushl %eax
+
+ mov $_kernel_end, %eax
+ pushl %eax
+ # Enter the high-level kernel.
+ call kernel_main
+
+ # Infinite loop if the system has nothing more to do.
+ cli
+1: hlt
+ jmp 1b
diff --git a/kernel/arch/i386/mmu.c b/kernel/arch/i386/mmu.c
new file mode 100644
index 0000000..ccfe894
--- /dev/null
+++ b/kernel/arch/i386/mmu.c
@@ -0,0 +1,565 @@
+#include <assert.h>
+#include <ksbrk.h>
+#include <log.h>
+#include <mmu.h>
+
+#define INDEX_FROM_BIT(a) (a / (32))
+#define OFFSET_FROM_BIT(a) (a % (32))
+#define PAGE_SIZE ((uintptr_t)0x1000)
+
+#define PAGE_ALLOCATE 1
+#define PAGE_NO_ALLOCATE 0
+
+PageDirectory *kernel_directory;
+PageDirectory real_kernel_directory;
+PageDirectory *active_directory = 0;
+
+#define END_OF_MEMORY 0x8000000 * 15
+const uint32_t num_of_frames = END_OF_MEMORY / PAGE_SIZE;
+uint32_t frames[END_OF_MEMORY / PAGE_SIZE] = {0};
+uint32_t num_allocated_frames = 0;
+
+#define KERNEL_START 0xc0000000
+extern uintptr_t data_end;
+
+void write_to_frame(uint32_t frame_address, uint8_t on);
+
+void *ksbrk(size_t s) {
+ uintptr_t rc = (uintptr_t)align_page((void *)data_end);
+ data_end += s;
+ data_end = (uintptr_t)align_page((void *)data_end);
+
+ if (!get_active_pagedirectory()) {
+ // If there is no active pagedirectory we
+ // just assume that the memory is
+ // already mapped.
+ return (void *)rc;
+ }
+ // Determine whether we are approaching a unallocated table
+ int table_index = 1 + (rc / (1024 * 0x1000));
+ if (!active_directory->tables[table_index]) {
+ uint32_t physical;
+ active_directory->tables[table_index] = (PageTable *)0xDEADBEEF;
+ active_directory->tables[table_index] =
+ (PageTable *)ksbrk_physical(sizeof(PageTable), (void **)&physical);
+ memset(active_directory->tables[table_index], 0, sizeof(PageTable));
+ active_directory->physical_tables[table_index] = (uint32_t)physical | 0x3;
+
+ kernel_directory->tables[table_index] =
+ active_directory->tables[table_index];
+ kernel_directory->physical_tables[table_index] =
+ active_directory->physical_tables[table_index];
+ return ksbrk(s);
+ }
+ mmu_allocate_shared_kernel_region((void *)rc, (data_end - (uintptr_t)rc));
+ assert(((uintptr_t)rc % PAGE_SIZE) == 0);
+ memset((void *)rc, 0x00, s);
+
+ return (void *)rc;
+}
+
+void *ksbrk_physical(size_t s, void **physical) {
+ void *r = ksbrk(s);
+ if (physical) {
+ *physical = (void *)virtual_to_physical(r, 0);
+ }
+ return r;
+}
+
+uint32_t mmu_get_number_of_allocated_frames(void) {
+ return num_allocated_frames;
+}
+
+Page *get_page(void *ptr, PageDirectory *directory, int create_new_page,
+ int set_user) {
+ uintptr_t address = (uintptr_t)ptr;
+ if (!directory)
+ directory = get_active_pagedirectory();
+ address /= 0x1000;
+
+ uint32_t table_index = address / 1024;
+ if (!directory->tables[table_index]) {
+ if (!create_new_page)
+ return 0;
+
+ uint32_t physical;
+ directory->tables[table_index] =
+ (PageTable *)ksbrk_physical(sizeof(PageTable), (void **)&physical);
+ memset(directory->tables[table_index], 0, sizeof(PageTable));
+ directory->physical_tables[table_index] =
+ (uint32_t)physical | ((set_user) ? 0x7 : 0x3);
+
+ if (!set_user) {
+ kernel_directory->tables[table_index] = directory->tables[table_index];
+ kernel_directory->physical_tables[table_index] =
+ directory->physical_tables[table_index];
+ }
+ }
+ Page *p = &directory->tables[table_index]->pages[address % 1024];
+ if (create_new_page) {
+ p->present = 0;
+ }
+ return &directory->tables[table_index]->pages[address % 1024];
+}
+
+void mmu_free_pages(void *a, uint32_t n) {
+ for (; n > 0; n--) {
+ Page *p = get_page(a, NULL, PAGE_NO_ALLOCATE, 0);
+ p->present = 0;
+ write_to_frame(p->frame * 0x1000, 0);
+ a += 0x1000;
+ }
+}
+
+void *next_page(void *ptr) {
+ uintptr_t a = (uintptr_t)ptr;
+ return (void *)(a + (PAGE_SIZE - ((uint32_t)a & (PAGE_SIZE - 1))));
+}
+
+void *align_page(void *a) {
+ if ((uintptr_t)a & (PAGE_SIZE - 1))
+ return next_page(a);
+
+ return a;
+}
+
+void flush_tlb(void) {
+ asm volatile("\
+ mov %cr3, %eax;\
+ mov %eax, %cr3");
+}
+
+uint32_t first_free_frame(void) {
+ for (uint32_t i = 1; i < INDEX_FROM_BIT(num_of_frames); i++) {
+ if (frames[i] == 0xFFFFFFFF)
+ continue;
+
+ for (uint32_t c = 0; c < 32; c++)
+ if (!(frames[i] & ((uint32_t)1 << c)))
+ return i * 32 + c;
+ }
+
+ kprintf("ERROR Num frames: %x\n", mmu_get_number_of_allocated_frames());
+ klog("No free frames, uh oh.", LOG_ERROR);
+ assert(0);
+ return 0;
+}
+
+void write_to_frame(uint32_t frame_address, uint8_t on) {
+ uint32_t frame = frame_address / 0x1000;
+ assert(INDEX_FROM_BIT(frame) < sizeof(frames) / sizeof(frames[0]));
+ if (on) {
+ num_allocated_frames++;
+ frames[INDEX_FROM_BIT(frame)] |= ((uint32_t)0x1 << OFFSET_FROM_BIT(frame));
+ return;
+ }
+ num_allocated_frames--;
+ frames[INDEX_FROM_BIT(frame)] &= ~((uint32_t)0x1 << OFFSET_FROM_BIT(frame));
+}
+
+PageDirectory *get_active_pagedirectory(void) { return active_directory; }
+
+PageTable *clone_table(uint32_t src_index, PageDirectory *src_directory,
+ uint32_t *physical_address) {
+ PageTable *new_table =
+ ksbrk_physical(sizeof(PageTable), (void **)physical_address);
+ PageTable *src = src_directory->tables[src_index];
+
+ // Copy all the pages
+ for (uint16_t i = 0; i < 1024; i++) {
+ if (!src->pages[i].present) {
+ new_table->pages[i].present = 0;
+ continue;
+ }
+ uint32_t frame_address = first_free_frame();
+ write_to_frame(frame_address * 0x1000, 1);
+ new_table->pages[i].frame = frame_address;
+
+ new_table->pages[i].present |= src->pages[i].present;
+ new_table->pages[i].rw |= src->pages[i].rw;
+ new_table->pages[i].user |= src->pages[i].user;
+ new_table->pages[i].accessed |= src->pages[i].accessed;
+ new_table->pages[i].dirty |= src->pages[i].dirty;
+ }
+
+ // Now copy all of the data to the new table. This is done by creating a
+ // virutal pointer to this newly created tables physical frame so we can
+ // copy data to it.
+ for (uint32_t i = 0; i < 1024; i++) {
+ // Find a unused table
+ if (src_directory->tables[i])
+ continue;
+
+ // Link the table to the new table temporarily
+ src_directory->tables[i] = new_table;
+ src_directory->physical_tables[i] = *physical_address | 0x7;
+ PageDirectory *tmp = get_active_pagedirectory();
+ switch_page_directory(src_directory);
+
+ // For each page in the table copy all the data over.
+ for (uint32_t c = 0; c < 1024; c++) {
+ // Only copy pages that are used.
+ if (!src->pages[c].frame || !src->pages[c].present)
+ continue;
+
+ uint32_t table_data_pointer = i << 22 | c << 12;
+ uint32_t src_data_pointer = src_index << 22 | c << 12;
+ memcpy((void *)table_data_pointer, (void *)src_data_pointer, 0x1000);
+ }
+ src_directory->tables[i] = 0;
+ src_directory->physical_tables[i] = 0;
+ switch_page_directory(tmp);
+ return new_table;
+ }
+ ASSERT_NOT_REACHED;
+ return 0;
+}
+
+PageTable *copy_table(PageTable *src, uint32_t *physical_address) {
+ PageTable *new_table =
+ ksbrk_physical(sizeof(PageTable), (void **)physical_address);
+
+ // copy all the pages
+ for (uint16_t i = 0; i < 1024; i++) {
+ if (!src->pages[i].present) {
+ new_table->pages[i].present = 0;
+ continue;
+ }
+ new_table->pages[i].frame = src->pages[i].frame;
+ new_table->pages[i].present = src->pages[i].present;
+ new_table->pages[i].rw = src->pages[i].rw;
+ new_table->pages[i].user = src->pages[i].user;
+ new_table->pages[i].accessed = src->pages[i].accessed;
+ new_table->pages[i].dirty = src->pages[i].dirty;
+ }
+ return new_table;
+}
+
+PageDirectory *clone_directory(PageDirectory *original) {
+ if (!original)
+ original = get_active_pagedirectory();
+
+ uint32_t physical_address;
+ PageDirectory *new_directory =
+ ksbrk_physical(sizeof(PageDirectory), (void **)&physical_address);
+ uint32_t offset =
+ (uint32_t)new_directory->physical_tables - (uint32_t)new_directory;
+ new_directory->physical_address = physical_address + offset;
+
+ for (int i = 0; i < 1024; i++) {
+ if (!original->tables[i]) {
+ new_directory->tables[i] = NULL;
+ new_directory->physical_tables[i] = (uint32_t)NULL;
+ continue;
+ }
+
+ // Make sure to copy instead of cloning the stack.
+ if (i >= 635 && i <= 641) {
+ uint32_t physical;
+ new_directory->tables[i] = clone_table(i, original, &physical);
+ new_directory->physical_tables[i] =
+ physical | (original->physical_tables[i] & 0xFFF);
+ continue;
+ }
+
+ // if (original->tables[i] == kernel_directory->tables[i]) {
+ if (i > 641) {
+ new_directory->tables[i] = kernel_directory->tables[i];
+ new_directory->physical_tables[i] = kernel_directory->physical_tables[i];
+ continue;
+ }
+
+ uint32_t physical;
+ new_directory->tables[i] = clone_table(i, original, &physical);
+ new_directory->physical_tables[i] =
+ physical | (original->physical_tables[i] & 0xFFF);
+ }
+
+ return new_directory;
+}
+
+void mmu_allocate_shared_kernel_region(void *rc, size_t n) {
+ size_t num_pages = n / PAGE_SIZE;
+ for (size_t i = 0; i <= num_pages; i++) {
+ Page *p = get_page((void *)(rc + i * 0x1000), NULL, PAGE_NO_ALLOCATE, 0);
+ if (!p) {
+ kprintf("don't have: %x\n", rc + i * 0x1000);
+ p = get_page((void *)(rc + i * 0x1000), NULL, PAGE_ALLOCATE, 0);
+ }
+ if (!p->present || !p->frame)
+ allocate_frame(p, 0, 1);
+ }
+}
+
+void mmu_remove_virtual_physical_address_mapping(void *ptr, size_t length) {
+ size_t num_pages = (uintptr_t)align_page((void *)length) / PAGE_SIZE;
+ for (size_t i = 0; i < num_pages; i++) {
+ Page *p = get_page(ptr + (i * PAGE_SIZE), NULL, PAGE_NO_ALLOCATE, 0);
+ if (!p)
+ return;
+ p->frame = 0;
+ p->present = 0;
+ }
+}
+
+void *mmu_find_unallocated_virtual_range(void *addr, size_t length) {
+ addr = align_page(addr);
+ // Check if the pages already exist
+ for (size_t i = 0; i < length; i += 0x1000) {
+ if (get_page(addr + i, NULL, PAGE_NO_ALLOCATE, 0)) {
+ // Page already exists
+ return mmu_find_unallocated_virtual_range(addr + 0x1000, length);
+ }
+ }
+ return addr;
+}
+
+int mmu_allocate_region(void *ptr, size_t n, mmu_flags flags,
+ PageDirectory *pd) {
+ pd = (pd) ? pd : get_active_pagedirectory();
+ size_t num_pages = n / 0x1000;
+ for (size_t i = 0; i <= num_pages; i++) {
+ Page *p = get_page((void *)(ptr + i * 0x1000), pd, PAGE_ALLOCATE, 1);
+ assert(p);
+ int rw = (flags & MMU_FLAG_RW);
+ int kernel = (flags & MMU_FLAG_KERNEL);
+ if (!allocate_frame(p, rw, kernel)) {
+ klog("MMU: Frame allocation failed", LOG_WARN);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void *mmu_map_frames(void *const ptr, size_t s) {
+ void *const r = mmu_find_unallocated_virtual_range((void *)0xEF000000, s);
+ kprintf("r: %x\n", r);
+ size_t num_pages = s / 0x1000;
+ for (size_t i = 0; i <= num_pages; i++) {
+ Page *p = get_page((void *)(r + i * 0x1000), NULL, PAGE_ALLOCATE, 1);
+ assert(p);
+ int rw = 1;
+ int is_kernel = 1;
+ p->present = 1;
+ p->rw = rw;
+ p->user = !is_kernel;
+ p->frame = (uintptr_t)(ptr + i * 0x1000) / 0x1000;
+ write_to_frame((uintptr_t)ptr + i * 0x1000, 1);
+ }
+ flush_tlb();
+ return r;
+}
+
+void *allocate_frame(Page *page, int rw, int is_kernel) {
+ if (page->present) {
+ dump_backtrace(5);
+ klog("Page is already set", 1);
+ for (;;)
+ ;
+ return 0;
+ }
+ uint32_t frame_address = first_free_frame();
+ assert(frame_address < sizeof(frames) / sizeof(frames[0]));
+ write_to_frame(frame_address * 0x1000, 1);
+
+ page->present = 1;
+ page->rw = rw;
+ page->user = !is_kernel;
+ page->frame = frame_address;
+ return (void *)(frame_address * 0x1000);
+}
+
+void mmu_free_address_range(void *ptr, size_t length) {
+ size_t num_pages = (size_t)align_page((void *)length) / PAGE_SIZE;
+ for (size_t i = 0; i < num_pages; i++, ptr += PAGE_SIZE) {
+ Page *page = get_page(ptr, NULL, PAGE_NO_ALLOCATE, 0);
+ if (!page)
+ continue;
+ if (!page->present)
+ continue;
+ if (!page->frame)
+ continue;
+ // Sanity check that none of the frames are invalid
+ assert(page->frame < sizeof(frames) / sizeof(frames[0]));
+ write_to_frame(((uint32_t)page->frame) * 0x1000, 0);
+ page->present = 0;
+ page->rw = 0;
+ page->user = 0;
+ page->frame = 0;
+ }
+}
+
+void mmu_map_directories(void *dst, PageDirectory *d, void *src,
+ PageDirectory *s, size_t length) {
+ d = (!d) ? get_active_pagedirectory() : d;
+ s = (!s) ? get_active_pagedirectory() : s;
+ size_t num_pages = (uint32_t)align_page((void *)length) / 0x1000;
+ for (size_t i = 0; i < num_pages; i++, dst += 0x1000, src += 0x1000) {
+ Page *p = get_page(dst, d, PAGE_ALLOCATE, 1);
+ p->present = 1;
+ p->rw = 1;
+ p->user = 1;
+ void *physical = virtual_to_physical(src, s);
+ p->frame = (uint32_t)physical / PAGE_SIZE;
+ }
+ flush_tlb();
+}
+
+void mmu_map_physical(void *dst, PageDirectory *d, void *physical,
+ size_t length) {
+ d = (!d) ? get_active_pagedirectory() : d;
+ size_t num_pages = (uint32_t)align_page((void *)length) / 0x1000;
+ for (size_t i = 0; i < num_pages; i++, dst += 0x1000, physical += 0x1000) {
+ Page *p = get_page(dst, d, PAGE_ALLOCATE, 1);
+ p->present = 1;
+ p->rw = 1;
+ p->user = 1;
+ p->frame = (uintptr_t)physical / PAGE_SIZE;
+ write_to_frame((uintptr_t)physical, 1);
+ }
+ flush_tlb();
+}
+
+void *virtual_to_physical(void *address, PageDirectory *directory) {
+ if (0 == directory)
+ directory = get_active_pagedirectory();
+ return (void *)((get_page((void *)address, directory, PAGE_NO_ALLOCATE, 0)
+ ->frame *
+ 0x1000) +
+ (((uintptr_t)address) & 0xFFF));
+}
+
+extern uint32_t inital_esp;
+void __attribute__((optimize("O0")))
+move_stack(uint32_t new_stack_address, uint32_t size) {
+ mmu_allocate_region((void *)(new_stack_address - size), size,
+ MMU_FLAG_KERNEL, NULL);
+
+ uint32_t old_stack_pointer, old_base_pointer;
+
+ register uint32_t eax asm("eax");
+ asm volatile("mov %esp, %eax");
+ old_stack_pointer = eax;
+ asm volatile("mov %ebp, %eax");
+ old_base_pointer = eax;
+
+ uint32_t new_stack_pointer =
+ old_stack_pointer + ((uint32_t)new_stack_address - inital_esp);
+ uint32_t new_base_pointer =
+ old_base_pointer + ((uint32_t)new_stack_address - inital_esp);
+
+ // Copy the stack
+ memcpy((void *)new_stack_pointer, (void *)old_stack_pointer,
+ inital_esp - old_stack_pointer);
+ for (uint32_t i = (uint32_t)new_stack_address; i > new_stack_address - size;
+ i -= 4) {
+ uint32_t tmp = *(uint32_t *)i;
+ if (old_stack_pointer < tmp && tmp < inital_esp) {
+ tmp = tmp + (new_stack_address - inital_esp);
+ uint32_t *tmp2 = (uint32_t *)i;
+ *tmp2 = tmp;
+ }
+ }
+
+ inital_esp = new_stack_pointer;
+ // Actually change the stack
+ eax = new_stack_pointer;
+ asm volatile("mov %eax, %esp");
+ eax = new_base_pointer;
+ asm volatile("mov %eax, %ebp");
+}
+
+// C strings have a unknown length so it does not makes sense to check
+// for a size on the pointer. Instead we check whether the page it
+// resides in is accessible to the user.
+void *is_valid_user_c_string(const char *ptr, size_t *size) {
+ void *r = (void *)ptr;
+ size_t s = 0;
+ for (;;) {
+ void *page = (void *)((uintptr_t)ptr & (uintptr_t)(~(PAGE_SIZE - 1)));
+ if (!is_valid_userpointer(page, PAGE_SIZE))
+ return NULL;
+ for (; (uintptr_t)ptr & (PAGE_SIZE - 1); ptr++, s++)
+ if (!*ptr) {
+ if (size)
+ *size = s;
+ return r;
+ }
+ }
+}
+
+void *is_valid_userpointer(const void *ptr, size_t s) {
+ uintptr_t t = (uintptr_t)ptr;
+ size_t num_pages = (uintptr_t)align_page((void *)s) / 0x1000;
+ for (size_t i = 0; i < num_pages; i++, t += 0x1000) {
+ Page *page = get_page((void *)t, NULL, PAGE_NO_ALLOCATE, 0);
+ if (!page)
+ return NULL;
+ if (!page->present)
+ return NULL;
+ if (!page->user)
+ return NULL;
+ }
+ return (void *)ptr;
+}
+
+void switch_page_directory(PageDirectory *directory) {
+ active_directory = directory;
+ asm("mov %0, %%cr3" ::"r"(directory->physical_address));
+}
+
+void enable_paging(void) {
+ asm("mov %cr0, %eax\n"
+ "or 0b10000000000000000000000000000000, %eax\n"
+ "mov %eax, %cr0\n");
+}
+
+extern uint32_t boot_page_directory;
+extern uint32_t boot_page_table1;
+
+void paging_init(void) {
+ uint32_t *cr3;
+ asm volatile("mov %%cr3, %0" : "=r"(cr3));
+ uint32_t *virtual = (uint32_t *)((uint32_t)cr3 + 0xC0000000);
+
+ kernel_directory = &real_kernel_directory;
+ kernel_directory->physical_address = (uint32_t)cr3;
+ for (uint32_t i = 0; i < 1024; i++) {
+ kernel_directory->physical_tables[i] = virtual[i];
+
+ if (!kernel_directory->physical_tables[i]) {
+ kernel_directory->tables[i] = NULL;
+ continue;
+ }
+
+ kernel_directory->tables[i] =
+ (PageTable *)(0xC0000000 + (virtual[i] & ~(0xFFF)));
+
+ // Loop through the pages in the table
+ PageTable *table = kernel_directory->tables[i];
+ for (size_t j = 0; j < 1024; j++) {
+ if (!table->pages[j].present)
+ continue;
+ // Add the frame to our bitmap to ensure it does not get used by
+ // another newly created page.
+ write_to_frame(table->pages[j].frame * 0x1000, 1);
+ }
+ }
+
+ switch_page_directory(kernel_directory);
+ kernel_directory = clone_directory(kernel_directory);
+
+ // Make null dereferences crash.
+ switch_page_directory(kernel_directory);
+ get_page(NULL, kernel_directory, PAGE_ALLOCATE, 0)->present = 0;
+ switch_page_directory(clone_directory(kernel_directory));
+ // FIXME: Really hacky solution. Since page table creation needs to
+ // allocate memory but memory allocation requires page table creation
+ // they depend on eachother. The bad/current solution is just to
+ // allocate a bunch of tables so the memory allocator has enough.
+ for (uintptr_t i = 0; i < 140; i++)
+ allocate_frame(
+ get_page((void *)((0x302 + i) * 0x1000 * 1024), NULL, PAGE_ALLOCATE, 0),
+ 1, 1);
+ move_stack(0xA0000000, 0xC00000);
+}
diff --git a/kernel/cpu/gdt.c b/kernel/cpu/gdt.c
new file mode 100644
index 0000000..28853cf
--- /dev/null
+++ b/kernel/cpu/gdt.c
@@ -0,0 +1,72 @@
+#include "gdt.h"
+
+extern void flush_tss(void);
+extern void load_gdt(void *);
+
+typedef struct tss_entry_struct tss_entry_t;
+
+tss_entry_t tss_entry;
+
+typedef union {
+ struct GDT_Entry s;
+ uint64_t raw;
+} GDT_Entry;
+
+GDT_Entry gdt_entries[6] = {0};
+GDT_Pointer gdtr;
+
+extern uint32_t inital_esp;
+void write_tss(struct GDT_Entry *gdt_entry) {
+ uint32_t base = (uint32_t)&tss_entry;
+ uint32_t limit = sizeof(tss_entry);
+
+ gdt_entry->limit_low = limit;
+ gdt_entry->base_low = base;
+ gdt_entry->accessed = 1;
+ gdt_entry->read_write = 0;
+ gdt_entry->conforming_expand_down = 0;
+ gdt_entry->code = 1;
+ gdt_entry->code_data_segment = 0;
+ gdt_entry->DPL = 0;
+ gdt_entry->present = 1;
+ gdt_entry->limit_high = limit >> 16;
+ gdt_entry->available = 0;
+ gdt_entry->long_mode = 0;
+ gdt_entry->big = 0;
+ gdt_entry->gran = 0;
+ gdt_entry->base_high = (base & ((uint32_t)0xff << 24)) >> 24;
+
+ memset(&tss_entry, 0, sizeof tss_entry);
+ tss_entry.ss0 = GDT_KERNEL_DATA_SEGMENT * GDT_ENTRY_SIZE;
+ register uint32_t esp asm("esp");
+ tss_entry.esp0 = esp;
+}
+
+void gdt_init() {
+ gdt_entries[GDT_NULL_SEGMENT].raw = 0x0;
+ gdt_entries[GDT_KERNEL_CODE_SEGMENT].raw =
+ 0xCF9A000000FFFF; // Kernel code segment
+ gdt_entries[GDT_KERNEL_DATA_SEGMENT].raw =
+ 0xCF92000000FFFF; // Kernel data segment
+
+ // Usermode code segment
+ memcpy(&gdt_entries[GDT_USERMODE_CODE_SEGMENT],
+ &gdt_entries[GDT_KERNEL_CODE_SEGMENT], GDT_ENTRY_SIZE);
+
+ // Usermode data segment
+ memcpy(&gdt_entries[GDT_USERMODE_DATA_SEGMENT],
+ &gdt_entries[GDT_KERNEL_DATA_SEGMENT], GDT_ENTRY_SIZE);
+
+ // Set DPL to 3 to indicate that the segment is in ring 3
+ gdt_entries[GDT_USERMODE_CODE_SEGMENT].s.DPL = 3;
+ gdt_entries[GDT_USERMODE_DATA_SEGMENT].s.DPL = 3;
+
+ write_tss((struct GDT_Entry *)&gdt_entries[GDT_TSS_SEGMENT]);
+
+ gdtr.offset = (uint32_t)&gdt_entries;
+ gdtr.size = sizeof(gdt_entries) - 1;
+
+ asm("cli");
+ load_gdt(&gdtr);
+ flush_tss();
+}
diff --git a/kernel/cpu/gdt.h b/kernel/cpu/gdt.h
new file mode 100644
index 0000000..a490dea
--- /dev/null
+++ b/kernel/cpu/gdt.h
@@ -0,0 +1,76 @@
+#ifndef GDT_H
+#define GDT_H
+#include <stdint.h>
+#include <string.h>
+
+#define GDT_ENTRY_SIZE 0x8
+#define GDT_NULL_SEGMENT 0x0
+#define GDT_KERNEL_CODE_SEGMENT 0x1
+#define GDT_KERNEL_DATA_SEGMENT 0x2
+#define GDT_USERMODE_CODE_SEGMENT 0x3
+#define GDT_USERMODE_DATA_SEGMENT 0x4
+#define GDT_TSS_SEGMENT 0x5
+
+struct GDT_Entry
+{
+ uint16_t limit_low;
+ uint32_t base_low : 24;
+ uint32_t accessed : 1;
+ uint32_t read_write : 1; // readable for code, writable for data
+ uint32_t conforming_expand_down : 1; // conforming for code, expand down for data
+ uint32_t code : 1; // 1 for code, 0 for data
+ uint32_t code_data_segment : 1; // should be 1 for everything but TSS and LDT
+ uint32_t DPL : 2; // privilege level
+ uint32_t present : 1;
+ uint32_t limit_high : 4;
+ uint32_t available : 1; // only used in software; has no effect on hardware
+ uint32_t long_mode : 1;
+ uint32_t big : 1; // 32-bit opcodes for code, uint32_t stack for data
+ uint32_t gran : 1; // 1 to use 4k page addressing, 0 for byte addressing
+ uint8_t base_high;
+}__attribute__((packed));
+
+typedef struct GDT_Pointer
+{
+ uint16_t size;
+ uint32_t offset;
+}__attribute__((packed)) GDT_Pointer;
+
+struct tss_entry_struct
+{
+ uint32_t prev_tss; // The previous TSS - with hardware task switching these form a kind of backward linked list.
+ uint32_t esp0; // The stack pointer to load when changing to kernel mode.
+ uint32_t ss0; // The stack segment to load when changing to kernel mode.
+ // Everything below here is unused.
+ uint32_t esp1; // esp and ss 1 and 2 would be used when switching to rings 1 or 2.
+ uint32_t ss1;
+ uint32_t esp2;
+ uint32_t ss2;
+ uint32_t cr3;
+ uint32_t eip;
+ uint32_t eflags;
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t es;
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t fs;
+ uint32_t gs;
+ uint32_t ldt;
+ uint16_t trap;
+ uint16_t iomap_base;
+} __attribute__((packed));
+
+void gdt_init();
+
+uint8_t gen_access_byte(uint8_t priv, uint8_t s, uint8_t ex, uint8_t dc, uint8_t rw);
+uint64_t gen_gdt_entry(uint32_t base, uint32_t limit, uint8_t access_byte, uint8_t flag);
+uint8_t gen_flag(uint8_t gr, uint8_t sz);
+#endif
diff --git a/kernel/cpu/idt.c b/kernel/cpu/idt.c
new file mode 100644
index 0000000..abcafad
--- /dev/null
+++ b/kernel/cpu/idt.c
@@ -0,0 +1,195 @@
+#include <cpu/idt.h>
+#include <sched/scheduler.h>
+#include <stdio.h>
+
+#define MASTER_PIC_COMMAND_PORT 0x20
+#define MASTER_PIC_DATA_PORT 0x21
+#define SLAVE_PIC_COMMAND_PORT 0xA0
+#define SLAVE_PIC_DATA_PORT 0xA1
+#define KEYBOARD_DATA_PORT 0x60
+#define KEYBOARD_STATUS_PORT 0x64
+#define KERNEL_CODE_SEGMENT_OFFSET GDT_ENTRY_SIZE *GDT_KERNEL_CODE_SEGMENT
+
+#define IDT_MAX_ENTRY 256
+
+struct IDT_Descriptor {
+ uint16_t low_offset;
+ uint16_t code_segment_selector;
+ uint8_t zero; // Always should be zero
+ uint8_t type_attribute;
+ uint16_t high_offset;
+} __attribute__((packed)) __attribute__((aligned(4)));
+
+struct IDT_Pointer {
+ uint16_t size;
+ struct IDT_Descriptor **interrupt_table;
+} __attribute__((packed));
+
+struct IDT_Descriptor IDT_Entry[IDT_MAX_ENTRY];
+struct IDT_Pointer idtr;
+
+extern void load_idtr(void *idtr);
+
+void format_descriptor(uint32_t offset, uint16_t code_segment,
+ uint8_t type_attribute,
+ struct IDT_Descriptor *descriptor) {
+ descriptor->low_offset = offset & 0xFFFF;
+ descriptor->high_offset = offset >> 16;
+ descriptor->type_attribute = type_attribute;
+ descriptor->code_segment_selector = code_segment;
+ descriptor->zero = 0;
+}
+
+void install_handler(void (*handler_function)(), uint16_t type_attribute,
+ uint8_t entry) {
+ format_descriptor((uint32_t)handler_function, KERNEL_CODE_SEGMENT_OFFSET,
+ type_attribute, &IDT_Entry[entry]);
+}
+
+__attribute__((no_caller_saved_registers)) void EOI(uint8_t irq) {
+ if (irq > 7)
+ outb(SLAVE_PIC_COMMAND_PORT, 0x20);
+
+ outb(MASTER_PIC_COMMAND_PORT, 0x20);
+}
+
+__attribute__((interrupt)) void general_protection_fault(registers_t *regs) {
+ klog("General Protetion Fault", 0x1);
+ kprintf(" Error Code: %x\n", regs->error_code);
+ kprintf("Instruction Pointer: %x\n", regs->eip);
+ dump_backtrace(12);
+ asm("hlt");
+ for (;;)
+ ;
+ EOI(0xD - 8);
+}
+
+__attribute__((interrupt)) void double_fault(registers_t *regs) {
+ (void)regs;
+ klog("DOUBLE FAULT", LOG_ERROR);
+ asm("hlt");
+ for (;;)
+ ;
+}
+__attribute__((interrupt)) void page_fault(registers_t *regs) {
+ if (0xFFFFDEAD == regs->eip) {
+ asm("sti");
+ for (;;)
+ switch_task();
+ }
+ klog("Page Fault", LOG_ERROR);
+ if (get_current_task()) {
+ kprintf("PID: %x\n", get_current_task()->pid);
+ kprintf("Name: %s\n", get_current_task()->program_name);
+ }
+ kprintf("Error Code: %x\n", regs->error_code);
+ kprintf("Instruction Pointer: %x\n", regs->eip);
+
+ if (regs->error_code & (1 << 0))
+ kprintf("page-protection violation\n");
+ else
+ kprintf("non-present page\n");
+
+ if (regs->error_code & (1 << 1))
+ kprintf("write access\n");
+ else
+ kprintf("read access\n");
+
+ if (regs->error_code & (1 << 2))
+ kprintf("CPL = 3\n");
+
+ if (regs->error_code & (1 << 4))
+ kprintf("Attempted instruction fetch\n");
+
+ dump_backtrace(5);
+ asm("hlt");
+ for (;;)
+ ;
+}
+
+static inline void io_wait(void) { outb(0x80, 0); }
+
+#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
+#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
+#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
+#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
+#define ICW1_INIT 0x10 /* Initialization - required! */
+
+#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
+#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
+#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
+#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
+#define ICW4_SFNM 0x10 /* Special fully nested (not) */
+
+void PIC_remap(int offset) {
+ unsigned char a1, a2;
+ a1 = inb(MASTER_PIC_DATA_PORT);
+ a2 = inb(SLAVE_PIC_DATA_PORT);
+
+ // Send ICW1 and tell the PIC that we will issue a ICW4
+ // Since there is no ICW1_SINGLE sent to indicate it is cascaded
+ // it means we will issue a ICW3.
+ outb(MASTER_PIC_COMMAND_PORT, ICW1_INIT | ICW1_ICW4);
+ io_wait();
+ outb(SLAVE_PIC_COMMAND_PORT, ICW1_INIT | ICW1_ICW4);
+ io_wait();
+
+ // As a part of ICW2 this sends the offsets(the position to put IRQ0) of the
+ // vector tables.
+ outb(MASTER_PIC_DATA_PORT, offset);
+ io_wait();
+ outb(SLAVE_PIC_DATA_PORT, offset + 0x8);
+ io_wait();
+
+ // This tells the master on which lines it is having slaves
+ outb(MASTER_PIC_DATA_PORT, 4);
+ io_wait();
+ // This tells the slave the cascading identity.
+ outb(SLAVE_PIC_DATA_PORT, 2);
+ io_wait();
+
+ outb(MASTER_PIC_DATA_PORT, ICW4_8086);
+ io_wait();
+ outb(SLAVE_PIC_DATA_PORT, ICW4_8086);
+ io_wait();
+
+ outb(MASTER_PIC_DATA_PORT, a1);
+ outb(SLAVE_PIC_DATA_PORT, a2);
+}
+
+void IRQ_set_mask(unsigned char IRQline) {
+ uint16_t port;
+ uint8_t value;
+ port = (IRQline < 8) ? MASTER_PIC_DATA_PORT : SLAVE_PIC_DATA_PORT;
+ if (IRQline >= 8)
+ IRQline -= 8;
+ value = inb(port) | (1 << IRQline);
+ outb(port, value);
+}
+
+void IRQ_clear_mask(unsigned char IRQline) {
+ uint16_t port;
+ uint8_t value;
+ port = (IRQline < 8) ? MASTER_PIC_DATA_PORT : SLAVE_PIC_DATA_PORT;
+ if (IRQline >= 8) {
+ IRQline -= 8;
+ }
+ value = inb(port) & ~(1 << IRQline);
+ outb(port, value);
+}
+
+void idt_init(void) {
+ install_handler(page_fault, INT_32_INTERRUPT_GATE(0x0), 0xE);
+ install_handler(double_fault, INT_32_INTERRUPT_GATE(0x0), 0x8);
+ install_handler(general_protection_fault, INT_32_INTERRUPT_GATE(0x0), 0xD);
+
+ PIC_remap(0x20);
+ IRQ_clear_mask(0xb);
+ IRQ_set_mask(0xe);
+ IRQ_set_mask(0xf);
+ IRQ_clear_mask(2);
+
+ idtr.interrupt_table = (struct IDT_Descriptor **)&IDT_Entry;
+ idtr.size = (sizeof(struct IDT_Descriptor) * IDT_MAX_ENTRY) - 1;
+ load_idtr(&idtr);
+}
diff --git a/kernel/cpu/idt.h b/kernel/cpu/idt.h
new file mode 100644
index 0000000..025ba75
--- /dev/null
+++ b/kernel/cpu/idt.h
@@ -0,0 +1,70 @@
+typedef struct kernel_registers kernel_registers_t;
+typedef struct registers registers_t;
+#ifndef IDT_H
+#define IDT_H
+#include <cpu/gdt.h>
+#include <cpu/io.h>
+#include <log.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/*
+ * the type_attribute in the IDT_Entry struct
+ * is divded like this
+ * 7 0
+ * +---+---+---+---+---+---+---+---+
+ * | P | DPL | S | GateType |
+ * +---+---+---+---+---+---+---+---+
+ * It is 8 bits(1 byte) long
+ *
+ * P
+ * Present bit. Should be zero for unused
+ * interrupts.
+ *
+ * DPL
+ * Specifices the maximum ring(0 to 3) the
+ * interrupt can be called from.
+ *
+ * S
+ * Storage segment. This should be set to
+ * zero for all interrupt and trap gates.
+ *
+ * GateType
+ * Possible IDT gate types:
+ * 0b0101 0x5 5 80386 32 bit task gate
+ * 0b0110 0x6 6 80286 16-bit interrupt gate
+ * 0b0111 0x7 7 80286 16-bit trap gate
+ * 0b1110 0xE 14 80386 32-bit interrupt gate
+ * 0b1111 0xF 15 80386 32-bit trap gate
+ */
+
+// This enables the present bit.
+#define INT_PRESENT 0x80 /* 0b10000000 */
+
+#define INT_32_TASK_GATE(min_privlege) \
+ (INT_PRESENT | 0x05 | (min_privlege << 5))
+#define INT_16_INTERRUPT_GATE(min_privlege) \
+ (INT_PRESENT | 0x06 | (min_privlege << 5))
+#define INT_16_TRAP_GATE(min_privlege) \
+ (INT_PRESENT | 0x07 | (min_privlege << 5))
+#define INT_32_INTERRUPT_GATE(min_privlege) \
+ (INT_PRESENT | 0x0E | (min_privlege << 5))
+#define INT_32_TRAP_GATE(min_privlege) \
+ (INT_PRESENT | 0x0F | (min_privlege << 5))
+
+struct interrupt_frame;
+
+struct registers {
+ uint32_t error_code;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+};
+
+void idt_init(void);
+__attribute__((no_caller_saved_registers)) void EOI(unsigned char irq);
+void install_handler(void (*handler_function)(), uint16_t type_attribute,
+ uint8_t entry);
+#endif
diff --git a/kernel/cpu/int_syscall.s b/kernel/cpu/int_syscall.s
new file mode 100644
index 0000000..8c3c25f
--- /dev/null
+++ b/kernel/cpu/int_syscall.s
@@ -0,0 +1,20 @@
+.intel_syntax noprefix
+.global int_syscall
+.extern syscall_function_handler
+int_syscall:
+ push ebp
+ push edi
+ push esi
+ push edx
+ push ecx
+ push ebx
+ push eax
+ call syscall_function_handler
+ add esp, 4
+ pop ebx
+ pop ecx
+ pop edx
+ pop esi
+ pop edi
+ pop ebp
+ iretd
diff --git a/kernel/cpu/io.h b/kernel/cpu/io.h
new file mode 100644
index 0000000..38858a4
--- /dev/null
+++ b/kernel/cpu/io.h
@@ -0,0 +1,15 @@
+#include <stdint.h>
+
+extern void outsw(uint16_t, uint32_t);
+extern void outb(uint16_t, uint8_t);
+extern void outw(uint16_t, uint16_t);
+extern void outl(uint16_t, uint32_t);
+
+extern uint32_t inl(uint16_t);
+extern uint16_t inw(uint16_t);
+extern uint16_t inb(uint16_t);
+
+extern void rep_outsw(uint16_t count, uint16_t port, volatile void *addy);
+__attribute__((no_caller_saved_registers)) extern void
+rep_insw(uint16_t count, uint16_t port, volatile void *addy);
+extern void jump_usermode(void (*address)(), uint32_t stack_pointer);
diff --git a/kernel/cpu/io.s b/kernel/cpu/io.s
new file mode 100644
index 0000000..31e9df0
--- /dev/null
+++ b/kernel/cpu/io.s
@@ -0,0 +1,147 @@
+.intel_syntax noprefix
+.global outsw
+.global outb
+.global outw
+.global outl
+.global inb
+.global inw
+.global inl
+.global rep_outsw
+.global rep_insw
+.global flush_tss
+.global load_idtr
+
+# ebx, esi, edi, ebp, and esp;
+outsw:
+ push ebp
+ mov ebp, esp
+ push esi
+ mov dx, [ebp + 4+4]
+ mov esi, [ebp + 8+4]
+ outsw
+ pop esi
+ mov esp, ebp
+ pop ebp
+ ret
+
+outl:
+ mov eax, [esp + 8]
+ mov dx, [esp + 4]
+ out dx, eax
+ ret
+
+outb:
+ mov al, [esp + 8]
+ mov dx, [esp + 4]
+ out dx, al
+ ret
+
+outw:
+ mov ax, [esp + 8]
+ mov dx, [esp + 4]
+ out dx, ax
+ ret
+
+inl:
+ mov dx, [esp + 4]
+ in eax, dx
+ ret
+
+inw:
+ mov dx, [esp + 4]
+ in ax, dx
+ ret
+
+inb:
+ mov dx, [esp + 4]
+ in al, dx
+ ret
+
+rep_outsw:
+ push ebp
+ mov ebp, esp
+ push edi
+ mov ecx, [ebp + 4+4] #ECX is counter for OUTSW
+ mov edx, [ebp + 8+4] #Data port, in and out
+ mov edi, [ebp + 12+4] #Memory area
+ rep outsw #in to [RDI]
+ pop edi
+ mov esp, ebp
+ pop ebp
+ ret
+
+rep_insw:
+ push ebp
+ mov ebp, esp
+ push edi
+ mov ecx, [ebp + 4+4] #ECX is counter for INSW
+ mov edx, [ebp + 8+4] #Data port, in and out
+ mov edi, [ebp + 12+4] #Memory area
+ rep insw #in to [RDI]
+ pop edi
+ mov esp, ebp
+ pop ebp
+ ret
+
+flush_tss:
+ mov ax, 40
+ ltr ax
+ ret
+
+load_idtr:
+ mov edx, [esp + 4]
+ lidt [edx]
+ ret
+
+test_user_function:
+ mov eax, 0x00
+ int 0x80
+ mov ecx, 0x03
+ mov ebx, 0x04
+ mov eax, 0x02
+ int 0x80
+ mov ebx, eax
+ mov eax, 0x01
+ int 0x80
+loop:
+ jmp loop
+ ret
+
+.global spin_lock
+.global spin_unlock
+.extern locked
+
+spin_lock:
+ mov eax, 1
+ xchg eax, ebx
+ test eax, eax
+ jnz spin_lock
+ ret
+
+spin_unlock:
+ xor eax, eax
+ xchg eax, ebx
+ ret
+
+
+.global jump_usermode
+jump_usermode:
+ mov ax, (4 * 8) | 3 # user data segment with RPL 3
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax # sysexit sets SS
+
+ # setup wrmsr inputs
+ xor edx, edx # not necessary; set to 0
+ mov eax, 0x100008 # SS=0x10+0x10=0x20, CS=0x8+0x10=0x18
+ mov ecx, 0x174 # MSR specifier: IA32_SYSENTER_CS
+ wrmsr # set sysexit segments
+
+ # setup sysexit inputs
+ mov edx, [esp + 4] # to be loaded into EIP
+ mov ecx, [esp + 8] # to be loaded into ESP
+ mov esp, ecx
+ mov ebp, ecx
+ sti
+ sysexit
diff --git a/kernel/cpu/reload_gdt.s b/kernel/cpu/reload_gdt.s
new file mode 100644
index 0000000..3a0119b
--- /dev/null
+++ b/kernel/cpu/reload_gdt.s
@@ -0,0 +1,17 @@
+.section .text
+.global load_gdt
+
+load_gdt:
+ mov 4(%esp), %eax
+ lgdt (%eax)
+
+ mov $0x10, %eax
+ mov %eax, %ds
+ mov %eax, %es
+ mov %eax, %fs
+ mov %eax, %gs
+ mov %eax, %ss
+ jmp $0x8, $.long_jump
+.long_jump:
+ ret
+
diff --git a/kernel/cpu/spinlock.c b/kernel/cpu/spinlock.c
new file mode 100644
index 0000000..3f87423
--- /dev/null
+++ b/kernel/cpu/spinlock.c
@@ -0,0 +1,2 @@
+#include <stdint.h>
+uint8_t locked;
diff --git a/kernel/cpu/spinlock.h b/kernel/cpu/spinlock.h
new file mode 100644
index 0000000..93290c4
--- /dev/null
+++ b/kernel/cpu/spinlock.h
@@ -0,0 +1,5 @@
+#ifndef SPINLOCK_H
+#define SPINLOCK_H
+void spin_lock(int *l);
+void spin_unlock(int *l);
+#endif
diff --git a/kernel/cpu/syscall.c b/kernel/cpu/syscall.c
new file mode 100644
index 0000000..4978732
--- /dev/null
+++ b/kernel/cpu/syscall.c
@@ -0,0 +1,183 @@
+// FIXME: Make sure that the args variabel actually points to something
+// valid.
+#include <assert.h>
+#include <cpu/syscall.h>
+#include <drivers/pst.h>
+#include <errno.h>
+#include <fs/tmpfs.h>
+#include <fs/vfs.h>
+#include <kmalloc.h>
+#include <scalls/accept.h>
+#include <scalls/bind.h>
+#include <scalls/ftruncate.h>
+#include <scalls/kill.h>
+#include <scalls/mkdir.h>
+#include <scalls/mmap.h>
+#include <scalls/msleep.h>
+#include <scalls/ppoll.h>
+#include <scalls/recvfrom.h>
+#include <scalls/sendto.h>
+#include <scalls/shm.h>
+#include <scalls/socket.h>
+#include <scalls/stat.h>
+#include <scalls/uptime.h>
+#include <scalls/sigaction.h>
+#include <stdint.h>
+#include <string.h>
+
+#pragma GCC diagnostic ignored "-Wpedantic"
+
+int syscall_open(SYS_OPEN_PARAMS *args) {
+ char file[256];
+ strcpy(file, args->file);
+ // const char *file = copy_and_allocate_user_string(args->file);
+ int flags = args->flags;
+ int mode = args->mode;
+ int rc = vfs_open(file, flags, mode);
+ // kfree((void *)file);
+ return rc;
+}
+
+int syscall_exec(SYS_EXEC_PARAMS *args) {
+ const char *filename = copy_and_allocate_user_string(args->path);
+
+ int argc = 0;
+ for (; args->argv[argc];) {
+ argc++;
+ }
+
+ char **new_argv = kallocarray(argc + 1, sizeof(char *));
+ for (int i = 0; i < argc; i++)
+ new_argv[i] = copy_and_allocate_user_string(args->argv[i]);
+
+ new_argv[argc] = NULL;
+
+ exec(filename, new_argv);
+ kfree((void *)filename);
+ for (int i = 0; i < argc; i++)
+ kfree(new_argv[i]);
+ kfree(new_argv);
+ return -1;
+}
+
+int syscall_pipe(int fd[2]) {
+ pipe(fd); // FIXME: Error checking
+ return 0;
+}
+
+int syscall_pread(SYS_PREAD_PARAMS *args) {
+ return vfs_pread(args->fd, args->buf, args->count, args->offset);
+}
+
+int syscall_read(SYS_READ_PARAMS *args) {
+ if (!get_vfs_fd(args->fd))
+ return -EBADF;
+ int rc = vfs_pread(args->fd, args->buf, args->count,
+ get_current_task()->file_descriptors[args->fd]->offset);
+ get_current_task()->file_descriptors[args->fd]->offset += rc;
+ return rc;
+}
+
+int syscall_pwrite(SYS_PWRITE_PARAMS *args) {
+ return vfs_pwrite(args->fd, args->buf, args->count, args->offset);
+}
+
+int syscall_write(int fd, const char *buf, size_t count) {
+ if (!get_vfs_fd(fd))
+ return -EBADF;
+ int rc = vfs_pwrite(fd, (char *)buf, count,
+ get_current_task()->file_descriptors[fd]->offset);
+ get_current_task()->file_descriptors[fd]->offset += rc;
+ return rc;
+}
+
+int syscall_dup2(SYS_DUP2_PARAMS *args) {
+ return vfs_dup2(args->org_fd, args->new_fd);
+}
+
+void syscall_exit(int status) {
+ exit(status);
+ assert(0);
+}
+
+void syscall_wait(int *status) {
+ asm("cli");
+ if (!get_current_task()->child) {
+ if (status)
+ *status = -1;
+ return;
+ }
+ if (get_current_task()->child->dead) {
+ if (status)
+ *status = get_current_task()->child_rc;
+ return;
+ }
+ get_current_task()->halts[WAIT_CHILD_HALT] = 1;
+ switch_task();
+ if (status)
+ *status = get_current_task()->child_rc;
+}
+
+int syscall_fork(void) { return fork(); }
+
+int syscall_getpid(void) { return get_current_task()->pid; }
+
+void *align_page(void *a);
+
+int syscall_brk(void *addr) {
+ void *end = get_current_task()->data_segment_end;
+ if (!mmu_allocate_region(end, addr - end, MMU_FLAG_RW, NULL))
+ return -ENOMEM;
+ get_current_task()->data_segment_end = align_page(addr);
+ return 0;
+}
+
+void *syscall_sbrk(uintptr_t increment) {
+ asm("cli");
+ void *rc = get_current_task()->data_segment_end;
+ void *n =
+ (void *)((uintptr_t)(get_current_task()->data_segment_end) + increment);
+ int rc2;
+ if (0 > (rc2 = syscall_brk(n)))
+ return (void *)rc2;
+ return rc;
+}
+
+int syscall_close(int fd) { return vfs_close(fd); }
+
+int syscall_openpty(SYS_OPENPTY_PARAMS *args) {
+ assert(is_valid_userpointer(args, sizeof(SYS_OPENPTY_PARAMS)));
+ return openpty(args->amaster, args->aslave, args->name, args->termp,
+ args->winp);
+}
+
+void (*syscall_functions[])() = {
+ (void(*))syscall_open, (void(*))syscall_read,
+ (void(*))syscall_write, (void(*))syscall_pread,
+ (void(*))syscall_pwrite, (void(*))syscall_fork,
+ (void(*))syscall_exec, (void(*))syscall_getpid,
+ (void(*))syscall_exit, (void(*))syscall_wait,
+ (void(*))syscall_brk, (void(*))syscall_sbrk,
+ (void(*))syscall_pipe, (void(*))syscall_dup2,
+ (void(*))syscall_close, (void(*))syscall_openpty,
+ (void(*))syscall_poll, (void(*))syscall_mmap,
+ (void(*))syscall_accept, (void(*))syscall_bind,
+ (void(*))syscall_socket, (void(*))syscall_shm_open,
+ (void(*))syscall_ftruncate, (void(*))syscall_stat,
+ (void(*))syscall_msleep, (void(*))syscall_uptime,
+ (void(*))syscall_mkdir, (void(*))syscall_recvfrom,
+ (void(*))syscall_sendto, (void(*))syscall_kill,
+ (void(*))syscall_sigaction,
+};
+
+void syscall_function_handler(uint32_t eax, uint32_t arg1, uint32_t arg2,
+ uint32_t arg3, uint32_t arg4, uint32_t arg5) {
+ assert(eax < sizeof(syscall_functions) / sizeof(syscall_functions[0]));
+ syscall_functions[eax](arg1, arg2, arg3, arg4, arg5);
+}
+
+extern void int_syscall(void);
+
+void syscalls_init(void) {
+ install_handler(int_syscall, INT_32_INTERRUPT_GATE(0x3), 0x80);
+}
diff --git a/kernel/cpu/syscall.h b/kernel/cpu/syscall.h
new file mode 100644
index 0000000..51d50f2
--- /dev/null
+++ b/kernel/cpu/syscall.h
@@ -0,0 +1,59 @@
+#include "idt.h"
+#include <stddef.h>
+#include <stdint.h>
+
+void syscalls_init(void);
+
+typedef struct SYS_OPEN_PARAMS {
+ char *file;
+ int flags;
+ int mode;
+} __attribute__((packed)) SYS_OPEN_PARAMS;
+
+typedef struct SYS_PREAD_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PREAD_PARAMS;
+
+typedef struct SYS_READ_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_READ_PARAMS;
+
+typedef struct SYS_PWRITE_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PWRITE_PARAMS;
+
+typedef struct SYS_WRITE_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_WRITE_PARAMS;
+
+typedef struct SYS_EXEC_PARAMS {
+ char *path;
+ char **argv;
+} __attribute__((packed)) SYS_EXEC_PARAMS;
+
+typedef struct SYS_WAIT_PARAMS {
+ int *status;
+} __attribute__((packed)) SYS_WAIT_PARAMS;
+
+typedef struct SYS_DUP2_PARAMS {
+ int org_fd;
+ int new_fd;
+} __attribute__((packed)) SYS_DUP2_PARAMS;
+
+typedef struct SYS_OPENPTY_PARAMS {
+ int *amaster;
+ int *aslave;
+ char *name;
+ /*const struct termios*/ void *termp;
+ /*const struct winsize*/ void *winp;
+} __attribute__((packed)) SYS_OPENPTY_PARAMS;
diff --git a/kernel/crypto/ChaCha20/chacha20.c b/kernel/crypto/ChaCha20/chacha20.c
new file mode 100644
index 0000000..5bf7aa2
--- /dev/null
+++ b/kernel/crypto/ChaCha20/chacha20.c
@@ -0,0 +1,29 @@
+#include "chacha20.h"
+
+#define ROTL(a, b) (((a) << (b)) | ((a) >> (32 - (b))))
+#define QR(a, b, c, d) \
+ (a += b, d ^= a, d = ROTL(d, 16), c += d, b ^= c, b = ROTL(b, 12), \
+ a += b, d ^= a, d = ROTL(d, 8), c += d, b ^= c, b = ROTL(b, 7))
+#define ROUNDS 20
+
+void chacha_block(uint32_t out[16], uint32_t const in[16])
+{
+ int i;
+ uint32_t x[16];
+
+ for (i = 0; i < 16; ++i)
+ x[i] = in[i];
+ for (i = 0; i < ROUNDS; i += 2) {
+ QR(x[0], x[4], x[8], x[12]);
+ QR(x[1], x[5], x[9], x[13]);
+ QR(x[2], x[6], x[10], x[14]);
+ QR(x[3], x[7], x[11], x[15]);
+
+ QR(x[0], x[5], x[10], x[15]);
+ QR(x[1], x[6], x[11], x[12]);
+ QR(x[2], x[7], x[8], x[13]);
+ QR(x[3], x[4], x[9], x[14]);
+ }
+ for (i = 0; i < 16; ++i)
+ out[i] = x[i] + in[i];
+}
diff --git a/kernel/crypto/ChaCha20/chacha20.h b/kernel/crypto/ChaCha20/chacha20.h
new file mode 100644
index 0000000..17532f3
--- /dev/null
+++ b/kernel/crypto/ChaCha20/chacha20.h
@@ -0,0 +1,15 @@
+#ifndef CHACHA20_H
+#define CHACHA20_H
+#include <stdint.h>
+
+#define KEY 4
+#define KEY_SIZE 8*sizeof(uint32_t)
+#define COUNT 12
+#define COUNT_SIZE sizeof(uint32_t)
+#define COUNT_MAX (0x100000000-1) // 2^32 - 1
+#define NONCE 13
+#define NONCE_SIZE 2*sizeof(uint32_t)
+#define BLOCK_SIZE 16*sizeof(uint32_t)
+
+void chacha_block(uint32_t out[16], uint32_t const in[16]);
+#endif
diff --git a/kernel/crypto/SHA1 b/kernel/crypto/SHA1
new file mode 160000
+Subproject 237ea7023cad8402932dfbde337d69e5f4d515f
diff --git a/kernel/drivers/ata.c b/kernel/drivers/ata.c
new file mode 100644
index 0000000..fd9b504
--- /dev/null
+++ b/kernel/drivers/ata.c
@@ -0,0 +1,253 @@
+#include <assert.h>
+#include <cpu/io.h>
+#include <drivers/ata.h>
+#include <drivers/pit.h>
+
+#define PRIMARY_BUS_BASEPORT 0x1F0
+#define SECONDAY_BUS_BASEPORT 0x170
+
+#define PRIMARY_BUS_IRQ 14
+#define SECONDAY_BUS_IRQ 15
+
+#define STATUS_PORT 7
+#define COMMAND_PORT 7
+#define DRIVE_SELECT 6
+#define LBAhi 5
+#define LBAmid 4
+#define LBAlo 3
+#define SECTOR_COUNT 2
+#define DATA_PORT 0
+
+#define IDENTIFY 0xEC
+#define READ_SECTORS 0x20
+#define WRITE_SECTORS 0x30
+#define CACHE_FLUSH 0xE7
+
+#define STATUS_BSY ((1 << 7))
+#define STATUS_DF ((1 << 5))
+#define STATUS_DRQ ((1 << 3))
+#define STATUS_ERR ((1 << 0))
+
+uint32_t io_base;
+
+unsigned char read_buffer[SECTOR_SIZE];
+
+void select_drive(uint8_t master_slave) {
+ outb(io_base + DRIVE_SELECT, (master_slave) ? 0xA0 : 0xB0);
+}
+
+int identify(int master_slave) {
+ select_drive(master_slave);
+ outb(io_base + SECTOR_COUNT, 0);
+ outb(io_base + LBAlo, 0);
+ outb(io_base + LBAmid, 0);
+ outb(io_base + LBAhi, 0);
+ outb(io_base + COMMAND_PORT, IDENTIFY);
+ if (0 == inb(io_base + STATUS_PORT))
+ return 0; // Drive does not exist
+
+ for (; 0 != (inb(io_base + STATUS_PORT) & STATUS_BSY);)
+ ;
+
+ // Because of some ATAPI drives that do not
+ // follow spec, at this point we need to check
+ // the LBAmid and LBAhi ports to see if they are
+ // non-zero. If so, the drive is not ATA, and we
+ // should stop polling.
+ if (0 != inb(io_base + LBAmid) || 0 != inb(io_base + LBAhi)) {
+ klog("Drive is not ATA.", LOG_ERROR);
+ return -1;
+ }
+
+ for (uint16_t status;;) {
+ status = inb(io_base + STATUS_PORT);
+
+ if (1 == (status & STATUS_ERR)) {
+ klog("Drive ERR set.", LOG_ERROR);
+ return -2;
+ }
+
+ if ((status & STATUS_DRQ))
+ break;
+ }
+
+ // The data is ready to read from the Data
+ // port (0x1F0). Read 256 16-bit values,
+ // and store them.
+ // TODO: This returns some intreasting information.
+ // https://wiki.osdev.org/ATA_PIO_Mode#Interesting_information_returned_by_IDENTIFY
+ uint16_t array[256];
+ rep_insw(1 * SECTOR_SIZE / 16, io_base + DATA_PORT, array);
+ return 1;
+}
+
+int poll_status(void) {
+ for (uint16_t status;;) {
+ // Read the Regular Status port until...
+ // We read this 15 times to give some
+ // time for the drive to catch up.
+ for (int n = 0; n < 15; n++)
+ status = inb(io_base + STATUS_PORT);
+
+ // ERR or
+ // DF sets
+ if ((status & STATUS_ERR) || (status & STATUS_DF)) {
+ klog("Drive error set.", LOG_ERROR);
+ return 0;
+ }
+
+ // BSY clears
+ // DRQ sets
+ if (0 == (status & STATUS_BSY) && 0 != (status & STATUS_DRQ))
+ break;
+ }
+ return 1;
+}
+
+// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO
+void __attribute__((optimize("O0")))
+setup_drive_for_command(uint32_t lba, uint32_t sector_count) {
+ // 1. Send 0xE0 for the "master" or 0xF0 for
+ // the "slave", ORed with the highest 4 bits
+ // of the LBA to port 0x1F6
+ outb(io_base + DRIVE_SELECT, 0xE0 | (0 << 4) | ((lba >> 24) & 0x0F));
+
+ // 2. Send a NULL byte to port 0x1F1, if you
+ // like (it is ignored and wastes
+ // lots of CPU time)
+
+ // NOP
+
+ // 3. Send the sectorcount to port 0x1F2
+ outb(io_base + SECTOR_COUNT, sector_count);
+
+ // 4. Send the low 8 bits of the LBA to port 0x1F3
+ outb(io_base + LBAlo, (lba >> 0) & 0xFF);
+
+ // 5. Send the next 8 bits of the LBA to port 0x1F4
+ outb(io_base + LBAmid, (lba >> 8) & 0xFF);
+
+ // 6. Send the next 8 bits of the LBA to port 0x1F5
+ outb(io_base + LBAhi, (lba >> 16) & 0xFF);
+}
+
+void delayed_rep_outsw(size_t n, uint16_t port, volatile uint8_t *buffer) {
+ for (volatile size_t i = 0; i < n; i++) {
+ outsw(port, (uint32_t)buffer);
+ buffer += 2;
+ // outsw(port, buffer);
+ }
+}
+
+void __attribute__((optimize("O0")))
+ata_write_lba28(uint32_t lba, uint32_t sector_count,
+ volatile const uint8_t *buffer) {
+ setup_drive_for_command(lba, sector_count);
+
+ outb(io_base + COMMAND_PORT, WRITE_SECTORS);
+
+ for (volatile uint32_t i = 0; i < sector_count; i++) {
+ if (!poll_status()) {
+ // FIXME: Fail properly
+ for (;;)
+ ;
+ }
+
+ delayed_rep_outsw(256, io_base + DATA_PORT,
+ (void *)((uint32_t)buffer + i * 256));
+ }
+
+ // Cache flush
+ outb(io_base + COMMAND_PORT, CACHE_FLUSH);
+
+ // Wait for BSY to clear
+ for (;;) {
+ uint16_t status = inb(io_base + STATUS_PORT);
+ if (!(status & STATUS_BSY))
+ break;
+ }
+}
+
+// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO
+void __attribute__((optimize("O0")))
+ata_read_lba28(uint32_t lba, uint32_t sector_count, volatile void *address) {
+ // Step 1-6 is done in this function.
+ setup_drive_for_command(lba, sector_count);
+
+ // 7. Send the "READ SECTORS" command to port 0x17F
+ outb(io_base + COMMAND_PORT, READ_SECTORS);
+
+ // 8. Wait for an IRQ or poll.
+
+ // This step can be found in the for loop
+
+ // 9. Transfer 256 16-bit values, a uint16_t at a time,
+ // into your buffer from I/O port 0x1F0
+ for (volatile uint32_t i = 0; i < sector_count; i++) {
+ // 10. Then loop back to waiting for the next IRQ
+ // or poll again for each successive sector.
+ // 8. Wait for an IRQ or poll.
+ if (!poll_status()) {
+ // FIXME: Fail properly
+ for (;;)
+ ;
+ }
+ rep_insw(256, io_base + DATA_PORT, (void *)((uint32_t)address + i * 256));
+ }
+}
+
+void ata_init(void) {
+ io_base = PRIMARY_BUS_BASEPORT;
+
+ // Before sending any data to the IO ports,
+ // read the Regular Status byte. The value
+ // 0xFF is an illegal status value, and
+ // indicates that the bus has no drives
+ if (0xFF == inb(io_base + STATUS_PORT)) {
+ klog("Bus has no drives", LOG_ERROR);
+ }
+
+ // Issue IDENTIFY command
+ select_drive(1);
+}
+
+void __attribute__((optimize("O0")))
+read_lba(uint32_t lba, void *address, size_t size, size_t offset) {
+ uintptr_t ptr = (uintptr_t)address;
+ lba += offset / SECTOR_SIZE;
+ offset = offset % SECTOR_SIZE;
+ asm("cli");
+ size_t total_read = 0;
+ for (int i = 0; size > 0; i++) {
+ uint32_t read_len =
+ (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size;
+ ata_read_lba28(lba + i, 1, read_buffer);
+ memcpy((void *)ptr, read_buffer + offset, read_len);
+ size -= read_len;
+ total_read += read_len;
+ ptr += read_len;
+ offset = 0;
+ }
+}
+
+void write_lba(uint32_t lba, volatile void *address, size_t size,
+ size_t offset) {
+ uintptr_t ptr = (uintptr_t)address;
+ lba += offset / SECTOR_SIZE;
+ offset = offset % SECTOR_SIZE;
+ size_t total_write = 0;
+ uint8_t sector_buffer[512];
+ for (int i = 0; size > 0; i++) {
+ ata_read_lba28(lba + i, 1, sector_buffer);
+ uint32_t write_len =
+ (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size;
+
+ memcpy(sector_buffer + offset, (void *)ptr, write_len);
+ ata_write_lba28(lba + i, 1, sector_buffer);
+
+ size -= write_len;
+ total_write += write_len;
+ ptr += write_len;
+ offset = 0;
+ }
+}
diff --git a/kernel/drivers/ata.h b/kernel/drivers/ata.h
new file mode 100644
index 0000000..3e88d7f
--- /dev/null
+++ b/kernel/drivers/ata.h
@@ -0,0 +1,16 @@
+#include "../cpu/idt.h"
+#include <stddef.h>
+
+#define SECTOR_SIZE 512
+
+void ata_init(void);
+
+void read_drive_chs(uint8_t head_index, uint8_t sector_count,
+ uint8_t sector_index, uint8_t cylinder_low_value,
+ uint8_t cylinder_high_value, void *address);
+void read_drive_lba(uint32_t lba, uint8_t sector_count, void *address);
+void read_lba(uint32_t lba, void *address, size_t size, size_t offset);
+void write_lba(uint32_t lba, volatile void *address, size_t size,
+ size_t offset);
+void ata_write_lba28(uint32_t lba, uint32_t sector_count,
+ volatile const uint8_t *buffer);
diff --git a/kernel/drivers/keyboard.c b/kernel/drivers/keyboard.c
new file mode 100644
index 0000000..8c96606
--- /dev/null
+++ b/kernel/drivers/keyboard.c
@@ -0,0 +1,188 @@
+#include <assert.h>
+#include <drivers/keyboard.h>
+#include <errno.h>
+#include <fs/devfs.h>
+#include <fs/fifo.h>
+#include <fs/vfs.h>
+#include <sched/scheduler.h>
+#include <stdint.h>
+
+#define PS2_REG_DATA 0x60
+#define PS2_REG_STATUS 0x64
+#define PS2_REG_COMMAND 0x64
+
+#define PS2_CMD_ENABLE_FIRST_PORT 0xAE // no rsp
+
+#define PS2_CMD_TEST_CONTROLLER 0xAA // has rsp
+
+#define PS2_RSP_TEST_PASSED 0x55
+#define PS2_RSP_TEST_FAILED 0xFC
+
+#define PS2_CMD_SET_SCANCODE 0xF0 // has rsp
+#define PS2_KB_ACK 0xFA
+#define PS2_KB_RESEND 0xFE
+
+#define PS2_CMD_SET_MAKE_RELEASE 0xF8 // has rsp
+
+uint8_t kb_scancodes[3] = {0x43, 0x41, 0x3f};
+
+FIFO_FILE *keyboard_fifo;
+
+uint8_t ascii_table[] = {
+ 'e', '\x1B', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8,
+ '\t',
+
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
+ // 0, // [
+ // 0, // ]
+ // 0,
+ // 0, // ?
+ '[', ']',
+ '\n', // ENTER
+ 'C',
+
+ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',
+ ';', // ;
+ '\'', // ;
+ '`', // ;
+ 'D', // LEFT SHIFT
+ '\\', // ;
+ 'z', 'x', 'c', 'v', 'b', 'n', 'm',
+ ',', // ;
+ '.', // ;
+ '/', // ;
+ 'U', // ;
+ 'U', // ;
+ 'U', // ;
+ ' ', // ;
+};
+
+uint8_t capital_ascii_table[] = {
+ 'e', '\x1B', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 8,
+ '\t',
+
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
+ // 0, // [
+ // 0, // ]
+ // 0,
+ // 0, // ?
+ '{', '}',
+ '\n', // ENTER
+ 'C',
+
+ 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
+ ':', // ;
+ '\"', // ;
+ '~', // ;
+ 'D', // LEFT SHIFT
+ '\\', // ;
+ 'Z', 'X', 'C', 'V', 'B', 'N', 'M',
+ '<', // ;
+ '>', // ;
+ '?', // ;
+ 'U', // ;
+ 'U', // ;
+ 'U', // ;
+ ' ', // ;
+};
+
+vfs_inode_t *kb_inode;
+
+uint8_t keyboard_to_ascii(uint16_t key, uint8_t capital) {
+ if ((key & 0xFF) > sizeof(ascii_table))
+ return 'U';
+ if (capital)
+ return capital_ascii_table[key & 0xFF];
+ else
+ return ascii_table[key & 0xFF];
+}
+
+uint8_t is_shift_down = 0;
+uint8_t is_alt_down = 0;
+
+struct KEY_EVENT {
+ char c;
+ uint8_t mode; // (shift (0 bit)) (alt (1 bit))
+ uint8_t release; // 0 pressed, 1 released
+};
+
+extern process_t *ready_queue;
+__attribute__((interrupt)) void
+int_keyboard(__attribute__((unused)) struct interrupt_frame *frame) {
+ outb(0x20, 0x20);
+ uint16_t c;
+ c = inb(PS2_REG_DATA);
+ int released = 0;
+ if (c & 0x80) {
+ switch ((c & ~(0x80)) & 0xFF) {
+ case 0x2A: // Left shift
+ case 0x36: // Right shift
+ is_shift_down = 0;
+ return;
+ case 0x38:
+ is_alt_down = 0;
+ return;
+ }
+ released = 1;
+ } else {
+ switch (c & 0xFF) {
+ case 0x2A: // Left shift
+ case 0x36: // Right shift
+ is_shift_down = 1;
+ return;
+ case 0x38:
+ is_alt_down = 1;
+ return;
+ }
+ released = 0;
+ }
+ unsigned char a = keyboard_to_ascii((c & ~(0x80)) & 0xFF, is_shift_down);
+ struct KEY_EVENT ev;
+ ev.c = a;
+ ev.release = released;
+ ev.mode = 0;
+ ev.mode |= is_shift_down << 0;
+ ev.mode |= is_alt_down << 1;
+ fifo_object_write((uint8_t *)&ev, 0, sizeof(ev), keyboard_fifo);
+ kb_inode->has_data = keyboard_fifo->has_data;
+}
+
+#define PS2_WAIT_RECV \
+ { \
+ for (;;) { \
+ uint8_t status = inb(PS2_REG_STATUS); \
+ if (status & 0x1) \
+ break; \
+ } \
+ }
+
+#define PS2_WAIT_SEND \
+ { \
+ for (;;) { \
+ uint8_t status = inb(PS2_REG_STATUS); \
+ if (!(status & (0x1 << 1))) \
+ break; \
+ } \
+ }
+
+void install_keyboard(void) {
+ keyboard_fifo = create_fifo_object();
+ install_handler(int_keyboard, INT_32_INTERRUPT_GATE(0x3), 0x21);
+}
+
+int keyboard_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ (void)offset;
+
+ if (0 == fd->inode->has_data) {
+ return -EAGAIN;
+ }
+ int rc = fifo_object_read(buffer, 0, len, keyboard_fifo);
+ fd->inode->has_data = keyboard_fifo->has_data;
+ return rc;
+}
+
+void add_keyboard(void) {
+ kb_inode = devfs_add_file("/keyboard", keyboard_read, NULL, NULL, 0, 0,
+ FS_TYPE_CHAR_DEVICE);
+}
diff --git a/kernel/drivers/keyboard.h b/kernel/drivers/keyboard.h
new file mode 100644
index 0000000..4b76f22
--- /dev/null
+++ b/kernel/drivers/keyboard.h
@@ -0,0 +1,11 @@
+#ifndef KEYBOARD_H
+#define KEYBOARD_H
+
+#include <stdint.h>
+#include <cpu/io.h>
+#include <cpu/idt.h>
+
+void install_keyboard(void);
+void add_keyboard(void);
+
+#endif
diff --git a/kernel/drivers/mouse.c b/kernel/drivers/mouse.c
new file mode 100644
index 0000000..23619a0
--- /dev/null
+++ b/kernel/drivers/mouse.c
@@ -0,0 +1,144 @@
+#include <cpu/idt.h>
+#include <drivers/mouse.h>
+#include <fs/devfs.h>
+#include <fs/fifo.h>
+#include <fs/vfs.h>
+#include <stdint.h>
+
+uint8_t mouse_cycle = 0; // unsigned char
+uint8_t mouse_uint8_t[3]; // signed char
+uint8_t mouse_x = 0; // signed char
+uint8_t mouse_y = 0; // signed char
+vfs_inode_t *mouse_inode;
+vfs_fd_t *mouse_fd;
+
+struct mouse_event {
+ uint8_t buttons;
+ uint8_t x;
+ uint8_t y;
+};
+
+int fs_mouse_write(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ int rc = fifo_object_write(buffer, offset, len, fd->inode->internal_object);
+ FIFO_FILE *f = fd->inode->internal_object;
+ mouse_inode->has_data = f->has_data;
+ return rc;
+}
+
+int fs_mouse_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ FIFO_FILE *f = fd->inode->internal_object;
+ if (!mouse_inode->has_data)
+ return 0;
+ int rc = fifo_object_read(buffer, offset, len, f);
+ mouse_inode->has_data = f->has_data;
+ return rc;
+}
+
+void add_mouse(void) {
+ mouse_inode = devfs_add_file("/mouse", fs_mouse_read, fs_mouse_write, NULL, 0,
+ 0, FS_TYPE_CHAR_DEVICE);
+ mouse_inode->internal_object = create_fifo_object();
+ // Don't look at this
+ int fd = vfs_open("/dev/mouse", O_RDWR, 0);
+ mouse_fd = get_vfs_fd(fd);
+ get_current_task()->file_descriptors[fd] = NULL;
+}
+
+__attribute__((interrupt)) void what(registers_t *r) { EOI(0xe); }
+
+__attribute__((interrupt)) void int_mouse(registers_t *r) {
+ (void)r;
+ EOI(12);
+ switch (mouse_cycle) {
+ case 0:
+ mouse_uint8_t[0] = inb(0x60);
+ if(!(mouse_uint8_t[0] & (1 << 3))) {
+ mouse_cycle = 0;
+ return;
+ }
+ mouse_cycle++;
+ break;
+ case 1:
+ mouse_uint8_t[1] = inb(0x60);
+ mouse_cycle++;
+ break;
+ case 2:
+ mouse_uint8_t[2] = inb(0x60);
+ mouse_x = mouse_uint8_t[1];
+ mouse_y = mouse_uint8_t[2];
+ mouse_cycle = 0;
+ struct mouse_event e;
+ e.buttons = mouse_uint8_t[0];
+ e.x = mouse_x;
+ e.y = mouse_y;
+ raw_vfs_pwrite(mouse_fd, &e, sizeof(e), 0);
+ break;
+ }
+}
+
+void mouse_wait(uint8_t a_type) {
+ uint32_t _time_out = 100000;
+ if (a_type == 0) {
+ while (_time_out--) {
+ if ((inb(0x64) & 1) == 1) {
+ return;
+ }
+ }
+ return;
+ } else {
+ while (_time_out--) {
+ if ((inb(0x64) & 2) == 0) {
+ return;
+ }
+ }
+ return;
+ }
+}
+
+void mouse_write(uint8_t a_write) {
+ // Wait to be able to send a command
+ mouse_wait(1);
+ // Tell the mouse we are sending a command
+ outb(0x64, 0xD4);
+ // Wait for the final part
+ mouse_wait(1);
+ // Finally write
+ outb(0x60, a_write);
+}
+
+uint8_t mouse_read() {
+ // Get's response from mouse
+ mouse_wait(0);
+ return inb(0x60);
+}
+
+void install_mouse(void) {
+ uint8_t _status; // unsigned char
+ asm("cli");
+ // Enable the auxiliary mouse device
+ mouse_wait(1);
+ outb(0x64, 0xA8);
+
+ // Enable the interrupts
+ mouse_wait(1);
+ outb(0x64, 0x20);
+ mouse_wait(0);
+ _status = (inb(0x60) | 2);
+ mouse_wait(1);
+ outb(0x64, 0x60);
+ mouse_wait(1);
+ outb(0x60, _status);
+
+ // Tell the mouse to use default settings
+ mouse_write(0xF6);
+ mouse_read(); // Acknowledge
+
+ // Enable the mouse
+ mouse_write(0xF4);
+ mouse_read(); // Acknowledge
+
+ install_handler(int_mouse, INT_32_INTERRUPT_GATE(0x3), 12 + 0x20);
+ install_handler(what, INT_32_INTERRUPT_GATE(0x3), 0xe + 0x20);
+}
diff --git a/kernel/drivers/mouse.h b/kernel/drivers/mouse.h
new file mode 100644
index 0000000..5248143
--- /dev/null
+++ b/kernel/drivers/mouse.h
@@ -0,0 +1,5 @@
+#ifndef MOUSE_H
+#define MOUSE_H
+void install_mouse(void);
+void add_mouse(void);
+#endif
diff --git a/kernel/drivers/pci.c b/kernel/drivers/pci.c
new file mode 100644
index 0000000..5fe14bd
--- /dev/null
+++ b/kernel/drivers/pci.c
@@ -0,0 +1,69 @@
+#include <assert.h>
+#include <cpu/io.h>
+#include <drivers/pci.h>
+#include <stdio.h>
+
+#define CONFIG_ADDRESS 0xCF8
+#define CONFIG_DATA 0xCFC
+
+void pci_config_write32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset, uint32_t data) {
+ uint32_t address;
+ uint32_t lbus = (uint32_t)device->bus;
+ uint32_t lslot = (uint32_t)device->slot;
+ uint32_t lfunc = (uint32_t)func;
+
+ // Create configuration address as per Figure 1
+ address = (uint32_t)((lbus << 16) | (lslot << 11) | (lfunc << 8) |
+ (offset & 0xFC) | ((uint32_t)0x80000000));
+
+ // Write out the address
+ outl(CONFIG_ADDRESS, address);
+ outl(CONFIG_DATA, data);
+}
+
+uint32_t pci_config_read32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset) {
+ uint32_t address;
+ uint32_t lbus = (uint32_t)device->bus;
+ uint32_t lslot = (uint32_t)device->slot;
+ uint32_t lfunc = (uint32_t)func;
+
+ // Create configuration address as per Figure 1
+ address = (uint32_t)((lbus << 16) | (lslot << 11) | (lfunc << 8) |
+ (offset & 0xFC) | ((uint32_t)0x80000000));
+
+ // Write out the address
+ outl(CONFIG_ADDRESS, address);
+ return inl(CONFIG_DATA);
+}
+
+int pci_populate_device_struct(uint16_t vendor, uint16_t device,
+ struct PCI_DEVICE *pci_device) {
+ pci_device->vendor = vendor;
+ pci_device->device = device;
+
+ for (int bus = 0; bus < 256; bus++) {
+ for (int slot = 0; slot < 256; slot++) {
+ struct PCI_DEVICE tmp;
+ tmp.bus = bus;
+ tmp.slot = slot;
+ uint32_t device_vendor = pci_config_read32(&tmp, 0, 0);
+ if (vendor != (device_vendor & 0xFFFF))
+ continue;
+ if (device != (device_vendor >> 16))
+ continue;
+ pci_device->bus = bus;
+ pci_device->slot = slot;
+ uint32_t bar0 = pci_config_read32(pci_device, 0, 0x10);
+ assert(bar0 & 0x1 && "Only support memory IO");
+ pci_device->gen.base_mem_io = bar0 & (~0x3);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+uint8_t pci_get_interrupt_line(const struct PCI_DEVICE *device) {
+ return pci_config_read32(device, 0, 0x3C) & 0xFF;
+}
diff --git a/kernel/drivers/pci.h b/kernel/drivers/pci.h
new file mode 100644
index 0000000..7511cee
--- /dev/null
+++ b/kernel/drivers/pci.h
@@ -0,0 +1,26 @@
+#include <stdint.h>
+
+struct GENERAL_DEVICE {
+ uint32_t base_mem_io;
+ uint8_t interrupt_line;
+};
+
+struct PCI_DEVICE {
+ uint16_t vendor;
+ uint16_t device;
+ uint8_t bus;
+ uint8_t slot;
+ union {
+ struct GENERAL_DEVICE gen;
+ };
+};
+
+uint32_t pci_config_read32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset);
+void pci_config_write32(const struct PCI_DEVICE *device, uint8_t func,
+ uint8_t offset, uint32_t data);
+
+int pci_populate_device_struct(uint16_t vendor, uint16_t device,
+ struct PCI_DEVICE *pci_device);
+
+uint8_t pci_get_interrupt_line(const struct PCI_DEVICE *device);
diff --git a/kernel/drivers/pit.c b/kernel/drivers/pit.c
new file mode 100644
index 0000000..711883c
--- /dev/null
+++ b/kernel/drivers/pit.c
@@ -0,0 +1,55 @@
+#include "pit.h"
+
+#define PIT_IO_CHANNEL_0 0x40
+#define PIT_IO_MODE_COMMAND 0x43
+
+uint64_t clock_num_ms_ticks = 0;
+uint64_t pit_counter = 0;
+uint16_t hertz;
+
+uint64_t pit_num_ms(void) { return clock_num_ms_ticks; }
+
+uint16_t read_pit_count(void) {
+ uint16_t count = 0;
+
+ outb(PIT_IO_MODE_COMMAND, 0x0 /*0b00000000*/);
+
+ count = inb(PIT_IO_CHANNEL_0);
+ count |= inb(PIT_IO_CHANNEL_0) << 8;
+
+ return count;
+}
+
+void set_pit_count(uint16_t hertz) {
+ uint16_t divisor = 1193180 / hertz;
+
+ /*
+ * 0b00110110
+ * ^^
+ * channel - 0
+ * ^^
+ * r/w mode - LSB then MSB
+ * ^^^
+ * mode - 3 Square Wave Mode
+ * ^
+ * BCD - no
+ */
+ outb(PIT_IO_MODE_COMMAND, 0x36 /*0b00110110*/);
+ outb(PIT_IO_CHANNEL_0, divisor & 0xFF);
+ outb(PIT_IO_CHANNEL_0, (divisor & 0xFF00) >> 8);
+}
+
+__attribute__((interrupt)) void
+int_clock(__attribute__((unused)) struct interrupt_frame *frame) {
+ outb(0x20, 0x20);
+ pit_counter++;
+ if (pit_counter >= hertz / 1000) {
+ pit_counter = 0;
+ clock_num_ms_ticks++;
+ }
+ switch_task();
+}
+
+void pit_install(void) {
+ install_handler(int_clock, INT_32_INTERRUPT_GATE(0x0), 0x20);
+}
diff --git a/kernel/drivers/pit.h b/kernel/drivers/pit.h
new file mode 100644
index 0000000..8d9ce98
--- /dev/null
+++ b/kernel/drivers/pit.h
@@ -0,0 +1,12 @@
+#ifndef PIT_H
+#define PIT_H
+#include <cpu/idt.h>
+#include <cpu/io.h>
+#include <sched/scheduler.h>
+#include <stdint.h>
+#include <stdio.h>
+
+void pit_install(void);
+void set_pit_count(uint16_t hertz);
+uint64_t pit_num_ms(void);
+#endif
diff --git a/kernel/drivers/pst.c b/kernel/drivers/pst.c
new file mode 100644
index 0000000..d063ec9
--- /dev/null
+++ b/kernel/drivers/pst.c
@@ -0,0 +1,17 @@
+#include <drivers/pst.h>
+#include <fs/tmpfs.h>
+
+int openpty(int *amaster, int *aslave, char *name,
+ /*const struct termios*/ void *termp,
+ /*const struct winsize*/ void *winp) {
+ (void)name;
+ (void)termp;
+ (void) winp;
+ int fd[2];
+ pipe(fd); // This depends upon that pipe will support read and write
+ // through the same fd. In reality this should not be the
+ // case.
+ *amaster = fd[0];
+ *aslave = fd[1];
+ return 0;
+}
diff --git a/kernel/drivers/pst.h b/kernel/drivers/pst.h
new file mode 100644
index 0000000..e8fdfaa
--- /dev/null
+++ b/kernel/drivers/pst.h
@@ -0,0 +1,7 @@
+#ifndef PST_H
+#define PST_H
+#include "../fs/vfs.h"
+
+int openpty(int *amaster, int *aslave, char *name, /*const struct termios*/ void *termp,
+ /*const struct winsize*/ void *winp);
+#endif
diff --git a/kernel/drivers/rtl8139.c b/kernel/drivers/rtl8139.c
new file mode 100644
index 0000000..3853eab
--- /dev/null
+++ b/kernel/drivers/rtl8139.c
@@ -0,0 +1,182 @@
+#include <assert.h>
+#include <cpu/idt.h>
+#include <cpu/io.h>
+#include <drivers/pci.h>
+#include <drivers/rtl8139.h>
+#include <mmu.h>
+#include <network/arp.h>
+#include <network/ethernet.h>
+
+#define RBSTART 0x30
+#define CMD 0x37
+#define IMR 0x3C
+
+#define TSD0 0x10 // transmit status
+#define TSAD0 0x20 // transmit start address
+
+struct PCI_DEVICE rtl8139;
+uint8_t *device_buffer;
+
+uint8_t *send_buffers[4];
+
+struct _INT_PACKET_HEADER {
+ uint8_t ROK : 1;
+ uint8_t FAE : 1;
+ uint8_t CRC : 1;
+ uint8_t LONG : 1;
+ uint8_t RUNT : 1;
+ uint8_t ISE : 1;
+ uint8_t reserved : 5;
+ uint8_t BAR : 1;
+ uint8_t PAM : 1;
+ uint8_t MAR : 1;
+};
+
+struct PACKET_HEADER {
+ union {
+ uint16_t raw;
+ struct _INT_PACKET_HEADER data;
+ };
+};
+
+uint16_t current_packet_read = 0;
+
+void handle_packet(void) {
+ assert(sizeof(struct _INT_PACKET_HEADER) == sizeof(uint16_t));
+
+ uint16_t *buf = (uint16_t *)(device_buffer + current_packet_read);
+ struct PACKET_HEADER packet_header;
+ packet_header.raw = *buf;
+ assert(packet_header.data.ROK);
+ kprintf("packet_header.raw: %x\n", packet_header.raw);
+ uint16_t packet_length = *(buf + 1);
+ kprintf("packet_length: %x\n", packet_length);
+
+ uint8_t packet_buffer[8192 + 16];
+ if (current_packet_read + packet_length >= 8192 + 16) {
+ uint32_t first_run = ((uint8_t *)buf + (8192 + 16)) - device_buffer;
+ memcpy(packet_buffer, buf, first_run);
+ memcpy(packet_buffer, device_buffer, packet_length - first_run);
+ } else {
+ memcpy(packet_buffer, buf, packet_length);
+ }
+
+ handle_ethernet((uint8_t *)packet_buffer + 4, packet_length);
+
+ // Thanks to exscape
+ // https://github.com/exscape/exscapeOS/blob/master/src/kernel/net/rtl8139.c
+ // and the programmers guide
+ // https://www.cs.usfca.edu/~cruse/cs326f04/RTL8139_ProgrammersGuide.pdf I
+ // have no clue what this calculation, I can't find anything possibly relating
+ // to this in the manual, but it does work I guess.
+ current_packet_read = (current_packet_read + packet_length + 4 + 3) & (~3);
+ current_packet_read %= 8192 + 16;
+ outw(rtl8139.gen.base_mem_io + 0x38, current_packet_read - 0x10);
+}
+
+__attribute__((interrupt)) void rtl8139_handler(void *regs) {
+ (void)regs;
+ uint16_t status = inw(rtl8139.gen.base_mem_io + 0x3e);
+ kprintf("status: %x\n", status);
+
+ outw(rtl8139.gen.base_mem_io + 0x3E, 0x5);
+ if (status & (1 << 2)) {
+ kprintf("Packet sent\n");
+ }
+ if (status & (1 << 0)) {
+ kprintf("Received packet\n");
+ handle_packet();
+ }
+
+ EOI(0xB);
+}
+
+int rtl8139_send_data(uint8_t *data, uint16_t data_size) {
+ const struct PCI_DEVICE *device = &rtl8139;
+ // FIXME: It should block or fail if there is too little space for the
+ // buffer
+ if (data_size > 0x1000)
+ return 0;
+ static int loop = 0;
+ if (loop > 3) {
+ loop = 0;
+ }
+ memcpy(send_buffers[loop], data, data_size);
+ outl(device->gen.base_mem_io + 0x20 + loop * 4,
+ (uint32_t)virtual_to_physical(send_buffers[loop], NULL));
+ outl(device->gen.base_mem_io + 0x10 + loop * 4, data_size);
+ loop += 1;
+ return 1;
+}
+
+void get_mac_address(uint8_t mac[6]) {
+ uint32_t base_address = rtl8139.gen.base_mem_io;
+ // Read the MAC address
+ uint64_t mac_address;
+ {
+ uint32_t low_mac = inl(base_address);
+ uint16_t high_mac = inw(base_address + 0x4);
+ mac_address = ((uint64_t)high_mac << 32) | low_mac;
+ }
+ kprintf("mac_address: %x\n", mac_address);
+ memcpy(mac, &mac_address, sizeof(uint8_t[6]));
+}
+
+uint8_t rtl8139_get_transmit_status(uint32_t base_address) {
+ uint32_t status_register = inl(base_address + 0x3E);
+ if ((status_register >> 3) & 0x1)
+ kprintf("transmit error :(\n");
+ uint8_t status = (status_register >> 2) & 0x1;
+ outl(base_address + 0x3E, 0x5);
+ return status;
+}
+
+void rtl8139_init(void) {
+ if (!pci_populate_device_struct(0x10EC, 0x8139, &rtl8139)) {
+ kprintf("RTL8139 not found :(\n");
+ return;
+ }
+ kprintf("RTL8139 found at bus: %x slot: %x\n", rtl8139.bus, rtl8139.slot);
+
+ uint8_t header_type = (pci_config_read32(&rtl8139, 0, 0xC) >> 16) & 0xFF;
+ assert(0 == header_type);
+
+ uint32_t base_address = rtl8139.gen.base_mem_io;
+ uint8_t interrupt_line = pci_get_interrupt_line(&rtl8139);
+
+ // Enable bus mastering
+ uint32_t register1 = pci_config_read32(&rtl8139, 0, 0x4);
+ register1 |= (1 << 2);
+ pci_config_write32(&rtl8139, 0, 0x4, register1);
+
+ // Turning on the device
+ outb(base_address + 0x52, 0x0);
+
+ // Reset the device and clear the RX and TX buffers
+ outb(base_address + CMD, 0x10);
+ for (; 0 != (inb(base_address + CMD) & 0x10);)
+ ;
+ device_buffer = ksbrk(8192 + 16);
+ memset(device_buffer, 0, 8192 + 16);
+ // Setupt the recieve buffer
+ uint32_t rx_buffer = (uint32_t)virtual_to_physical(device_buffer, NULL);
+ outl(base_address + RBSTART, rx_buffer);
+
+ // Set IMR + ISR
+ outw(base_address + IMR, (1 << 2) | (1 << 3) | (1 << 0));
+
+ // Set transmit and reciever enable
+ outb(base_address + 0x37, (1 << 2) | (1 << 3));
+
+ // Configure the recieve buffer
+ outl(base_address + 0x44,
+ 0xf); // 0xf is AB+AM+APM+AAP
+
+ install_handler(rtl8139_handler, INT_32_INTERRUPT_GATE(0x0),
+ 0x20 + interrupt_line);
+
+ // ksbrk() seems to have the magical ability of disabling interrupts?
+ // I have no fucking clue why that happens and it was a pain to debug.
+ for (int i = 0; i < 4; i++)
+ send_buffers[i] = ksbrk(0x1000);
+}
diff --git a/kernel/drivers/rtl8139.h b/kernel/drivers/rtl8139.h
new file mode 100644
index 0000000..6e13fdd
--- /dev/null
+++ b/kernel/drivers/rtl8139.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+void get_mac_address(uint8_t mac[6]);
+void rtl8139_init(void);
+int rtl8139_send_data(uint8_t *data, uint16_t data_size);
diff --git a/kernel/drivers/serial.c b/kernel/drivers/serial.c
new file mode 100644
index 0000000..549d852
--- /dev/null
+++ b/kernel/drivers/serial.c
@@ -0,0 +1,35 @@
+#include "cpu/io.h"
+
+#define PORT 0x3f8 // COM1
+
+int serial_init(void) {
+ outb(PORT + 1, 0x00); // Disable all interrupts
+ outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
+ outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
+ outb(PORT + 1, 0x00); // (hi byte)
+ outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
+ outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
+ outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
+ outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
+ 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 (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)
+ outb(PORT + 4, 0x0F);
+ return 0;
+}
+
+int is_transmit_empty() { return inb(PORT + 5) & 0x20; }
+
+void write_serial(char a) {
+ while (is_transmit_empty() == 0)
+ ;
+
+ outb(PORT, a);
+}
diff --git a/kernel/drivers/serial.h b/kernel/drivers/serial.h
new file mode 100644
index 0000000..327765b
--- /dev/null
+++ b/kernel/drivers/serial.h
@@ -0,0 +1,2 @@
+int serial_init(void);
+void write_serial(char a);
diff --git a/kernel/drivers/vbe.c b/kernel/drivers/vbe.c
new file mode 100644
index 0000000..c67b7b4
--- /dev/null
+++ b/kernel/drivers/vbe.c
@@ -0,0 +1,73 @@
+#include <assert.h>
+#include <drivers/vbe.h>
+#include <fs/devfs.h>
+#include <fs/vfs.h>
+#include <mmu.h>
+#include <stdio.h>
+
+uint8_t *framebuffer;
+uint32_t framebuffer_physical;
+uint32_t framebuffer_width;
+uint32_t framebuffer_height;
+uint64_t framebuffer_size;
+
+vfs_vm_object_t vbe_vm_object;
+
+#define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit)))
+#define min(_a, _b) (((_a) > (_b)) ? (_b) : (_a))
+
+struct DISPLAY_INFO {
+ uint32_t width;
+ uint32_t height;
+ uint8_t bpp;
+};
+
+struct DISPLAY_INFO vbe_info;
+
+void display_driver_init(multiboot_info_t *mbi) {
+ assert(CHECK_FLAG(mbi->flags, 12));
+ framebuffer_width = mbi->framebuffer_width;
+ framebuffer_height = mbi->framebuffer_height;
+
+ uint32_t bits_pp = mbi->framebuffer_bpp;
+ uint32_t bytes_pp = (bits_pp / 8) + (8 - (bits_pp % 8));
+
+ framebuffer_size = bytes_pp * framebuffer_width * framebuffer_height;
+
+ framebuffer_physical = mbi->framebuffer_addr;
+ framebuffer =
+ mmu_map_frames((void *)(uint32_t)mbi->framebuffer_addr, framebuffer_size);
+
+ vbe_info.width = framebuffer_width;
+ vbe_info.height = framebuffer_height;
+ vbe_info.bpp = mbi->framebuffer_bpp;
+}
+
+vfs_vm_object_t *vbe_get_vm_object(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd) {
+ (void)fd;
+ (void)length;
+ (void)offset;
+ vbe_vm_object.size = framebuffer_size;
+ int n = (uintptr_t)align_page((void *)(uint32_t)framebuffer_size) / 0x1000;
+ vbe_vm_object.object = kmalloc(sizeof(void *) * n);
+ for (int i = 0; i < n; i++) {
+ vbe_vm_object.object[i] = (void *)framebuffer_physical + (i * 0x1000);
+ }
+ return &vbe_vm_object;
+}
+
+int display_info_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd) {
+ (void)offset;
+ int read_len = min(sizeof(struct DISPLAY_INFO), len);
+ memcpy(buffer, &vbe_info, read_len);
+ return read_len;
+}
+
+void add_vbe_device(void) {
+ devfs_add_file("/vbe", NULL, NULL, vbe_get_vm_object, 1, 1,
+ FS_TYPE_BLOCK_DEVICE);
+ devfs_add_file("/display_info", display_info_read, NULL, NULL, 1, 0,
+ FS_TYPE_BLOCK_DEVICE);
+}
diff --git a/kernel/drivers/vbe.h b/kernel/drivers/vbe.h
new file mode 100644
index 0000000..4df2221
--- /dev/null
+++ b/kernel/drivers/vbe.h
@@ -0,0 +1,7 @@
+#ifndef VBE_H
+#define VBE_H
+#include <multiboot.h>
+void display_driver_init(multiboot_info_t *mb);
+void display_driver_cross(multiboot_info_t *mbi);
+void add_vbe_device(void);
+#endif // VBE_H
diff --git a/kernel/elf.c b/kernel/elf.c
new file mode 100644
index 0000000..7316a0c
--- /dev/null
+++ b/kernel/elf.c
@@ -0,0 +1,73 @@
+#include <assert.h>
+#include <crypto/SHA1/sha1.h>
+#include <elf.h>
+#include <sched/scheduler.h>
+#include <stddef.h>
+#include <stdint.h>
+
+void *load_elf_file(const char *f, uint32_t *ds) {
+ // ELFHeader *header = kmalloc(sizeof(ELFHeader));
+ ELFHeader header;
+ int fd = vfs_open(f, O_RDONLY, 0);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ if (sizeof(header) != vfs_pread(fd, &header, sizeof(header), 0)) {
+ return NULL;
+ }
+
+ if (0 != memcmp(header.e_ident, "\x7F\x45\x4C\x46" /* "\x7FELF" */, 4)) {
+ klog("Incorrect ELF signature", LOG_ERROR);
+ return NULL;
+ }
+
+ if (0 > fd) {
+ return NULL;
+ }
+ Elf32_Phdr program_header;
+ assert(sizeof(program_header) == header.e_phentsize);
+ uint32_t header_offset = header.e_phoff;
+ uintptr_t end_of_code = 0;
+ for (int i = 0; i < header.e_phnum;
+ i++, header_offset += header.e_phentsize) {
+ if (0 >=
+ vfs_pread(fd, &program_header, sizeof(program_header), header_offset)) {
+ return NULL;
+ }
+
+ // FIXME: Only one type is supported, which is 1(load). More should be
+ // added.
+ assert(1 == program_header.p_type);
+
+ // 1. Clear p_memsz bytes at p_vaddr to 0.(We also allocate frames for
+ // that range)
+ uint32_t p_memsz = program_header.p_memsz;
+ uint32_t p_vaddr = program_header.p_vaddr;
+
+ uint32_t pages_to_allocate =
+ (uint32_t)align_page((void *)(p_vaddr + p_memsz));
+ pages_to_allocate -= p_vaddr - (p_vaddr % 0x1000);
+ pages_to_allocate /= 0x1000;
+
+ mmu_allocate_region((void *)p_vaddr, pages_to_allocate * 0x1000,
+ MMU_FLAG_RW, NULL);
+
+ flush_tlb();
+
+ uintptr_t e = program_header.p_vaddr + program_header.p_memsz;
+ if (e > end_of_code)
+ end_of_code = e;
+
+ memset((void *)program_header.p_vaddr, 0, program_header.p_memsz);
+
+ // 2. Copy p_filesz bytes from p_offset to p_vaddr
+ int rc = vfs_pread(fd, (void *)program_header.p_vaddr,
+ program_header.p_filesz, program_header.p_offset);
+
+ assert(rc == (int)program_header.p_filesz);
+ }
+ *ds = end_of_code;
+ vfs_close(fd);
+ return (void *)header.e_entry;
+}
diff --git a/kernel/elf.h b/kernel/elf.h
new file mode 100644
index 0000000..452ddbd
--- /dev/null
+++ b/kernel/elf.h
@@ -0,0 +1,99 @@
+#ifndef ELF_H
+#define ELF_H
+#include <stdint.h>
+#include <mmu.h>
+#include <fs/vfs.h>
+#include <assert.h>
+
+#define ET_NONE 0 // No file type
+#define ET_REL 1 // Relocatable file
+#define ET_EXEC 2 // Executable file
+#define ET_DYN 3 // Shared object file
+#define ET_CORE 4 // Core file
+#define ET_LOPROC 0xff00 // Processor-specific
+#define ET_HIPROC 0xffff // Processor-specific
+
+#define EM_NONE 0 // No machine
+#define EM_M32 1 // AT&T WE 32100
+#define EM_SPARC 2 // SPARC
+#define EM_386 3 // Intel 80386
+#define EM_68K 4 // Motorola 68000
+#define EM_88K 5 // Motorola 88000
+#define EM_860 7 // Intel 80860
+#define EM_MIPS 8 // MIPS RS3000
+
+#define EV_NONE 0 // Invalid version
+#define EV_CURRENT 1 // Current version
+
+#define ELF_EXECUTABLE (1 << 0)
+#define ELF_WRITABLE (1 << 1)
+#define ELF_READABLE (1 << 2)
+
+#define Elf32_Addr uint32_t // Unsigned program address
+#define Elf32_Half uint16_t // Unsigned medium integer
+#define Elf32_Off uint32_t // Unsigned file offset
+#define Elf32_Sword uint32_t // Signed large integer
+#define Elf32_Word uint32_t // Unsigned large integer
+
+#define ELF_EXEC (1 << 0)
+#define ELF_WRITE (1 << 1)
+#define ELF_READ (1 << 2)
+
+// ELF header
+typedef struct {
+ unsigned char e_ident[16];
+ Elf32_Half e_type; // Object file type (ET_*)
+ Elf32_Half e_machine; // Required architecture (EM_*)
+ Elf32_Word e_version; // Object file version (EV_*)
+ Elf32_Addr e_entry; // File entry point
+ Elf32_Off e_phoff; // Program header table's offset(bytes)
+ Elf32_Off e_shoff; // Section header table's offset(bytes)
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize; // ELF Header size
+ Elf32_Half
+ e_phentsize; // Size of program's header tables(all are the same size)
+ Elf32_Half e_phnum; // Amount of program headers
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} __attribute__((packed)) ELFHeader;
+
+// Section header
+typedef struct {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+enum ShT_Types {
+ SHT_NULL = 0, // Null section
+ SHT_PROGBITS = 1, // Program information
+ SHT_SYMTAB = 2, // Symbol table
+ SHT_STRTAB = 3, // String table
+ SHT_RELA = 4, // Relocation (w/ addend)
+ SHT_NOBITS = 8, // Not present in file
+ SHT_REL = 9, // Relocation (no addend)
+};
+
+// Program header
+typedef struct {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} __attribute__((packed)) Elf32_Phdr;
+
+
+void *load_elf_file(const char *f, uint32_t *ds);
+#endif
diff --git a/kernel/fs/devfs.c b/kernel/fs/devfs.c
new file mode 100644
index 0000000..14748a7
--- /dev/null
+++ b/kernel/fs/devfs.c
@@ -0,0 +1,91 @@
+#include <drivers/keyboard.h>
+#include <drivers/serial.h>
+#include <fs/devfs.h>
+#include <fs/vfs.h>
+#include <random.h>
+
+devfs_file files[20];
+int num_files = 0;
+
+vfs_inode_t *devfs_add_file(
+ char *path,
+ int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ vfs_vm_object_t *(get_vm_object)(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd),
+ uint8_t has_data, uint8_t can_write, int type) {
+ files[num_files].name = copy_and_allocate_string(path);
+
+ vfs_inode_t *i = kmalloc(sizeof(vfs_inode_t));
+ files[num_files].inode = i;
+ i->type = type;
+ i->read = read;
+ i->write = write;
+ i->close = NULL;
+ i->get_vm_object = get_vm_object;
+ i->has_data = has_data;
+ i->is_open = 1;
+ i->can_write = can_write;
+ num_files++;
+ return i;
+}
+
+vfs_inode_t *devfs_open(const char *file) {
+ for (int i = 0; i < num_files; i++)
+ if (isequal_n(files[i].name, file, strlen(files[i].name)))
+ return files[i].inode;
+
+ return 0;
+}
+
+int devfs_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ return fd->inode->read(buffer, offset, len, fd);
+}
+
+int devfs_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ return fd->inode->write(buffer, offset, len, fd);
+}
+
+vfs_vm_object_t *devfs_get_vm_object(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd) {
+ return fd->inode->get_vm_object(length, offset, fd);
+}
+
+int stdout_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ (void)offset;
+ (void)fd;
+
+ int rc = len;
+ for (; len--;)
+ putc(*buffer++);
+ return rc;
+}
+
+int serial_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ (void)offset;
+ (void)fd;
+
+ int rc = len;
+ for (; len--;)
+ write_serial(*buffer++);
+ return rc;
+}
+
+void add_serial(void) {
+ devfs_add_file("/serial", NULL, serial_write, NULL, 0, 1,
+ FS_TYPE_CHAR_DEVICE);
+}
+
+void add_stdout(void) {
+ devfs_add_file("/stdout", NULL, stdout_write, NULL, 0, 1,
+ FS_TYPE_CHAR_DEVICE);
+}
+
+vfs_inode_t *devfs_mount(void) {
+ vfs_inode_t *root = kmalloc_eternal(sizeof(vfs_inode_t));
+ root->open = devfs_open;
+ root->read = devfs_read;
+ root->write = devfs_write;
+ root->close = NULL;
+ return root;
+}
diff --git a/kernel/fs/devfs.h b/kernel/fs/devfs.h
new file mode 100644
index 0000000..23a499e
--- /dev/null
+++ b/kernel/fs/devfs.h
@@ -0,0 +1,26 @@
+#ifndef DEVFS_H
+#define DEVFS_H
+#include <defs.h>
+#include <fs/vfs.h>
+#include <stdint.h>
+
+typedef struct devfs_file {
+ char *name;
+ vfs_inode_t *inode;
+} devfs_file;
+
+vfs_inode_t *devfs_mount(void);
+vfs_inode_t *devfs_open(const char *file);
+int devfs_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd);
+int devfs_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd);
+void add_stdout(void);
+void add_serial(void);
+vfs_inode_t *devfs_add_file(
+ char *path,
+ int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd),
+ uint8_t has_data, uint8_t can_write, int type);
+
+#endif
diff --git a/kernel/fs/ext2.c b/kernel/fs/ext2.c
new file mode 100644
index 0000000..bd0fb07
--- /dev/null
+++ b/kernel/fs/ext2.c
@@ -0,0 +1,763 @@
+#include <assert.h>
+#include <fs/ext2.h>
+#include <fs/vfs.h>
+#include <stdint.h>
+#include <string.h>
+
+#define EXT2_SUPERBLOCK_SECTOR 2
+#define EXT2_ROOT_INODE 2
+
+#define BLOCKS_REQUIRED(_a, _b) ((_a) / (_b) + (((_a) % (_b)) != 0))
+
+superblock_t *superblock;
+uint32_t block_byte_size;
+uint32_t inode_size;
+uint32_t inodes_per_block;
+
+#define BLOCK_SIZE (block_byte_size)
+
+void ext2_close(vfs_fd_t *fd) {
+ return; // There is nothing to clear
+}
+
+int read_inode(int inode_num, unsigned char *data, uint64_t size,
+ uint64_t offset, uint64_t *file_size);
+
+struct BLOCK_CACHE {
+ uint32_t block_num;
+ uint8_t block[1024];
+};
+
+#define NUM_BLOCK_CACHE 30
+struct BLOCK_CACHE cache[NUM_BLOCK_CACHE] = {0};
+uint8_t last_taken_cache = 0;
+
+void cached_read_block(uint32_t block, void *address, size_t size,
+ size_t offset) {
+ int free_found = -1;
+ for (int i = 0; i < NUM_BLOCK_CACHE; i++) {
+ if (cache[i].block_num == block) {
+ memcpy(address, cache[i].block + offset, size);
+ return;
+ }
+ if (0 == cache[i].block_num)
+ free_found = i;
+ }
+
+ if (-1 == free_found) {
+ free_found = last_taken_cache;
+ last_taken_cache++;
+ if (last_taken_cache >= NUM_BLOCK_CACHE)
+ last_taken_cache = 0;
+ }
+
+ struct BLOCK_CACHE *c = &cache[free_found];
+ c->block_num = block;
+ read_lba(block * block_byte_size / 512, c->block, 1024, 0);
+ return cached_read_block(block, address, size, offset);
+}
+
+void ext2_read_block(uint32_t block, void *address, size_t size,
+ size_t offset) {
+ cached_read_block(block, address, size, offset);
+}
+
+void ext2_write_block(uint32_t block, void *address, size_t size,
+ size_t offset) {
+ // Invalidate a old cache
+ for (int i = 0; i < NUM_BLOCK_CACHE; i++) {
+ if (cache[i].block_num == block) {
+ cache[i].block_num = 0;
+ break;
+ }
+ }
+ write_lba(block * block_byte_size / 512, address, size, offset);
+}
+
+void write_group_descriptor(uint32_t group_index, bgdt_t *block_group) {
+ int starting_block = (1024 == block_byte_size) ? 2 : 1;
+ ext2_write_block(starting_block, block_group, sizeof(bgdt_t),
+ group_index * sizeof(bgdt_t));
+}
+
+void get_group_descriptor(uint32_t group_index, bgdt_t *block_group) {
+ int starting_block = (1024 == block_byte_size) ? 2 : 1;
+ ext2_read_block(starting_block, block_group, sizeof(bgdt_t),
+ group_index * sizeof(bgdt_t));
+}
+
+uint32_t num_block_groups(void) {
+ // Determining the Number of Block Groups
+
+ // From the Superblock, extract the size of each block, the total
+ // number of inodes, the total number of blocks, the number of blocks
+ // per block group, and the number of inodes in each block group. From
+ // this information we can infer the number of block groups there are
+ // by:
+
+ // Rounding up the total number of blocks divided by the number of
+ // blocks per block group
+ uint32_t num_blocks = superblock->num_blocks;
+ uint32_t num_blocks_in_group = superblock->num_blocks_in_group;
+ uint32_t b = num_blocks / num_blocks_in_group;
+ if (num_blocks % num_blocks_in_group != 0)
+ b++;
+
+ // Rounding up the total number of inodes divided by the number of
+ // inodes per block group
+ uint32_t num_inodes = superblock->num_inodes;
+ uint32_t num_inodes_in_group = superblock->num_inodes_in_group;
+ uint32_t i = num_inodes / num_inodes_in_group;
+ if (num_inodes % num_inodes_in_group != 0)
+ i++;
+ // Both (and check them against each other)
+ assert(i == b);
+ return i;
+}
+
+void ext2_block_containing_inode(uint32_t inode_index, uint32_t *block_index,
+ uint32_t *offset) {
+ assert(0 != inode_index);
+ bgdt_t block_group;
+ get_group_descriptor((inode_index - 1) / superblock->num_inodes_in_group,
+ &block_group);
+
+ uint64_t full_offset =
+ ((inode_index - 1) % superblock->num_inodes_in_group) * inode_size;
+ *block_index = block_group.starting_inode_table +
+ (full_offset >> (superblock->block_size + 10));
+ *offset = full_offset & (block_byte_size - 1);
+}
+
+int ext2_last_inode_read = -1;
+inode_t ext2_last_inode;
+
+void ext2_get_inode_header(int inode_index, inode_t *data) {
+ // Very simple cache. If the inode_index is a inode already read then
+ // just copy the old data.
+ if (ext2_last_inode_read == inode_index) {
+ memcpy(data, &ext2_last_inode, sizeof(inode_t));
+ return;
+ }
+ uint32_t block_index;
+ uint32_t block_offset;
+ ext2_block_containing_inode(inode_index, &block_index, &block_offset);
+
+ uint8_t mem_block[inode_size];
+ ext2_read_block(block_index, mem_block, inode_size, block_offset);
+
+ memcpy(data, mem_block, inode_size);
+ memcpy(&ext2_last_inode, mem_block, sizeof(inode_t));
+ ext2_last_inode_read = inode_index;
+}
+
+void ext2_write_inode(int inode_index, inode_t *data) {
+ if (ext2_last_inode_read == inode_index)
+ ext2_last_inode_read = -1; // Invalidate the cache
+ uint32_t block_index;
+ uint32_t block_offset;
+ ext2_block_containing_inode(inode_index, &block_index, &block_offset);
+
+ uint8_t mem_block[inode_size];
+ memcpy(mem_block, data, inode_size);
+ ext2_write_block(block_index, mem_block, inode_size, block_offset);
+}
+
+int ext2_get_inode_in_directory(int dir_inode, char *file,
+ direntry_header_t *entry) {
+ // FIXME: Allocate sufficent size each time
+ unsigned char *data = kmalloc(block_byte_size * 5);
+ ASSERT_BUT_FIXME_PROPOGATE(
+ -1 != read_inode(dir_inode, data, block_byte_size * 5, 0, 0));
+
+ direntry_header_t *dir;
+ unsigned char *data_p = data;
+ for (; (dir = (direntry_header_t *)data_p)->inode; data_p += dir->size) {
+ if (0 == dir->size)
+ break;
+ if (0 == dir->name_length)
+ continue;
+ if (0 ==
+ memcmp(data_p + sizeof(direntry_header_t), file, dir->name_length)) {
+ if (strlen(file) > dir->name_length)
+ continue;
+ if (entry)
+ memcpy(entry, data_p, sizeof(direntry_header_t));
+ return dir->inode;
+ }
+ }
+ return 0;
+}
+
+int ext2_read_dir(int dir_inode, unsigned char *buffer, size_t len,
+ size_t offset) {
+ unsigned char data[block_byte_size];
+ read_inode(dir_inode, data, block_byte_size, 0, 0);
+
+ direntry_header_t *dir;
+ struct dirent tmp_entry;
+ size_t n_dir = 0;
+ int rc = 0;
+ unsigned char *data_p = data;
+ for (; (dir = (direntry_header_t *)data_p)->inode && len > 0;
+ data_p += dir->size, n_dir++) {
+ if (0 == dir->size)
+ break;
+ if (0 == dir->name_length)
+ continue;
+ if (n_dir < (offset / sizeof(struct dirent)))
+ continue;
+
+ memcpy(tmp_entry.d_name, data_p + sizeof(direntry_header_t),
+ dir->name_length);
+ tmp_entry.d_name[dir->name_length] = '\0';
+ uint8_t *p = (uint8_t *)&tmp_entry;
+ size_t l = sizeof(struct dirent);
+
+ l = (len < l) ? len : l;
+ memcpy(buffer, p, l);
+ len -= l;
+ rc += l;
+ }
+ return rc;
+}
+
+uint32_t ext2_find_inode(const char *file) {
+ int cur_path_inode = EXT2_ROOT_INODE;
+
+ if (*file == '/' && *(file + 1) == '\0')
+ return cur_path_inode;
+
+ char *str = copy_and_allocate_string(file);
+ char *orig_str = str;
+
+ char *start;
+ for (;;) {
+ int final = 0;
+ start = str + 1;
+ str++;
+
+ for (; '/' != *str && '\0' != *str; str++)
+ ;
+ if ('\0' == *str)
+ final = 1;
+
+ *str = '\0';
+
+ direntry_header_t a;
+ if (0 == (cur_path_inode =
+ ext2_get_inode_in_directory(cur_path_inode, start, &a))) {
+ kfree(orig_str);
+ return 0;
+ }
+
+ if (final)
+ break;
+
+ // The expected returned entry is a directory
+ if (TYPE_INDICATOR_DIRECTORY != a.type_indicator) {
+ kfree(orig_str);
+ kprintf("FAILED\n");
+ return 0;
+ }
+ }
+ kfree(orig_str);
+ return cur_path_inode;
+}
+
+uint32_t get_singly_block_index(uint32_t singly_block_ptr, uint32_t i) {
+ uint8_t block[block_byte_size];
+ ext2_read_block(singly_block_ptr, block, block_byte_size, 0);
+ uint32_t index = *(uint32_t *)(block + (i * (32 / 8)));
+ return index;
+}
+
+int get_block(inode_t *inode, uint32_t i) {
+ if (i < 12)
+ return inode->block_pointers[i];
+
+ i -= 12;
+ uint32_t singly_block_size = block_byte_size / (32 / 8);
+ uint32_t double_block_size = (singly_block_size * singly_block_size);
+ if (i < singly_block_size) {
+ return get_singly_block_index(inode->single_indirect_block_pointer, i);
+ } else if (i < double_block_size) {
+ i -= singly_block_size;
+ uint32_t singly_entry = get_singly_block_index(
+ inode->double_indirect_block_pointer, i / singly_block_size);
+ uint32_t offset_in_entry = i % singly_block_size;
+ int block = get_singly_block_index(singly_entry, offset_in_entry);
+ return block;
+ }
+ assert(0);
+ return 0;
+}
+
+int get_free_block(int allocate) {
+ bgdt_t block_group;
+ uint8_t bitmap[BLOCK_SIZE];
+ assert(0 < superblock->num_blocks_unallocated);
+ for (uint32_t g = 0; g < num_block_groups(); g++) {
+ get_group_descriptor(g, &block_group);
+
+ if (block_group.num_unallocated_blocks_in_group == 0) {
+ kprintf("skip\n");
+ continue;
+ }
+
+ ext2_read_block(block_group.block_usage_bitmap, bitmap, BLOCK_SIZE, 0);
+ for (uint32_t i = 0; i < superblock->num_blocks_in_group; i++) {
+ if (!(bitmap[i >> 3] & (1 << (i % 8)))) {
+ if (allocate) {
+ bitmap[i >> 3] |= (1 << (i % 8));
+ ext2_write_block(block_group.block_usage_bitmap, bitmap, BLOCK_SIZE,
+ 0);
+ block_group.num_unallocated_blocks_in_group--;
+ write_group_descriptor(g, &block_group);
+ superblock->num_blocks_unallocated--;
+ write_lba(EXT2_SUPERBLOCK_SECTOR, (void *)superblock, 2 * SECTOR_SIZE,
+ 0);
+ }
+ return i + g * superblock->num_blocks_in_group + 1;
+ }
+ }
+ }
+ return -1;
+}
+
+int get_free_inode(int allocate) {
+ bgdt_t block_group;
+ assert(0 < superblock->num_inodes_unallocated);
+ for (uint32_t g = 0; g < num_block_groups(); g++) {
+ get_group_descriptor(g, &block_group);
+
+ if (0 == block_group.num_unallocated_inodes_in_group)
+ continue;
+
+ uint8_t bitmap[BLOCK_SIZE];
+ ext2_read_block(block_group.inode_usage_bitmap, bitmap, BLOCK_SIZE, 0);
+ for (uint32_t i = 0; i < superblock->num_inodes_in_group; i++) {
+ if (!(bitmap[i / 8] & (1 << (i % 8)))) {
+ if (allocate) {
+ bitmap[i / 8] |= (1 << (i % 8));
+ ext2_write_block(block_group.inode_usage_bitmap, bitmap, BLOCK_SIZE,
+ 0);
+ block_group.num_unallocated_inodes_in_group--;
+ write_group_descriptor(g, &block_group);
+ superblock->num_inodes_unallocated--;
+ write_lba(EXT2_SUPERBLOCK_SECTOR, (void *)superblock, 2 * SECTOR_SIZE,
+ 0);
+ }
+ return i + g * superblock->num_inodes_in_group + 1;
+ }
+ }
+ }
+ return -1;
+}
+
+int write_inode(int inode_num, unsigned char *data, uint64_t size,
+ uint64_t offset, uint64_t *file_size, int append) {
+ (void)file_size;
+ uint8_t inode_buffer[inode_size];
+ ext2_get_inode_header(inode_num, (inode_t *)inode_buffer);
+ inode_t *inode = (inode_t *)inode_buffer;
+
+ uint64_t fsize = (uint64_t)(((uint64_t)inode->_upper_32size << 32) |
+ (uint64_t)inode->low_32size);
+ if (append)
+ offset = fsize;
+
+ uint32_t block_start = offset / block_byte_size;
+ uint32_t block_offset = offset % block_byte_size;
+
+ int num_blocks_used = inode->num_disk_sectors / (BLOCK_SIZE / SECTOR_SIZE);
+
+ if (size + offset > fsize)
+ fsize = size + offset;
+
+ int num_blocks_required = BLOCKS_REQUIRED(fsize, BLOCK_SIZE);
+
+ for (int i = num_blocks_used; i < num_blocks_required; i++) {
+ if (i > 12)
+ assert(0);
+ int b = get_free_block(1 /*true*/);
+ assert(-1 != b);
+ inode->block_pointers[i] = b;
+ }
+
+ inode->num_disk_sectors = num_blocks_required * (BLOCK_SIZE / SECTOR_SIZE);
+
+ int bytes_written = 0;
+ for (int i = block_start; size; i++) {
+ uint32_t block = get_block(inode, i);
+ if (0 == block) {
+ kprintf("block_not_found\n");
+ break;
+ }
+
+ int write_len = ((size + block_offset) > block_byte_size)
+ ? (block_byte_size - block_offset)
+ : size;
+ ext2_write_block(block, data + bytes_written, write_len, block_offset);
+ block_offset = 0;
+ bytes_written += write_len;
+ size -= write_len;
+ }
+ inode->low_32size = fsize;
+ inode->_upper_32size = (fsize >> 32);
+ ext2_write_inode(inode_num, inode);
+ return bytes_written;
+}
+
+int read_inode(int inode_num, unsigned char *data, uint64_t size,
+ uint64_t offset, uint64_t *file_size) {
+ // TODO: Fail if size is lower than the size of the file being read, and
+ // return the size of the file the callers is trying to read.
+ uint8_t inode_buffer[inode_size];
+ ext2_get_inode_header(inode_num, (inode_t *)inode_buffer);
+ inode_t *inode = (inode_t *)inode_buffer;
+
+ uint64_t fsize = (uint64_t)(((uint64_t)inode->_upper_32size << 32) |
+ (uint64_t)inode->low_32size);
+
+ if (file_size)
+ *file_size = fsize;
+
+ if (size > fsize - offset)
+ size -= ((size + offset) - fsize);
+
+ if (size == 0)
+ return 0;
+
+ if (offset > fsize)
+ return 0;
+
+ uint32_t block_start = offset / block_byte_size;
+ uint32_t block_offset = offset % block_byte_size;
+
+ int bytes_read = 0;
+ for (int i = block_start; size; i++) {
+ uint32_t block = get_block(inode, i);
+ if (0 == block) {
+ klog("Filesystem EXT2: Unable to find block", LOG_WARN);
+ return -1;
+ }
+
+ int read_len = ((size + block_offset) > block_byte_size)
+ ? (block_byte_size - block_offset)
+ : size;
+ ext2_read_block(block, data + bytes_read, read_len, block_offset);
+ block_offset = 0;
+ bytes_read += read_len;
+ size -= read_len;
+ }
+ return bytes_read;
+}
+
+size_t ext2_read_file_offset(const char *file, unsigned char *data,
+ uint64_t size, uint64_t offset,
+ uint64_t *file_size) {
+ // TODO: Fail if the file does not exist.
+ uint32_t inode = ext2_find_inode(file);
+ return read_inode(inode, data, size, offset, file_size);
+}
+
+size_t ext2_read_file(const char *file, unsigned char *data, size_t size,
+ uint64_t *file_size) {
+ return ext2_read_file_offset(file, data, size, 0, file_size);
+}
+
+int resolve_link(int inode_num) {
+ uint8_t tmp[inode_size];
+ inode_t *inode = (inode_t *)tmp;
+ uint64_t inode_size =
+ (((uint64_t)inode->_upper_32size) << 32) & inode->low_32size;
+ assert(inode_size <= 60);
+ ext2_get_inode_header(inode_num, inode);
+ char *path = (char *)(tmp + (10 * 4));
+ path--;
+ *path = '/';
+ return ext2_find_inode(path);
+}
+
+int ext2_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ uint64_t file_size;
+ int rc;
+ int inode_num = fd->inode->inode_num;
+ assert(fd->inode->type != FS_TYPE_DIRECTORY);
+ if (fd->inode->type == FS_TYPE_LINK) {
+ inode_num = resolve_link(inode_num);
+ }
+ rc = write_inode(inode_num, buffer, len, offset, &file_size, 0);
+ return rc;
+}
+
+int ext2_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ uint64_t file_size;
+ int rc;
+ int inode_num = fd->inode->inode_num;
+ if (fd->inode->type == FS_TYPE_DIRECTORY) {
+ rc = ext2_read_dir(inode_num, buffer, len, offset);
+ return rc;
+ }
+ if (fd->inode->type == FS_TYPE_LINK) {
+ inode_num = resolve_link(inode_num);
+ }
+ rc = read_inode(inode_num, buffer, len, offset, &file_size);
+ return rc;
+}
+
+int ext2_truncate(vfs_fd_t *fd, size_t length) {
+ // TODO: Blocks that are no longer used should be freed.
+ char inode_buffer[inode_size];
+ inode_t *ext2_inode = (inode_t *)inode_buffer;
+
+ ext2_get_inode_header(fd->inode->inode_num, ext2_inode);
+
+ // FIXME: ftruncate should support 64 bit lengths
+ ext2_inode->_upper_32size = 0;
+ ext2_inode->low_32size = length;
+
+ ext2_write_inode(fd->inode->inode_num, ext2_inode);
+ return 0;
+}
+
+vfs_inode_t *ext2_open(const char *path) {
+ uint32_t inode_num = ext2_find_inode(path);
+ if (0 == inode_num)
+ return NULL;
+
+ inode_t ext2_inode[inode_size];
+ ext2_get_inode_header(inode_num, ext2_inode);
+ uint64_t file_size =
+ ((uint64_t)(ext2_inode->_upper_32size) << 32) | ext2_inode->low_32size;
+
+ uint8_t type;
+ switch ((ext2_inode->types_permissions / 0x1000)) {
+ case 0xA:
+ type = FS_TYPE_LINK;
+ break;
+ case 0x4:
+ type = FS_TYPE_DIRECTORY;
+ break;
+ default:
+ type = FS_TYPE_FILE;
+ break;
+ }
+
+ return vfs_create_inode(inode_num, type, 1 /*has_data*/, 1 /*can_write*/,
+ 1 /*is_open*/, NULL /*internal_object*/, file_size,
+ ext2_open, ext2_create_file, ext2_read, ext2_write,
+ ext2_close, ext2_create_directory,
+ NULL /*get_vm_object*/, ext2_truncate /*truncate*/);
+}
+
+uint64_t end_of_last_entry_position(int dir_inode, uint64_t *entry_offset,
+ direntry_header_t *meta) {
+ // FIXME: Allocate sufficent size each time
+ unsigned char data[block_byte_size * 5];
+ uint64_t file_size = 0;
+ read_inode(dir_inode, data, block_byte_size * 5, 0, &file_size);
+ assert(block_byte_size * 5 > file_size);
+
+ direntry_header_t *dir;
+ unsigned char *data_p = data;
+ uint64_t pos = 0;
+ uint64_t prev = pos;
+ for (; pos < file_size && (dir = (direntry_header_t *)data_p)->size;
+ data_p += dir->size, prev = pos, pos += dir->size)
+ ;
+ if (entry_offset)
+ *entry_offset = prev;
+ if (meta)
+ memcpy(meta, ((char *)data) + prev, sizeof(direntry_header_t));
+ return pos;
+}
+
+void ext2_create_entry(int directory_inode, direntry_header_t entry_header,
+ const char *name) {
+ uint64_t entry_offset = 0;
+ direntry_header_t meta;
+ end_of_last_entry_position(directory_inode, &entry_offset, &meta);
+
+ uint32_t padding_in_use = block_byte_size - entry_offset;
+
+ // assert(padding_in_use == meta.size);
+ assert(padding_in_use >=
+ (sizeof(direntry_header_t) + entry_header.name_length));
+
+ // Modify the entry to have its real size
+ meta.size = sizeof(direntry_header_t) + meta.name_length;
+ meta.size += (4 - (meta.size % 4));
+ write_inode(directory_inode, (unsigned char *)&meta,
+ sizeof(direntry_header_t), entry_offset, NULL, 0);
+
+ // Create new entry
+ uint32_t new_entry_offset = entry_offset + meta.size;
+ entry_header.size = (sizeof(direntry_header_t) + entry_header.name_length);
+ entry_header.size += (4 - (entry_header.size % 4));
+
+ uint32_t length_till_next_block = 1024 - (new_entry_offset % 1024);
+ if (0 == length_till_next_block)
+ length_till_next_block = 1024;
+ assert(entry_header.size < length_till_next_block);
+ entry_header.size = length_till_next_block;
+
+ uint8_t buffer[entry_header.size];
+ memset(buffer, 0, entry_header.size);
+ memcpy(buffer, &entry_header, sizeof(entry_header));
+ memcpy(buffer + sizeof(entry_header), name, entry_header.name_length);
+ write_inode(directory_inode, (unsigned char *)buffer, entry_header.size,
+ new_entry_offset, NULL, 0);
+}
+
+int ext2_find_parent(char *path, uint32_t *parent_inode, char **filename) {
+ char *e = path;
+ for (; *e; e++)
+ ;
+ for (; *e != '/'; e--)
+ ;
+ *e = '\0';
+ *filename = e + 1;
+ if (*path == '\0') {
+ *parent_inode = EXT2_ROOT_INODE;
+ return 1;
+ } else {
+ int r = ext2_find_inode(path);
+ if (0 == r)
+ return 0;
+ *parent_inode = r;
+ return 1;
+ }
+ return 0;
+}
+
+int ext2_create_directory(const char *path, int mode) {
+ (void)mode;
+ // Check if the directory already exists
+ uint32_t inode_num = ext2_find_inode(path);
+ if (0 != inode_num) {
+ klog("ext2_create_directory: Directory already exists", LOG_WARN);
+ return inode_num;
+ }
+
+ uint32_t parent_inode;
+ // Get the parent directory
+ char path_buffer[strlen(path) + 1];
+ char *filename;
+ strcpy(path_buffer, path);
+ if (!ext2_find_parent(path_buffer, &parent_inode, &filename)) {
+ klog("ext2_create_file: Parent does not exist", LOG_WARN);
+ return -1;
+ }
+
+ int new_file_inode = get_free_inode(1);
+ if (-1 == new_file_inode) {
+ klog("ext2_create_file: Unable to find free inode", LOG_WARN);
+ return -1;
+ }
+ assert(0 != new_file_inode);
+
+ direntry_header_t entry_header;
+ entry_header.inode = new_file_inode;
+ entry_header.name_length = strlen(filename);
+ entry_header.type_indicator = TYPE_INDICATOR_DIRECTORY;
+ entry_header.size = sizeof(entry_header) + entry_header.name_length;
+
+ ext2_create_entry(parent_inode, entry_header, filename);
+ // Create the inode header
+ uint8_t inode_buffer[inode_size];
+ inode_t *new_inode = (inode_t *)inode_buffer;
+ memset(inode_buffer, 0, inode_size);
+ new_inode->types_permissions = DIRECTORY;
+ new_inode->num_hard_links = 2; // 2 since the directory references
+ // itself with the "." entry
+ ext2_write_inode(new_file_inode, new_inode);
+
+ // Populate the new directory with "." and ".."
+ {
+ // "."
+ direntry_header_t child_entry_header;
+ child_entry_header.inode = new_file_inode;
+ child_entry_header.name_length = 1;
+ child_entry_header.type_indicator = TYPE_INDICATOR_DIRECTORY;
+ child_entry_header.size = sizeof(entry_header) + entry_header.name_length;
+ ext2_create_entry(new_file_inode, child_entry_header, ".");
+ // ".."
+ child_entry_header.inode = parent_inode;
+ child_entry_header.name_length = 2;
+ child_entry_header.type_indicator = TYPE_INDICATOR_DIRECTORY;
+ child_entry_header.size = sizeof(entry_header) + entry_header.name_length;
+ ext2_create_entry(new_file_inode, child_entry_header, "..");
+ }
+ return new_file_inode;
+}
+
+int ext2_create_file(const char *path, int mode) {
+ // Check if the file already exists
+ uint32_t inode_num = ext2_find_inode(path);
+ if (0 != inode_num) {
+ klog("ext2_create_file: File already exists", LOG_WARN);
+ return inode_num;
+ }
+
+ uint32_t parent_inode;
+ // Get the parent directory
+ char path_buffer[strlen(path) + 1];
+ char *filename;
+ strcpy(path_buffer, path);
+ if (!ext2_find_parent(path_buffer, &parent_inode, &filename)) {
+ klog("ext2_create_file: Parent does not exist", LOG_WARN);
+ return -1;
+ }
+
+ int new_file_inode = get_free_inode(1);
+ if (-1 == new_file_inode) {
+ klog("ext2_create_file: Unable to find free inode", LOG_WARN);
+ return -1;
+ }
+ assert(0 != new_file_inode);
+
+ direntry_header_t entry_header;
+ entry_header.inode = new_file_inode;
+ entry_header.name_length = strlen(filename);
+ entry_header.type_indicator = TYPE_INDICATOR_REGULAR;
+ entry_header.size = sizeof(entry_header) + entry_header.name_length;
+
+ ext2_create_entry(parent_inode, entry_header, filename);
+ // Create the inode header
+ uint8_t inode_buffer[inode_size];
+ inode_t *new_inode = (inode_t *)inode_buffer;
+ memset(inode_buffer, 0, inode_size);
+ new_inode->types_permissions = 0x8000;
+ new_inode->num_hard_links = 1;
+ ext2_write_inode(new_file_inode, new_inode);
+ return new_file_inode;
+}
+
+vfs_inode_t *ext2_mount(void) {
+ parse_superblock();
+ return vfs_create_inode(0 /*inode_num*/, 0 /*type*/, 0 /*has_data*/,
+ 0 /*can_write*/, 0 /*is_open*/,
+ NULL /*internal_object*/, 0 /*file_size*/, ext2_open,
+ ext2_create_file, ext2_read, ext2_write, ext2_close,
+ ext2_create_directory, NULL /*get_vm_object*/,
+ ext2_truncate /*truncate*/);
+}
+
+void parse_superblock(void) {
+ superblock = ksbrk(2 * SECTOR_SIZE);
+ read_lba(EXT2_SUPERBLOCK_SECTOR, (void *)superblock, 2 * SECTOR_SIZE, 0);
+ block_byte_size = 1024 << superblock->block_size;
+
+ if (0xEF53 != superblock->ext2_signature) {
+ klog("Incorrect ext2 signature in superblock.", LOG_ERROR);
+ for (;;)
+ ; // TODO: Fail properly
+ }
+
+ if (1 <= superblock->major_version)
+ inode_size = ((ext_superblock_t *)superblock)->inode_size;
+
+ inodes_per_block = block_byte_size / inode_size;
+}
diff --git a/kernel/fs/ext2.h b/kernel/fs/ext2.h
new file mode 100644
index 0000000..3a2f800
--- /dev/null
+++ b/kernel/fs/ext2.h
@@ -0,0 +1,140 @@
+#ifndef EXT2_H
+#define EXT2_H
+#include <drivers/ata.h>
+#include <fs/vfs.h>
+#include <kmalloc.h>
+#include <stdint.h>
+
+typedef struct Superblock {
+ uint32_t num_inodes;
+ uint32_t num_blocks;
+ uint32_t num_blocks_reserved;
+ uint32_t num_blocks_unallocated;
+ uint32_t num_inodes_unallocated;
+ uint32_t superblock_block_num;
+ uint32_t block_size;
+ uint32_t fragment_size;
+ uint32_t num_blocks_in_group;
+ uint32_t num_fragments_in_group;
+ uint32_t num_inodes_in_group;
+ uint32_t last_mount;
+ uint32_t last_write;
+ uint16_t num_mounts_since_fsck;
+ uint16_t num_mounts_allowed;
+ uint16_t ext2_signature; // 0xEF53
+ uint16_t fs_state;
+ uint16_t when_error;
+ uint16_t minor_version;
+ uint32_t last_fsck;
+ uint32_t interval_fsck;
+ uint32_t os_id;
+ uint32_t major_version;
+ uint16_t userid_reserved_blocks;
+ uint16_t groupid_reserved_blocks;
+} __attribute__((packed)) superblock_t;
+
+typedef struct ExtendedSuperblock {
+ uint32_t num_inodes;
+ uint32_t num_blocks;
+ uint32_t num_blocks_reserved;
+ uint32_t num_blocks_unallocated;
+ uint32_t num_inodes_unallocated;
+ uint32_t superblock_block_num;
+ uint32_t block_size;
+ uint32_t fragment_size;
+ uint32_t num_blocks_group;
+ uint32_t num_fragments_group;
+ uint32_t num_inodes_group;
+ uint32_t last_mount;
+ uint32_t last_write;
+ uint16_t num_mounts_since_fsck;
+ uint16_t num_mounts_allowed;
+ uint16_t ext2_signature; // 0xEF53
+ uint16_t fs_state;
+ uint16_t when_error;
+ uint16_t minor_version;
+ uint32_t last_fsck;
+ uint32_t interval_fsck;
+ uint32_t os_id;
+ uint32_t major_version;
+ uint16_t userid_reserved_blocks;
+ uint16_t groupid_reserved_blocks;
+ uint32_t pad;
+ uint16_t inode_size;
+} __attribute__((packed)) ext_superblock_t;
+
+typedef struct BlockGroupDescriptorTable {
+ uint32_t block_usage_bitmap;
+ uint32_t inode_usage_bitmap;
+ uint32_t starting_inode_table;
+ uint16_t num_unallocated_blocks_in_group;
+ uint16_t num_unallocated_inodes_in_group;
+ uint16_t num_directories_group;
+} __attribute__((packed)) bgdt_t;
+
+typedef struct INode {
+ uint16_t types_permissions;
+ uint16_t user_id;
+ uint32_t low_32size;
+ uint32_t last_access_time;
+ uint32_t creation_time;
+ uint32_t last_modification_time;
+ uint32_t deletion_time;
+ uint16_t group_id;
+ uint16_t num_hard_links;
+ uint32_t num_disk_sectors;
+ uint32_t flags;
+ uint32_t os_specific;
+ uint32_t block_pointers[12];
+ uint32_t single_indirect_block_pointer;
+ uint32_t double_indirect_block_pointer;
+ uint32_t triple_indirect_block_pointer;
+ uint32_t gen_number;
+ uint32_t _extended_attribute_block;
+ uint32_t _upper_32size;
+ uint32_t address_fragment;
+ uint32_t os_specific2;
+} __attribute__((packed)) inode_t;
+
+// 0 Unknown type
+// 1 Regular file
+// 2 Directory
+// 3 Character device
+// 4 Block device
+// 5 FIFO
+// 6 Socket
+// 7 Symbolic link (soft link)
+#define TYPE_INDICATOR_UNKOWN 0
+#define TYPE_INDICATOR_REGULAR 1
+#define TYPE_INDICATOR_DIRECTORY 2
+#define TYPE_INDICATOR_CHARACTER_DEVICE 3
+#define TYPE_INDICATOR_BLOCK_DEVICE 4
+#define TYPE_INDICATOR_FIFO 5
+#define TYPE_INDICATOR_SOCKET 6
+#define TYPE_INDICATOR_SOFT_LINK 7
+
+#define FIFO 0x1000
+#define CHARACTER_DEVICE 0x2000
+#define DIRECTORY 0x4000
+#define BLOCK_DEVICE 0x6000
+#define REGULAR_FILE 0x8000
+#define SYMBOLIC_LINK 0xA000
+#define UNIX_SOCKET 0xC000
+
+typedef struct DirectoryEntryHeader {
+ uint32_t inode;
+ uint16_t size;
+ uint8_t name_length;
+ uint8_t type_indicator;
+} __attribute__((packed)) direntry_header_t;
+
+int ext2_create_file(const char *path, int mode);
+vfs_inode_t *ext2_mount(void);
+void parse_superblock(void);
+size_t ext2_read_file_offset(const char *file, unsigned char *data,
+ uint64_t size, uint64_t offset,
+ uint64_t *file_size);
+size_t ext2_read_file(const char *file, unsigned char *data, size_t size,
+ uint64_t *file_size);
+int ext2_create_directory(const char *path, int mode);
+#endif
diff --git a/kernel/fs/fifo.c b/kernel/fs/fifo.c
new file mode 100644
index 0000000..d515ed7
--- /dev/null
+++ b/kernel/fs/fifo.c
@@ -0,0 +1,97 @@
+#include "fifo.h"
+#include <errno.h>
+
+#define STARTING_SIZE 4096
+
+void fifo_close(vfs_fd_t *fd) {
+ // TODO: Implement
+ (void)fd;
+ return;
+}
+
+int fifo_object_write(uint8_t *buffer, uint64_t offset, uint64_t len,
+ FIFO_FILE *file) {
+ (void)offset;
+ file->has_data = 1;
+ if (file->write_len + len >= file->buffer_len) {
+ file->can_write = 0;
+ return -EAGAIN;
+ }
+ memcpy(file->buffer + file->write_len, buffer, len);
+ file->write_len += len;
+ return len;
+}
+
+int fifo_object_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ FIFO_FILE *file) {
+ (void)offset;
+ if (file->write_len == 0) {
+ file->has_data = 0;
+ return -EAGAIN;
+ }
+
+ if (len == 0)
+ return 0;
+
+ file->can_write = 1;
+ if (len > file->write_len)
+ len = file->write_len;
+
+ memcpy(buffer, file->buffer, len);
+ // Shift bufffer to the left
+ memcpy(file->buffer, file->buffer + len, file->buffer_len - len);
+
+ file->write_len -= len;
+ if (file->write_len == 0) {
+ file->has_data = 0;
+ }
+ return len;
+}
+
+FIFO_FILE *create_fifo_object(void) {
+ FIFO_FILE *n = kmalloc(sizeof(FIFO_FILE));
+ n->buffer = kmalloc(STARTING_SIZE);
+ n->buffer_len = STARTING_SIZE;
+ n->write_len = 0;
+ return n;
+}
+
+int create_fifo(void) {
+
+ int fd_n = 0;
+ for (; get_current_task()->file_descriptors[fd_n]; fd_n++)
+ ;
+
+ vfs_fd_t *fd = kmalloc(sizeof(vfs_fd_t));
+ fd->flags = O_RDWR | O_NONBLOCK;
+ get_current_task()->file_descriptors[fd_n] = fd;
+ fd->inode = kmalloc(sizeof(vfs_inode_t));
+
+ fd->inode->internal_object = (void *)create_fifo_object();
+ fd->inode->open = NULL;
+ fd->inode->read = fifo_read;
+ fd->inode->write = fifo_write;
+ fd->inode->close = fifo_close;
+ fd->inode->get_vm_object = NULL;
+ fd->inode->is_open = 1;
+
+ return fd_n;
+}
+
+int fifo_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ (void)offset;
+ FIFO_FILE *file = (FIFO_FILE *)fd->inode->internal_object;
+ int rc = fifo_object_write(buffer, offset, len, file);
+ fd->inode->has_data = file->has_data;
+ fd->inode->can_write = file->can_write;
+ return rc;
+}
+
+int fifo_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ FIFO_FILE *file = (FIFO_FILE *)fd->inode->internal_object;
+ file->is_blocking = !(fd->flags & O_NONBLOCK);
+ int rc = fifo_object_read(buffer, offset, len, file);
+ fd->inode->has_data = file->has_data;
+ fd->inode->can_write = file->can_write;
+ return rc;
+}
diff --git a/kernel/fs/fifo.h b/kernel/fs/fifo.h
new file mode 100644
index 0000000..1ba7168
--- /dev/null
+++ b/kernel/fs/fifo.h
@@ -0,0 +1,27 @@
+typedef struct S_FIFO_FILE FIFO_FILE;
+#ifndef FIFO_H
+#define FIFO_H
+#include "vfs.h"
+#include <stddef.h>
+#include <stdint.h>
+
+struct S_FIFO_FILE {
+ char *buffer;
+ uint64_t buffer_len;
+ uint64_t write_len;
+ uint8_t is_blocking;
+ uint8_t has_data;
+ uint8_t can_write;
+};
+
+int create_fifo(void);
+FIFO_FILE *create_fifo_object(void);
+int fifo_object_write(uint8_t *buffer, uint64_t offset, uint64_t len,
+ FIFO_FILE *file);
+int fifo_object_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ FIFO_FILE *file);
+int fifo_write(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd);
+int fifo_read(uint8_t *buffer, uint64_t offset, uint64_t len,
+ vfs_fd_t *fd);
+#endif
diff --git a/kernel/fs/shm.c b/kernel/fs/shm.c
new file mode 100644
index 0000000..4d5f2ab
--- /dev/null
+++ b/kernel/fs/shm.c
@@ -0,0 +1,94 @@
+#include <assert.h>
+#include <errno.h>
+#include <fs/shm.h>
+#include <fs/vfs.h>
+#include <hashmap/hashmap.h>
+#include <mmu.h>
+#include <sched/scheduler.h>
+#include <stddef.h>
+
+HashMap *shared_memory_objects;
+
+void shm_init(void) { shared_memory_objects = hashmap_create(10); }
+
+int shm_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ vfs_vm_object_t *p = fd->inode->internal_object;
+
+ if (offset > p->size)
+ return -EFBIG;
+
+ if (offset + len > p->size)
+ len = p->size - offset;
+
+ memcpy((void *)((uintptr_t)((uintptr_t)p->virtual_object + offset)), buffer,
+ len);
+ return len;
+}
+
+int shm_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ vfs_vm_object_t *p = fd->inode->internal_object;
+
+ if (offset > p->size)
+ return -EFBIG;
+
+ if (offset + len > p->size)
+ len = p->size - offset;
+
+ memcpy((void *)buffer,
+ (void *)((uintptr_t)((uintptr_t)p->virtual_object + offset)), len);
+ return len;
+}
+
+vfs_vm_object_t *shm_get_vm_object(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd) {
+ (void)length;
+ (void)offset;
+ vfs_vm_object_t *p = fd->inode->internal_object;
+ return p;
+}
+
+int shm_ftruncate(vfs_fd_t *fd, size_t length) {
+ vfs_vm_object_t *p = fd->inode->internal_object;
+ p->size = length;
+ p->virtual_object = ksbrk(length);
+ int n = (uintptr_t)align_page((void *)(uint32_t)length) / 0x1000;
+ p->object = kmalloc(sizeof(void *) * n);
+ for (int i = 0; i < n; i++)
+ p->object[i] =
+ (void *)(get_page(p->virtual_object + (i * 0x1000), NULL, 0, 0)->frame *
+ 0x1000);
+ return 0;
+}
+
+int shm_open(const char *name, int oflag, mode_t mode) {
+ // Try to find or create a new shared memory object.
+ vfs_vm_object_t *internal_object =
+ hashmap_get_entry(shared_memory_objects, name);
+ if (!internal_object) {
+ // if (!(oflag & O_CREAT))
+ // return -EMFILE;
+ internal_object = kmalloc(sizeof(vfs_vm_object_t));
+ internal_object->object = NULL;
+ internal_object->size = 0;
+ hashmap_add_entry(shared_memory_objects, name, internal_object, NULL, 0);
+ }
+
+ vfs_inode_t *inode = vfs_create_inode(
+ 0 /*inode_num*/, 0 /*type*/, 1 /*has_data*/, 1 /*can_write*/,
+ 1 /*is_open*/, internal_object, 0 /*file_size*/, NULL /*open*/,
+ NULL /*create_file*/, shm_read, shm_write, NULL /*close*/,
+ NULL /*create_directory*/, shm_get_vm_object, shm_ftruncate);
+
+ vfs_fd_t *fd_ptr;
+ int fd = vfs_create_fd(oflag, mode, inode, &fd_ptr);
+ if (-1 == fd) {
+ kfree(inode);
+ return -EMFILE;
+ }
+ return fd;
+}
+
+int shm_unlink(const char *name) {
+ (void)name;
+ return 0;
+}
diff --git a/kernel/fs/shm.h b/kernel/fs/shm.h
new file mode 100644
index 0000000..fbbdb5c
--- /dev/null
+++ b/kernel/fs/shm.h
@@ -0,0 +1,13 @@
+#ifndef SHM_H
+#define SHM_H
+#include <stddef.h>
+#include <stdint.h>
+
+typedef int mode_t;
+
+void shm_init(void);
+int shm_open(const char *name, int oflag, mode_t mode);
+int shm_unlink(const char *name);
+int ftruncate(int fildes, uint64_t length);
+
+#endif
diff --git a/kernel/fs/tmpfs.c b/kernel/fs/tmpfs.c
new file mode 100644
index 0000000..a9a3c1f
--- /dev/null
+++ b/kernel/fs/tmpfs.c
@@ -0,0 +1,96 @@
+#include <assert.h>
+#include <errno.h>
+#include <fs/fifo.h>
+#include <fs/tmpfs.h>
+#include <halts.h>
+#include <sched/scheduler.h>
+#include <stdint.h>
+
+void tmp_close(vfs_fd_t *fd) {
+ fd->inode->is_open = 0;
+ ((tmp_inode *)fd->inode->internal_object)->read_inode->is_open = 0;
+}
+
+int tmp_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ tmp_inode *calling_file = fd->inode->internal_object;
+ tmp_inode *child_file = calling_file->read_inode->internal_object;
+ if (child_file->is_closed)
+ return -EPIPE;
+
+ int rc = fifo_object_write(buffer, offset, len, child_file->fifo);
+ calling_file->read_inode->has_data = child_file->fifo->has_data;
+ fd->inode->can_write = child_file->fifo->can_write;
+ return rc;
+}
+
+int tmp_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ tmp_inode *calling_file = fd->inode->internal_object;
+ tmp_inode *child_file = calling_file->read_inode->internal_object;
+ if (calling_file->is_closed)
+ return -EPIPE;
+
+ int rc = fifo_object_read(buffer, offset, len, calling_file->fifo);
+ fd->inode->has_data = calling_file->fifo->has_data;
+ calling_file->read_inode->can_write = child_file->fifo->can_write;
+ return rc;
+}
+
+void dual_pipe(int fd[2]) {
+ for (int i = 0; i < 2; i++) {
+ tmp_inode *pipe = kmalloc(sizeof(tmp_inode));
+ pipe->fifo = create_fifo_object();
+
+ int has_data = 0;
+ int can_write = 1;
+ int is_open = 1;
+ void *internal_object = pipe;
+ vfs_inode_t *inode = vfs_create_inode(
+ 0 /*inode_num*/, 0 /*type*/, has_data, can_write, is_open,
+ internal_object, 0 /*file_size*/, NULL /*open*/, NULL /*create_file*/,
+ tmp_read, tmp_write, tmp_close, NULL /*create_directory*/,
+ NULL /*get_vm_object*/, NULL /*truncate*/);
+ assert(inode);
+
+ vfs_fd_t *fd_ptr;
+ fd[i] = vfs_create_fd(O_RDWR | O_NONBLOCK, 0, inode, &fd_ptr);
+ assert(-1 != fd[i]);
+ }
+ vfs_inode_t *f_inode = get_current_task()->file_descriptors[fd[0]]->inode;
+ vfs_inode_t *s_inode = get_current_task()->file_descriptors[fd[1]]->inode;
+ tmp_inode *f_pipe = f_inode->internal_object;
+ tmp_inode *s_pipe = s_inode->internal_object;
+ f_pipe->read_inode = s_inode;
+ s_pipe->read_inode = f_inode;
+ f_pipe->is_closed = 0;
+ s_pipe->is_closed = 0;
+}
+
+void pipe(int fd[2]) {
+ for (int i = 0; i < 2; i++) {
+ tmp_inode *pipe = kmalloc(sizeof(tmp_inode));
+ pipe->fifo = create_fifo_object();
+
+ int has_data = 0;
+ int can_write = 1;
+ int is_open = 1;
+ void *internal_object = pipe;
+ vfs_inode_t *inode = vfs_create_inode(
+ 0 /*inode_num*/, 0 /*type*/, has_data, can_write, is_open,
+ internal_object, 0 /*file_size*/, NULL /*open*/, NULL /*create_file*/,
+ tmp_read, tmp_write, tmp_close, NULL /*create_directory*/,
+ NULL /*get_vm_object*/, NULL/*truncate*/);
+ assert(inode);
+
+ vfs_fd_t *fd_ptr;
+ fd[i] = vfs_create_fd(O_RDWR, 0, inode, &fd_ptr);
+ assert(-1 != fd[i]);
+ }
+ vfs_inode_t *f_inode = get_current_task()->file_descriptors[fd[0]]->inode;
+ vfs_inode_t *s_inode = get_current_task()->file_descriptors[fd[1]]->inode;
+ tmp_inode *f_pipe = f_inode->internal_object;
+ tmp_inode *s_pipe = s_inode->internal_object;
+ f_pipe->read_inode = s_inode;
+ s_pipe->read_inode = f_inode;
+ f_pipe->is_closed = 0;
+ s_pipe->is_closed = 0;
+}
diff --git a/kernel/fs/tmpfs.h b/kernel/fs/tmpfs.h
new file mode 100644
index 0000000..4052bd5
--- /dev/null
+++ b/kernel/fs/tmpfs.h
@@ -0,0 +1,16 @@
+#ifndef TMP_H
+#define TMP_H
+#include <fs/fifo.h>
+#include <fs/vfs.h>
+
+#define TMP_BUFFER_SIZE (1024 * 10)
+
+typedef struct {
+ FIFO_FILE *fifo;
+ uint8_t is_closed;
+ vfs_inode_t *read_inode;
+} tmp_inode;
+
+void pipe(int fd[2]);
+void dual_pipe(int fd[2]);
+#endif
diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c
new file mode 100644
index 0000000..0c616a2
--- /dev/null
+++ b/kernel/fs/vfs.c
@@ -0,0 +1,318 @@
+#include <assert.h>
+#include <errno.h>
+#include <fs/vfs.h>
+#include <mmu.h>
+#include <poll.h>
+
+vfs_inode_t *root_dir;
+vfs_mounts_t mounts[10];
+int num_mounts = 0;
+
+vfs_fd_t *get_vfs_fd(int fd) {
+ if (fd >= 100) {
+ klog("get_vfs_fd(): Tried to get out of range fd", LOG_WARN);
+ dump_backtrace(12);
+ return NULL;
+ }
+ if (fd < 0) {
+ klog("get_vfs_fd(): Tried to get out of range fd", LOG_WARN);
+ dump_backtrace(12);
+ return NULL;
+ }
+ return get_current_task()->file_descriptors[fd];
+}
+
+vfs_inode_t *vfs_create_inode(
+ int inode_num, int type, uint8_t has_data, uint8_t can_write,
+ uint8_t is_open, void *internal_object, uint64_t file_size,
+ vfs_inode_t *(*open)(const char *path),
+ int (*create_file)(const char *path, int mode),
+ int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ void (*close)(vfs_fd_t *fd),
+ int (*create_directory)(const char *path, int mode),
+ vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd),
+ int (*truncate)(vfs_fd_t *fd, size_t length)) {
+ vfs_inode_t *r = kmalloc(sizeof(inode_t));
+ r->inode_num = inode_num;
+ r->type = type;
+ r->has_data = has_data;
+ r->can_write = can_write;
+ r->is_open = is_open;
+ r->internal_object = internal_object;
+ r->file_size = file_size;
+ r->open = open;
+ r->create_file = create_file;
+ r->read = read;
+ r->write = write;
+ r->close = close;
+ r->create_directory = create_directory;
+ r->get_vm_object = get_vm_object;
+ r->truncate = truncate;
+ return r;
+}
+
+int vfs_create_fd(int flags, int mode, vfs_inode_t *inode, vfs_fd_t **fd) {
+ process_t *p = (process_t *)get_current_task();
+ int i;
+ for (i = 0; i < 100; i++)
+ if (!p->file_descriptors[i])
+ break;
+ if (p->file_descriptors[i])
+ return -1;
+ vfs_fd_t *r = kmalloc(sizeof(vfs_fd_t));
+ r->flags = flags;
+ r->mode = mode;
+ r->inode = inode;
+ r->reference_count = 1;
+ p->file_descriptors[i] = r;
+ if (fd)
+ *fd = r;
+ return i;
+}
+
+int vfs_create_file(const char *file) {
+ vfs_mounts_t *file_mount = 0;
+ int length = 0;
+ for (int i = 0; i < num_mounts; i++) {
+ int path_len = strlen(mounts[i].path);
+ if (path_len <= length)
+ continue;
+
+ if (isequal_n(mounts[i].path, file, path_len)) {
+ length = path_len;
+ file_mount = &mounts[i];
+ }
+ }
+ if (1 != length)
+ file += length;
+
+ if (!file_mount) {
+ kprintf("vfs_internal_open could not find mounted path for file : %s\n",
+ file);
+ return 0;
+ }
+ // ext2_create_file("/etc/oscreated", 0);
+ assert(file_mount->local_root->create_file);
+ kprintf("Creating a file\n");
+ return file_mount->local_root->create_file(file, 0);
+}
+
+vfs_inode_t *vfs_internal_open(const char *file) {
+ vfs_mounts_t *file_mount = 0;
+ int length = 0;
+ for (int i = 0; i < num_mounts; i++) {
+ int path_len = strlen(mounts[i].path);
+ if (path_len <= length)
+ continue;
+
+ if (isequal_n(mounts[i].path, file, path_len)) {
+ length = path_len;
+ file_mount = &mounts[i];
+ }
+ }
+ if (1 != length)
+ file += length;
+
+ if (!file_mount) {
+ kprintf("vfs_internal_open could not find mounted path for file : %s\n",
+ file);
+ return NULL;
+ }
+
+ vfs_inode_t *ret = file_mount->local_root->open(file);
+ return ret;
+}
+
+char *vfs_clean_path(const char *path, char *resolved_path) {
+ // char *const clean = kmalloc(strlen(path) + 1);
+ char *clean = resolved_path;
+ int prev_slash = 0;
+ char *ptr = clean;
+ for (; *path; path++) {
+ if (prev_slash && '/' == *path) {
+ continue;
+ }
+ prev_slash = ('/' == *path);
+ *ptr = *path;
+ ptr++;
+ }
+ *ptr = '\0';
+ return clean;
+}
+
+char *vfs_resolve_path(const char *file, char *resolved_path) {
+ if ('/' == *file) {
+ return vfs_clean_path(file, resolved_path);
+ }
+ const char *cwd = get_current_task()->current_working_directory;
+ size_t l = strlen(cwd);
+ assert(l > 0);
+ assert('/' == cwd[l - 1]);
+ // char *r = kmalloc(l + strlen(file) + 1);
+ char r[256];
+ strcpy(r, cwd);
+ strcat(r, file);
+ char *final = vfs_clean_path(r, resolved_path);
+ // kfree(r);
+ return final;
+}
+
+int vfs_mkdir(const char *path, int mode) {
+ vfs_mounts_t *file_mount = 0;
+ int length = 0;
+ for (int i = 0; i < num_mounts; i++) {
+ int path_len = strlen(mounts[i].path);
+ if (path_len <= length)
+ continue;
+
+ if (isequal_n(mounts[i].path, path, path_len)) {
+ length = path_len;
+ file_mount = &mounts[i];
+ }
+ }
+ if (1 != length)
+ path += length;
+
+ if (!file_mount) {
+ kprintf("vfs_internal_open could not find mounted path for file : %s\n",
+ path);
+ return 0;
+ }
+ assert(file_mount->local_root->create_directory);
+ // TODO: Error checking, don't just assume it is fine
+ file_mount->local_root->create_directory(path, mode);
+ return 0;
+}
+
+int vfs_open(const char *file, int flags, int mode) {
+ char resolved_path[256] = {0};
+ vfs_resolve_path(file, resolved_path);
+ vfs_inode_t *inode = vfs_internal_open(resolved_path);
+ if (0 == inode) {
+ if (mode & O_CREAT) {
+ if (vfs_create_file(resolved_path)) {
+ klog("VFS: File created", LOG_NOTE);
+ return vfs_open(file, flags, mode);
+ }
+ klog("VFS: Could not create file", LOG_WARN);
+ }
+ return -ENOENT;
+ }
+ if (inode->type == FS_TYPE_UNIX_SOCKET) {
+ return uds_open(resolved_path);
+ }
+
+ return vfs_create_fd(flags, mode, inode, NULL);
+}
+
+int vfs_close(int fd) {
+ vfs_fd_t *fd_ptr = get_vfs_fd(fd);
+ if (NULL == fd_ptr) {
+ return -1;
+ }
+ assert(0 < fd_ptr->reference_count);
+ // Remove process reference
+ fd_ptr->reference_count--;
+ get_current_task()->file_descriptors[fd] = 0;
+ // If no references left then free the contents
+ if (0 == fd_ptr->reference_count) {
+ if (fd_ptr->inode->close)
+ fd_ptr->inode->close(fd_ptr);
+
+ kfree(fd_ptr);
+ }
+ return 0;
+}
+
+int raw_vfs_pread(vfs_fd_t *vfs_fd, void *buf, uint64_t count,
+ uint64_t offset) {
+ if (!(vfs_fd->flags & O_READ))
+ return -EBADF;
+ return vfs_fd->inode->read(buf, offset, count, vfs_fd);
+}
+
+int vfs_pread(int fd, void *buf, uint64_t count, uint64_t offset) {
+ if (fd >= 100) {
+ kprintf("EBADF : %x\n", fd);
+ return -EBADF;
+ }
+ if (fd < 0) {
+ dump_backtrace(12);
+ kprintf("EBADF : %x\n", fd);
+ return -EBADF;
+ }
+ vfs_fd_t *vfs_fd = get_current_task()->file_descriptors[fd];
+ if (!vfs_fd)
+ return -EBADF;
+ int rc = raw_vfs_pread(vfs_fd, buf, count, offset);
+ if (-EAGAIN == rc && count > 0) {
+ if (!(vfs_fd->flags & O_NONBLOCK)) {
+ struct pollfd fds;
+ fds.fd = fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ poll(&fds, 1, 0);
+ return vfs_pread(fd, buf, count, offset);
+ }
+ }
+ return rc;
+}
+
+int raw_vfs_pwrite(vfs_fd_t *vfs_fd, void *buf, uint64_t count,
+ uint64_t offset) {
+ assert(vfs_fd);
+ assert(vfs_fd->inode);
+ assert(vfs_fd->inode->write);
+ return vfs_fd->inode->write(buf, offset, count, vfs_fd);
+}
+
+int vfs_pwrite(int fd, void *buf, uint64_t count, uint64_t offset) {
+ vfs_fd_t *vfs_fd = get_vfs_fd(fd);
+ if (!vfs_fd)
+ return -EBADF;
+ if (!(vfs_fd->flags & O_WRITE)) {
+ return -EBADF;
+ }
+ return raw_vfs_pwrite(vfs_fd, buf, count, offset);
+}
+
+vfs_vm_object_t *vfs_get_vm_object(int fd, uint64_t length, uint64_t offset) {
+ vfs_fd_t *vfs_fd = get_vfs_fd(fd);
+ if (!vfs_fd)
+ return NULL;
+ vfs_vm_object_t *r = vfs_fd->inode->get_vm_object(length, offset, vfs_fd);
+ return r;
+}
+
+int vfs_dup2(int org_fd, int new_fd) {
+ get_current_task()->file_descriptors[new_fd] =
+ get_current_task()->file_descriptors[org_fd];
+ get_current_task()->file_descriptors[new_fd]->reference_count++;
+ return 1;
+}
+
+int vfs_ftruncate(int fd, size_t length) {
+ vfs_fd_t *fd_ptr = get_vfs_fd(fd);
+ if (!fd_ptr)
+ return -EBADF;
+ if (!(fd_ptr->flags & O_READ))
+ return -EINVAL;
+ vfs_inode_t *inode = fd_ptr->inode;
+ if (!inode)
+ return -EINVAL;
+ if (!inode->truncate)
+ return -EINVAL;
+
+ return inode->truncate(fd_ptr, length);
+}
+
+void vfs_mount(char *path, vfs_inode_t *local_root) {
+ int len = strlen(path);
+ mounts[num_mounts].path = kmalloc_eternal(len + 1);
+ memcpy(mounts[num_mounts].path, path, len);
+ mounts[num_mounts].path[len] = '\0';
+ mounts[num_mounts].local_root = local_root;
+ num_mounts++;
+}
diff --git a/kernel/fs/vfs.h b/kernel/fs/vfs.h
new file mode 100644
index 0000000..f8a4b19
--- /dev/null
+++ b/kernel/fs/vfs.h
@@ -0,0 +1,100 @@
+typedef struct vfs_fd vfs_fd_t;
+typedef struct vfs_inode vfs_inode_t;
+typedef struct vfs_vm_object vfs_vm_object_t;
+typedef struct vfs_mounts vfs_mounts_t;
+#ifndef VFS_H
+#define VFS_H
+#include <limits.h>
+#include <sched/scheduler.h>
+#include <socket.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// FIXME: Is there some standard value for this?
+#define O_NONBLOCK (1 << 0)
+#define O_READ (1 << 1)
+#define O_WRITE (1 << 2)
+#define O_CREAT (1 << 3)
+#define O_RDONLY O_READ
+#define O_WRONLY O_WRITE
+#define O_RDWR (O_WRITE | O_READ)
+
+#define FS_TYPE_FILE 0
+#define FS_TYPE_UNIX_SOCKET 1
+#define FS_TYPE_CHAR_DEVICE 2
+#define FS_TYPE_BLOCK_DEVICE 3
+#define FS_TYPE_DIRECTORY 4
+#define FS_TYPE_LINK 7
+
+struct vfs_vm_object {
+ void *virtual_object;
+ void **object;
+ uint64_t size;
+};
+
+struct vfs_mounts {
+ char *path;
+ vfs_inode_t *local_root;
+};
+
+struct dirent {
+ unsigned int d_ino; // File serial number.
+ char d_name[PATH_MAX]; // Filename string of entry.
+};
+
+struct vfs_fd {
+ size_t offset;
+ int flags;
+ int mode;
+ int reference_count; // Number of usages of this file descriptor,
+ // once it reaches zero then the contents can
+ // be freed.
+ vfs_inode_t *inode;
+};
+
+struct vfs_inode {
+ int inode_num;
+ int type;
+ uint8_t has_data;
+ uint8_t can_write;
+ uint8_t is_open;
+ void *internal_object;
+ uint64_t file_size;
+ vfs_inode_t *(*open)(const char *path);
+ int (*create_file)(const char *path, int mode);
+ int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd);
+ int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd);
+ void (*close)(vfs_fd_t *fd);
+ int (*create_directory)(const char *path, int mode);
+ vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd);
+ int (*truncate)(vfs_fd_t *fd, size_t length);
+};
+
+int vfs_close(int fd);
+vfs_fd_t *get_vfs_fd(int fd);
+int vfs_open(const char *file, int flags, int mode);
+void vfs_mount(char *path, vfs_inode_t *local_root);
+int vfs_pwrite(int fd, void *buf, uint64_t count, uint64_t offset);
+int raw_vfs_pwrite(vfs_fd_t *vfs_fd, void *buf, uint64_t count,
+ uint64_t offset);
+int vfs_pread(int fd, void *buf, uint64_t count, uint64_t offset);
+vfs_vm_object_t *vfs_get_vm_object(int fd, uint64_t length, uint64_t offset);
+int vfs_dup2(int org_fd, int new_fd);
+vfs_inode_t *vfs_internal_open(const char *file);
+int vfs_mkdir(const char *path, int mode);
+int vfs_create_fd(int flags, int mode, vfs_inode_t *inode, vfs_fd_t **fd);
+int vfs_ftruncate(int fd, size_t length);
+vfs_inode_t *vfs_create_inode(
+ int inode_num, int type, uint8_t has_data, uint8_t can_write,
+ uint8_t is_open, void *internal_object, uint64_t file_size,
+ vfs_inode_t *(*open)(const char *path),
+ int (*create_file)(const char *path, int mode),
+ int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd),
+ void (*close)(vfs_fd_t *fd),
+ int (*create_directory)(const char *path, int mode),
+ vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset,
+ vfs_fd_t *fd),
+ int (*truncate)(vfs_fd_t *fd, size_t length));
+#endif
diff --git a/kernel/halts.c b/kernel/halts.c
new file mode 100644
index 0000000..821299d
--- /dev/null
+++ b/kernel/halts.c
@@ -0,0 +1,102 @@
+#include <assert.h>
+#include <halts.h>
+#include <sched/scheduler.h>
+
+int create_disconnect_inode_halt(vfs_inode_t *inode) {
+ volatile process_t *p = get_current_task();
+ int i;
+ for (i = 0; i < 100; i++)
+ if (!p->disconnect_halt_inode[i])
+ break;
+
+ if (p->disconnect_halt_inode[i])
+ return -1;
+
+ p->disconnect_halt_inode[i] = inode;
+
+ return i;
+}
+
+int create_disconnect_fdhalt(vfs_fd_t *fd) {
+ assert(fd);
+ return create_disconnect_inode_halt(fd->inode);
+}
+
+int create_read_inode_halt(vfs_inode_t *inode) {
+ volatile process_t *p = get_current_task();
+ int i;
+ for (i = 0; i < 100; i++)
+ if (!p->read_halt_inode[i])
+ break;
+
+ if (p->read_halt_inode[i])
+ return -1;
+
+ p->read_halt_inode[i] = inode;
+
+ return i;
+}
+
+int create_read_fdhalt(vfs_fd_t *fd) {
+ assert(fd);
+ return create_read_inode_halt(fd->inode);
+}
+
+int create_write_inode_halt(vfs_inode_t *inode) {
+ volatile process_t *p = get_current_task();
+ int i;
+ for (i = 0; i < 100; i++)
+ if (!p->write_halt_inode[i])
+ break;
+
+ if (p->write_halt_inode[i])
+ return -1;
+
+ p->write_halt_inode[i] = inode;
+
+ return i;
+}
+
+int create_write_fdhalt(vfs_fd_t *fd) {
+ return create_write_inode_halt(fd->inode);
+}
+
+void unset_read_fdhalt(int i) { get_current_task()->read_halt_inode[i] = NULL; }
+
+void unset_write_fdhalt(int i) {
+ get_current_task()->write_halt_inode[i] = NULL;
+}
+
+void unset_disconnect_fdhalt(int i) {
+ get_current_task()->disconnect_halt_inode[i] = NULL;
+}
+
+int isset_fdhalt(vfs_inode_t *read_halts[], vfs_inode_t *write_halts[],
+ vfs_inode_t *disconnect_halts[]) {
+ int blocked = 0;
+ for (int i = 0; i < 100; i++) {
+ if (!read_halts[i])
+ continue;
+ if (read_halts[i]->has_data) {
+ return 0;
+ }
+ blocked = 1;
+ }
+ for (int i = 0; i < 100; i++) {
+ if (!write_halts[i])
+ continue;
+ if (write_halts[i]->can_write) {
+ return 0;
+ }
+ blocked = 1;
+ }
+ for (int i = 0; i < 100; i++) {
+ if (!disconnect_halts[i])
+ continue;
+ if (!disconnect_halts[i]->is_open) {
+ return 0;
+ }
+ blocked = 1;
+ }
+ return blocked;
+}
diff --git a/kernel/halts.h b/kernel/halts.h
new file mode 100644
index 0000000..bd71673
--- /dev/null
+++ b/kernel/halts.h
@@ -0,0 +1,21 @@
+#ifndef HALTS_H
+#define HALTS_H
+#include <fs/vfs.h>
+#include <stdint.h>
+
+typedef struct {
+ uint8_t *ptr;
+ uint8_t active;
+} halt_t;
+
+int create_read_fdhalt(vfs_fd_t *fd);
+int create_read_inode_halt(vfs_inode_t *inode);
+void unset_read_fdhalt(int i);
+int create_write_fdhalt(vfs_fd_t *fd);
+int create_write_inode_halt(vfs_inode_t *inode);
+void unset_write_fdhalt(int i);
+int create_disconnect_fdhalt(vfs_fd_t *fd);
+void unset_disconnect_fdhalt(int i);
+int isset_fdhalt(vfs_inode_t *read_halts[], vfs_inode_t *write_halts[],
+ vfs_inode_t *disconnect_halts[]);
+#endif
diff --git a/kernel/hashmap/hashmap.c b/kernel/hashmap/hashmap.c
new file mode 100644
index 0000000..2eeef98
--- /dev/null
+++ b/kernel/hashmap/hashmap.c
@@ -0,0 +1,212 @@
+//
+// Copyright (C) 2022 by Anton Kling <anton@kling.gg>
+//
+// SPDX-License-Identifier: BSD-2-Clause
+//
+/*
+ * HashMap
+ * -------
+ * This hashmap works by creating a array of linked lists and associating
+ * the "key" with a specific entry in the array. This is done through a
+ * hash. Once the linked list is found it goes through it until it finds
+ * a entry that has the "key" provided.
+ *
+ * Changing the hashing function
+ * -----------------------------
+ * The default hashing function in this library is a custom made
+ * one that can be found in hash.c. But it should be possible to use any
+ * other hashing algorithm should you want to as long as it uses these
+ * types:
+ * uint32_t hash_function(uint8_t*, size_t)
+ *
+ * The hashing algorithm can be changed via changing the "hash_function"
+ * pointer in the HashMap structure.
+ *
+ * Important to note that the hashing function shall not be changed
+ * after having added entries to the hashmap as the key to linked list
+ * pairing will change.
+ */
+#include "hashmap.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kmalloc.h>
+
+#define CONSTANT 0x031b5515
+
+uint32_t mix(uint32_t x) {
+ x ^= CONSTANT;
+ x ^= (x << 13);
+ x ^= (x >> 7);
+ x ^= (x << 17);
+ return x;
+}
+
+uint32_t hash(const uint8_t *data, size_t len) {
+ uint32_t hash = 0;
+ for (; len;) {
+ uint32_t value = 0;
+ uint8_t read_len = (sizeof(uint32_t) < len) ? sizeof(uint32_t) : len;
+ memcpy(&value, data, read_len);
+ hash ^= mix(value);
+ data += read_len;
+ len -= read_len;
+ }
+ return hash;
+}
+
+char *copy_c_string(const char *str) {
+ char *ret_string;
+ size_t len = strlen(str);
+ ret_string = kmalloc(len + 1);
+ if (!ret_string)
+ return NULL;
+ memcpy(ret_string, str, len);
+ ret_string[len] = '\0';
+ return ret_string;
+}
+
+uint32_t limit_hash(HashMap *m, uint32_t hash) { return hash % m->size; }
+
+void free_linkedlist_entry(LinkedList *entry) {
+ if (entry->key_allocated) {
+ // If the key is allocated by the hashmap library then it owns the
+ // key and can safley discard the const qualifier and override its
+ // contents
+ kfree((char *)entry->key);
+ }
+ kfree(entry);
+}
+
+LinkedList *get_linkedlist_entry(LinkedList *list, const char *key,
+ LinkedList **prev) {
+ if (prev)
+ *prev = NULL;
+ for (; list; list = list->next) {
+ const char *str1 = key;
+ const char *str2 = list->key;
+ for (; *str1 && *str2; str1++, str2++)
+ if (*str1 != *str2)
+ break;
+ if (*str1 == *str2)
+ return list;
+ if (prev)
+ prev = &list;
+ }
+ return NULL;
+}
+
+void *get_linkedlist_value(LinkedList *list, const char *key) {
+ LinkedList *entry = get_linkedlist_entry(list, key, NULL);
+ if (!entry)
+ return NULL;
+ return entry->value;
+}
+
+uint32_t find_index(HashMap *m, const char *key) {
+ return limit_hash(m, m->hash_function((uint8_t *)key, strlen(key)));
+}
+
+int hashmap_add_entry(HashMap *m, const char *key, void *value,
+ void (*upon_deletion)(const char *, void *),
+ int do_not_allocate_key) {
+ // Create the entry
+ LinkedList *entry = kmalloc(sizeof(LinkedList));
+ if (!entry)
+ return 0;
+
+ entry->key_allocated = !do_not_allocate_key;
+ if (do_not_allocate_key) {
+ entry->key = key;
+ } else {
+ if (!(entry->key = copy_c_string(key)))
+ return 0;
+ }
+ entry->value = value;
+ entry->next = NULL;
+ entry->upon_deletion = upon_deletion;
+
+ // Add the new entry to the list.
+ uint32_t index = find_index(m, key);
+ LinkedList **list_pointer = &m->entries[index];
+ for (; *list_pointer;)
+ list_pointer = &(*list_pointer)->next;
+
+ *list_pointer = entry;
+ m->num_entries++;
+ return 1;
+}
+
+void *hashmap_get_entry(HashMap *m, const char *key) {
+ uint32_t index = find_index(m, key);
+ if (!m->entries[index])
+ return NULL;
+ return get_linkedlist_value(m->entries[index], key);
+}
+
+int hashmap_delete_entry(HashMap *m, const char *key) {
+ LinkedList *list = m->entries[find_index(m, key)];
+ if (!list)
+ return 0;
+ LinkedList **prev = NULL;
+ LinkedList *entry = get_linkedlist_entry(list, key, prev);
+ if (!entry)
+ return 0;
+ if (!prev)
+ prev = &m->entries[find_index(m, key)];
+
+ if (entry->upon_deletion)
+ entry->upon_deletion(entry->key, entry->value);
+
+ LinkedList *next = entry->next;
+ free_linkedlist_entry(entry);
+ if (*prev != entry)
+ (*prev)->next = next;
+ else
+ *prev = NULL;
+
+ // Redo the delete process incase there are multiple
+ // entires that have the same key.
+ hashmap_delete_entry(m, key);
+ m->num_entries--;
+ return 1;
+}
+
+void hashmap_free(HashMap *m) {
+ for (int i = 0; i < m->size; i++) {
+ if (!m->entries[i])
+ continue;
+ LinkedList *list = m->entries[i];
+ for (; list;) {
+ if (list->upon_deletion)
+ list->upon_deletion(list->key, list->value);
+ LinkedList *old = list;
+ list = list->next;
+ free_linkedlist_entry(old);
+ }
+ }
+ kfree(m->entries);
+ kfree(m);
+}
+
+HashMap *hashmap_create(size_t size) {
+ HashMap *m = kmalloc(sizeof(HashMap));
+ if (!m)
+ return NULL;
+
+ m->size = size;
+ m->num_entries = 0;
+
+ // Create a array of pointers to linkedlists but don't create them
+ // yet.
+ m->entries = kcalloc(size, sizeof(LinkedList **));
+ if (!m->entries)
+ return NULL;
+
+ for (size_t i = 0; i < m->size; i++)
+ m->entries[i] = NULL;
+
+ m->hash_function = hash;
+ return m;
+}
diff --git a/kernel/hashmap/hashmap.h b/kernel/hashmap/hashmap.h
new file mode 100644
index 0000000..40f146a
--- /dev/null
+++ b/kernel/hashmap/hashmap.h
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2022 by Anton Kling <anton@kling.gg>
+//
+// SPDX-License-Identifier: BSD-2-Clause
+//
+#ifndef HASHMAP_H
+#define HASHMAP_H
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct LinkedList {
+ const char *key;
+ int key_allocated;
+ void *value;
+ void (*upon_deletion)(const char *, void *);
+ struct LinkedList *next;
+} LinkedList;
+
+typedef struct HashMap {
+ LinkedList **entries;
+ size_t size;
+ size_t num_entries;
+ uint32_t (*hash_function)(const uint8_t *data, size_t len);
+} HashMap;
+
+HashMap *hashmap_create(size_t size);
+void hashmap_free(HashMap *m);
+
+// hashmap_add_entry()
+// -------------------
+// This function adds a entry to the hashmap. The "key" passed to the
+// hashmap will be allocated by default unless (do_not_allocate_key) is
+// set to 1.
+int hashmap_add_entry(HashMap *m, const char *key, void *value,
+ void (*upon_deletion)(const char *, void *),
+ int do_not_allocate_key);
+void *hashmap_get_entry(HashMap *m, const char *key);
+int hashmap_delete_entry(HashMap *m, const char *key);
+#endif
diff --git a/kernel/includes/defs.h b/kernel/includes/defs.h
new file mode 100644
index 0000000..3a373b3
--- /dev/null
+++ b/kernel/includes/defs.h
@@ -0,0 +1,5 @@
+#ifndef DEFS_H
+#define DEFS_H
+#define BYTE unsigned char
+#define BYTEPTR BYTE*
+#endif
diff --git a/kernel/includes/math.h b/kernel/includes/math.h
new file mode 100644
index 0000000..19fc595
--- /dev/null
+++ b/kernel/includes/math.h
@@ -0,0 +1,2 @@
+int min(int a, int b);
+int max(int a, int b);
diff --git a/kernel/includes/mmu.h b/kernel/includes/mmu.h
new file mode 100644
index 0000000..1df337d
--- /dev/null
+++ b/kernel/includes/mmu.h
@@ -0,0 +1,61 @@
+#ifndef PAGING_H
+#define PAGING_H
+#include "kmalloc.h"
+#include <stdint.h>
+
+typedef uint8_t mmu_flags;
+
+#define MMU_FLAG_RW (1 << 0)
+#define MMU_FLAG_KERNEL (1 << 1)
+
+void *next_page(void *a);
+void *align_page(void *a);
+
+typedef struct Page {
+ uint32_t present : 1;
+ uint32_t rw : 1;
+ uint32_t user : 1;
+ uint32_t accessed : 1;
+ uint32_t dirty : 1;
+ uint32_t unused : 7;
+ uint32_t frame : 20;
+} __attribute__((packed)) Page;
+
+typedef struct PageTable {
+ Page pages[1024];
+} __attribute__((packed)) PageTable;
+
+typedef struct PageDirectory {
+ PageTable *tables[1024];
+ uint32_t physical_tables[1024];
+ uint32_t physical_address;
+} PageDirectory;
+
+int mmu_allocate_region(void *ptr, size_t n, mmu_flags flags, PageDirectory *pd);
+void mmu_allocate_shared_kernel_region(void *rc, size_t n);
+void *mmu_find_unallocated_virtual_range(void *addr, size_t length);
+void mmu_remove_virtual_physical_address_mapping(void *ptr, size_t length);
+void mmu_free_address_range(void *ptr, size_t length);
+void mmu_map_directories(void *dst, PageDirectory *d, void *src,
+ PageDirectory *s, size_t length);
+uint32_t mmu_get_number_of_allocated_frames(void);
+void *mmu_map_frames(void *ptr, size_t s);
+void mmu_map_physical(void *dst, PageDirectory *d, void *physical,
+ size_t length);
+void mmu_free_pages(void *a, uint32_t n);
+
+void flush_tlb(void);
+void paging_init(void);
+PageDirectory *get_active_pagedirectory(void);
+void move_stack(uint32_t new_stack_address, uint32_t size);
+void switch_page_directory(PageDirectory *directory);
+void *allocate_frame(Page *page, int rw, int is_kernel);
+PageDirectory *clone_directory(PageDirectory *original);
+void *virtual_to_physical(void *address, PageDirectory *directory);
+void *is_valid_userpointer(const void *const p, size_t s);
+void *is_valid_user_c_string(const char *ptr, size_t *size);
+void *ksbrk(size_t s);
+
+Page *get_page(void *ptr, PageDirectory *directory, int create_new_page,
+ int set_user);
+#endif
diff --git a/kernel/includes/signal.h b/kernel/includes/signal.h
new file mode 100644
index 0000000..3de9998
--- /dev/null
+++ b/kernel/includes/signal.h
@@ -0,0 +1,46 @@
+#ifndef SIGNAL_H
+#define SIGNAL_H
+#include <sys/types.h>
+#define SIGHUP 0
+#define SIGINT 1
+#define SIGWINCH 2
+#define SIGQUIT 3
+#define SIG_IGN 4
+typedef int pid_t;
+typedef int uid_t;
+typedef int sigset_t;
+
+union sigval {
+ int sival_int; // Integer signal value.
+ void *sival_ptr; // Pointer signal value.
+};
+
+struct siginfo {
+ int si_signo; // Signal number.
+ int si_code; // Signal code.
+ int si_errno; // If non-zero, an errno value associated with
+ // this signal, as described in <errno.h>.
+ pid_t si_pid; // Sending process ID.
+ uid_t si_uid; // Real user ID of sending process.
+ void *si_addr; // Address of faulting instruction.
+ int si_status; // Exit value or signal.
+ long si_band; // Band event for SIGPOLL.
+ union sigval si_value; // Signal value.
+};
+
+typedef struct siginfo siginfo_t;
+
+int kill(pid_t pid, int sig);
+
+struct sigaction {
+ void (*sa_handler)(int); // Pointer to a signal-catching function or one of
+ // the macros SIG_IGN or SIG_DFL.
+ sigset_t sa_mask; // Additional set of signals to be blocked during execution
+ // of signal-catching function.
+ int sa_flags; // Special flags to affect behavior of signal.
+ void (*sa_sigaction)(int, siginfo_t *,
+ void *); // Pointer to a signal-catching function.
+};
+
+int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
+#endif // SIGNAL_H
diff --git a/kernel/includes/sys/types.h b/kernel/includes/sys/types.h
new file mode 100644
index 0000000..1ccbf63
--- /dev/null
+++ b/kernel/includes/sys/types.h
@@ -0,0 +1,2 @@
+typedef int time_t;
+typedef int pid_t;
diff --git a/kernel/init/kernel.c b/kernel/init/kernel.c
new file mode 100644
index 0000000..8c0f70c
--- /dev/null
+++ b/kernel/init/kernel.c
@@ -0,0 +1,103 @@
+#include <assert.h>
+#include <cpu/gdt.h>
+#include <cpu/idt.h>
+#include <cpu/spinlock.h>
+#include <cpu/syscall.h>
+#include <crypto/SHA1/sha1.h>
+#include <drivers/ata.h>
+#include <drivers/keyboard.h>
+#include <drivers/mouse.h>
+#include <drivers/pit.h>
+#include <drivers/rtl8139.h>
+#include <drivers/serial.h>
+#include <drivers/vbe.h>
+#include <fs/devfs.h>
+#include <fs/ext2.h>
+#include <fs/shm.h>
+#include <fs/vfs.h>
+#include <log.h>
+#include <mmu.h>
+#include <multiboot.h>
+#include <random.h>
+#include <sched/scheduler.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(__linux__)
+#error "You are not using a cross-compiler."
+#endif
+
+#if !defined(__i386__)
+#error "This OS needs to be compiled with a ix86-elf compiler"
+#endif
+
+uint32_t inital_esp;
+uintptr_t data_end;
+
+void kernel_main(uint32_t kernel_end, unsigned long magic, unsigned long addr,
+ uint32_t inital_stack) {
+ data_end = kernel_end;
+ inital_esp = inital_stack;
+
+ asm("cli");
+ kprintf("If you see this then the serial driver works :D.\n");
+ assert(magic == MULTIBOOT_BOOTLOADER_MAGIC);
+
+ paging_init();
+ klog("Paging Initalized", LOG_SUCCESS);
+ multiboot_info_t *mb =
+ mmu_map_frames((multiboot_info_t *)addr, sizeof(multiboot_info_t));
+
+ gdt_init();
+ klog("GDT Initalized", LOG_SUCCESS);
+
+ idt_init();
+ klog("IDT Initalized", LOG_SUCCESS);
+
+ syscalls_init();
+ klog("Syscalls Initalized", LOG_SUCCESS);
+
+ pit_install();
+ set_pit_count(2000);
+ klog("PIT driver installed", LOG_SUCCESS);
+
+ ata_init();
+ klog("ATA Initalized", LOG_SUCCESS);
+
+ tasking_init();
+ klog("Tasking Initalized", LOG_SUCCESS);
+
+ install_mouse();
+ klog("PS2 Mouse driver installed", LOG_SUCCESS);
+
+ install_keyboard();
+ klog("PS2 Keyboard driver installed", LOG_SUCCESS);
+
+ vfs_mount("/", ext2_mount());
+ vfs_mount("/dev", devfs_mount());
+ add_stdout();
+ add_serial();
+ add_random_devices();
+ shm_init();
+
+ setup_random();
+
+ add_keyboard();
+ add_mouse();
+ rtl8139_init();
+
+ display_driver_init(mb);
+ add_vbe_device();
+ int pid;
+ if (0 == (pid = fork())) {
+ char *argv[] = {"/init", NULL};
+ if (0 == exec("/init", argv)) {
+ kprintf("exec() failed\n");
+ }
+ }
+ for (;;)
+ ;
+}
diff --git a/kernel/isodir/boot/grub/grub.cfg b/kernel/isodir/boot/grub/grub.cfg
new file mode 100644
index 0000000..b2f8404
--- /dev/null
+++ b/kernel/isodir/boot/grub/grub.cfg
@@ -0,0 +1,3 @@
+menuentry "myos" {
+ multiboot /boot/myos.bin
+}
diff --git a/kernel/kmalloc.c b/kernel/kmalloc.c
new file mode 100644
index 0000000..34c466d
--- /dev/null
+++ b/kernel/kmalloc.c
@@ -0,0 +1,230 @@
+#include <assert.h>
+#include <kmalloc.h>
+#include <ksbrk.h>
+#include <math.h>
+#define NEW_ALLOC_SIZE 0x30000
+
+#define IS_FREE (1 << 0)
+#define IS_FINAL (1 << 1)
+
+typedef struct MallocHeader {
+ uint64_t magic;
+ uint32_t size;
+ uint8_t flags;
+ struct MallocHeader *n;
+} MallocHeader;
+
+uint64_t delta_page(uint64_t a) { return 0x1000 - (a % 0x1000); }
+
+MallocHeader *head = NULL;
+MallocHeader *final = NULL;
+uint32_t total_heap_size = 0;
+
+int init_heap(void) {
+ head = (MallocHeader *)ksbrk(NEW_ALLOC_SIZE);
+ total_heap_size += NEW_ALLOC_SIZE - sizeof(MallocHeader);
+ head->magic = 0xdde51ab9410268b1;
+ head->size = NEW_ALLOC_SIZE - sizeof(MallocHeader);
+ head->flags = IS_FREE | IS_FINAL;
+ head->n = NULL;
+ final = head;
+ return 1;
+}
+
+int add_heap_memory(size_t min_desired) {
+ min_desired += sizeof(MallocHeader) + 0x1000;
+ size_t allocation_size = max(min_desired, NEW_ALLOC_SIZE);
+ allocation_size += delta_page(allocation_size);
+ void *p;
+ if ((void *)(-1) == (p = (void *)ksbrk(allocation_size))) {
+ return 0;
+ }
+ total_heap_size += allocation_size - sizeof(MallocHeader);
+ void *e = final;
+ e = (void *)((uint32_t)e + final->size);
+ if (p == e) {
+ final->size += allocation_size - sizeof(MallocHeader);
+ return 1;
+ }
+ MallocHeader *new_entry = p;
+ new_entry->size = allocation_size - sizeof(MallocHeader);
+ new_entry->flags = IS_FREE | IS_FINAL;
+ new_entry->n = NULL;
+ new_entry->magic = 0xdde51ab9410268b1;
+ final->n = new_entry;
+ final = new_entry;
+ return 1;
+}
+
+MallocHeader *next_header(MallocHeader *a) {
+ assert(a->magic == 0xdde51ab9410268b1);
+ if (a->n) {
+ assert(a->n->magic == 0xdde51ab9410268b1);
+ return a->n;
+ }
+ return NULL;
+}
+
+MallocHeader *next_close_header(MallocHeader *a) {
+ if (!a) {
+ kprintf("next close header fail\n");
+ for (;;)
+ ;
+ }
+ if (a->flags & IS_FINAL)
+ return NULL;
+ return next_header(a);
+}
+
+MallocHeader *find_free_entry(uint32_t s) {
+ // A new header is required as well as the newly allocated chunk
+ s += sizeof(MallocHeader);
+ if (!head)
+ init_heap();
+ MallocHeader *p = head;
+ for (; p; p = next_header(p)) {
+ assert(p->magic == 0xdde51ab9410268b1);
+ if (!(p->flags & IS_FREE))
+ continue;
+ uint64_t required_size = s;
+ if (p->size < required_size)
+ continue;
+ return p;
+ }
+ return NULL;
+}
+
+void merge_headers(MallocHeader *b) {
+ if (!(b->flags & IS_FREE))
+ return;
+
+ MallocHeader *n = next_close_header(b);
+ if (!n)
+ return;
+
+ if (!(n->flags & IS_FREE))
+ return;
+
+ b->size += n->size;
+ b->flags |= n->flags & IS_FINAL;
+ b->n = n->n;
+ if (n == final)
+ final = b;
+}
+
+void *kmalloc(size_t s) {
+ size_t n = s;
+ MallocHeader *free_entry = find_free_entry(s);
+ if (!free_entry) {
+ if (!add_heap_memory(s)) {
+ klog("Ran out of memory.", LOG_ERROR);
+ assert(0);
+ return NULL;
+ }
+ return kmalloc(s);
+ }
+
+ void *rc = (void *)(free_entry + 1);
+
+ // Create a new header
+ MallocHeader *new_entry = (MallocHeader *)((uintptr_t)rc + n);
+ new_entry->flags = free_entry->flags;
+ new_entry->n = free_entry->n;
+ new_entry->size = free_entry->size - n - sizeof(MallocHeader);
+ new_entry->magic = 0xdde51ab9410268b1;
+
+ if (free_entry == final)
+ final = new_entry;
+ merge_headers(new_entry);
+
+ // Modify the free entry
+ free_entry->size = n;
+ free_entry->flags = 0;
+ free_entry->n = new_entry;
+ free_entry->magic = 0xdde51ab9410268b1;
+ return rc;
+}
+
+#define HEAP 0x00E00000
+#define PHYS 0x403000
+
+void *latest = NULL;
+uint64_t left = 0;
+
+void *kmalloc_eternal_physical_align(size_t s, void **physical) {
+ void *return_address = ksbrk(s);
+ if (physical) {
+ if (0 == get_active_pagedirectory())
+ *physical =
+ (void *)((uintptr_t)return_address - (0xC0000000 + PHYS) + HEAP);
+ else
+ *physical = (void *)virtual_to_physical(return_address, 0);
+ }
+ memset(return_address, 0, 0x1000);
+ return return_address;
+}
+
+void *kmalloc_eternal_align(size_t s) {
+ return kmalloc_eternal_physical_align(s, NULL);
+}
+
+void *kmalloc_eternal(size_t s) { return kmalloc_eternal_align(s); }
+
+size_t get_mem_size(void *ptr) {
+ if (!ptr)
+ return 0;
+ return ((MallocHeader *)((uintptr_t)ptr - sizeof(MallocHeader)))->size;
+}
+
+void *krealloc(void *ptr, size_t size) {
+ void *rc = kmalloc(size);
+ if (!rc)
+ return NULL;
+ if (!ptr)
+ return rc;
+ size_t l = get_mem_size(ptr);
+ size_t to_copy = min(l, size);
+ memcpy(rc, ptr, to_copy);
+ // kfree(ptr);
+ return rc;
+}
+
+// This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+// if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *kreallocarray(void *ptr, size_t nmemb, size_t size) {
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 &&
+ SIZE_MAX / nmemb < size) {
+ return NULL;
+ }
+
+ return krealloc(ptr, nmemb * size);
+}
+
+void *kallocarray(size_t nmemb, size_t size) {
+ return kreallocarray(NULL, nmemb, size);
+}
+
+void *kcalloc(size_t nelem, size_t elsize) {
+ void *rc = kallocarray(nelem, elsize);
+ if (!rc)
+ return NULL;
+ memset(rc, 0, nelem * elsize);
+ return rc;
+}
+
+void kfree(void *p) {
+ /*
+if (!p)
+return;
+// FIXME: This assumes that p is at the start of a allocated area.
+// Could this be avoided in a simple way?
+MallocHeader *h = (MallocHeader *)((uintptr_t)p - sizeof(MallocHeader));
+assert(h->magic == 0xdde51ab9410268b1);
+if (h->flags & IS_FREE)
+return;
+
+h->flags |= IS_FREE;
+merge_headers(h);*/
+}
diff --git a/kernel/kmalloc.h b/kernel/kmalloc.h
new file mode 100644
index 0000000..69e0de3
--- /dev/null
+++ b/kernel/kmalloc.h
@@ -0,0 +1,21 @@
+#include <log.h>
+#include <mmu.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+void kmalloc_allocate_heap(void);
+
+void *kmalloc_eternal(size_t size);
+void *kmalloc_eternal_align(size_t size);
+void *kmalloc_eternal_physical(size_t size, void **physical);
+void *kmalloc_eternal_physical_align(size_t size, void **physical);
+
+void *kmalloc(size_t s);
+void *kmalloc_align(size_t s);
+void *krealloc(void *ptr, size_t size);
+void *kreallocarray(void *ptr, size_t nmemb, size_t size);
+void *kallocarray(size_t nmemb, size_t size);
+void *kcalloc(size_t nelem, size_t elsize);
+void kfree(void *p);
+int init_heap(void);
diff --git a/kernel/ksbrk.c b/kernel/ksbrk.c
new file mode 100644
index 0000000..755a4ed
--- /dev/null
+++ b/kernel/ksbrk.c
@@ -0,0 +1,40 @@
+#include <assert.h>
+#include <ksbrk.h>
+#include <mmu.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+extern uintptr_t data_end;
+extern PageDirectory *kernel_directory;
+
+#define HEAP 0x00E00000
+#define PHYS 0x403000
+#define PAGE_SIZE ((uintptr_t)0x1000)
+void *ksbrk(size_t s) {
+ uintptr_t rc = (uintptr_t)align_page((void *)data_end);
+ data_end += s;
+ data_end = (uintptr_t)align_page((void *)data_end);
+
+ if (!get_active_pagedirectory()) {
+ // If there is no active pagedirectory we
+ // just assume that the memory is
+ // already mapped.
+ return (void *)rc;
+ }
+ mmu_allocate_shared_kernel_region((void *)rc, (data_end - (uintptr_t)rc));
+ assert(((uintptr_t)rc % PAGE_SIZE) == 0);
+ memset((void *)rc, 0xFF, s);
+ return (void *)rc;
+}
+
+void *ksbrk_physical(size_t s, void **physical) {
+ void *r = ksbrk(s);
+ if (physical) {
+ // if (0 == get_active_pagedirectory())
+ // *physical = (void *)((uintptr_t)r - (0xC0000000 + PHYS) + HEAP);
+ // else
+ *physical = (void *)virtual_to_physical(r, 0);
+ }
+ return r;
+}*/
diff --git a/kernel/ksbrk.h b/kernel/ksbrk.h
new file mode 100644
index 0000000..34987dc
--- /dev/null
+++ b/kernel/ksbrk.h
@@ -0,0 +1,8 @@
+#ifndef KSBRK_H
+#define KSBRK_H
+#include <stddef.h>
+#include <stdint.h>
+
+void* ksbrk(size_t s);
+void *ksbrk_physical(size_t s, void **physical);
+#endif
diff --git a/kernel/kubsan.c b/kernel/kubsan.c
new file mode 100644
index 0000000..c81b3fc
--- /dev/null
+++ b/kernel/kubsan.c
@@ -0,0 +1,58 @@
+#include <kubsan.h>
+#include <log.h>
+#include <stdio.h>
+
+void ubsan_log(const char *cause, struct source_location source) {
+ kprintf("%s: %s : %d\n", cause, source.file_name, source.line);
+ dump_backtrace(5);
+ asm("cli");
+ asm volatile("1: jmp 1b");
+ asm("hlt");
+ for (;;)
+ ;
+}
+
+void __ubsan_handle_shift_out_of_bounds(struct ShiftOutOfBoundsData *data,
+ unsigned long lhs, unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_shift_out_of_bounds", data->location);
+}
+
+void __ubsan_handle_add_overflow(struct OverflowData *data, unsigned long lhs,
+ unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_add_overflow", data->location);
+}
+
+void __ubsan_handle_sub_overflow(struct OverflowData *data, unsigned long lhs,
+ unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_sub_overflow", data->location);
+}
+
+void __ubsan_handle_mul_overflow(struct OverflowData *data, unsigned long lhs,
+ unsigned long rhs) {
+ (void)lhs;
+ (void)rhs;
+ ubsan_log("handle_mul_overflow", data->location);
+}
+
+void __ubsan_handle_out_of_bounds(struct OutOfBoundsData *data, void *index) {
+ (void)index;
+ ubsan_log("handle_out_of_bounds", data->location);
+}
+
+void __ubsan_handle_pointer_overflow(struct OutOfBoundsData *data,
+ void *index) {
+ (void)index;
+ ubsan_log("handle_pointer_overflow", data->location);
+}
+
+void __ubsan_handle_vla_bound_not_positive(struct OutOfBoundsData *data,
+ void *index) {
+ (void)index;
+ ubsan_log("handle_vla_bound_not_positive", data->location);
+}
diff --git a/kernel/kubsan.h b/kernel/kubsan.h
new file mode 100644
index 0000000..dac5407
--- /dev/null
+++ b/kernel/kubsan.h
@@ -0,0 +1,79 @@
+#include <stdint.h>
+
+enum { type_kind_int = 0, type_kind_float = 1, type_unknown = 0xffff };
+
+struct type_descriptor {
+ uint16_t type_kind;
+ uint16_t type_info;
+ char type_name[1];
+};
+
+struct source_location {
+ const char *file_name;
+ union {
+ unsigned long reported;
+ struct {
+ uint32_t line;
+ uint32_t column;
+ };
+ };
+};
+
+struct OverflowData {
+ struct source_location location;
+ struct type_descriptor *type;
+};
+
+struct type_mismatch_data {
+ struct source_location location;
+ struct type_descriptor *type;
+ unsigned long alignment;
+ unsigned char type_check_kind;
+};
+
+struct type_mismatch_data_v1 {
+ struct source_location location;
+ struct type_descriptor *type;
+ unsigned char log_alignment;
+ unsigned char type_check_kind;
+};
+
+struct type_mismatch_data_common {
+ struct source_location *location;
+ struct type_descriptor *type;
+ unsigned long alignment;
+ unsigned char type_check_kind;
+};
+
+struct nonnull_arg_data {
+ struct source_location location;
+ struct source_location attr_location;
+ int arg_index;
+};
+
+struct OutOfBoundsData {
+ struct source_location location;
+ struct type_descriptor *array_type;
+ struct type_descriptor *index_type;
+};
+
+struct ShiftOutOfBoundsData {
+ struct source_location location;
+ struct type_descriptor *lhs_type;
+ struct type_descriptor *rhs_type;
+};
+
+struct unreachable_data {
+ struct source_location location;
+};
+
+struct invalid_value_data {
+ struct source_location location;
+ struct type_descriptor *type;
+};
+
+struct alignment_assumption_data {
+ struct source_location location;
+ struct source_location assumption_location;
+ struct type_descriptor *type;
+};
diff --git a/kernel/libc/exit/assert.c b/kernel/libc/exit/assert.c
new file mode 100644
index 0000000..b48773a
--- /dev/null
+++ b/kernel/libc/exit/assert.c
@@ -0,0 +1,12 @@
+#include <assert.h>
+#include <log.h>
+#include <stdio.h>
+#include <log.h>
+
+void aFailed(char *f, int l) {
+ kprintf("Assert failed\n");
+ kprintf("%s : %d\n", f, l);
+ dump_backtrace(10);
+ for (;;)
+ ;
+}
diff --git a/kernel/libc/include/assert.h b/kernel/libc/include/assert.h
new file mode 100644
index 0000000..90a0be4
--- /dev/null
+++ b/kernel/libc/include/assert.h
@@ -0,0 +1,19 @@
+#include <log.h>
+#include <stdio.h>
+
+#define assert(expr) \
+ { \
+ if (!(expr)) \
+ aFailed(__FILE__, __LINE__); \
+ }
+
+#define ASSERT_BUT_FIXME_PROPOGATE(expr) \
+ { \
+ if (!(expr)) \
+ kprintf("Performing assert that should have been a propogated error."); \
+ assert(expr); \
+ }
+
+void aFailed(char *f, int l);
+#define ASSERT_NOT_REACHED \
+ { assert(0) }
diff --git a/kernel/libc/include/errno.h b/kernel/libc/include/errno.h
new file mode 100644
index 0000000..1ad1004
--- /dev/null
+++ b/kernel/libc/include/errno.h
@@ -0,0 +1,85 @@
+#ifndef ERRNO_H
+#define ERRNO_H
+// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+#define E2BIG 1 // Argument list too long.
+#define EACCES 2 // Permission denied.
+#define EADDRINUSE 3 // Address in use.
+#define EADDRNOTAVAIL 4 // Address not available.
+#define EAFNOSUPPORT 5 // Address family not supported.
+#define EAGAIN 6 // Resource unavailable, try again.
+#define EALREADY 7 // Connection already in progress.
+#define EBADF 8 // Bad file descriptor.
+#define EBADMSG 9 // Bad message.
+#define EBUSY 10 // Device or resource busy.
+#define ECANCELED 11 // Operation canceled.
+#define ECHILD 12 // No child processes.
+#define ECONNABORTED 13 // Connection aborted.
+#define ECONNREFUSED 14 // Connection refused.
+#define ECONNRESET 15 // Connection reset.
+#define EDEADLK 16 // Resource deadlock would occur.
+#define EDESTADDRREQ 17 // Destination address required.
+#define EDOM 18 // Mathematics argument out of domain of function.
+#define EDQUOT 19 // Reserved.
+#define EEXIST 20 // File exists.
+#define EFAULT 21 // Bad address.
+#define EFBIG 22 // File too large.
+#define EHOSTUNREACH 23 // Host is unreachable.
+#define EIDRM 24 // Identifier removed.
+#define EILSEQ 25 // Illegal byte sequence.
+#define EINPROGRESS 26 // Operation in progress.
+#define EINTR 27 // Interrupted function.
+#define EINVAL 28 // Invalid argument.
+#define EIO 29 // I/O error.
+#define EISCONN 30 // Socket is connected.
+#define EISDIR 31 // Is a directory.
+#define ELOOP 32 // Too many levels of symbolic links.
+#define EMFILE 33 // File descriptor value too large.
+#define EMLINK 34 // Too many links.
+#define EMSGSIZE 35 // Message too large.
+#define EMULTIHOP 36 // Reserved.
+#define ENAMETOOLONG 37 // Filename too long.
+#define ENETDOWN 38 // Network is down.
+#define ENETRESET 39 // Connection aborted by network.
+#define ENETUNREACH 40 // Network unreachable.
+#define ENFILE 41 // Too many files open in system.
+#define ENOBUFS 42 // No buffer space available.
+#define ENODATA 43 // No message is available on the STREAM head read queue.
+#define ENODEV 44 // No such device.
+#define ENOENT 45 // No such file or directory.
+#define ENOEXEC 46 // Executable file format error.
+#define ENOLCK 47 // No locks available.
+#define ENOLINK 48 // Reserved.
+#define ENOMEM 49 // Not enough space.
+#define ENOMSG 50 // No message of the desired type.
+#define ENOPROTOOPT 51 // Protocol not available.
+#define ENOSPC 52 // No space left on device.
+#define ENOSR 53 // No STREAM resources.
+#define ENOSTR 54 // Not a STREAM.
+#define ENOSYS 55 // Functionality not supported.
+#define ENOTCONN 56 // The socket is not connected.
+#define ENOTDIR 57 // Not a directory or a symbolic link to a directory.
+#define ENOTEMPTY 58 // Directory not empty.
+#define ENOTRECOVERABLE 59 // State not recoverable.
+#define ENOTSOCK 60 // Not a socket.
+#define ENOTSUP 61 // Not supported (may be the same value as.
+#define ENOTTY 62 // Inappropriate I/O control operation.
+#define ENXIO 63 // No such device or address.
+#define EOPNOTSUPP ENOTSUP // Operation not supported on socket.
+#define EOVERFLOW 65 // Value too large to be stored in data type.
+#define EOWNERDEAD 66 // Previous owner died.
+#define EPERM 67 // Operation not permitted.
+#define EPIPE 68 // Broken pipe.
+#define EPROTO 69 // Protocol error.
+#define EPROTONOSUPPORT 70 // Protocol not supported.
+#define EPROTOTYPE 71 // Protocol wrong type for socket.
+#define ERANGE 72 // Result too large.
+#define EROFS 73 // Read-only file system.
+#define ESPIPE 74 // Invalid seek.
+#define ESRCH 75 // No such process.
+#define ESTALE 76 // Reserved.
+#define ETIME 77 // Stream ioctl() timeout.
+#define ETIMEDOUT 78 // Connection timed out.
+#define ETXTBSY 79 // Text file busy.
+#define EWOULDBLOCK EAGAIN // Operation would block.
+#define EXDEV 81 // Cross-device link.
+#endif
diff --git a/kernel/libc/include/limits.h b/kernel/libc/include/limits.h
new file mode 100644
index 0000000..05022ec
--- /dev/null
+++ b/kernel/libc/include/limits.h
@@ -0,0 +1 @@
+#define PATH_MAX 256
diff --git a/kernel/libc/include/stdio.h b/kernel/libc/include/stdio.h
new file mode 100644
index 0000000..f2bbb32
--- /dev/null
+++ b/kernel/libc/include/stdio.h
@@ -0,0 +1,9 @@
+#ifndef STDIO_H
+#define STDIO_H
+
+void putc(const char c);
+int puts(char *str);
+void delete_characther(void);
+int kprintf(const char *format, ...);
+
+#endif
diff --git a/kernel/libc/include/stdlib.h b/kernel/libc/include/stdlib.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/kernel/libc/include/stdlib.h
diff --git a/kernel/libc/include/string.h b/kernel/libc/include/string.h
new file mode 100644
index 0000000..7cee4b2
--- /dev/null
+++ b/kernel/libc/include/string.h
@@ -0,0 +1,19 @@
+#ifndef STRING_H
+#define STRING_H
+#include <stddef.h>
+#include <stdint.h>
+
+unsigned long strlen(const char *s);
+void *memcpy(void *dest, const void *src, uint32_t n);
+void *memset(void *dst, const unsigned char c, uint32_t n);
+int memcmp(const void *s1, const void *s2, uint32_t n);
+char *strcpy(char *d, const char *s);
+int strcmp(const char *s1, const char *s2);
+int isequal(const char *s1, const char *s2);
+int isequal_n(const char *s1, const char *s2, uint32_t n);
+char *copy_and_allocate_string(const char *s);
+char *copy_and_allocate_user_string(const char *s);
+char *strncpy(char *dest, const char *src, size_t n);
+size_t strlcpy(char *dst, const char *src, size_t dsize);
+char *strcat(char *s1, const char *s2);
+#endif
diff --git a/kernel/libc/include/time.h b/kernel/libc/include/time.h
new file mode 100644
index 0000000..4e356d1
--- /dev/null
+++ b/kernel/libc/include/time.h
@@ -0,0 +1,6 @@
+#include <types.h>
+
+struct timespec {
+ time_t tv_sec; // Seconds.
+ long tv_nsec; // Nanoseconds.
+};
diff --git a/kernel/libc/include/types.h b/kernel/libc/include/types.h
new file mode 100644
index 0000000..ffcf281
--- /dev/null
+++ b/kernel/libc/include/types.h
@@ -0,0 +1,27 @@
+#ifndef TYPES_H
+#define TYPES_H
+typedef unsigned int ino_t;
+
+typedef int mode_t;
+
+typedef int nlink_t;
+typedef int uid_t;
+typedef int gid_t;
+typedef int id_t;
+
+typedef int blkcnt_t;
+typedef int off_t;
+
+typedef int dev_t;
+typedef unsigned int fsblkcnt_t;
+typedef unsigned int fsfilcnt_t;
+typedef unsigned int ino_t;
+//typedef unsigned int size_t;
+
+typedef int blksize_t;
+typedef int pid_t;
+typedef int ssize_t;
+
+//typedef int clock_t;
+typedef int time_t;
+#endif
diff --git a/kernel/libc/stdio/print.c b/kernel/libc/stdio/print.c
new file mode 100644
index 0000000..8174983
--- /dev/null
+++ b/kernel/libc/stdio/print.c
@@ -0,0 +1,97 @@
+#include "../../drivers/serial.h"
+#include "../include/assert.h"
+#include "../include/stdio.h"
+#include <stdarg.h>
+
+#define TAB_SIZE 8
+
+const char HEX_SET[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+inline void putc(const char c) { write_serial(c); }
+
+int kprint_hex(uint64_t num) {
+ int c = 2;
+
+ if (num == 0) {
+ putc('0');
+ c++;
+ return c;
+ }
+
+ char str[16] = {0};
+ int i = 0;
+ for (; num != 0 && i < 16; i++, num /= 16)
+ str[i] = HEX_SET[(num % 16)];
+
+ c += i;
+ for (i--; i >= 0; i--)
+ putc(str[i]);
+
+ return c;
+}
+
+int kprint_int(int num) {
+ int c = 0;
+ if (0 == num) {
+ putc('0');
+ c++;
+ return c;
+ }
+ char str[10];
+ int i = 0;
+ for (; num != 0 && i < 10; i++, num /= 10)
+ str[i] = (num % 10) + '0';
+
+ c += i;
+ for (i--; i >= 0; i--)
+ putc(str[i]);
+ return c;
+}
+
+int kprintf(const char *format, ...) {
+ int c = 0;
+ va_list list;
+ va_start(list, format);
+
+ const char *s = format;
+ for (; *s; s++) {
+ if ('%' != *s) {
+ putc(*s);
+ c++;
+ continue;
+ }
+
+ char flag = *(s + 1);
+ if ('\0' == flag)
+ break;
+
+ switch (flag) {
+ case 'c':
+ putc((char)va_arg(list, int));
+ c++;
+ break;
+ case 'd':
+ c += kprint_int(va_arg(list, int));
+ break;
+ case 's':
+ for (char *string = va_arg(list, char *); *string; putc(*string++), c++)
+ ;
+ break;
+ case 'x':
+ c += kprint_hex(va_arg(list, const uint32_t));
+ break;
+ case '%':
+ putc('%');
+ c++;
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ s++;
+ }
+ return c;
+}
+
+int puts(char *str) { return kprintf("%s\n", str); }
diff --git a/kernel/libc/string/copy.c b/kernel/libc/string/copy.c
new file mode 100644
index 0000000..277c808
--- /dev/null
+++ b/kernel/libc/string/copy.c
@@ -0,0 +1,26 @@
+#include <assert.h>
+#include <kmalloc.h>
+#include <stddef.h>
+#include <string.h>
+
+char *copy_and_allocate_string(const char *s) {
+ size_t l = strlen(s);
+ char *r = kmalloc(l + 1);
+ if (!r)
+ return NULL;
+ return strncpy(r, s, l);
+}
+
+char *copy_and_allocate_user_string(const char *s) {
+ size_t len;
+ if (!is_valid_user_c_string(s, &len))
+ return NULL;
+ size_t real_len = strlen(s);
+ assert(real_len == len);
+ len = real_len;
+ char *r = kmalloc(len + 1);
+ if (!r)
+ return NULL;
+ strlcpy(r, s, len);
+ return r;
+}
diff --git a/kernel/libc/string/isequal.c b/kernel/libc/string/isequal.c
new file mode 100644
index 0000000..cdbd3cc
--- /dev/null
+++ b/kernel/libc/string/isequal.c
@@ -0,0 +1,15 @@
+#include "../include/string.h"
+
+int isequal(const char *s1, const char *s2) {
+ for(;*s1;s1++,s2++)
+ if(*s1 != *s2)
+ return 0;
+ return 1;
+}
+
+int isequal_n(const char *s1, const char *s2, uint32_t n) {
+ for(;*s1 && n;s1++,s2++,n--)
+ if(*s1 != *s2)
+ return 0;
+ return 1;
+}
diff --git a/kernel/libc/string/memcmp.c b/kernel/libc/string/memcmp.c
new file mode 100644
index 0000000..72c680a
--- /dev/null
+++ b/kernel/libc/string/memcmp.c
@@ -0,0 +1,12 @@
+#include "../include/string.h"
+
+int memcmp(const void *s1, const void *s2, uint32_t n)
+{
+ int return_value = 0;
+
+ for(uint32_t i = 0;i < n;i++)
+ if(((unsigned char *)(s1))[i] != ((unsigned char *)(s2))[i])
+ return_value++;
+
+ return return_value;
+}
diff --git a/kernel/libc/string/memcpy.c b/kernel/libc/string/memcpy.c
new file mode 100644
index 0000000..5c04407
--- /dev/null
+++ b/kernel/libc/string/memcpy.c
@@ -0,0 +1,20 @@
+#include "../include/string.h"
+
+void *
+memcpy(void *dest, const void *src, uint32_t n) {
+ unsigned char *d = dest;
+ const unsigned char *s = src;
+
+ for (; n >= 8; n -= 8, d += 8, s += 8)
+ *(uint64_t *)d = *(uint64_t *)s;
+
+ for (; n >= 4; n -= 4, d += 4, s += 4)
+ *(uint32_t *)d = *(uint32_t *)s;
+
+ for (; n >= 2; n -= 2, d += 2, s += 2)
+ *(uint16_t *)d = *(uint16_t *)s;
+
+ for (; n; n--)
+ *d++ = *s++;
+ return dest;
+}
diff --git a/kernel/libc/string/memset.c b/kernel/libc/string/memset.c
new file mode 100644
index 0000000..a446eb4
--- /dev/null
+++ b/kernel/libc/string/memset.c
@@ -0,0 +1,9 @@
+#include <string.h>
+
+void *memset(void *dst, const unsigned char c, uint32_t n) {
+ uintptr_t d = (uintptr_t)dst;
+ for (uint32_t i = 0; i < n; i++, d++)
+ *(unsigned char *)d = c;
+
+ return (void *)d;
+}
diff --git a/kernel/libc/string/strcat.c b/kernel/libc/string/strcat.c
new file mode 100644
index 0000000..78a9ec6
--- /dev/null
+++ b/kernel/libc/string/strcat.c
@@ -0,0 +1,6 @@
+#include <string.h>
+
+char *strcat(char *s1, const char *s2) {
+ strcpy(s1 + strlen(s1), s2);
+ return s1;
+}
diff --git a/kernel/libc/string/strcmp.c b/kernel/libc/string/strcmp.c
new file mode 100644
index 0000000..d7039e2
--- /dev/null
+++ b/kernel/libc/string/strcmp.c
@@ -0,0 +1,8 @@
+#include "../include/string.h"
+
+int strcmp(const char *s1, const char *s2)
+{
+ for(;*s1 == *s2 && *s1; s1++, s2++)
+ ;
+ return *s1 - *s2;
+}
diff --git a/kernel/libc/string/strcpy.c b/kernel/libc/string/strcpy.c
new file mode 100644
index 0000000..fa1e336
--- /dev/null
+++ b/kernel/libc/string/strcpy.c
@@ -0,0 +1,8 @@
+#include <string.h>
+
+char *strcpy(char *d, const char *s) {
+ char *s1 = d;
+ for (; (*d++ = *s++);)
+ ;
+ return s1;
+}
diff --git a/kernel/libc/string/strlcpy.c b/kernel/libc/string/strlcpy.c
new file mode 100644
index 0000000..43d0e58
--- /dev/null
+++ b/kernel/libc/string/strlcpy.c
@@ -0,0 +1,21 @@
+#include <stddef.h>
+#include <string.h>
+
+// Copy string src to buffer dst of size dsize. At most dsize-1
+// chars will be copied. Always NUL terminates (unless dsize == 0).
+// Returns strlen(src); if retval >= dsize, truncation occurred.
+size_t strlcpy(char *dst, const char *src, size_t dsize) {
+ size_t n = dsize;
+ const char *osrc = src;
+ for (; n; n--) {
+ if ((*dst++ = *src++) == '\0')
+ break;
+ }
+ if (n == 0) {
+ if (dsize != 0)
+ *dst = '\0'; /* NUL-terminate dst */
+ while (*src++)
+ ;
+ }
+ return src - osrc - 1;
+}
diff --git a/kernel/libc/string/strlen.c b/kernel/libc/string/strlen.c
new file mode 100644
index 0000000..4346383
--- /dev/null
+++ b/kernel/libc/string/strlen.c
@@ -0,0 +1,8 @@
+#include "../include/string.h"
+
+unsigned long strlen(const char *s)
+{
+ const char * tmp = s;
+ for(;*tmp++;);
+ return (tmp - s)-1;
+}
diff --git a/kernel/libc/string/strncpy.c b/kernel/libc/string/strncpy.c
new file mode 100644
index 0000000..a886895
--- /dev/null
+++ b/kernel/libc/string/strncpy.c
@@ -0,0 +1,11 @@
+#include <stddef.h>
+#include <string.h>
+
+// FIXME: Something is weird with this function
+char *strncpy(char *dest, const char *src, size_t n) {
+ char *r = dest;
+ for (; n && (*dest = *src); n--, src++, dest++)
+ ;
+ *dest = '\0';
+ return r;
+}
diff --git a/kernel/linker.ld b/kernel/linker.ld
new file mode 100644
index 0000000..59b150d
--- /dev/null
+++ b/kernel/linker.ld
@@ -0,0 +1,41 @@
+ENTRY (_start)
+
+SECTIONS
+{
+ . = 0x00100000;
+ /* The kernel will live at 3GB + 1MB in the virtual address space, */
+ /* which will be mapped to 1MB in the physical address space. */
+ /* Note that we page-align the sections. */
+
+ _kernel_start = .;
+ .multiboot.data : {
+ *(.multiboot.data)
+ }
+
+ .multiboot.text : {
+ *(.multiboot.text)
+ }
+
+ . += 0xC0000000;
+ /* Add a symbol that indicates the start address of the kernel. */
+ .text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000)
+ {
+ *(.text)
+ }
+ .rodata ALIGN (4K) : AT (ADDR (.rodata) - 0xC0000000)
+ {
+ *(.rodata)
+ }
+ .data ALIGN (4K) : AT (ADDR (.data) - 0xC0000000)
+ {
+ *(.data)
+ }
+ .bss ALIGN (4K) : AT (ADDR (.bss) - 0xC0000000)
+ {
+ *(COMMON)
+ *(.bss)
+ *(.bootstrap_stack)
+ }
+ /* Add a symbol that indicates the end address of the kernel. */
+ _kernel_end = .;
+}
diff --git a/kernel/log.c b/kernel/log.c
new file mode 100644
index 0000000..56d38e8
--- /dev/null
+++ b/kernel/log.c
@@ -0,0 +1,40 @@
+#include "log.h"
+#include <sched/scheduler.h>
+
+struct stackframe {
+ struct stackframe *ebp;
+ uint32_t eip;
+};
+
+void dump_backtrace(uint32_t max_frames) {
+ struct stackframe *stk;
+ asm("mov %%ebp,%0" : "=r"(stk)::);
+ kprintf("Stack trace:\n");
+ for (uint32_t frame = 0; stk && frame < max_frames; ++frame) {
+ kprintf(" 0x%x\n", stk->eip);
+ stk = stk->ebp;
+ }
+ if (get_current_task()) {
+ kprintf(" PID: %x\n", get_current_task()->pid);
+ }
+}
+
+void klog(char *str, int code) {
+ switch (code) {
+ case LOG_NOTE:
+ kprintf("[NOTE] ");
+ break;
+ case LOG_WARN:
+ kprintf("[WARN] ");
+ break;
+ case LOG_ERROR:
+ kprintf("[ERROR] ");
+ break;
+ default:
+ case LOG_SUCCESS:
+ kprintf("[SUCCESS] ");
+ break;
+ }
+
+ puts(str);
+}
diff --git a/kernel/log.h b/kernel/log.h
new file mode 100644
index 0000000..fe499bc
--- /dev/null
+++ b/kernel/log.h
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include <stdint.h>
+
+#define LOG_NOTE 3
+#define LOG_WARN 2
+#define LOG_ERROR 1
+#define LOG_SUCCESS 0
+
+void klog(char *str, int code);
+void dump_backtrace(uint32_t max_frames);
diff --git a/kernel/math.c b/kernel/math.c
new file mode 100644
index 0000000..2d5e30c
--- /dev/null
+++ b/kernel/math.c
@@ -0,0 +1,3 @@
+#include <math.h>
+int min(int a, int b) { return ((a) > (b) ? b : a); }
+int max(int a, int b) { return (a > b) ? a : b; }
diff --git a/kernel/mount/cat b/kernel/mount/cat
new file mode 120000
index 0000000..a24e907
--- /dev/null
+++ b/kernel/mount/cat
@@ -0,0 +1 @@
+./minibox \ No newline at end of file
diff --git a/kernel/mount/init b/kernel/mount/init
new file mode 120000
index 0000000..a24e907
--- /dev/null
+++ b/kernel/mount/init
@@ -0,0 +1 @@
+./minibox \ No newline at end of file
diff --git a/kernel/mount/pid1 b/kernel/mount/pid1
new file mode 100755
index 0000000..773d54c
--- /dev/null
+++ b/kernel/mount/pid1
Binary files differ
diff --git a/kernel/mount/program b/kernel/mount/program
new file mode 100755
index 0000000..c93d7ba
--- /dev/null
+++ b/kernel/mount/program
Binary files differ
diff --git a/kernel/mount/sh b/kernel/mount/sh
new file mode 100755
index 0000000..c05684c
--- /dev/null
+++ b/kernel/mount/sh
Binary files differ
diff --git a/kernel/multiboot.h b/kernel/multiboot.h
new file mode 100644
index 0000000..ecbb6a0
--- /dev/null
+++ b/kernel/multiboot.h
@@ -0,0 +1,243 @@
+#include <stdint.h>
+#ifndef MULTIBOOT_HEADER
+#define MULTIBOOT_HEADER 1
+
+/* How many bytes from the start of the file we search for the header. */
+#define MULTIBOOT_SEARCH 8192
+#define MULTIBOOT_HEADER_ALIGN 4
+
+/* The magic field should contain this. */
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+
+/* This should be in %eax. */
+#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
+
+/* Alignment of multiboot modules. */
+#define MULTIBOOT_MOD_ALIGN 0x00001000
+
+/* Alignment of the multiboot info structure. */
+#define MULTIBOOT_INFO_ALIGN 0x00000004
+
+/* Flags set in the ’flags’ member of the multiboot header. */
+
+/* Align all boot modules on i386 page (4KB) boundaries. */
+#define MULTIBOOT_PAGE_ALIGN 0x00000001
+
+/* Must pass memory information to OS. */
+#define MULTIBOOT_MEMORY_INFO 0x00000002
+
+/* Must pass video information to OS. */
+#define MULTIBOOT_VIDEO_MODE 0x00000004
+
+/* This flag indicates the use of the address fields in the header. */
+#define MULTIBOOT_AOUT_KLUDGE 0x00010000
+
+/* Flags to be set in the ’flags’ member of the multiboot info structure. */
+
+/* is there basic lower/upper memory information? */
+#define MULTIBOOT_INFO_MEMORY 0x00000001
+/* is there a boot device set? */
+#define MULTIBOOT_INFO_BOOTDEV 0x00000002
+/* is the command-line defined? */
+#define MULTIBOOT_INFO_CMDLINE 0x00000004
+/* are there modules to do something with? */
+#define MULTIBOOT_INFO_MODS 0x00000008
+
+/* These next two are mutually exclusive */
+
+/* is there a symbol table loaded? */
+#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
+/* is there an ELF section header table? */
+#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
+
+/* is there a full memory map? */
+#define MULTIBOOT_INFO_MEM_MAP 0x00000040
+
+/* Is there drive info? */
+#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
+
+/* Is there a config table? */
+#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
+
+/* Is there a boot loader name? */
+#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
+
+/* Is there a APM table? */
+#define MULTIBOOT_INFO_APM_TABLE 0x00000400
+
+/* Is there video information? */
+#define MULTIBOOT_INFO_VBE_INFO 0x00000800
+#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000
+
+#ifndef ASM_FILE
+
+typedef uint8_t multiboot_uint8_t;
+typedef uint16_t multiboot_uint16_t;
+typedef uint32_t multiboot_uint32_t;
+typedef uint64_t multiboot_uint64_t;
+
+struct multiboot_header {
+ /* Must be MULTIBOOT_MAGIC - see above. */
+ multiboot_uint32_t magic;
+
+ /* Feature flags. */
+ multiboot_uint32_t flags;
+
+ /* The above fields plus this one must equal 0 mod 2^32. */
+ multiboot_uint32_t checksum;
+
+ /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
+ multiboot_uint32_t header_addr;
+ multiboot_uint32_t load_addr;
+ multiboot_uint32_t load_end_addr;
+ multiboot_uint32_t bss_end_addr;
+ multiboot_uint32_t entry_addr;
+
+ /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
+ multiboot_uint32_t mode_type;
+ multiboot_uint32_t width;
+ multiboot_uint32_t height;
+ multiboot_uint32_t depth;
+};
+
+/* The symbol table for a.out. */
+struct multiboot_aout_symbol_table {
+ multiboot_uint32_t tabsize;
+ multiboot_uint32_t strsize;
+ multiboot_uint32_t addr;
+ multiboot_uint32_t reserved;
+};
+typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
+
+/* The section header table for ELF. */
+struct multiboot_elf_section_header_table {
+ multiboot_uint32_t num;
+ multiboot_uint32_t size;
+ multiboot_uint32_t addr;
+ multiboot_uint32_t shndx;
+};
+typedef struct multiboot_elf_section_header_table
+ multiboot_elf_section_header_table_t;
+
+struct multiboot_info {
+ /* Multiboot info version number */
+ multiboot_uint32_t flags;
+
+ /* Available memory from BIOS */
+ multiboot_uint32_t mem_lower;
+ multiboot_uint32_t mem_upper;
+
+ /* "root" partition */
+ multiboot_uint32_t boot_device;
+
+ /* Kernel command line */
+ multiboot_uint32_t cmdline;
+
+ /* Boot-Module list */
+ multiboot_uint32_t mods_count;
+ multiboot_uint32_t mods_addr;
+
+ union {
+ multiboot_aout_symbol_table_t aout_sym;
+ multiboot_elf_section_header_table_t elf_sec;
+ } u;
+
+ /* Memory Mapping buffer */
+ multiboot_uint32_t mmap_length;
+ multiboot_uint32_t mmap_addr;
+
+ /* Drive Info buffer */
+ multiboot_uint32_t drives_length;
+ multiboot_uint32_t drives_addr;
+
+ /* ROM configuration table */
+ multiboot_uint32_t config_table;
+
+ /* Boot Loader Name */
+ multiboot_uint32_t boot_loader_name;
+
+ /* APM table */
+ multiboot_uint32_t apm_table;
+
+ /* Video */
+ multiboot_uint32_t vbe_control_info;
+ multiboot_uint32_t vbe_mode_info;
+ multiboot_uint16_t vbe_mode;
+ multiboot_uint16_t vbe_interface_seg;
+ multiboot_uint16_t vbe_interface_off;
+ multiboot_uint16_t vbe_interface_len;
+
+ 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;
+ union {
+ struct {
+ multiboot_uint32_t framebuffer_palette_addr;
+ multiboot_uint16_t framebuffer_palette_num_colors;
+ };
+ 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;
+ };
+ };
+};
+typedef struct multiboot_info multiboot_info_t;
+
+struct multiboot_color {
+ multiboot_uint8_t red;
+ multiboot_uint8_t green;
+ multiboot_uint8_t blue;
+};
+
+struct multiboot_mmap_entry {
+ multiboot_uint32_t size;
+ 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;
+} __attribute__((packed));
+typedef struct multiboot_mmap_entry multiboot_memory_map_t;
+
+struct multiboot_mod_list {
+ /* the memory used goes from bytes ’mod_start’ to ’mod_end-1’ inclusive */
+ multiboot_uint32_t mod_start;
+ multiboot_uint32_t mod_end;
+
+ /* Module command line */
+ multiboot_uint32_t cmdline;
+
+ /* padding to take it to 16 bytes (must be zero) */
+ multiboot_uint32_t pad;
+};
+typedef struct multiboot_mod_list multiboot_module_t;
+
+/* APM BIOS info. */
+struct multiboot_apm_info {
+ 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;
+};
+
+#endif /* ! ASM_FILE */
+
+#endif /* ! MULTIBOOT_HEADER */
diff --git a/kernel/network/arp.c b/kernel/network/arp.c
new file mode 100644
index 0000000..de6d898
--- /dev/null
+++ b/kernel/network/arp.c
@@ -0,0 +1,153 @@
+#include <assert.h>
+#include <drivers/rtl8139.h>
+#include <network/arp.h>
+#include <network/bytes.h>
+#include <network/ethernet.h>
+#include <stdio.h>
+#include <string.h>
+
+struct ARP_DATA {
+ uint16_t htype; // Hardware type
+ uint16_t ptype; // Protocol type
+ uint8_t hlen; // Hardware address length (Ethernet = 6)
+ uint8_t plen; // Protocol address length (IPv4 = 4)
+ uint16_t opcode; // ARP Operation Code
+ uint8_t srchw[6]; // Source hardware address - hlen bytes (see above)
+ uint8_t srcpr[4]; // Source protocol address - plen bytes (see above).
+ // If IPv4 can just be a "u32" type.
+ uint8_t dsthw[6]; // Destination hardware address - hlen bytes (see above)
+ uint8_t dstpr[4]; // Destination protocol address - plen bytes (see
+ // above). If IPv4 can just be a "u32" type.
+};
+
+struct ARP_TABLE_ENTRY {
+ uint8_t is_used;
+ uint8_t mac[6];
+ uint8_t ip[4];
+};
+
+struct ARP_TABLE_ENTRY arp_table[10] = {0};
+
+// FIXME: This is hardcoded, don't do this.
+uint8_t ip_address[4] = {10, 0, 2, 15};
+
+struct ARP_TABLE_ENTRY *find_arp_entry_to_use(void) {
+ // This does not need to find a "free" entry as a ARP table is
+ // just a cache, it just has to pick a entry efficently.
+ for (int i = 0; i < 10; i++) {
+ if (!arp_table[i].is_used) {
+ return &arp_table[i];
+ }
+ }
+ return &arp_table[0];
+}
+
+void print_mac(const char *str, uint8_t *mac) {
+ kprintf("%s: ", str);
+ for (int i = 0; i < 6; i++) {
+ kprintf("%x", mac[i]);
+ if (5 != i)
+ kprintf(":");
+ }
+ kprintf("\n");
+}
+
+void print_ip(const char *str, const uint8_t *ip) {
+ kprintf("%s: ", str);
+ for (int i = 0; i < 4; i++) {
+ kprintf("%d", ip[i]);
+ if (3 != i)
+ kprintf(".");
+ }
+ kprintf("\n");
+}
+
+void send_arp_request(const uint8_t ip[4]) {
+ struct ARP_DATA data;
+ data.htype = htons(1);
+ data.ptype = htons(0x0800);
+
+ data.hlen = 6;
+ data.plen = 4;
+
+ data.opcode = htons(0x0001);
+ get_mac_address(data.srchw);
+ memcpy(data.srcpr, ip_address, sizeof(uint8_t[4]));
+
+ memset(data.dsthw, 0, sizeof(uint8_t[6]));
+ memcpy(data.dstpr, ip, sizeof(uint8_t[4]));
+
+ uint8_t broadcast[6];
+ memset(broadcast, 0xFF, sizeof(broadcast));
+ send_ethernet_packet(broadcast, 0x0806, (uint8_t *)&data, sizeof(data));
+}
+
+int get_mac_from_ip(const uint8_t ip[4], uint8_t mac[6]) {
+ print_ip("ARP GETTING MAC FROM IP: ", ip);
+ for (int i = 0; i < 10; i++) {
+ if (0 != memcmp(arp_table[i].ip, ip, sizeof(uint8_t[4])))
+ continue;
+ memcpy(mac, arp_table[i].mac, sizeof(uint8_t[6]));
+ return 1;
+ }
+ klog("ARP cache miss", LOG_NOTE);
+ asm("sti");
+ send_arp_request(ip);
+ // TODO: Maybe wait a bit?
+ for (int i = 0; i < 10; i++) {
+ if (0 != memcmp(arp_table[i].ip, ip, sizeof(uint8_t[4])))
+ continue;
+ memcpy(mac, arp_table[i].mac, sizeof(uint8_t[6]));
+ return 1;
+ }
+ assert(0);
+ return 0;
+}
+
+void handle_arp(const uint8_t *payload) {
+ struct ARP_DATA *data = (struct ARP_DATA *)payload;
+
+ // Assert that communication is over ethernet
+ assert(1 == ntohs(data->htype));
+ // Assert that request uses IP
+ assert(0x0800 == ntohs(data->ptype));
+
+ assert(6 == data->hlen);
+ assert(4 == data->plen);
+ // Assert it is a request
+ if (0x0001 /*arp_request*/ == ntohs(data->opcode)) {
+ print_mac("srchw: ", data->srchw);
+ print_ip("srcpr: ", data->srcpr);
+
+ print_mac("dsthw: ", data->dsthw);
+ print_ip("dstpr: ", data->dstpr);
+
+ assert(0 == memcmp(data->dstpr, ip_address, sizeof(uint8_t[4])));
+
+ // Now we have to construct a ARP response
+ struct ARP_DATA response;
+ response.htype = htons(1);
+ response.ptype = htons(0x0800);
+ response.opcode = htons(0x00002);
+ response.hlen = 6;
+ response.plen = 4;
+ get_mac_address(response.srchw);
+ memcpy(response.srcpr, ip_address, sizeof(uint8_t[4]));
+
+ memcpy(response.dsthw, data->srchw, sizeof(uint8_t[6]));
+ memcpy(response.dstpr, data->srcpr, sizeof(uint8_t[4]));
+
+ send_ethernet_packet(data->srchw, 0x0806, (uint8_t *)&response,
+ sizeof(response));
+ } else if (0x0002 /*arp_response*/ == ntohs(data->opcode)) {
+ // Find a entry to fill
+ struct ARP_TABLE_ENTRY *entry = find_arp_entry_to_use();
+ entry->is_used = 1;
+ memcpy(entry->mac, data->srchw, sizeof(uint8_t[6]));
+ memcpy(entry->ip, data->srcpr, sizeof(uint8_t[4]));
+ print_ip("Added ip: ", entry->ip);
+ } else {
+ kprintf("GOT A ARP REQEUST WITH TYPE: %x\n", ntohs(data->opcode));
+ assert(0);
+ }
+}
diff --git a/kernel/network/arp.h b/kernel/network/arp.h
new file mode 100644
index 0000000..c2beb94
--- /dev/null
+++ b/kernel/network/arp.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+
+int get_mac_from_ip(const uint8_t ip[4], uint8_t mac[6]);
+void handle_arp(const uint8_t *payload);
diff --git a/kernel/network/bytes.c b/kernel/network/bytes.c
new file mode 100644
index 0000000..94afa73
--- /dev/null
+++ b/kernel/network/bytes.c
@@ -0,0 +1,10 @@
+#include <network/bytes.h>
+
+uint16_t ntohs(uint16_t net) { return (net >> 8) | (net << 8); }
+
+uint16_t htons(uint16_t net) { return (net >> 8) | (net << 8); }
+
+uint32_t htonl(uint32_t net) {
+ return (((net & 0x000000FF) << 24) | ((net & 0x0000FF00) << 8) |
+ ((net & 0x00FF0000) >> 8) | ((net & 0xFF000000) >> 24));
+}
diff --git a/kernel/network/bytes.h b/kernel/network/bytes.h
new file mode 100644
index 0000000..c291589
--- /dev/null
+++ b/kernel/network/bytes.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+uint16_t ntohs(uint16_t net);
+uint16_t htons(uint16_t net);
+uint32_t htonl(uint32_t net);
diff --git a/kernel/network/ethernet.c b/kernel/network/ethernet.c
new file mode 100644
index 0000000..e97ccbd
--- /dev/null
+++ b/kernel/network/ethernet.c
@@ -0,0 +1,93 @@
+#include <assert.h>
+#include <drivers/rtl8139.h>
+#include <kmalloc.h>
+#include <network/arp.h>
+#include <network/bytes.h>
+#include <network/ethernet.h>
+#include <network/ipv4.h>
+#include <stdio.h>
+
+struct ETHERNET_HEADER {
+ uint8_t mac_dst[6];
+ uint8_t mac_src[6];
+ uint16_t type;
+};
+
+uint32_t crc32(const char *buf, size_t len) {
+ static uint32_t table[256];
+ static int have_table = 0;
+ uint32_t rem;
+ uint8_t octet;
+ int i, j;
+ const char *p, *q;
+
+ if (have_table == 0) {
+ for (i = 0; i < 256; i++) {
+ rem = i;
+ for (j = 0; j < 8; j++) {
+ if (rem & 1) {
+ rem >>= 1;
+ rem ^= 0xedb88320;
+ } else
+ rem >>= 1;
+ }
+ table[i] = rem;
+ }
+ have_table = 1;
+ }
+
+ uint32_t crc = 0xFFFFFFFF;
+ q = buf + len;
+ for (p = buf; p < q; p++) {
+ octet = *p;
+ crc = (crc >> 8) ^ table[(crc & 0xff) ^ octet];
+ }
+ return ~crc;
+}
+
+void handle_ethernet(const uint8_t *packet, uint64_t packet_length) {
+ struct ETHERNET_HEADER *eth_header = (struct ETHERNET_HEADER *)packet;
+ packet += sizeof(struct ETHERNET_HEADER);
+ const uint8_t *payload = packet;
+ packet += packet_length - sizeof(struct ETHERNET_HEADER);
+ uint32_t crc = *((uint32_t *)packet - 1);
+ kprintf("PACKET crc: %x\n", crc);
+ kprintf("OUR OWN CALCULATED crc: %x\n",
+ crc32((const char *)eth_header, (packet_length - 4)));
+
+ uint16_t type = ntohs(eth_header->type);
+ switch (type) {
+ case 0x0806:
+ handle_arp(payload);
+ break;
+ case 0x0800:
+ handle_ipv4(payload, packet_length - sizeof(struct ETHERNET_HEADER) - 4);
+ break;
+ default:
+ kprintf("Can't handle ethernet type\n");
+ break;
+ }
+}
+
+void send_ethernet_packet(uint8_t mac_dst[6], uint16_t type, uint8_t *payload,
+ uint64_t payload_length) {
+ // FIXME: Janky allocation, do this better
+ uint64_t buffer_size =
+ sizeof(struct ETHERNET_HEADER) + payload_length + sizeof(uint32_t);
+ uint8_t *buffer = kmalloc(buffer_size);
+ uint8_t *buffer_start = buffer;
+ struct ETHERNET_HEADER *eth_header = (struct ETHERNET_HEADER *)buffer;
+ buffer += sizeof(struct ETHERNET_HEADER);
+ memcpy(buffer, payload, payload_length);
+ buffer += payload_length;
+
+ memcpy(eth_header->mac_dst, mac_dst, sizeof(uint8_t[6]));
+ get_mac_address(eth_header->mac_src);
+ eth_header->type = htons(type);
+ *(uint32_t *)(buffer) =
+ htonl(crc32((const char *)buffer_start, buffer_size - 4));
+
+ assert(rtl8139_send_data(buffer_start, buffer_size));
+ kfree(buffer_start);
+ kprintf("sent data\n");
+}
diff --git a/kernel/network/ethernet.h b/kernel/network/ethernet.h
new file mode 100644
index 0000000..0fdcee3
--- /dev/null
+++ b/kernel/network/ethernet.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+void handle_ethernet(const uint8_t *packet, uint64_t packet_length);
+void send_ethernet_packet(uint8_t mac_dst[6], uint16_t type, uint8_t *payload,
+ uint64_t payload_length);
diff --git a/kernel/network/ipv4.c b/kernel/network/ipv4.c
new file mode 100644
index 0000000..099aa0d
--- /dev/null
+++ b/kernel/network/ipv4.c
@@ -0,0 +1,98 @@
+#include <assert.h>
+#include <kmalloc.h>
+#include <network/arp.h>
+#include <network/bytes.h>
+#include <network/ethernet.h>
+#include <network/ipv4.h>
+#include <network/udp.h>
+#include <string.h>
+
+uint16_t ip_checksum(void *vdata, size_t length) {
+ // Cast the data pointer to one that can be indexed.
+ char *data = (char *)vdata;
+
+ // Initialise the accumulator.
+ uint32_t acc = 0xffff;
+
+ // Handle complete 16-bit blocks.
+ for (size_t i = 0; i + 1 < length; i += 2) {
+ uint16_t word;
+ memcpy(&word, data + i, 2);
+ acc += ntohs(word);
+ if (acc > 0xffff) {
+ acc -= 0xffff;
+ }
+ }
+
+ // Handle any partial block at the end of the data.
+ if (length & 1) {
+ uint16_t word = 0;
+ memcpy(&word, data + length - 1, 1);
+ acc += ntohs(word);
+ if (acc > 0xffff) {
+ acc -= 0xffff;
+ }
+ }
+
+ // Return the checksum in network byte order.
+ return htons(~acc);
+}
+
+extern uint8_t ip_address[4];
+void send_ipv4_packet(uint32_t ip, uint8_t protocol, const uint8_t *payload,
+ uint16_t length) {
+ uint8_t header[20] = {0};
+ header[0] = (4 /*version*/ << 4) | (5 /*IHL*/);
+ *((uint16_t *)(header + 2)) = htons(length + 20);
+ header[8 /*TTL*/] = 0xF8;
+ header[9] = protocol;
+
+ memcpy(header + 12 /*src_ip*/, ip_address, sizeof(uint8_t[4]));
+ memcpy(header + 16, &ip, sizeof(uint8_t[4]));
+
+ *((uint16_t *)(header + 10 /*checksum*/)) = ip_checksum(header, 20);
+ uint16_t packet_length = length + 20;
+ uint8_t *packet = kmalloc(packet_length);
+ memcpy(packet, header, 20);
+ memcpy(packet + 20, payload, length);
+
+ uint8_t mac[6];
+ uint8_t ip_copy[4]; // TODO: Do I need to do this?
+ memcpy(ip_copy, &ip, sizeof(uint8_t[4]));
+ get_mac_from_ip(ip_copy, mac);
+ send_ethernet_packet(mac, 0x0800, packet, packet_length);
+ kfree(packet);
+}
+
+void handle_ipv4(const uint8_t *payload, uint32_t packet_length) {
+ assert(packet_length > 4);
+
+ uint16_t saved_checksum = *(uint16_t *)(payload + 10);
+ *(uint16_t *)(payload + 10) = 0;
+ uint16_t calc_checksum = ip_checksum((uint8_t *)payload, 20);
+ *(uint16_t *)(payload + 10) = saved_checksum;
+ assert(calc_checksum == saved_checksum);
+
+ uint8_t version = (*payload & 0xF0) >> 4;
+ uint8_t IHL = (*payload & 0xF);
+ kprintf("version: %x\n", version);
+ assert(4 == version);
+ assert(5 == IHL);
+ uint16_t ipv4_total_length = ntohs(*(uint16_t *)(payload + 2));
+ assert(ipv4_total_length >= 20);
+ // Make sure the ipv4 header is not trying to get uninitalized memory
+ assert(ipv4_total_length <= packet_length);
+
+ uint8_t src_ip[4];
+ memcpy(src_ip, payload + 12, sizeof(uint8_t[4]));
+
+ uint8_t protocol = *(payload + 9);
+ switch (protocol) {
+ case 0x11:
+ handle_udp(src_ip, payload + 20, ipv4_total_length - 20);
+ break;
+ default:
+ kprintf("Protocol given in IPv4 header not handeld: %x\n", protocol);
+ break;
+ }
+}
diff --git a/kernel/network/ipv4.h b/kernel/network/ipv4.h
new file mode 100644
index 0000000..f578202
--- /dev/null
+++ b/kernel/network/ipv4.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+void handle_ipv4(const uint8_t *payload, uint32_t packet_length);
+void send_ipv4_packet(uint32_t ip, uint8_t protocol, const uint8_t *payload,
+ uint16_t length);
diff --git a/kernel/network/udp.c b/kernel/network/udp.c
new file mode 100644
index 0000000..23411da
--- /dev/null
+++ b/kernel/network/udp.c
@@ -0,0 +1,64 @@
+#include <assert.h>
+#include <network/bytes.h>
+#include <network/ipv4.h>
+#include <network/udp.h>
+#include <socket.h>
+
+void send_udp_packet(struct sockaddr_in *src, const struct sockaddr_in *dst,
+ const uint8_t *payload, uint16_t payload_length) {
+ uint16_t header[4] = {0};
+ header[0] = src->sin_port;
+ header[1] = dst->sin_port;
+ header[2] = htons(payload_length + 8);
+
+ uint16_t packet_length = sizeof(header) + payload_length;
+ uint8_t *packet = kmalloc(packet_length);
+ memcpy(packet, header, sizeof(header));
+ memcpy(packet + sizeof(header), payload, payload_length);
+ send_ipv4_packet(dst->sin_addr.s_addr, 0x11, packet, packet_length);
+ kfree(packet);
+}
+
+void handle_udp(uint8_t src_ip[4], const uint8_t *payload,
+ uint32_t packet_length) {
+ assert(packet_length >= 8);
+ // n_.* means network format(big endian)
+ // h_.* means host format((probably) little endian)
+ uint16_t n_source_port = *(uint16_t *)payload;
+ uint16_t h_source_port = ntohs(n_source_port);
+ (void)h_source_port;
+ uint16_t h_dst_port = ntohs(*(uint16_t *)(payload + 2));
+ uint16_t h_length = ntohs(*(uint16_t *)(payload + 4));
+ assert(h_length == packet_length);
+ uint16_t data_length = h_length - 8;
+ const uint8_t *data = payload + 8;
+
+ // Find the open port
+ OPEN_INET_SOCKET *in_s = find_open_udp_port(htons(h_dst_port));
+ assert(in_s);
+ SOCKET *s = in_s->s;
+ vfs_fd_t *fifo_file = s->ptr_socket_fd;
+
+ // Write the sockaddr struct such that it can later be
+ // given to userland if asked.
+ struct sockaddr_in /*{
+ sa_family_t sin_family;
+ union {
+ uint32_t s_addr;
+ } sin_addr;
+ uint16_t sin_port;
+ }*/ in;
+ in.sin_family = AF_INET;
+ memcpy(&in.sin_addr.s_addr, src_ip, sizeof(uint32_t));
+ in.sin_port = n_source_port;
+ socklen_t sock_length = sizeof(struct sockaddr_in);
+
+ raw_vfs_pwrite(fifo_file, &sock_length, sizeof(sock_length), 0);
+ raw_vfs_pwrite(fifo_file, &in, sizeof(in), 0);
+
+ // Write the UDP payload length(not including header)
+ raw_vfs_pwrite(fifo_file, &data_length, sizeof(uint16_t), 0);
+
+ // Write the UDP payload
+ raw_vfs_pwrite(fifo_file, (char *)data, data_length, 0);
+}
diff --git a/kernel/network/udp.h b/kernel/network/udp.h
new file mode 100644
index 0000000..c30b8c8
--- /dev/null
+++ b/kernel/network/udp.h
@@ -0,0 +1,7 @@
+#include <socket.h>
+#include <stdint.h>
+
+void handle_udp(uint8_t src_ip[4], const uint8_t *payload,
+ uint32_t packet_length);
+void send_udp_packet(struct sockaddr_in *src, const struct sockaddr_in *dst,
+ const uint8_t *payload, uint16_t payload_length);
diff --git a/kernel/poll.c b/kernel/poll.c
new file mode 100644
index 0000000..5e02723
--- /dev/null
+++ b/kernel/poll.c
@@ -0,0 +1,54 @@
+#include <fs/vfs.h>
+#include <halts.h>
+#include <poll.h>
+#include <sched/scheduler.h>
+
+int poll(struct pollfd *fds, size_t nfds, int timeout) {
+ (void)timeout;
+ int read_locks[nfds];
+ int write_locks[nfds];
+ int disconnect_locks[nfds];
+ for (size_t i = 0; i < nfds; i++) {
+ if (fds[i].fd < 0)
+ continue;
+ vfs_fd_t *f = get_vfs_fd(fds[i].fd);
+ if (fds[i].events & POLLIN)
+ read_locks[i] = create_read_fdhalt(f);
+ if (fds[i].events & POLLOUT)
+ write_locks[i] = create_write_fdhalt(f);
+ if (fds[i].events & POLLHUP)
+ disconnect_locks[i] = create_disconnect_fdhalt(f);
+ }
+
+ switch_task();
+
+ for (size_t i = 0; i < nfds; i++) {
+ if (fds[i].fd < 0)
+ continue;
+ if (fds[i].events & POLLIN)
+ unset_read_fdhalt(read_locks[i]);
+ if (fds[i].events & POLLOUT)
+ unset_write_fdhalt(write_locks[i]);
+ if (fds[i].events & POLLHUP)
+ unset_disconnect_fdhalt(disconnect_locks[i]);
+ }
+ for (size_t i = 0; i < nfds; i++) {
+ if (0 > fds[i].fd) {
+ fds[i].revents = 0;
+ continue;
+ }
+ vfs_fd_t *f = get_vfs_fd(fds[i].fd);
+ if (!f) {
+ if (fds[i].events & POLLHUP)
+ fds[i].revents |= POLLHUP;
+ } else {
+ if (f->inode->has_data && fds[i].events & POLLIN)
+ fds[i].revents |= POLLIN;
+ if (f->inode->can_write && fds[i].events & POLLOUT)
+ fds[i].revents |= POLLOUT;
+ if (!(f->inode->is_open) && fds[i].events & POLLHUP)
+ fds[i].revents |= POLLHUP;
+ }
+ }
+ return 0;
+}
diff --git a/kernel/poll.h b/kernel/poll.h
new file mode 100644
index 0000000..19698dd
--- /dev/null
+++ b/kernel/poll.h
@@ -0,0 +1,15 @@
+#include <stdint.h>
+#include <stddef.h>
+
+#define POLLIN (1 << 0)
+#define POLLPRI (1 << 1)
+#define POLLOUT (1 << 2)
+#define POLLHUP (1 << 3)
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+int poll(struct pollfd *fds, size_t nfds, int timeout);
diff --git a/kernel/process.s b/kernel/process.s
new file mode 100644
index 0000000..24e1869
--- /dev/null
+++ b/kernel/process.s
@@ -0,0 +1,33 @@
+.intel_syntax noprefix
+.global copy_page_physical
+.global loop_m
+copy_page_physical:
+ push ebx
+ pushf
+
+ cli
+
+ mov ebx, [esp+12]
+ mov ecx, [esp+16]
+
+ mov edx, cr0
+ and edx, 0x7fffffff
+ mov cr0, edx
+
+ mov edx, 1024
+
+.loop:
+ mov eax, [ebx]
+ mov [ecx], eax
+ add ebx, 4
+ add ecx, 4
+ dec edx
+ jnz .loop
+
+ mov edx, cr0
+ or edx, 0x80000000
+ mov cr0, edx
+
+ popf
+ pop ebx
+ ret
diff --git a/kernel/random.c b/kernel/random.c
new file mode 100644
index 0000000..0e1b760
--- /dev/null
+++ b/kernel/random.c
@@ -0,0 +1,142 @@
+// FIXME: This is mostlikely incredibly inefficent and insecure.
+#include <crypto/ChaCha20/chacha20.h>
+#include <crypto/SHA1/sha1.h>
+#include <fs/devfs.h>
+#include <fs/vfs.h>
+#include <random.h>
+#include <stddef.h>
+#include <string.h>
+
+#define HASH_CTX SHA1_CTX
+#define HASH_LEN SHA1_LEN
+
+uint32_t internal_chacha_block[16] = {
+ // Constant ascii values of "expand 32-byte k"
+ 0x61707865,
+ 0x3320646e,
+ 0x79622d32,
+ 0x6b206574,
+ // The unique key
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ // Block counter
+ 0x00000000,
+ // Nonce
+ 0x00000000,
+ 0x00000000,
+};
+
+void mix_chacha(void) {
+ uint8_t rand_data[BLOCK_SIZE];
+ get_random((BYTEPTR)rand_data, BLOCK_SIZE);
+ memcpy(internal_chacha_block + KEY, rand_data, KEY_SIZE);
+ memcpy(internal_chacha_block + NONCE, rand_data + KEY_SIZE, NONCE_SIZE);
+ internal_chacha_block[COUNT] = 0;
+}
+
+void get_random(BYTEPTR buffer, uint64_t len) {
+ uint8_t rand_data[BLOCK_SIZE];
+ for (; len > 0;) {
+ if (COUNT_MAX - 1 == internal_chacha_block[COUNT]) {
+ // The current block has used up all the 2^32 counts. If the
+ // key and/or the nonce are not changed and the count
+ // overflows back to zero then the random values would
+ // repeate. This is of course not desiered behaviour. The
+ // solution is to create a new nonce and key using the
+ // already established chacha block.
+ internal_chacha_block[COUNT]++;
+ mix_chacha();
+ }
+ uint32_t read_len = (BLOCK_SIZE < len) ? (BLOCK_SIZE) : len;
+ chacha_block((uint32_t *)rand_data, internal_chacha_block);
+ internal_chacha_block[COUNT]++;
+ memcpy(buffer, rand_data, read_len);
+ buffer += read_len;
+ len -= read_len;
+ }
+}
+
+HASH_CTX hash_pool;
+uint32_t hash_pool_size = 0;
+
+void add_hash_pool(void) {
+ uint8_t new_chacha_key[KEY_SIZE];
+ get_random(new_chacha_key, KEY_SIZE);
+
+ uint8_t hash_buffer[HASH_LEN];
+ SHA1_Final(&hash_pool, hash_buffer);
+ for (size_t i = 0; i < HASH_LEN; i++)
+ new_chacha_key[i % KEY_SIZE] ^= hash_buffer[i];
+
+ SHA1_Init(&hash_pool);
+ SHA1_Update(&hash_pool, hash_buffer, HASH_LEN);
+
+ uint8_t block[BLOCK_SIZE];
+ get_random(block, BLOCK_SIZE);
+ SHA1_Update(&hash_pool, block, BLOCK_SIZE);
+
+ memcpy(internal_chacha_block + KEY, new_chacha_key, KEY_SIZE);
+
+ mix_chacha();
+}
+
+void add_entropy(uint8_t *buffer, size_t size) {
+ SHA1_Update(&hash_pool, buffer, size);
+ hash_pool_size += size;
+ if (hash_pool_size >= HASH_LEN * 2)
+ add_hash_pool();
+}
+
+void setup_random(void) {
+ SHA1_Init(&hash_pool);
+
+ BYTE seed[1024];
+ int rand_fd = vfs_open("/etc/seed", O_RDWR, 0);
+ if (0 > rand_fd) {
+ klog("/etc/seed not found", LOG_WARN);
+ return;
+ }
+
+ size_t offset = 0;
+ for (int rc; (rc = vfs_pread(rand_fd, seed, 1024, offset)); offset += rc) {
+ if (0 > rc) {
+ klog("/etc/seed read error", LOG_WARN);
+ break;
+ }
+ add_entropy(seed, rc);
+ }
+ add_hash_pool();
+
+ // Update the /etc/seed file to ensure we get a new state upon next
+ // boot.
+ get_random(seed, 1024);
+ vfs_pwrite(rand_fd, seed, 1024, 0);
+ vfs_close(rand_fd);
+}
+
+int random_write(BYTEPTR buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ (void)offset;
+ (void)fd;
+ add_entropy(buffer, len);
+ return len; // add_entropy() never fails to recieve (len) amount of data.
+}
+
+int random_read(BYTEPTR buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ (void)offset;
+ (void)fd;
+ get_random(buffer, len);
+ return len; // get_random() never fails to give "len" amount of data.
+}
+
+void add_random_devices(void) {
+ devfs_add_file("/random", random_read, random_write, NULL, 1, 1,
+ FS_TYPE_CHAR_DEVICE);
+ devfs_add_file("/urandom", random_read, random_write, NULL, 1, 1,
+ FS_TYPE_CHAR_DEVICE);
+}
diff --git a/kernel/random.h b/kernel/random.h
new file mode 100644
index 0000000..437927f
--- /dev/null
+++ b/kernel/random.h
@@ -0,0 +1,7 @@
+#include <stdint.h>
+#include <fs/vfs.h>
+#include <defs.h>
+
+void setup_random(void);
+void add_random_devices(void);
+void get_random(uint8_t* buffer, uint64_t len);
diff --git a/kernel/read_eip.s b/kernel/read_eip.s
new file mode 100644
index 0000000..6041db0
--- /dev/null
+++ b/kernel/read_eip.s
@@ -0,0 +1,5 @@
+.intel_syntax noprefix
+.global read_eip
+read_eip:
+ pop eax
+ jmp eax
diff --git a/kernel/scalls/accept.c b/kernel/scalls/accept.c
new file mode 100644
index 0000000..3c3f5ad
--- /dev/null
+++ b/kernel/scalls/accept.c
@@ -0,0 +1,5 @@
+#include "accept.h"
+
+int syscall_accept(SYS_ACCEPT_PARAMS *args) {
+ return accept(args->socket, args->address, args->address_len);
+}
diff --git a/kernel/scalls/accept.h b/kernel/scalls/accept.h
new file mode 100644
index 0000000..d022999
--- /dev/null
+++ b/kernel/scalls/accept.h
@@ -0,0 +1,9 @@
+#include "../socket.h"
+
+typedef struct SYS_ACCEPT_PARAMS {
+ int socket;
+ struct sockaddr *address;
+ socklen_t *address_len;
+} __attribute__((packed)) SYS_ACCEPT_PARAMS;
+
+int syscall_accept(SYS_ACCEPT_PARAMS *args);
diff --git a/kernel/scalls/bind.c b/kernel/scalls/bind.c
new file mode 100644
index 0000000..76e36ab
--- /dev/null
+++ b/kernel/scalls/bind.c
@@ -0,0 +1,5 @@
+#include "bind.h"
+
+int syscall_bind(SYS_BIND_PARAMS *args) {
+ return bind(args->sockfd, args->addr, args->addrlen);
+}
diff --git a/kernel/scalls/bind.h b/kernel/scalls/bind.h
new file mode 100644
index 0000000..5661ad0
--- /dev/null
+++ b/kernel/scalls/bind.h
@@ -0,0 +1,9 @@
+#include "../socket.h"
+
+typedef struct SYS_BIND_PARAMS {
+ int sockfd;
+ const struct sockaddr *addr;
+ socklen_t addrlen;
+} __attribute__((packed)) SYS_BIND_PARAMS;
+
+int syscall_bind(SYS_BIND_PARAMS *args);
diff --git a/kernel/scalls/clock_gettime.c b/kernel/scalls/clock_gettime.c
new file mode 100644
index 0000000..632ea08
--- /dev/null
+++ b/kernel/scalls/clock_gettime.c
@@ -0,0 +1,10 @@
+#include <scalls/clock_gettime.h>
+
+int syscall_clock_gettime(SYS_CLOCK_GETTIME_PARAMS *args) {
+ // FIXME: Actually implement this
+ if (args->ts) {
+ args->ts->tv_sec = 0;
+ args->ts->tv_nsec = 0;
+ }
+ return 0;
+}
diff --git a/kernel/scalls/clock_gettime.h b/kernel/scalls/clock_gettime.h
new file mode 100644
index 0000000..145aa24
--- /dev/null
+++ b/kernel/scalls/clock_gettime.h
@@ -0,0 +1,8 @@
+#include <time.h>
+
+typedef struct SYS_CLOCK_GETTIME_PARAMS {
+ clockid_t clk;
+ struct timespec *ts;
+} __attribute__((packed)) SYS_CLOCK_GETTIME_PARAMS;
+
+int syscall_clock_gettime(SYS_CLOCK_GETTIME_PARAMS *args);
diff --git a/kernel/scalls/ftruncate.c b/kernel/scalls/ftruncate.c
new file mode 100644
index 0000000..6bc1170
--- /dev/null
+++ b/kernel/scalls/ftruncate.c
@@ -0,0 +1,6 @@
+#include <fs/vfs.h>
+#include <scalls/ftruncate.h>
+
+int syscall_ftruncate(int fd, size_t length) {
+ return vfs_ftruncate(fd, length);
+}
diff --git a/kernel/scalls/ftruncate.h b/kernel/scalls/ftruncate.h
new file mode 100644
index 0000000..9213e85
--- /dev/null
+++ b/kernel/scalls/ftruncate.h
@@ -0,0 +1,2 @@
+#include <stddef.h>
+int syscall_ftruncate(int fd, size_t length);
diff --git a/kernel/scalls/kill.c b/kernel/scalls/kill.c
new file mode 100644
index 0000000..d5b7445
--- /dev/null
+++ b/kernel/scalls/kill.c
@@ -0,0 +1,5 @@
+#include <scalls/kill.h>
+#include <sched/scheduler.h>
+#include <signal.h>
+
+int syscall_kill(pid_t pid, int sig) {return kill(pid, sig);}
diff --git a/kernel/scalls/kill.h b/kernel/scalls/kill.h
new file mode 100644
index 0000000..acf5f30
--- /dev/null
+++ b/kernel/scalls/kill.h
@@ -0,0 +1,2 @@
+#include <signal.h>
+int syscall_kill(pid_t pid, int sig);
diff --git a/kernel/scalls/mkdir.c b/kernel/scalls/mkdir.c
new file mode 100644
index 0000000..43f2424
--- /dev/null
+++ b/kernel/scalls/mkdir.c
@@ -0,0 +1,5 @@
+#include <scalls/mkdir.h>
+
+int syscall_mkdir(const char *path, int mode) {
+ return vfs_mkdir(path, mode);
+}
diff --git a/kernel/scalls/mkdir.h b/kernel/scalls/mkdir.h
new file mode 100644
index 0000000..0bf0043
--- /dev/null
+++ b/kernel/scalls/mkdir.h
@@ -0,0 +1,2 @@
+#include <fs/vfs.h>
+int syscall_mkdir(const char *path, int mode);
diff --git a/kernel/scalls/mmap.c b/kernel/scalls/mmap.c
new file mode 100644
index 0000000..83fff6a
--- /dev/null
+++ b/kernel/scalls/mmap.c
@@ -0,0 +1,7 @@
+#include <scalls/mmap.h>
+#include <sched/scheduler.h>
+
+void *syscall_mmap(SYS_MMAP_PARAMS *args) {
+ return mmap(args->addr, args->length, args->prot, args->flags, args->fd,
+ args->offset);
+}
diff --git a/kernel/scalls/mmap.h b/kernel/scalls/mmap.h
new file mode 100644
index 0000000..f5e121e
--- /dev/null
+++ b/kernel/scalls/mmap.h
@@ -0,0 +1,13 @@
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct SYS_MMAP_PARAMS {
+ void *addr;
+ size_t length;
+ int prot;
+ int flags;
+ int fd;
+ size_t offset;
+} __attribute__((packed)) SYS_MMAP_PARAMS;
+
+void *syscall_mmap(SYS_MMAP_PARAMS *args);
diff --git a/kernel/scalls/msleep.c b/kernel/scalls/msleep.c
new file mode 100644
index 0000000..0120f08
--- /dev/null
+++ b/kernel/scalls/msleep.c
@@ -0,0 +1,9 @@
+#include <drivers/pit.h>
+#include <scalls/msleep.h>
+#include <sched/scheduler.h>
+#include <stdio.h>
+
+void syscall_msleep(uint32_t ms) {
+ get_current_task()->sleep_until = pit_num_ms() + ms;
+ switch_task();
+}
diff --git a/kernel/scalls/msleep.h b/kernel/scalls/msleep.h
new file mode 100644
index 0000000..71bf269
--- /dev/null
+++ b/kernel/scalls/msleep.h
@@ -0,0 +1,5 @@
+#ifndef MSLEEP_H
+#define MSLEEP_H
+#include <stdint.h>
+void syscall_msleep(uint32_t ms);
+#endif
diff --git a/kernel/scalls/ppoll.c b/kernel/scalls/ppoll.c
new file mode 100644
index 0000000..8feb35c
--- /dev/null
+++ b/kernel/scalls/ppoll.c
@@ -0,0 +1,11 @@
+#include <scalls/ppoll.h>
+#include <fs/vfs.h>
+#include <poll.h>
+#include <sched/scheduler.h>
+
+int syscall_poll(SYS_POLL_PARAMS *args) {
+ struct pollfd *fds = args->fds;
+ size_t nfds = args->nfds;
+ int timeout = args->timeout;
+ return poll(fds, nfds, timeout);
+}
diff --git a/kernel/scalls/ppoll.h b/kernel/scalls/ppoll.h
new file mode 100644
index 0000000..13700b5
--- /dev/null
+++ b/kernel/scalls/ppoll.h
@@ -0,0 +1,9 @@
+#include <stddef.h>
+
+typedef struct SYS_POLL_PARAMS {
+ struct pollfd *fds;
+ size_t nfds;
+ int timeout;
+} __attribute__((packed)) SYS_POLL_PARAMS;
+
+int syscall_poll(SYS_POLL_PARAMS *args);
diff --git a/kernel/scalls/recvfrom.c b/kernel/scalls/recvfrom.c
new file mode 100644
index 0000000..1770fa1
--- /dev/null
+++ b/kernel/scalls/recvfrom.c
@@ -0,0 +1,59 @@
+#include <assert.h>
+#include <fs/vfs.h>
+#include <math.h>
+#include <poll.h>
+#include <scalls/recvfrom.h>
+
+size_t syscall_recvfrom(
+ int socket, void *buffer, size_t length, int flags,
+ struct two_args
+ *extra_args /*struct sockaddr *address, socklen_t *address_len*/) {
+
+ struct sockaddr *address = (struct sockaddr *)extra_args->a;
+ socklen_t *address_len = (socklen_t *)extra_args->b;
+ kprintf("address: %x\n", address);
+ kprintf("address_len: %x\n", address_len);
+
+ if (flags & MSG_WAITALL) {
+ struct pollfd fds[1];
+ fds[0].fd = socket;
+ fds[0].events = POLLIN;
+ poll(fds, 1, 0);
+ }
+
+ uint16_t data_length;
+ socklen_t tmp_socklen;
+ vfs_pread(socket, &tmp_socklen, sizeof(socklen_t), 0);
+ if (address_len)
+ *address_len = tmp_socklen;
+ if (address) {
+ vfs_pread(socket, address, tmp_socklen, 0);
+ } else {
+ // We still have to throwaway the data.
+ char devnull[100];
+ for (; tmp_socklen;) {
+ int rc = vfs_pread(socket, devnull, min(tmp_socklen, 100), 0);
+ assert(rc >= 0);
+ tmp_socklen -= rc;
+ }
+ }
+
+ vfs_pread(socket, &data_length, sizeof(data_length), 0);
+ // If it is reading less than the packet length that could cause
+ // problems as the next read will not be put at a new header. Luckily
+ // it seems as if other UNIX systems can discard the rest of the
+ // packet if not read.
+
+ // Read in the data requested
+ int read_len = min(length, data_length);
+ int rc = vfs_pread(socket, buffer, read_len, 0);
+ // Discard the rest of the packet
+ int rest = data_length - read_len;
+ char devnull[100];
+ for (; rest;) {
+ int rc = vfs_pread(socket, devnull, 100, 0);
+ assert(rc >= 0);
+ rest -= rc;
+ }
+ return rc;
+}
diff --git a/kernel/scalls/recvfrom.h b/kernel/scalls/recvfrom.h
new file mode 100644
index 0000000..d81a1e0
--- /dev/null
+++ b/kernel/scalls/recvfrom.h
@@ -0,0 +1,11 @@
+#include <socket.h>
+
+struct two_args {
+ uint32_t a;
+ uint32_t b;
+};
+
+size_t syscall_recvfrom(
+ int socket, void *buffer, size_t length, int flags,
+ struct two_args
+ *extra_args /*struct sockaddr *address, socklen_t *address_len*/);
diff --git a/kernel/scalls/sendto.c b/kernel/scalls/sendto.c
new file mode 100644
index 0000000..48c4020
--- /dev/null
+++ b/kernel/scalls/sendto.c
@@ -0,0 +1,23 @@
+#include <assert.h>
+#include <network/bytes.h>
+#include <network/udp.h>
+#include <scalls/sendto.h>
+
+size_t syscall_sendto(int socket, const void *message, size_t length,
+ int flags, struct t_two_args *extra_args /*
+ const struct sockaddr *dest_addr,
+ socklen_t dest_len*/) {
+ const struct sockaddr *dest_addr = (const struct sockaddr *)extra_args->a;
+ socklen_t dest_len = (socklen_t)extra_args->b;
+ (void)dest_len;
+ vfs_fd_t *fd = get_vfs_fd(socket);
+ assert(fd);
+ SOCKET *s = (SOCKET *)fd->inode->internal_object;
+ OPEN_INET_SOCKET *inet = s->child;
+ assert(inet);
+ struct sockaddr_in in;
+ in.sin_addr.s_addr = inet->address;
+ in.sin_port = inet->port;
+ send_udp_packet(&in, (const struct sockaddr_in *)dest_addr, message, length);
+ return length; // FIXME: This is probably not true.
+}
diff --git a/kernel/scalls/sendto.h b/kernel/scalls/sendto.h
new file mode 100644
index 0000000..0f852de
--- /dev/null
+++ b/kernel/scalls/sendto.h
@@ -0,0 +1,11 @@
+#include <socket.h>
+#include <stdint.h>
+
+struct t_two_args {
+ uint32_t a;
+ uint32_t b;
+};
+size_t syscall_sendto(int socket, const void *message, size_t length,
+ int flags, struct t_two_args *extra_args /*
+ const struct sockaddr *dest_addr,
+ socklen_t dest_len*/);
diff --git a/kernel/scalls/shm.c b/kernel/scalls/shm.c
new file mode 100644
index 0000000..979084a
--- /dev/null
+++ b/kernel/scalls/shm.c
@@ -0,0 +1,6 @@
+#include "shm.h"
+#include "../fs/shm.h"
+
+int syscall_shm_open(SYS_SHM_OPEN_PARAMS *args) {
+ return shm_open(args->name, args->oflag, args->mode);
+}
diff --git a/kernel/scalls/shm.h b/kernel/scalls/shm.h
new file mode 100644
index 0000000..80e4366
--- /dev/null
+++ b/kernel/scalls/shm.h
@@ -0,0 +1,14 @@
+#ifndef SYS_SHM_H
+#define SYS_SHM_H
+#include <stddef.h>
+#include <stdint.h>
+typedef int mode_t;
+
+typedef struct SYS_SHM_OPEN_PARAMS {
+ const char *name;
+ int oflag;
+ mode_t mode;
+} __attribute__((packed)) SYS_SHM_OPEN_PARAMS;
+
+int syscall_shm_open(SYS_SHM_OPEN_PARAMS *args);
+#endif
diff --git a/kernel/scalls/socket.c b/kernel/scalls/socket.c
new file mode 100644
index 0000000..594c745
--- /dev/null
+++ b/kernel/scalls/socket.c
@@ -0,0 +1,5 @@
+#include "socket.h"
+
+int syscall_socket(SYS_SOCKET_PARAMS *args) {
+ return socket(args->domain, args->type, args->protocol);
+}
diff --git a/kernel/scalls/socket.h b/kernel/scalls/socket.h
new file mode 100644
index 0000000..6540b0f
--- /dev/null
+++ b/kernel/scalls/socket.h
@@ -0,0 +1,9 @@
+#include "../socket.h"
+
+typedef struct SYS_SOCKET_PARAMS {
+ int domain;
+ int type;
+ int protocol;
+} __attribute__((packed)) SYS_SOCKET_PARAMS;
+
+int syscall_socket(SYS_SOCKET_PARAMS *args);
diff --git a/kernel/scalls/stat.c b/kernel/scalls/stat.c
new file mode 100644
index 0000000..0850151
--- /dev/null
+++ b/kernel/scalls/stat.c
@@ -0,0 +1,13 @@
+#include <errno.h>
+#include <fs/vfs.h>
+#include <scalls/stat.h>
+
+int syscall_stat(SYS_STAT_PARAMS *args) {
+ const char *pathname = copy_and_allocate_user_string(args->pathname);
+ struct stat *statbuf = args->statbuf;
+ vfs_inode_t *i = vfs_internal_open(pathname);
+ if (!i)
+ return -ENOENT;
+ statbuf->st_size = i->file_size;
+ return 0;
+}
diff --git a/kernel/scalls/stat.h b/kernel/scalls/stat.h
new file mode 100644
index 0000000..78e8c45
--- /dev/null
+++ b/kernel/scalls/stat.h
@@ -0,0 +1,33 @@
+#include <types.h>
+#include <time.h>
+
+typedef struct SYS_STAT_PARAMS {
+ const char *pathname;
+ struct stat *statbuf;
+} __attribute__((packed)) SYS_STAT_PARAMS;
+
+struct stat {
+ dev_t st_dev; // Device ID of device containing file.
+ ino_t st_ino; // File serial number.
+ mode_t st_mode; // Mode of file (see below).
+ nlink_t st_nlink; // Number of hard links to the file.
+ uid_t st_uid; // User ID of file.
+ gid_t st_gid; // Group ID of file.
+ dev_t st_rdev; // Device ID (if file is character or block special).
+ off_t st_size; // For regular files, the file size in bytes.
+ // For symbolic links, the length in bytes of the
+ // pathname contained in the symbolic link.
+ // For a shared memory object, the length in bytes.
+ // For a typed memory object, the length in bytes.
+ // For other file types, the use of this field is
+ // unspecified.
+ struct timespec st_atim; // Last data access timestamp.
+ struct timespec st_mtim; // Last data modification timestamp.
+ struct timespec st_ctim; // Last file status change timestamp.
+ blksize_t st_blksize; // A file system-specific preferred I/O block size
+ // for this object. In some file system types, this
+ // may vary from file to file.
+ blkcnt_t st_blocks; // Number of blocks allocated for this object.
+};
+
+int syscall_stat(SYS_STAT_PARAMS *args);
diff --git a/kernel/scalls/uptime.c b/kernel/scalls/uptime.c
new file mode 100644
index 0000000..866c7e5
--- /dev/null
+++ b/kernel/scalls/uptime.c
@@ -0,0 +1,4 @@
+#include <scalls/uptime.h>
+#include <drivers/pit.h>
+
+uint32_t syscall_uptime(void) { return (uint32_t)pit_num_ms(); }
diff --git a/kernel/scalls/uptime.h b/kernel/scalls/uptime.h
new file mode 100644
index 0000000..2b5b0c9
--- /dev/null
+++ b/kernel/scalls/uptime.h
@@ -0,0 +1,2 @@
+#include <stdint.h>
+uint32_t syscall_uptime(void);
diff --git a/kernel/sched/scheduler.c b/kernel/sched/scheduler.c
new file mode 100644
index 0000000..a6ace7d
--- /dev/null
+++ b/kernel/sched/scheduler.c
@@ -0,0 +1,449 @@
+#include <assert.h>
+#include <cpu/io.h>
+#include <defs.h>
+#include <drivers/pit.h>
+#include <elf.h>
+#include <errno.h>
+#include <fs/vfs.h>
+
+#define STACK_LOCATION ((void *)0x90000000)
+#define STACK_SIZE 0x80000
+#define HEAP_SIZE 0x400000
+#define HEAP_LOCATION (STACK_LOCATION - STACK_SIZE - HEAP_SIZE)
+
+process_t *ready_queue;
+process_t *current_task = NULL;
+uint32_t next_pid = 0;
+
+extern uint32_t read_eip(void);
+
+process_t *get_current_task(void) { return current_task; }
+
+void set_signal_handler(int sig, void (*handler)(int)) {
+ if (sig >= 20 || sig < 0)
+ return;
+ if (9 == sig)
+ return;
+ current_task->signal_handlers[sig] = handler;
+}
+
+process_t *create_process(process_t *p) {
+ process_t *r;
+ r = ksbrk(sizeof(process_t));
+ r->dead = 0;
+ r->pid = next_pid++;
+ r->esp = r->ebp = 0;
+ r->eip = 0;
+ r->sleep_until = 0;
+ if (!p) {
+ assert(1 == next_pid);
+ strncpy(r->program_name, "[kernel]", sizeof(current_task->program_name));
+ } else
+ strncpy(r->program_name, "[Not yet named]",
+ sizeof(current_task->program_name));
+
+ r->cr3 = (p) ? clone_directory(get_active_pagedirectory())
+ : get_active_pagedirectory();
+ r->next = 0;
+ r->incoming_signal = 0;
+ r->parent = p;
+ r->child = NULL;
+ r->halt_list = NULL;
+
+ mmu_allocate_region((void *)(0x80000000 - 0x1000), 0x1000, MMU_FLAG_RW,
+ r->cr3);
+ r->signal_handler_stack = 0x80000000;
+
+ strcpy(r->current_working_directory, "/");
+ r->data_segment_end = (p) ? p->data_segment_end : NULL;
+ memset((void *)r->halts, 0, 2 * sizeof(uint32_t));
+ for (int i = 0; i < 100; i++) {
+ if (p) {
+ r->file_descriptors[i] = p->file_descriptors[i];
+ if (r->file_descriptors[i]) {
+ r->file_descriptors[i]->reference_count++;
+ }
+ }
+ if (i < 20)
+ r->signal_handlers[i] = NULL;
+ r->read_halt_inode[i] = NULL;
+ r->write_halt_inode[i] = NULL;
+ r->disconnect_halt_inode[i] = NULL;
+ r->maps[i] = NULL;
+ }
+ return r;
+}
+
+int get_free_fd(process_t *p, int allocate) {
+ if (!p)
+ p = (process_t *)current_task;
+ int i;
+ for (i = 0; i < 100; i++)
+ if (!p->file_descriptors[i])
+ break;
+ if (p->file_descriptors[i])
+ return -1;
+ if (allocate) {
+ vfs_fd_t *fd = p->file_descriptors[i] = kmalloc(sizeof(vfs_fd_t));
+ fd->inode = kmalloc(sizeof(vfs_inode_t));
+ }
+ return i;
+}
+
+void tasking_init(void) { current_task = ready_queue = create_process(NULL); }
+
+int i = 0;
+void __attribute__((optimize("O0"))) free_process(void) {
+ kprintf("Exiting process: %s\n", get_current_task()->program_name);
+ // free_process() will purge all contents such as allocated frames
+ // out of the current process. This will be called by exit() and
+ // exec*().
+
+ // Do a special free for shared memory which avoids labeling
+ // underlying frames as "unused".
+ for (int i = 0; i < 100; i++) {
+ vfs_close(i);
+ if (!current_task->maps[i])
+ continue;
+ MemoryMap *m = current_task->maps[i];
+ mmu_remove_virtual_physical_address_mapping(m->u_address, m->length);
+ }
+
+ // NOTE: Kernel stuff begins at 0x90000000
+ mmu_free_address_range((void *)0x1000, 0x90000000);
+}
+
+void exit(int status) {
+ assert(current_task->pid != 1);
+ if (current_task->parent) {
+ current_task->parent->halts[WAIT_CHILD_HALT] = 0;
+ current_task->parent->child_rc = status;
+ }
+ process_t *new_task = current_task;
+ for (; new_task == current_task;) {
+ if (!new_task->next)
+ new_task = ready_queue;
+ new_task = new_task->next;
+ }
+
+ free_process();
+ // Remove current_task from list
+ for (process_t *tmp = ready_queue; tmp;) {
+ if (tmp == current_task) // current_task is ready_queue(TODO:
+ // Figure out whether this could even
+ // happen)
+ {
+ ready_queue = current_task->next;
+ break;
+ }
+ if (tmp->next == current_task) {
+ tmp->next = tmp->next->next;
+ break;
+ }
+ tmp = tmp->next;
+ }
+ current_task->dead = 1;
+ // This function will enable interrupts
+ switch_task();
+}
+
+uint32_t setup_stack(uint32_t stack_pointer, int argc, char **argv) {
+ mmu_allocate_region(STACK_LOCATION - STACK_SIZE, STACK_SIZE, MMU_FLAG_RW,
+ NULL);
+ flush_tlb();
+
+ uint32_t ptr = stack_pointer;
+
+ char *argv_ptrs[argc + 1];
+ for (int i = 0; i < argc; i++) {
+ char *s = argv[i];
+ size_t l = strlen(s);
+ ptr -= l + 1;
+ char *b = (char *)ptr;
+ memcpy(b, s, l);
+ b[l] = '\0';
+ argv_ptrs[i] = b;
+ }
+
+ char **ptrs[argc + 1];
+ for (int i = argc; i >= 0; i--) {
+ ptr -= sizeof(char *);
+ ptrs[i] = (char **)ptr;
+ if (i != argc) {
+ *(ptrs[i]) = argv_ptrs[i];
+ } else {
+ *(ptrs[i]) = NULL;
+ }
+ }
+
+ char *s = (char *)ptr;
+ ptr -= sizeof(char **);
+ *(char ***)ptr = (char **)s;
+
+ ptr -= sizeof(int);
+ *(int *)ptr = argc;
+ return ptr;
+}
+
+int exec(const char *filename, char **argv) {
+ // exec() will "takeover" the process by loading the file specified in
+ // filename into memory, change from ring 0 to ring 3 and jump to the
+ // files entry point as decided by the ELF header of the file.
+ int argc = 0;
+ for (; argv[argc];) {
+ argc++;
+ }
+
+ uint32_t end_of_code;
+ void *entry = load_elf_file(filename, &end_of_code);
+ if (!entry) {
+ return 0;
+ }
+
+ strncpy(current_task->program_name, filename,
+ sizeof(current_task->program_name));
+
+ current_task->data_segment_end = align_page((void *)end_of_code);
+
+ uint32_t ptr = setup_stack(0x90000000, argc, argv);
+
+ jump_usermode((void (*)())(entry), ptr);
+ ASSERT_NOT_REACHED;
+ return 0;
+}
+
+int __attribute__((optimize("O0"))) fork(void) {
+ process_t *parent_task = (process_t *)current_task;
+
+ process_t *new_task = create_process(parent_task);
+
+ process_t *tmp_task = (process_t *)ready_queue;
+ for (; tmp_task->next;)
+ tmp_task = tmp_task->next;
+
+ tmp_task->next = new_task;
+
+ uint32_t eip = read_eip();
+
+ if (current_task != parent_task) {
+ return 0;
+ }
+
+ new_task->child_rc = -1;
+ new_task->parent = current_task;
+ current_task->child = new_task;
+
+ new_task->eip = eip;
+ asm("\
+ mov %%esp, %0;\
+ mov %%ebp, %1;"
+ : "=r"(new_task->esp), "=r"(new_task->ebp));
+ asm("sti");
+ return new_task->pid;
+}
+
+int is_halted(process_t *process) {
+ for (int i = 0; i < 2; i++)
+ if (process->halts[i])
+ return 1;
+
+ if (isset_fdhalt(process->read_halt_inode, process->write_halt_inode,
+ process->disconnect_halt_inode)) {
+ return 1;
+ }
+ return 0;
+}
+
+extern PageDirectory *active_directory;
+
+process_t *next_task(process_t *c) {
+ for (;;) {
+ c = c->next;
+ if (!c)
+ c = ready_queue;
+ if (c->incoming_signal)
+ break;
+ if (c->sleep_until > pit_num_ms())
+ continue;
+ if (is_halted(c) || c->dead)
+ continue;
+ break;
+ }
+ return c;
+}
+
+int task_save_state(void) {
+ asm("mov %%esp, %0" : "=r"(current_task->esp));
+ asm("mov %%ebp, %0" : "=r"(current_task->ebp));
+
+ uint32_t eip = read_eip();
+
+ if (0x1 == eip) {
+ // Should the returned value from read_eip be equal to one it
+ // means that we have just switched over to this task after we
+ // saved the state(since the switch_task() function changes the
+ // eax register to 1).
+ return 0;
+ }
+
+ current_task->eip = eip;
+ return 1;
+}
+
+int kill(pid_t pid, int sig) {
+ process_t *p = current_task;
+ p = p->next;
+ if (!p)
+ p = ready_queue;
+ for (; p->pid != pid;) {
+ if (p == current_task)
+ break;
+ p = p->next;
+ if (!p)
+ p = ready_queue;
+ }
+ if (p->pid != pid)
+ return -ESRCH;
+ p->incoming_signal = sig;
+ return 0;
+}
+
+void jump_signal_handler(void *func, uint32_t esp);
+void switch_task() {
+ if (!current_task)
+ return;
+
+ if (0 == task_save_state()) {
+ return;
+ }
+
+ current_task = next_task((process_t *)current_task);
+
+ active_directory = current_task->cr3;
+
+ if (current_task->incoming_signal) {
+ uint8_t sig = current_task->incoming_signal;
+ current_task->incoming_signal = 0;
+ asm("mov %0, %%cr3" ::"r"(current_task->cr3->physical_address));
+
+ void *handler = current_task->signal_handlers[sig];
+ if (9 == sig) {
+ klog("Task recieved SIGKILL", LOG_NOTE);
+ exit(0);
+ }
+ if (!handler) {
+ klog("Task recieved unhandeled signal. Killing process.", LOG_WARN);
+ exit(1);
+ }
+ jump_signal_handler(handler, current_task->signal_handler_stack);
+ } else {
+ asm(" \
+ mov %0, %%esp; \
+ mov %1, %%ebp; \
+ mov %2, %%ecx; \
+ mov %3, %%cr3; \
+ mov $0x1, %%eax; \
+ jmp *%%ecx" ::"r"(current_task->esp),
+ "r"(current_task->ebp), "r"(current_task->eip),
+ "r"(current_task->cr3->physical_address));
+ }
+}
+
+MemoryMap **get_free_map(void) {
+ for (int i = 0; i < 100; i++)
+ if (!(current_task->maps[i]))
+ return &(current_task->maps[i]);
+ assert(0);
+ return NULL;
+}
+
+void *get_free_virtual_memory(size_t length) {
+ void *n =
+ (void *)((uintptr_t)(get_current_task()->data_segment_end) + length);
+
+ void *rc = get_current_task()->data_segment_end;
+ get_current_task()->data_segment_end = align_page(n);
+ return rc;
+}
+
+void *allocate_virtual_user_memory(size_t length, int prot, int flags) {
+ (void)prot;
+ (void)flags;
+ void *rc = get_free_virtual_memory(length);
+ if ((void *)-1 == rc)
+ return (void *)-1;
+
+ mmu_allocate_region(rc, length, MMU_FLAG_RW, NULL);
+ return rc;
+}
+
+void *user_kernel_mapping(void *kernel_addr, size_t length) {
+ void *rc = get_free_virtual_memory(length);
+ if ((void *)-1 == rc)
+ return (void *)-1;
+
+ mmu_map_directories(rc, NULL, kernel_addr, NULL, length);
+ return rc;
+}
+
+void *create_physical_mapping(void **physical_addresses, size_t length) {
+ void *rc = get_free_virtual_memory(length);
+ if ((void *)-1 == rc)
+ return (void *)-1;
+ int n = (uintptr_t)align_page((void *)length) / 0x1000;
+ for (int i = 0; i < n; i++) {
+ mmu_map_physical(rc + (i * 0x1000), NULL, physical_addresses[i], 0x1000);
+ }
+ return rc;
+}
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ size_t offset) {
+ (void)addr;
+ if (0 == length) {
+ kprintf("EINVAL\n");
+ return (void *)-EINVAL;
+ }
+
+ MemoryMap **ptr = get_free_map();
+ if (!ptr) {
+ klog("mmap(): No free memory map.", LOG_WARN);
+ return (void *)-1;
+ }
+ *ptr = kmalloc(sizeof(MemoryMap));
+ MemoryMap *free_map = *ptr;
+
+ if (fd == -1) {
+ void *rc = allocate_virtual_user_memory(length, prot, flags);
+ if ((void *)-1 == rc) {
+ kprintf("ENOMEM\n");
+ return (void *)-ENOMEM;
+ }
+ free_map->u_address = rc;
+ free_map->k_address = NULL;
+ free_map->length = length;
+ free_map->fd = -1;
+ return rc;
+ }
+
+ vfs_vm_object_t *vmobject = vfs_get_vm_object(fd, length, offset);
+ if (!vmobject) {
+ kprintf("ENODEV\n");
+ return (void *)-ENODEV;
+ }
+
+ if (vmobject->size < length) {
+ kprintf("EOVERFLOW\n");
+ return (void *)-EOVERFLOW; // TODO: Check if this is the correct
+ // code.
+ }
+
+ if (length > vmobject->size)
+ length = vmobject->size;
+ void *rc = create_physical_mapping(vmobject->object, length);
+ free_map->u_address = rc;
+ free_map->k_address = NULL;
+ free_map->length = length;
+ free_map->fd = fd;
+ return rc;
+}
diff --git a/kernel/sched/scheduler.h b/kernel/sched/scheduler.h
new file mode 100644
index 0000000..fc92ff3
--- /dev/null
+++ b/kernel/sched/scheduler.h
@@ -0,0 +1,65 @@
+#ifndef SCHEDULER_H
+#define SCHEDULER_H
+#include <fs/ext2.h>
+#include <fs/vfs.h>
+#include <halts.h>
+#include <mmu.h>
+#include <signal.h>
+
+#define MAX_PATH 256
+#define KEYBOARD_HALT 0
+#define WAIT_CHILD_HALT 1
+
+int fork(void);
+int exec(const char *filename, char **argv);
+void switch_task();
+void tasking_init(void);
+void exit(int status);
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ size_t offset);
+int munmap(void *addr, size_t length);
+int msync(void *addr, size_t length, int flags);
+int kill(pid_t pid, int sig);
+void set_signal_handler(int sig, void (*handler)(int));
+
+typedef struct {
+ void *u_address;
+ void *k_address;
+ uint32_t length;
+ int fd;
+} MemoryMap;
+
+typedef struct Process process_t;
+
+struct Process {
+ uint32_t pid;
+ char program_name[100];
+ char current_working_directory[MAX_PATH];
+ uint32_t eip, esp, ebp;
+ uint8_t incoming_signal;
+ uint32_t signal_handler_stack;
+ void *signal_handlers[20];
+ PageDirectory *cr3;
+ vfs_fd_t *file_descriptors[100];
+ vfs_inode_t *read_halt_inode[100];
+ vfs_inode_t *write_halt_inode[100];
+ vfs_inode_t *disconnect_halt_inode[100];
+ uint32_t halts[2];
+ struct Halt *halt_list;
+ void *data_segment_end;
+ process_t *next;
+ process_t *parent;
+ // TODO: Create a linkedlist of childs so that the parent process
+ // can do stuff such as reap zombies and get status.
+ process_t *child;
+ MemoryMap *maps[100];
+ uint32_t sleep_until;
+ int child_rc;
+ int dead;
+};
+
+process_t *get_current_task(void);
+int get_free_fd(process_t *p, int allocate);
+void free_process(void);
+#endif
diff --git a/kernel/signal.s b/kernel/signal.s
new file mode 100644
index 0000000..38604a4
--- /dev/null
+++ b/kernel/signal.s
@@ -0,0 +1,18 @@
+.intel_syntax noprefix
+.global jump_signal_handler
+jump_signal_handler:
+ mov ebx, [esp+8]
+ mov ecx, [esp+4]
+ mov ax, (4 * 8) | 3 # ring 3 data with bottom 2 bits set for ring 3
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax # SS is handled by iret
+
+ mov eax, ebx
+ push (4 * 8) | 3 # data selector
+ push eax
+ pushf
+ push (3 * 8) | 3
+ push ecx
+ iret
diff --git a/kernel/socket.c b/kernel/socket.c
new file mode 100644
index 0000000..0519c65
--- /dev/null
+++ b/kernel/socket.c
@@ -0,0 +1,180 @@
+#include <assert.h>
+#include <errno.h>
+#include <fs/devfs.h>
+#include <fs/tmpfs.h>
+#include <poll.h>
+#include <sched/scheduler.h>
+#include <socket.h>
+
+// FIXME: Make these more dynamic
+OPEN_UNIX_SOCKET *un_sockets[100] = {0};
+OPEN_INET_SOCKET *inet_sockets[100] = {0};
+
+OPEN_INET_SOCKET *find_open_udp_port(uint16_t port) {
+ for (int i = 0; i < 100; i++) {
+ if (!inet_sockets[i])
+ continue;
+ if (inet_sockets[i]->port == port)
+ return inet_sockets[i];
+ }
+ return NULL;
+}
+
+int uds_open(const char *path) {
+ // FIXME: This is super ugly
+
+ // Find the socket that path belongs to
+ SOCKET *s = NULL;
+ for (int i = 0; i < 100; i++) {
+ if (!un_sockets[i])
+ continue;
+ const char *p = path;
+ const char *e = p;
+ for (; *e; e++)
+ ;
+ for (; e != p && *e != '/'; e--)
+ ;
+ if (0 == strcmp(e, un_sockets[i]->path)) {
+ s = un_sockets[i]->s;
+ break;
+ }
+ }
+ if (!s) {
+ return -1;
+ }
+
+ // Create a pipe
+ int fd[2];
+ dual_pipe(fd);
+
+ char c = 'i';
+ fifo_object_write((uint8_t *)&c, 1, 0, s->fifo_file);
+ s->ptr_socket_fd->inode->has_data = 1;
+
+ s->incoming_fd = get_current_task()->file_descriptors[fd[1]];
+ // vfs_close(fd[1]);
+ return fd[0];
+}
+
+int accept(int socket, struct sockaddr *address, socklen_t *address_len) {
+ (void)address;
+ (void)address_len;
+ vfs_inode_t *inode = get_current_task()->file_descriptors[socket]->inode;
+ SOCKET *s = (SOCKET *)inode->internal_object;
+
+ if (NULL == s->incoming_fd) {
+ // Wait until we have gotten a connection
+ struct pollfd fds[1];
+ fds[0].fd = socket;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ poll(fds, 1, 0);
+ }
+
+ int n = 0;
+ for (; get_current_task()->file_descriptors[n]; n++)
+ ;
+ get_current_task()->file_descriptors[n] = s->incoming_fd;
+ get_current_task()->file_descriptors[n]->reference_count++;
+ s->incoming_fd = NULL;
+ // for (char c; 0 < vfs_pread(s->fifo_fd, &c, 1, 0);)
+ // ;
+ inode->has_data = 0;
+ // s->ptr_fifo_fd->inode->has_data = 0;
+
+ return n;
+}
+
+int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
+ (void)addrlen;
+ vfs_fd_t *fd = get_vfs_fd(sockfd);
+ if (!fd)
+ return -EBADF;
+ vfs_inode_t *inode = fd->inode;
+ if (!inode)
+ return -EBADF;
+ SOCKET *s = (SOCKET *)inode->internal_object;
+ if (AF_UNIX == s->domain) {
+ struct sockaddr_un *un = (struct sockaddr_un *)addr;
+ size_t path_len = strlen(un->sun_path);
+ s->path = kmalloc(path_len + 1);
+ memcpy(s->path, un->sun_path, path_len);
+ s->path[path_len] = '\0';
+
+ OPEN_UNIX_SOCKET *us;
+ int i = 0;
+ for (; i < 100; i++)
+ if (!un_sockets[i])
+ break;
+
+ us = un_sockets[i] = kmalloc(sizeof(OPEN_UNIX_SOCKET));
+
+ us->path = s->path;
+ us->s = s;
+ s->child = us;
+ devfs_add_file(us->path, NULL, NULL, NULL, 1, 1, FS_TYPE_UNIX_SOCKET);
+ return 0;
+ }
+ if (AF_INET == s->domain) {
+ struct sockaddr_in *in = (struct sockaddr_in *)addr;
+ assert(in->sin_family == AF_INET); // FIXME: Figure out error value
+ OPEN_INET_SOCKET *inet;
+ int i = 0;
+ for (; i < 100; i++)
+ if (!inet_sockets[i])
+ break;
+
+ inet = inet_sockets[i] = kmalloc(sizeof(OPEN_INET_SOCKET));
+ inet->address = in->sin_addr.s_addr;
+ inet->port = in->sin_port;
+ inet->s = s;
+ s->child = inet;
+ return 0;
+ }
+ return 0;
+}
+
+int socket_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ SOCKET *s = (SOCKET *)fd->inode->internal_object;
+ FIFO_FILE *file = s->fifo_file;
+ int rc = fifo_object_write(buffer, 0, len, file);
+ fd->inode->has_data = file->has_data;
+ return rc;
+}
+
+int socket_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) {
+ SOCKET *s = (SOCKET *)fd->inode->internal_object;
+ FIFO_FILE *file = s->fifo_file;
+ int rc = fifo_object_read(buffer, 0, len, file);
+ fd->inode->has_data = file->has_data;
+ return rc;
+}
+
+void socket_close(vfs_fd_t *fd) { fd->inode->is_open = 0; }
+
+int socket(int domain, int type, int protocol) {
+ if (!(AF_UNIX == domain || AF_INET == domain))
+ return -EINVAL;
+
+ SOCKET *new_socket = kmalloc_eternal(sizeof(SOCKET));
+ vfs_inode_t *inode = vfs_create_inode(
+ 0 /*inode_num*/, FS_TYPE_UNIX_SOCKET, 0 /*has_data*/, 1 /*can_write*/,
+ 1 /*is_open*/, new_socket /*internal_object*/, 0 /*file_size*/,
+ NULL /*open*/, NULL /*create_file*/, socket_read, socket_write,
+ socket_close, NULL /*create_directory*/, NULL /*get_vm_object*/,
+ NULL /*truncate*/);
+
+ vfs_fd_t *fd;
+ int n = vfs_create_fd(O_RDWR | O_NONBLOCK, 0, inode, &fd);
+
+ new_socket->domain = domain;
+ new_socket->type = type;
+ new_socket->protocol = protocol;
+ new_socket->path = NULL;
+ new_socket->incoming_fd = NULL;
+
+ new_socket->fifo_file = create_fifo_object();
+
+ new_socket->ptr_socket_fd = fd;
+ return n;
+}
diff --git a/kernel/socket.h b/kernel/socket.h
new file mode 100644
index 0000000..bb278c1
--- /dev/null
+++ b/kernel/socket.h
@@ -0,0 +1,72 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+#include <fs/fifo.h>
+#include <fs/vfs.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define AF_UNIX 0
+#define AF_INET 1
+#define AF_LOCAL AF_UNIX
+
+#define SOCK_DGRAM 0
+
+#define INADDR_ANY 0
+
+#define MSG_WAITALL 1
+
+typedef struct {
+ vfs_fd_t *ptr_socket_fd;
+ FIFO_FILE *fifo_file;
+
+ int domain;
+ int type;
+ int protocol;
+ void *child;
+
+ // UNIX socket
+ char *path;
+ vfs_fd_t *incoming_fd;
+} SOCKET;
+
+typedef struct {
+ char *path;
+ SOCKET *s;
+} OPEN_UNIX_SOCKET;
+
+typedef struct {
+ uint32_t address;
+ uint16_t port;
+ SOCKET *s;
+} OPEN_INET_SOCKET;
+
+typedef uint32_t in_addr_t;
+typedef uint16_t in_port_t;
+typedef unsigned int sa_family_t;
+typedef int socklen_t;
+
+struct sockaddr {
+ sa_family_t sa_family; /* Address family */
+ char *sa_data; /* Socket address */
+};
+
+struct sockaddr_in {
+ sa_family_t sin_family;
+ union {
+ uint32_t s_addr;
+ uint8_t a[4];
+ } sin_addr;
+ uint16_t sin_port;
+};
+
+struct sockaddr_un {
+ sa_family_t sun_family; /* Address family */
+ char *sun_path; /* Socket pathname */
+};
+
+OPEN_INET_SOCKET *find_open_udp_port(uint16_t port);
+int uds_open(const char *path);
+int socket(int domain, int type, int protocol);
+int accept(int socket, struct sockaddr *address, socklen_t *address_len);
+int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+#endif
diff --git a/kernel/time.h b/kernel/time.h
new file mode 100644
index 0000000..2d4eef6
--- /dev/null
+++ b/kernel/time.h
@@ -0,0 +1,10 @@
+#ifndef TIME_H
+#define TIME_H
+#include <sys/types.h>
+
+typedef int clockid_t;
+struct timespec {
+ time_t tv_sec; // Seconds.
+ long tv_nsec; // Nanoseconds.
+};
+#endif