diff options
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/pid1Binary files differ new file mode 100755 index 0000000..773d54c --- /dev/null +++ b/kernel/mount/pid1 diff --git a/kernel/mount/program b/kernel/mount/programBinary files differ new file mode 100755 index 0000000..c93d7ba --- /dev/null +++ b/kernel/mount/program diff --git a/kernel/mount/sh b/kernel/mount/shBinary files differ new 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 |