diff options
author | Anton Kling <anton@kling.gg> | 2023-10-30 22:12:14 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-10-31 00:18:38 +0100 |
commit | 8a9208612eec8ddae4c418485d848ecfa0613699 (patch) | |
tree | 2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel | |
parent | ca76600acc8bf7a02346efa5bd8f17072210ec01 (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')
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 Binary files differnew file mode 100755 index 0000000..773d54c --- /dev/null +++ b/kernel/mount/pid1 diff --git a/kernel/mount/program b/kernel/mount/program Binary files differnew file mode 100755 index 0000000..c93d7ba --- /dev/null +++ b/kernel/mount/program diff --git a/kernel/mount/sh b/kernel/mount/sh Binary files differnew file mode 100755 index 0000000..c05684c --- /dev/null +++ b/kernel/mount/sh 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 |