From a10aabfcd52f702057316018cd7847ab2bfe4aa1 Mon Sep 17 00:00:00 2001 From: Ada Christine Date: Tue, 26 May 2026 21:32:27 +0000 Subject: we're bringing kjarna back and not doing the crazy stuff with trying to have task management during efi. that was a bit extra. --- api/bits/x86_64/descriptor.h | 30 ++ api/bits/x86_64/msr.h | 20 +- api/lib/kstdio.h | 18 -- api/lib/kstring.h | 11 - api/libc/stdio.h | 20 -- api/libc/stdlib.h | 9 - api/libc/string.h | 12 - api/libc/wchar.h | 6 - api/posix/sys/mman.h | 8 +- boot/Makefile | 4 +- boot/efi/status.h | 110 ------- boot/efi/system_table.h | 31 -- boot/efi/table.h | 26 -- defaults.mk | 2 +- kc/boot/kc_main.c | 2 +- kc/core/Makefile | 10 +- kc/core/cpu/arch_thread.c | 81 +++++ kc/core/cpu/arch_thread.h | 12 + kc/core/cpu/cpu.c | 4 +- kc/core/cpu/cpu_task.S | 54 ---- kc/core/cpu/cpu_thread.S | 57 ++++ kc/core/cpu/exceptions.h | 4 +- kc/core/i8042.c | 18 +- kc/core/kc_main.c | 4 +- kc/core/kstdio.c | 18 -- kc/core/memory.h | 2 +- kc/core/memory/memory.c | 24 +- kc/core/memory/page_early.c | 12 +- kc/core/panic.c | 6 +- kc/core/pic8259.c | 12 +- kc/core/pit8253.c | 6 +- kc/core/stdio.c | 18 ++ kc/core/task.c | 445 +++++++++++++++------------ kc/core/task.h | 35 ++- kc/core/video.c | 6 +- kc/core/vm_tree.c | 556 ++++++++++++++++----------------- kc/defaults.mk | 1 + lib/api/libc/stdio.h | 20 ++ lib/api/libc/stdlib.h | 9 + lib/api/libc/string.h | 14 + lib/api/libc/wchar.h | 6 + lib/elf64.c | 6 +- lib/heap.c | 154 +++++++++- lib/kprintf.c | 653 --------------------------------------- lib/libc/memcmp.c | 16 + lib/libc/memcpy.c | 12 + lib/libc/memmove.c | 20 ++ lib/libc/memset.c | 11 + lib/libc/printf.c | 712 +++++++++++++++++++++++++++++++++++++++++++ lib/libc/stdio.c | 30 ++ lib/libc/string.c | 195 ++++++++++++ lib/memcmp.c | 16 - lib/memcpy.c | 12 - lib/memmove.c | 20 -- lib/memset.c | 11 - lib/printf.c | 648 --------------------------------------- lib/stdio.c | 30 -- lib/string.c | 195 ------------ rules.mk | 11 +- service/Makefile | 4 +- service/config.h | 5 +- service/main.c | 3 + 62 files changed, 2029 insertions(+), 2478 deletions(-) delete mode 100644 api/lib/kstdio.h delete mode 100644 api/lib/kstring.h delete mode 100644 api/libc/stdio.h delete mode 100644 api/libc/stdlib.h delete mode 100644 api/libc/string.h delete mode 100644 api/libc/wchar.h delete mode 100644 boot/efi/status.h delete mode 100644 boot/efi/system_table.h delete mode 100644 boot/efi/table.h create mode 100644 kc/core/cpu/arch_thread.c create mode 100644 kc/core/cpu/arch_thread.h delete mode 100644 kc/core/cpu/cpu_task.S create mode 100644 kc/core/cpu/cpu_thread.S delete mode 100644 kc/core/kstdio.c create mode 100644 kc/core/stdio.c create mode 100644 lib/api/libc/stdio.h create mode 100644 lib/api/libc/stdlib.h create mode 100644 lib/api/libc/string.h create mode 100644 lib/api/libc/wchar.h delete mode 100644 lib/kprintf.c create mode 100644 lib/libc/memcmp.c create mode 100644 lib/libc/memcpy.c create mode 100644 lib/libc/memmove.c create mode 100644 lib/libc/memset.c create mode 100644 lib/libc/printf.c create mode 100644 lib/libc/stdio.c create mode 100644 lib/libc/string.c delete mode 100644 lib/memcmp.c delete mode 100644 lib/memcpy.c delete mode 100644 lib/memmove.c delete mode 100644 lib/memset.c delete mode 100644 lib/printf.c delete mode 100644 lib/stdio.c delete mode 100644 lib/string.c diff --git a/api/bits/x86_64/descriptor.h b/api/bits/x86_64/descriptor.h index c0c00f6..c55a493 100644 --- a/api/bits/x86_64/descriptor.h +++ b/api/bits/x86_64/descriptor.h @@ -1,3 +1,5 @@ +#pragma once + #include struct descriptor_table_register_long @@ -16,3 +18,31 @@ struct segment_descriptor uint8_t base24; } __attribute__((packed, aligned(8))); +struct system_segment_descrptor_long +{ + struct segment_descriptor segment; + uint32_t base32; + uint32_t r0; +} __attribute__((packed, aligned(8))); + +struct gate_descriptor +{ + uint16_t offset0; + uint16_t selector; + uint8_t ist; + uint8_t access; + uint16_t offset16; + uint64_t offset32; +} __attribute__((packed, aligned(8))); + +struct task_state_segment_long +{ + uint32_t r0; + uint64_t rsp[3]; + uint64_t r1; + uint64_t ist[7]; + uint64_t r2; + uint16_t r3; + uint16_t iomap_base; +} __attribute__((packed, aligned(8))); + diff --git a/api/bits/x86_64/msr.h b/api/bits/x86_64/msr.h index a25af7d..95486cc 100644 --- a/api/bits/x86_64/msr.h +++ b/api/bits/x86_64/msr.h @@ -1,13 +1,13 @@ +#pragma once + #include #define MSR_INDEX_EFER 0xc0000080 #define MSR_INDEX_STAR 0xc0000081 #define MSR_INDEX_LSTAR 0xc0000082 - -union msr_efer -{ - uint64_t value; -}; +#define MSR_INDEX_SFMASK 0xc0000084 +#define MSR_INDEX_KERNELGSBASE 0xc0000102 +#define MSR_EFER_SCE 1 union msr_star { @@ -27,3 +27,13 @@ union msr_lstar uint64_t value; }; +union msr_sfmask +{ + struct + { + uint32_t mask; + uint32_t reserved; + }; + uint64_t value; +}; + diff --git a/api/lib/kstdio.h b/api/lib/kstdio.h deleted file mode 100644 index 80e47cc..0000000 --- a/api/lib/kstdio.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -#include - -typedef struct FILE FILE; - -extern FILE *kstdout; -extern FILE *kstderr; - -extern int kfputc(int c, FILE *f); - -int kvfprintf(FILE *f, const char *restrict format, va_list arguments); -int kfprintf(FILE *f, const char *restrict format, ...); -int kvprintf(const char *restrict format, va_list arguments); -int kprintf(const char *restrict format, ...); - diff --git a/api/lib/kstring.h b/api/lib/kstring.h deleted file mode 100644 index d1d45d5..0000000 --- a/api/lib/kstring.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -void *memcpy(void *dest, const void *src, size_t size); -void *memmove(void *dest, const void *src, size_t size); -void *memset(void *dest, int val, size_t size); -int memcmp(const void *str1, const void *str2, size_t count); -size_t strlen(const char *s); -unsigned long long strtoull(const char *restrict begin, char **restrict end, int base); - diff --git a/api/libc/stdio.h b/api/libc/stdio.h deleted file mode 100644 index 63d1ad8..0000000 --- a/api/libc/stdio.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -#include - -typedef struct FILE FILE; - -extern FILE *stdin; -extern FILE *stdout; -extern FILE *stderr; - -int fputc(int c, FILE *f); -int fputs(const char *str, FILE *f); - -int vfprintf(FILE *f, const char *restrict format, va_list arguments); -int fprintf(FILE *f, const char *restrict format, ...); -int vprintf(const char *restrict format, va_list arguments); -int printf(const char *restrict format, ...); - diff --git a/api/libc/stdlib.h b/api/libc/stdlib.h deleted file mode 100644 index d1f3d3b..0000000 --- a/api/libc/stdlib.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -size_t mbstowcs(wchar_t *dst, const char *src, size_t length); -void *malloc(size_t size); -void *calloc(size_t count, size_t size); -void free(void *block); - diff --git a/api/libc/string.h b/api/libc/string.h deleted file mode 100644 index 849275d..0000000 --- a/api/libc/string.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -void *memcpy(void *dest, const void *src, size_t size); -void *memmove(void *dest, const void *src, size_t size); -void *memset(void *dest, int val, size_t size); -int memcmp(const void *str1, const void *str2, size_t count); -size_t strlen(const char *s); -int strcmp(const char *s1, const char *s2); -unsigned long long strtoull(const char *restrict begin, char **restrict end, int base); - diff --git a/api/libc/wchar.h b/api/libc/wchar.h deleted file mode 100644 index 350c209..0000000 --- a/api/libc/wchar.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -size_t wcslen(const wchar_t *s); -wchar_t *wcsdup(const wchar_t *s); diff --git a/api/posix/sys/mman.h b/api/posix/sys/mman.h index dd8d6f5..d347c76 100644 --- a/api/posix/sys/mman.h +++ b/api/posix/sys/mman.h @@ -1,6 +1,12 @@ #pragma once -#define MAP_FAILED ((void *) 1) +#include + +#include "types.h" + +#define MAP_FAILED ((void *) -1) + +#define MAP_ANONYMOUS (1 >> 0) void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length); diff --git a/boot/Makefile b/boot/Makefile index 29151cd..87634d3 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -4,11 +4,11 @@ include ../defaults.mk $(CC.info) @$(CC) $(LDFLAGS) -o $@ $^ -VPATH := ../lib ../lib/asm/$(ARCH) efi/ +VPATH := ../lib ../lib/libc ../lib/asm/$(ARCH) efi/ IMAGE := kjarna.efi -CPPFLAGS += -I../api -I../lib +CPPFLAGS += -I../api -I../lib -I../lib/api CFLAGS += --target=$(ARCH)-unknown-windows -fshort-wchar -mno-avx \ -mno-sse -mno-mmx -funsigned-char -Wno-pointer-sign -ggdb \ diff --git a/boot/efi/status.h b/boot/efi/status.h deleted file mode 100644 index bcb6b96..0000000 --- a/boot/efi/status.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Efi -{ - class Status - { - using size_t = std::size_t; - using ssize_t = std::make_signed_t; - - size_t m_Value; - - constexpr Status(size_t value) - : m_Value(value) - { - } - - constexpr bool IsError() const - { - return static_cast(m_Value) < 0; - } - - constexpr bool IsWarning() const - { - return m_Value > 0; - } - - constexpr bool IsSuccess() const - { - return m_Value == 0; - } - - constexpr static Status ErrorStatus(size_t code) - { - auto errorBit = std::numeric_limits::min(); - return Status(static_cast(errorBit) | code); - } - - constexpr static Status WarningStatus(size_t code) - { - return Status(code); - } - - public: - - operator size_t() const - { - return m_Value; - } - - static const Status Success; - - class Warning - { - public: - - static const Status UnknownGlyph; - static const Status DeleteFailure; - static const Status WriteFailure; - static const Status BufferTooSmall; - static const Status StaleData; - static const Status FileSystem; - static const Status ResetRequired; - }; - - class Error - { - public: - - static const Status LoadError; - static const Status InvalidParameter; - static const Status Unsupported; - static const Status BadBufferSize; - static const Status BufferTooSmall; - static const Status NotReady; - static const Status DeviceError; - static const Status WriteProtected; - static const Status OutOfResources; - static const Status VolumeCorrupted; - static const Status VolumeFull; - static const Status NoMedia; - static const Status MediaChanged; - static const Status NotFound; - static const Status AccessDenied; - static const Status NoResponse; - static const Status NoMapping; - static const Status Timeout; - static const Status NotStarted; - static const Status AlreadyStarted; - static const Status Aborted; - static const Status IcmpError; - static const Status TftpError; - static const Status ProtocolError; - static const Status IncompatibleVersion; - static const Status SecurityViolation; - static const Status CrcError; - static const Status EndOfMedia; - static const Status EndOfFile; - static const Status InvalidLanguage; - static const Status CompromisedData; - static const Status IpAddressConflict; - static const Status HttpError; - }; - - }; -} - diff --git a/boot/efi/system_table.h b/boot/efi/system_table.h deleted file mode 100644 index 93e839d..0000000 --- a/boot/efi/system_table.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "table.h" - -extern "C" -{ - struct efi_system_table - { - void *m_Value; - }; -} - -namespace Efi -{ - class SystemTable : private Table - { - char16_t *m_FirmwareVendor; - uint32_t m_FirmwareRevision; - Handle m_ConsoleInHandle; - void *m_ConIn; - Handle m_ConsoleOutHandle; - void *m_ConOut; - Handle m_StandardErrorHandle; - void *m_StdErr; - void *m_RuntimeServices; - void *m_BootServices; - size_t m_NumberOfTableEntries; - void *m_ConfigurationTable; - }; -} - diff --git a/boot/efi/table.h b/boot/efi/table.h deleted file mode 100644 index 660bd1e..0000000 --- a/boot/efi/table.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include - -namespace Efi -{ - class TableHeader - { - using uint64_t = std::uint64_t; - using uint32_t = std::uint32_t; - using size_t = std::size_t; - - uint64_t m_Signature; - uint32_t m_Revision; - uint32_t m_HeaderSize; - uint32_t m_Crc32; - uint32_t m_Reserved; - }; - - class Table - { - TableHeader m_Header; - }; -} - diff --git a/defaults.mk b/defaults.mk index 585e9ab..73e8950 100644 --- a/defaults.mk +++ b/defaults.mk @@ -11,7 +11,7 @@ OBJCOPY := objcopy CFLAGS += -ffreestanding -mno-red-zone -ggdb -std=c2x \ -Wall -Wextra -Werror -fno-stack-protector \ - -Wpedantic + -Wpedantic -std=c2x CXXFLAGS += -ffreestanding -mno-red-zone -ggdb \ -Wall -Wextra -Werror -fno-stack-protector diff --git a/kc/boot/kc_main.c b/kc/boot/kc_main.c index 943c082..e0f1786 100644 --- a/kc/boot/kc_main.c +++ b/kc/boot/kc_main.c @@ -10,7 +10,7 @@ #include -#include +#include #include #define KERNEL_IMAGE_BASE 0xffffffff80000000 diff --git a/kc/core/Makefile b/kc/core/Makefile index dbbeef1..730eba7 100644 --- a/kc/core/Makefile +++ b/kc/core/Makefile @@ -1,22 +1,20 @@ include ../../defaults.mk include ../defaults.mk -VPATH := ../../lib ../ cpu/ memory/ +VPATH := ../../lib ../../lib/libc ../ cpu/ memory/ TARGET := kernel.os .DEFAULT: $(TARGET) -CPPFLAGS += -I../../api -I../api -I../../lib -I. +CPPFLAGS += -I../../api -I../api -I../../lib/api -I. -Icpu/ CFLAGS += -mcmodel=small -Wno-unused-const-variable -Wno-unused-function -fvisibility=hidden -g -LDSCRIPT := ../kc.ld - AOBJS := entry_x86_64.o reloc_x86_64.o dynamic_x86_64.o -COBJS := mmu.o cpu_task.o irq.o exceptions.o cpu.o msr.o mmu.o port.o +COBJS := mmu.o cpu_thread.o irq.o exceptions.o cpu.o msr.o mmu.o port.o arch_thread.o POBJS := pic8259.o pic8259_isr.o pit8253.o i8042.o GOBJS := serial.o kc_main.o memory.o vm_tree.o panic.o task.o \ kcc_memory.o page_early.o page_stack.o video.o -LOBJS := kprintf.o memset.o memcpy.o memmove.o memcmp.o kstdio.o string.o +LOBJS := printf.o memset.o memcpy.o memmove.o memcmp.o stdio.o string.o OBJS := $(AOBJS) $(COBJS) $(POBJS) $(GOBJS) $(LOBJS) DEPS := $(patsubst %.o,%.d,$(OBJS)) diff --git a/kc/core/cpu/arch_thread.c b/kc/core/cpu/arch_thread.c new file mode 100644 index 0000000..c0d707a --- /dev/null +++ b/kc/core/cpu/arch_thread.c @@ -0,0 +1,81 @@ +#include +#include +#include + +#include + +#include "task.h" + +struct arch_register_context +{ + uint64_t + r15, + r14, + r13, + r12, + r11, + r10, + r9, + r8, + rsi, + rdi, + rdx, + rcx, + rbx, + rax, + rbp, + rsp; +}; + +extern uint64_t *get_tss_rsp0(void); + +struct arch_thread_context +{ + struct arch_register_context register_context; + void *return_address; +}; + +extern void arch_swap_thread_stack( + void * volatile *outgoing_stack, + void *incoming_stack); + +extern void arch_begin_thread( + void *(func)(void *), + void *params); + +void arch_create_thread( + struct kc_thread *thread, + void *(*func)(void *), + void *params) +{ + uintptr_t stack_pointer = (uintptr_t)thread->kernel_stack_pointer; + struct arch_thread_context *context = (struct arch_thread_context *) + (stack_pointer -= sizeof(*context)); + thread->kernel_stack_pointer = (void *)stack_pointer; + + context->return_address = (void *)arch_begin_thread; + context->register_context.rdi = (uint64_t)func; + context->register_context.rsi = (uint64_t)params; + context->register_context.rdx = (uint64_t)thread; + context->register_context.rsp = (uint64_t)&context->return_address; +} + +void arch_swap_thread( + struct kc_thread *outgoing, + struct kc_thread *incoming) +{ + arch_swap_thread_stack(&outgoing->kernel_stack_pointer, incoming->kernel_stack_pointer); +} + +void *arch_idle_loop(void *) +{ + for (;;) __asm__ ("hlt"); + return nullptr; +} + +noreturn void arch_terminate_thread(union kc_thread_result result, struct kc_thread *) +{ + kct_terminate(result); + for (;;) __asm__ ("hlt"); +} + diff --git a/kc/core/cpu/arch_thread.h b/kc/core/cpu/arch_thread.h new file mode 100644 index 0000000..2b8d0fb --- /dev/null +++ b/kc/core/cpu/arch_thread.h @@ -0,0 +1,12 @@ +#pragma once + +void * arch_idle_loop(void *); + +void arch_create_thread( + struct kc_thread *thread, + void *(kc_thread_func)(void *), + void *params); + +void arch_swap_thread( + struct kc_thread *outgoing, + struct kc_thread *incoming); diff --git a/kc/core/cpu/cpu.c b/kc/core/cpu/cpu.c index da55d38..226c9cf 100644 --- a/kc/core/cpu/cpu.c +++ b/kc/core/cpu/cpu.c @@ -10,7 +10,7 @@ #include #include -#include +#include #define GDT_ENTRIES 16 #define IDT_ENTRIES 256 @@ -53,7 +53,7 @@ uint64_t *get_tss_rsp0(void) static void syscall_entry(void) { - kprintf("system call\n"); + printf("system call\n"); halt(); } diff --git a/kc/core/cpu/cpu_task.S b/kc/core/cpu/cpu_task.S deleted file mode 100644 index a411028..0000000 --- a/kc/core/cpu/cpu_task.S +++ /dev/null @@ -1,54 +0,0 @@ -/* thread switching routines for x86_64 -* adapted from: -* https://wiki.osdev.org/Brendan%27s_Multi-tasking_Tutorial -* -* -*/ - -.hidden cpu_set_thread -.global cpu_set_thread -.type cpu_set_thread, @function -cpu_set_thread: - /* x86_64 SysV ABI - * must save the following registers to the stack: - * rbx, rsp, rbp, r12, r13, r14, r15 - * the following are the parameters for switching tasks - * rdi: pointer to control block of thread being switched to - * rsi: pointer to control block of the current thread - * rdx: pointer to this cpu's task state segment rsp0 field - */ - // test for attempt to switch to same thread - cmp %rdi,%rsi - je .Lnothing - // test for a NULL origin thread - test %rsi,%rsi - // skip saving the state for a NULL origin thread - jz .Lnull_thread - push %rbx - push %r12 - push %r13 - push %r14 - push %r15 - push %rbp - /* the current task will be in rsi */ - mov %rsp, 0(%rsi) // save the stack pointer for the current task -.Lnull_thread: - mov 0(%rdi),%rsp // new stack is the next's task's stack - mov 8(%rdi),%rbx // top of the next task's kernel stack - mov %rbx,0(%rdx) // save the esp0 to the tss - mov 16(%rdi),%rax // page map for the next task - mov %cr3, %rcx // in case we need to change the page map - cmp %rax,%rcx - je .Lfinish - mov %rax,%cr3 // if the page maps are not the same, reload -.Lfinish: - pop %rbp // get all the registers off the new task's stack - pop %r15 - pop %r14 - pop %r13 - pop %r12 - pop %rbx -.Lnothing: // do nothing if the threads are the same - ret // leave into the new task -.size cpu_set_thread, . - cpu_set_thread - diff --git a/kc/core/cpu/cpu_thread.S b/kc/core/cpu/cpu_thread.S new file mode 100644 index 0000000..c4d04e1 --- /dev/null +++ b/kc/core/cpu/cpu_thread.S @@ -0,0 +1,57 @@ +.section .text + +.extern arch_terminate_thread + +.global arch_begin_thread +arch_begin_thread: + movq %rdi, %rax + movq %rsi, %rdi + pushq %rdx + pushq %rbp + movq %rsp, %rbp + andq $-16, %rsp + callq *%rax + movq %rax, %rdi + movq 8(%rbp), %rsi + callq arch_terminate_thread + +.global arch_swap_thread_stack +arch_swap_thread_stack: + pushq %rsp + pushq %rbp + pushq %rax + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rdi + pushq %rsi + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + movq %rsp, (%rdi) + .Lenter_thread: + movq %rsi, %rsp + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rsi + popq %rdi + popq %rdx + popq %rcx + popq %rbx + popq %rax + popq %rbp + popq %rsp + retq + + diff --git a/kc/core/cpu/exceptions.h b/kc/core/cpu/exceptions.h index 7babb65..14a2088 100644 --- a/kc/core/cpu/exceptions.h +++ b/kc/core/cpu/exceptions.h @@ -21,7 +21,7 @@ #ifndef __ASSEMBLER__ -#include +#include struct isr_context_param_regs { @@ -53,7 +53,7 @@ struct isr_context static inline void print_exception_context(struct isr_context *context) { - kprintf( + printf( "exception %#hhx:%x context:\n" "interrupt entry state:\n" "rip: %#hx:%p rflags: %#0.16lx\n" diff --git a/kc/core/i8042.c b/kc/core/i8042.c index 0ba12dc..f6b63b0 100644 --- a/kc/core/i8042.c +++ b/kc/core/i8042.c @@ -2,7 +2,7 @@ #include "port.h" #include "i8042.h" #include "pic8259.h" -#include +#include static void i8042_init(void); static void i8042_enable(void) {} @@ -99,21 +99,21 @@ static void i8042_set_config(void) i8042_command(0x20); uint8_t config_byte; i8042_read(&config_byte); - kprintf("i8042: current config: %#0.2x\n", config_byte); + printf("i8042: current config: %#0.2x\n", config_byte); config_byte &= 0xfc; if (config_byte & (0x1 << 5)) { maybe_dual_channel = true; } config_byte |= 0x01; - kprintf("i8042: new config: %#0.2x\n", config_byte); + printf("i8042: new config: %#0.2x\n", config_byte); i8042_command(0x60); i8042_write(config_byte); i8042_command(0xaa); uint8_t bist_result; if (i8042_read(&bist_result) && bist_result != 0x55) { - kprintf("i8042: warning: error self-testing device. aborting.\n"); + printf("i8042: warning: error self-testing device. aborting.\n"); return; } @@ -124,11 +124,11 @@ static void i8042_set_config(void) uint8_t config_byte; if (i8042_read(&config_byte) && config_byte & (0x1 << 5)) { - kprintf("i8042: single-channel controller detected\n"); + printf("i8042: single-channel controller detected\n"); } else { - kprintf("i8042: dual-channel controller detected\n"); + printf("i8042: dual-channel controller detected\n"); is_dual_channel = true; } } @@ -136,7 +136,7 @@ static void i8042_set_config(void) outb(PS2_COMMAND, 0xab); if (inb(PS2_DATA) != 0x00) { - kprintf("i8042: failed testing port a\n"); + printf("i8042: failed testing port a\n"); } else { @@ -147,7 +147,7 @@ static void i8042_set_config(void) outb(PS2_COMMAND, 0xa9); if (inb(PS2_DATA) != 0x00) { - kprintf("i8042: failed testing port b\n"); + printf("i8042: failed testing port b\n"); } else { @@ -162,7 +162,7 @@ static void i8042_set_config(void) i8042_write(0xff); if(inb(PS2_DATA) == 0xfa && inb(PS2_DATA) == 0xaa) { - kprintf("i8042: detected device on channel, type %#0.2x\n", inb(PS2_DATA)); + printf("i8042: detected device on channel, type %#0.2x\n", inb(PS2_DATA)); } } diff --git a/kc/core/kc_main.c b/kc/core/kc_main.c index 9a84727..4f70d00 100644 --- a/kc/core/kc_main.c +++ b/kc/core/kc_main.c @@ -10,7 +10,7 @@ #include "i8042.h" #include -#include +#include #include #include @@ -27,7 +27,7 @@ unsigned long kc_main(struct kc_boot_data *data) cpu_init(); serial_init(); video_init(); - kprintf("sophia starting, boot data %#lx\n", boot_data); + printf("sophia starting, boot data %#lx\n", boot_data); memory_init(); pic8259_init(); pit8253_timer_source.init(); diff --git a/kc/core/kstdio.c b/kc/core/kstdio.c deleted file mode 100644 index 680eb07..0000000 --- a/kc/core/kstdio.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "serial.h" -#include "video.h" - -#include -#include - -#include -#include - -FILE *kstdout; - -int kfputc(int c, FILE *f) -{ - (void)f; - video_putchar(c); - return serial_putchar(c); -} - diff --git a/kc/core/memory.h b/kc/core/memory.h index 6f70e57..425c2cd 100644 --- a/kc/core/memory.h +++ b/kc/core/memory.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/kc/core/memory/memory.c b/kc/core/memory/memory.c index ee0d9ca..7071018 100644 --- a/kc/core/memory/memory.c +++ b/kc/core/memory/memory.c @@ -20,7 +20,7 @@ #include #include -#include +#include #define align_next(x, a) (x + a - 1) & ~(a - 1) @@ -89,7 +89,7 @@ struct vm_tree *vm_get_tree(void) void page_init(void) { - kprintf("initializing page frame allocator\n"); + printf("initializing page frame allocator\n"); page_early_init(); page_stack_init(); } @@ -98,7 +98,7 @@ void page_init_final(void) { if (current_alloc_func == boot_page_alloc) { - kprintf("finishing page frame allocator initialization\n"); + printf("finishing page frame allocator initialization\n"); page_early_final(); current_alloc_func = page_stack_alloc; } @@ -176,7 +176,7 @@ static void *map_tableset(void *vaddr, uint64_t *tables[TABLESET_COUNT]) } else { - kprintf("error: failed allocating memory for page table\n"); + printf("error: failed allocating memory for page table\n"); PANIC(OUT_OF_MEMORY); } } @@ -380,7 +380,7 @@ static void temp_page_unmap(void *vaddr) static void vm_init(void) { - kprintf("initializing vm state\n"); + printf("initializing vm state\n"); struct kc_boot_data *boot_data = get_boot_data(); struct vm_core_static_state *states = &vm_state.statics[0]; @@ -433,7 +433,7 @@ static void vm_init(void) vm_state.zero_page = page_alloc(PAGE_ALLOC_CONV); if (!vm_state.zero_page) { - kprintf("failed allocating for zero page\n"); + printf("failed allocating for zero page\n"); PANIC(GENERAL_PANIC); } @@ -441,7 +441,7 @@ static void vm_init(void) if (!zero_temp) { - kprintf("failed mapping zero page for initialization\n"); + printf("failed mapping zero page for initialization\n"); PANIC(GENERAL_PANIC); } @@ -492,7 +492,7 @@ void *heap_alloc(size_t size) if ((void *)-1ULL == heap_root) { struct vm_tree_node *heap_node = &vm_state.statics[HEAP_VM_STATE].node; - kprintf("initializing heap at %#lx of %zu bytes\n", + printf("initializing heap at %#lx of %zu bytes\n", heap_node->key.address, heap_node->key.size); heap_root = NULL; header = (struct heap_header *)heap_node->key.address; @@ -650,7 +650,7 @@ int anonymous_page_handler( if (!paddr) { - kprintf("got zero from page_alloc :|\n"); + printf("got zero from page_alloc :|\n"); PANIC(OUT_OF_MEMORY); } page_map_at( @@ -676,14 +676,14 @@ void page_fault_handler(struct isr_context *context) if (!node) { print_exception_context(context); - kprintf("error: page fault in unmanaged address %#lx\n", address); + printf("error: page fault in unmanaged address %#lx\n", address); PANIC(UNHANDLED_FAULT); } if (!node->object->handler) { print_exception_context(context); - kprintf("error: vm object at %p has no fault handler\n"); + printf("error: vm object at %p has no fault handler\n"); PANIC(UNHANDLED_FAULT); } @@ -693,7 +693,7 @@ void page_fault_handler(struct isr_context *context) void general_protection_handler(struct isr_context *context) { //TODO: implement #gp handler - kprintf("general protection violation\n"); + printf("general protection violation\n"); print_exception_context(context); PANIC(UNHANDLED_FAULT); } diff --git a/kc/core/memory/page_early.c b/kc/core/memory/page_early.c index 9ac90d2..989f82a 100644 --- a/kc/core/memory/page_early.c +++ b/kc/core/memory/page_early.c @@ -1,7 +1,7 @@ #include "page_early.h" #include "panic.h" -#include +#include static struct page_early_state { @@ -13,20 +13,20 @@ early_state; void page_early_init(void) { - kprintf("initializing early page frame allocator\n"); + printf("initializing early page frame allocator\n"); struct kc_boot_data *boot_data = get_boot_data(); early_state.first = &boot_data->memory.entries[0]; early_state.last = &boot_data->memory.entries[boot_data->memory.count]; early_state.current = early_state.first; - /*kprintf("memory ranges\n"); + /*printf("memory ranges\n"); struct memory_range *current = early_state.first; do { - kprintf("base %#0.16lx size %#0.16lx type %d\n", current->base, current->size, current->type); + printf("base %#0.16lx size %#0.16lx type %d\n", current->base, current->size, current->type); current++; } while (current != early_state.last); @@ -37,7 +37,7 @@ void page_early_final(void) { if (early_state.first) { - kprintf("finalizing early page allocator\n"); + printf("finalizing early page allocator\n"); for (struct memory_range *current = early_state.first; current < early_state.last; current++) @@ -101,7 +101,7 @@ kc_phys_addr page_early_alloc(enum page_alloc_flags type) early_state.current++; } - kprintf("error: early allocator has run out of memory\n"); + printf("error: early allocator has run out of memory\n"); PANIC(OUT_OF_MEMORY); } diff --git a/kc/core/panic.c b/kc/core/panic.c index 7844487..02c58c7 100644 --- a/kc/core/panic.c +++ b/kc/core/panic.c @@ -1,6 +1,6 @@ #include "panic.h" -#include +#include #include @@ -13,12 +13,12 @@ static char const *reason_strings[] = { noreturn void panic(const char *file, int line, enum panic_reason reason) { - // TODO: kprintf() and friends + // TODO: printf() and friends if (reason > sizeof(reason_strings) / sizeof(*reason_strings)) { reason = 0; } - kprintf( + printf( "kernel panic:%s:%u %s\n", file, line, diff --git a/kc/core/pic8259.c b/kc/core/pic8259.c index 4ddd2e1..792d0bd 100644 --- a/kc/core/pic8259.c +++ b/kc/core/pic8259.c @@ -4,7 +4,7 @@ #include -#include +#include #define ICW1_ICW4 0x1 #define ICW1_SINGLE 0x2 @@ -28,7 +28,7 @@ void pic8259_irq_install(uint8_t irq, pic8259_handler_func h) { if (irq_handlers[irq]) { - kprintf("attempt to overwrite irq handler for %u\n", irq); + printf("attempt to overwrite irq handler for %u\n", irq); return; } irq_handlers[irq] = h; @@ -43,7 +43,7 @@ static void wait(void) void pic8259_init(void) { - kprintf("initializing legacy pic\n"); + printf("initializing legacy pic\n"); // basic init. not gonna fret with this too much outb(PIC_PRI_CMD, ICW1_INIT|ICW1_ICW4); wait(); @@ -171,7 +171,7 @@ void pic8259_irq_begin(uint8_t irq) // check for spurious activations if (irq_is_spurious(irq)) { - kprintf("spurious activation of irq%hhu\n", irq); + printf("spurious activation of irq%hhu\n", irq); return; } @@ -181,12 +181,12 @@ void pic8259_irq_begin(uint8_t irq) if (result) { - kprintf("error handling irq%hhu: % d\n", irq, result); + printf("error handling irq%hhu: % d\n", irq, result); } } else { - kprintf("unhandled irq%hhu\n", irq); + printf("unhandled irq%hhu\n", irq); } pic8259_irq_end(irq); diff --git a/kc/core/pit8253.c b/kc/core/pit8253.c index 360808f..31cf349 100644 --- a/kc/core/pit8253.c +++ b/kc/core/pit8253.c @@ -5,7 +5,7 @@ #include -#include +#include #define PIT8253_DATA0 0x40 #define PIT8253_CMD 0x43 @@ -82,9 +82,9 @@ void pit8253_set_frequency(uint32_t frequency) if (d > UINT16_MAX) { - kprintf("warning: %uHz is too slow for 8253 pit\n", PIT_HZ/d); + printf("warning: %uHz is too slow for 8253 pit\n", PIT_HZ/d); d = UINT16_MAX; - kprintf("warning: set timer to minimum frequency\n"); + printf("warning: set timer to minimum frequency\n"); } set_divisor(d); diff --git a/kc/core/stdio.c b/kc/core/stdio.c new file mode 100644 index 0000000..66c539f --- /dev/null +++ b/kc/core/stdio.c @@ -0,0 +1,18 @@ +#include "serial.h" +#include "video.h" + +#include +#include + +#include +#include + +FILE *stdout; + +int fputc(int c, FILE *f) +{ + (void)f; + video_putchar(c); + return serial_putchar(c); +} + diff --git a/kc/core/task.c b/kc/core/task.c index 3e0288d..a298a05 100644 --- a/kc/core/task.c +++ b/kc/core/task.c @@ -12,13 +12,9 @@ #include #include -#include +#include -static const size_t INTERRUPT_STACK_SIZE = 4096; -static const size_t THREAD_SIZE = 16834; -static const enum vm_alloc_flags ALLOC_FLAGS = VM_ALLOC_ANY|VM_ALLOC_ANONYMOUS; - -extern uint64_t *get_tss_rsp0(void); +#include "arch_thread.h" static void lock_scheduler(void); static void unlock_scheduler(void); @@ -27,7 +23,7 @@ static void unlock_preempt(void); static void update_time(void); -static struct kc_thread *create_thread(void (*thread_entry)(void)); +static struct kc_thread *create_thread(void *(*thread_f)(void *), void *p); static void destroy_thread(struct kc_thread *thread); static void set_thread(struct kc_thread *thread); static void block_thread(enum kc_thread_status reason); @@ -37,10 +33,6 @@ static void sleep_thread_until(uint64_t nanoseconds); static int sleeping_thread_callback(uint64_t nanoseconds); -static struct kc_thread *ready_thread_pop(void); -static void ready_thread_push(struct kc_thread *thread); -static void ready_thread_push_back(struct kc_thread *thread); - const struct timer_source * timesource; /* static threads that are always present @@ -50,131 +42,139 @@ static struct kc_thread *idle_thread; // thread lists for the scheduler to manipulate static struct kc_thread *current_thread; -static struct kc_thread *first_ready_thread; -static struct kc_thread *last_ready_thread; -static struct kc_thread *sleeping_threads; + +struct thread_queue +{ + struct kc_thread + *first, + *last; +}; + +static void thread_queue_push(struct thread_queue *queue, struct kc_thread *thread); +static void thread_queue_push_back(struct thread_queue *queue, struct kc_thread *thread); + +static void thread_queue_push(struct thread_queue *queue, struct kc_thread *thread) +{ + thread->next = queue->first; + queue->first = thread; + + if (!queue->last) + { + thread_queue_push_back(queue, thread); + } +} + +static void thread_queue_push_back(struct thread_queue *queue, struct kc_thread *thread) +{ + if (!queue->first) + { + thread_queue_push(queue, thread); + } + else if (queue->last) + { + queue->last->next = thread; + } + + queue->last = thread; +} + +struct kc_thread *thread_queue_pop(struct thread_queue *queue) +{ + if (!queue->first) + { + return nullptr; + } + + struct kc_thread *thread = queue->first; + queue->first = thread->next; + thread->next = nullptr; + if (!queue->first) + { + queue->last = nullptr; + } + + return thread; +} + +bool thread_queue_any(struct thread_queue queue) +{ + return queue.first && queue.last; +} + +static struct thread_queue ready_queue; +static struct thread_queue sleep_queue; +static struct thread_queue wait_queue; static volatile atomic_uint_fast64_t preempt_switch_count = 0; static volatile atomic_bool preempt_switch_flag = false; -static void idle_thread_entry(void) +static void *idle_thread_entry(void *) { - kprintf("idle thread started\n"); - unlock_scheduler(); - - __asm__ ("sti"); + printf("idle thread started\n"); + __asm__ ("sti"); + + while (true) { __asm__ ("hlt;"); } -} -#define PS2INPUT 0x60 -#define PS2STATUS 0x64 -#define PS2STATUS_IBF 0x1 - -static void keyboard_input_loop(void) -{ - bool has_bytes = true; - while (has_bytes) - { - has_bytes = inb(PS2STATUS) & PS2STATUS_IBF; - if (has_bytes) - { - kprintf("read byte from keyboard input: %hhd\n", inb(PS2INPUT)); - } - else - { - kprintf("no bytes in keyboard input\n"); - } - } + return nullptr; } -static void sleepy_thread_entry(void) +static void *sleepy_thread_entry(void *p) { + uint64_t sleep_nanoseconds = (uint64_t)p; int thread_slept_count = 0; while (true) { - sleep_thread(1000000000); + sleep_thread(sleep_nanoseconds); thread_slept_count++; - //keyboard_input_loop(); + printf("thread slept %d times\n", thread_slept_count); } (void)thread_slept_count; + return nullptr; } -static void create_interrupt_stack(size_t size) -{ - char *rsp0 = vm_alloc(size, ALLOC_FLAGS); - memset(rsp0, 0, size); - *get_tss_rsp0() = (uintptr_t)rsp0 + size; -} - -noreturn void task_init(void) -{ - lock_scheduler(); - timesource = &pit8253_timer_source; - timesource->append_callback(sleeping_thread_callback); - timesource->start(); - kprintf( - "starting task management, timesource delta %luns\n", - timesource->nanoseconds_delta()); - - // XXX: unfuck this mess at some point - // XXX: make this also not demand-allocated or it's gonna fail - create_interrupt_stack(INTERRUPT_STACK_SIZE); - // initalize static threads - idle_thread = create_thread(idle_thread_entry); - struct kc_thread *sleepy_thread = create_thread(sleepy_thread_entry); - current_thread = idle_thread; - ready_thread_push_back(sleepy_thread); +static uint64_t scheduler_flags; - idle_thread->status = RUNNING; - cpu_set_thread(&idle_thread->state, NULL, get_tss_rsp0()); +static inline bool check_preempt_count() +{ + if (atomic_load(&preempt_switch_count) > 0) + { + return true; + } - // shouldn't ever get here - PANIC(DEAD_END); + return false; } void task_schedule(void) { - if (atomic_load(&preempt_switch_count)) - { - preempt_switch_flag = true; - return; - } - - if (first_ready_thread) - { - struct kc_thread *this_thread = ready_thread_pop(); - - if (this_thread == idle_thread) - { - if (first_ready_thread) - { - this_thread->status = READY; - this_thread = ready_thread_pop(); - ready_thread_push(idle_thread); - } - else if (current_thread->status == RUNNING) - { - return; - } - else - { - // NULL statement????? idon't like - } - } - set_thread(this_thread); - } + if(check_preempt_count()) + { + preempt_switch_flag = true; + return; + } + + struct kc_thread *t = thread_queue_pop(&ready_queue); + + if (t == idle_thread) + { + thread_queue_push_back(&ready_queue, t); + t = thread_queue_pop(&ready_queue); + } + + if (t && current_thread->status != RUNNING) + { + set_thread(t); + } } -static uint64_t scheduler_flags; - static void lock_scheduler(void) { scheduler_flags = irq_lock(); @@ -193,7 +193,7 @@ static void unlock_scheduler(void) static void unlock_preempt(void) { - if (atomic_load(&preempt_switch_count) >= 1) + if (atomic_load(&preempt_switch_count) > 0) { preempt_switch_count--; } @@ -220,42 +220,21 @@ void update_time(void) } } -static struct kc_thread *create_thread(void (*thread_f)(void)) -{ - char *task_bottom = vm_alloc(16384, ALLOC_FLAGS); - struct kc_thread *thread = - (struct kc_thread *)(task_bottom + 16384 - sizeof(*thread)); - - thread->next = NULL; - - thread->time_elapsed = 0; - thread->sleep_expiration = 0; +#define KC_THREAD_SIZE 0x3000 - thread->state.stack = (uintptr_t)thread; - thread->state.stack_top = *get_tss_rsp0(); - thread->state.page_map = mmu_get_map(); - - // set up the expected stack values for the state - struct task_register_state - { - uint64_t rbp; - uint64_t r15; - uint64_t r14; - uint64_t r13; - uint64_t r12; - uint64_t rbx; - uint64_t rip; - } - *register_state = - (struct task_register_state *) - (thread->state.stack -= sizeof(*register_state)); +static struct kc_thread *create_thread(void *(*thread_f)(void *), void *p) +{ + void *buffer = vm_alloc(KC_THREAD_SIZE, VM_ALLOC_ANONYMOUS|VM_ALLOC_ANY); + memset(buffer, 0, KC_THREAD_SIZE); - memset(register_state, 0, sizeof(*register_state)); + uintptr_t stack_head = (uintptr_t)buffer + KC_THREAD_SIZE; + struct kc_thread *thread = (struct kc_thread *)(stack_head -= sizeof(*thread)); - register_state->rip = (uint64_t)thread_f; - register_state->rbp = thread->state.stack; + thread->kernel_stack_head = (void *)stack_head; + thread->kernel_stack_pointer = (void *)stack_head; + thread->status = READY; - thread->status = READY; + arch_create_thread(thread, thread_f, p); return thread; } @@ -267,11 +246,17 @@ static void destroy_thread(struct kc_thread *thread) static void set_thread(struct kc_thread *thread) { + if (thread == current_thread) + { + return; + } + if (atomic_load(&preempt_switch_count)) { preempt_switch_flag = true; return; } + update_time(); struct kc_thread *previous_thread = current_thread; current_thread = thread; @@ -279,15 +264,11 @@ static void set_thread(struct kc_thread *thread) if (previous_thread->status == RUNNING) { previous_thread->status = READY; - ready_thread_push(previous_thread); + thread_queue_push(&ready_queue, previous_thread); } - cpu_set_thread( - ¤t_thread->state, - &previous_thread->state, - get_tss_rsp0()); - current_thread->status = RUNNING; + arch_swap_thread(previous_thread, current_thread); } static void block_thread(enum kc_thread_status reason) @@ -304,14 +285,14 @@ static void unblock_thread(struct kc_thread *thread) thread->status = READY; - if (!first_ready_thread || (current_thread == idle_thread)) + if (!thread_queue_any(ready_queue) || (current_thread == idle_thread)) { unlock_preempt(); set_thread(thread); } else { - ready_thread_push_back(thread); + thread_queue_push_back(&ready_queue, thread); } unlock_scheduler(); @@ -332,89 +313,149 @@ static void sleep_thread_until(uint64_t nanoseconds) return; } -// kprintf("time elapsed is now %ld\r\n", timesource->nanoseconds_elapsed()); -// kprintf("thread will now sleep until %ld\r\n", nanoseconds); current_thread->sleep_expiration = nanoseconds; - current_thread->next = sleeping_threads; - sleeping_threads = current_thread; + + thread_queue_push_back(&sleep_queue, current_thread); unlock_preempt(); block_thread(SLEEPING); } -static struct kc_thread *ready_thread_pop(void) +static int sleeping_thread_callback(uint64_t timestamp) { - struct kc_thread *thread = first_ready_thread; - - if (thread) - { - first_ready_thread = thread->next; - } + lock_preempt(); - if (!first_ready_thread) - { - last_ready_thread = NULL; - } + struct thread_queue queue = sleep_queue; + + sleep_queue = (struct thread_queue){ nullptr, nullptr }; + + { + struct kc_thread *t; + while((t = thread_queue_pop(&queue)) != nullptr) + { + if (t->sleep_expiration <= timestamp) + { + t->sleep_expiration = 0; + unblock_thread(t); + } + else + { + thread_queue_push(&sleep_queue, t); + } + } + } + + unlock_preempt(); + lock_scheduler(); + task_schedule(); + unlock_scheduler(); - return thread; + return 0; } -static void ready_thread_push(struct kc_thread *thread) +typedef void (locked_scheduler_action)(void *); + +static void with_locked_scheduler( + locked_scheduler_action *action, + void *action_parameter) { - thread->next = first_ready_thread; - first_ready_thread = thread; + lock_scheduler(); + action(action_parameter); + unlock_scheduler(); +} - if (!last_ready_thread) - { - last_ready_thread = first_ready_thread; - } +static void terminate_action(void *result) +{ + union kc_thread_result *result_value = result; + current_thread->result = *result_value; + current_thread->kernel_stack_pointer = current_thread->kernel_stack_head; + + struct thread_queue queue = wait_queue; + + lock_preempt(); + + wait_queue = (struct thread_queue) { nullptr, nullptr }; + + { + struct kc_thread *t; + while((t = thread_queue_pop(&queue)) != nullptr) + { + if (t->wait_target == current_thread) + { + t->wait_target = nullptr; + t->status = READY; + thread_queue_push_back(&ready_queue, t); + } + else + { + thread_queue_push(&wait_queue, t); + } + } + } + + unlock_preempt(); + + block_thread(TERMINATED); } -static void ready_thread_push_back(struct kc_thread *thread) +void kct_terminate(union kc_thread_result result) { - if (last_ready_thread) - { - last_ready_thread->next = thread; - } - else - { - first_ready_thread = thread; - } - last_ready_thread = thread; + with_locked_scheduler(terminate_action, &result); } -static int sleeping_thread_callback(uint64_t nanoseconds) +union kc_thread_result kct_wait(struct kc_thread *target) { - lock_preempt(); + current_thread->wait_target = target; + thread_queue_push_back(&wait_queue, current_thread); + block_thread(WAITING); + return target->result; +} - struct kc_thread *sleeping = sleeping_threads; - sleeping_threads = NULL; +static void *waited_thread_test(void *) +{ + printf("worker thread, sleeping for some time...\n"); + sleep_thread(30000000000); + return (void *)1; +} - while (sleeping != NULL) - { - struct kc_thread *this_thread = sleeping; - sleeping = sleeping->next; - - if (this_thread->sleep_expiration <= nanoseconds) - { - //kprintf("thread awakened: %p\n", this_thread); - this_thread->sleep_expiration = 0; - unblock_thread(this_thread); - } - else - { - this_thread->next = sleeping_threads; - sleeping_threads = this_thread; - } - } +static void *waiting_thread_test(void *) +{ + printf("waiting thread\n"); + struct kc_thread *t = create_thread(waited_thread_test, nullptr); + t->status = READY; + thread_queue_push_back(&ready_queue, t); + union kc_thread_result r = kct_wait(t); + printf("waited thread terminated result: %d\n", r); + return nullptr; +} - unlock_preempt(); +noreturn void task_init(void) +{ + struct kc_thread boot_thread; + boot_thread.status = RUNNING; + current_thread = &boot_thread; - lock_scheduler(); - task_schedule(); - unlock_scheduler(); + timesource = &pit8253_timer_source; + timesource->append_callback(sleeping_thread_callback); + timesource->start(); + printf( + "starting task management, timesource delta %luns\n", + timesource->nanoseconds_delta()); - return 0; + idle_thread = create_thread(idle_thread_entry, nullptr); + struct kc_thread *sleepy_thread = create_thread(sleepy_thread_entry, (void *)3000000000); + struct kc_thread *sleepy_thread2 = create_thread(sleepy_thread_entry, (void *)5000000000); + struct kc_thread *waiting_thread = create_thread(waiting_thread_test, nullptr); + thread_queue_push(&ready_queue, sleepy_thread); + thread_queue_push(&ready_queue, sleepy_thread2); + thread_queue_push_back(&ready_queue, waiting_thread); + __asm__ volatile ("movq %%rsp, %0" : "=g"(boot_thread.kernel_stack_pointer) :); + thread_queue_push(&ready_queue, idle_thread); + block_thread(BLOCKED); + // shouldn't ever get here + printf("the idle thread exited somehow. we're back in the boot thread. this is not good.\n"); + PANIC(DEAD_END); } + diff --git a/kc/core/task.h b/kc/core/task.h index bea4756..5d9f39f 100644 --- a/kc/core/task.h +++ b/kc/core/task.h @@ -4,34 +4,39 @@ enum kc_thread_status { + AVAILABLE, + CREATED, READY, RUNNING, SLEEPING, BLOCKED, + WAITING, TERMINATED }; -struct kc_thread_state +union kc_thread_result { - uint64_t stack; - uint64_t stack_top; - uint64_t page_map; + uint64_t scalar; + void *pointer; }; struct kc_thread { - struct kc_thread *next; - uint64_t time_elapsed; - uint64_t sleep_expiration; - enum kc_thread_status status; - struct kc_thread_state state; + void *kernel_stack_head; + void *kernel_stack_pointer; + uint64_t time_elapsed; + union kc_thread_result result; + enum kc_thread_status status; + struct kc_thread *next; + union + { + uint64_t sleep_expiration; + struct kc_thread *wait_target; + }; }; -extern void cpu_set_thread( - struct kc_thread_state *current, - struct kc_thread_state *next, - uint64_t *cpu_tss_rsp0); - void task_init(void); -void task_schedule(void); + +void kct_terminate(union kc_thread_result result); +union kc_thread_result kct_wait(struct kc_thread *target); diff --git a/kc/core/video.c b/kc/core/video.c index 6d89c08..a3bdce2 100644 --- a/kc/core/video.c +++ b/kc/core/video.c @@ -46,8 +46,8 @@ #include -#include -#include +#include +#include #include #include @@ -227,7 +227,7 @@ int video_init(void) } } - kprintf("video output %ix%i pixels, %i bytes per line, %s 32 bytes per pixel\n", + printf("video output %ix%i pixels, %i bytes per line, %s 32 bytes per pixel\n", boot_data->width, boot_data->height, boot_data->pitch, video_format_name); diff --git a/kc/core/vm_tree.c b/kc/core/vm_tree.c index dd3667e..72affe8 100644 --- a/kc/core/vm_tree.c +++ b/kc/core/vm_tree.c @@ -31,8 +31,8 @@ #include "vm_tree.h" #include "panic.h" -#include -#include +#include +#include #define assert(expr) @@ -44,92 +44,92 @@ static int compare(uintptr_t a1, size_t s1, uintptr_t a2, size_t s2) { - // case 1: a1:a1+s1 occupies a region entirely below a2 - // and is therefore less than. - if ((a1 + s1) <= a2) - { - return -1; - } - // case 2: a1:a1+s1 occupies a region entirely above (a2+s2) - // and is therefore greater than. - else if (a1 >= (a2 + s2)) - { - return 1; - } - // case 3: the above conditions are not satisfied and therefore - // the regions overlap and are considered equivalent. - return 0; + // case 1: a1:a1+s1 occupies a region entirely below a2 + // and is therefore less than. + if ((a1 + s1) <= a2) + { + return -1; + } + // case 2: a1:a1+s1 occupies a region entirely above (a2+s2) + // and is therefore greater than. + else if (a1 >= (a2 + s2)) + { + return 1; + } + // case 3: the above conditions are not satisfied and therefore + // the regions overlap and are considered equivalent. + return 0; } static int compare_key(struct vm_tree_key const * const k1, - struct vm_tree_key const * const k2) + struct vm_tree_key const * const k2) { - return compare(k1->address, k1->size, k2->address, k2->size); + return compare(k1->address, k1->size, k2->address, k2->size); } void vmt_init_node( - struct vm_tree *tree, - struct vm_tree_node *node, - struct vm_object *object, - void *base, - void *head) + struct vm_tree *tree, + struct vm_tree_node *node, + struct vm_object *object, + void *base, + void *head) { - memset(node, 0, sizeof(*node)); - node->key = - (struct vm_tree_key) - { - (uintptr_t)base, - (uintptr_t)head - (uintptr_t)base - }; - - struct vm_tree_node *ek = NULL; - if (!(ek = vmt_search_key(tree, &node->key))) - { - struct vm_tree_node *p = vmn_predecessor_key( - tree->root, - &node->key); - if (!p) - { - // the tree root should be in place of a missing predecessor - p = tree->root; - } - vmt_insert( - tree, - node, - p, - vmn_child_direction(node, p)); - node->object = object; - } - else - { - kprintf("fatal: attempt to insert overlapping vm node\n" - "node 1: %p: %p @ %zu bytes\n" - "node 2: %p: %p @ %zu bytes\n", - node->key.address, node->key.size, - ek->key.address, ek->key.size); - - PANIC(GENERAL_PANIC); - } + memset(node, 0, sizeof(*node)); + node->key = + (struct vm_tree_key) + { + (uintptr_t)base, + (uintptr_t)head - (uintptr_t)base + }; + + struct vm_tree_node *ek = NULL; + if (!(ek = vmt_search_key(tree, &node->key))) + { + struct vm_tree_node *p = vmn_predecessor_key( + tree->root, + &node->key); + if (!p) + { + // the tree root should be in place of a missing predecessor + p = tree->root; + } + vmt_insert( + tree, + node, + p, + vmn_child_direction(node, p)); + node->object = object; + } + else + { + printf("fatal: attempt to insert overlapping vm node\n" + "node 1: %p: %p @ %zu bytes\n" + "node 2: %p: %p @ %zu bytes\n", + node->key.address, node->key.size, + ek->key.address, ek->key.size); + + PANIC(GENERAL_PANIC); + } } static struct vm_tree_node* RotateDirRoot( - struct vm_tree* T, // red–black tree - struct vm_tree_node* P, // root of subtree (may be the root of T) - enum vm_tree_direction dir) { // dir ∈ { LEFT, RIGHT } - struct vm_tree_node* G = P->parent; - struct vm_tree_node* S = P->child[1-dir]; - struct vm_tree_node* C; - assert(S != NIL); // pointer to true node required - C = S->child[dir]; - P->child[1-dir] = C; if (C != NIL) C->parent = P; - S->child[ dir] = P; P->parent = S; - S->parent = G; - if (G != NULL) - G->child[ P == G->right ? RIGHT : LEFT ] = S; - else - T->root = S; - return S; // new root of subtree + struct vm_tree* T, // red–black tree + struct vm_tree_node* P, // root of subtree (may be the root of T) + enum vm_tree_direction dir) { // dir ∈ { LEFT, RIGHT } + struct vm_tree_node* G = P->parent; + struct vm_tree_node* S = P->child[1-dir]; + struct vm_tree_node* C; + assert(S != NIL); // pointer to true node required + C = S->child[dir]; + P->child[1-dir] = C; if (C != NIL) C->parent = P; + S->child[ dir] = P; P->parent = S; + S->parent = G; + if (G != NULL) + G->child[ P == G->right ? RIGHT : LEFT ] = S; + else + T->root = S; + return S; // new root of subtree } #define RotateDir(N,dir) RotateDirRoot(T,N,dir) @@ -137,251 +137,251 @@ static struct vm_tree_node* RotateDirRoot( #define RotateRight(N) RotateDirRoot(T,N,RIGHT) void vmt_insert( - struct vm_tree* T, // -> red–black tree - struct vm_tree_node* N, // -> node to be inserted - struct vm_tree_node* P, // -> parent node of N ( may be NULL ) - enum vm_tree_direction dir) // side ( LEFT or RIGHT ) of P where to insert N + struct vm_tree* T, // -> red–black tree + struct vm_tree_node* N, // -> node to be inserted + struct vm_tree_node* P, // -> parent node of N ( may be NULL ) + enum vm_tree_direction dir) // side ( LEFT or RIGHT ) of P where to insert N { - struct vm_tree_node* G; // -> parent node of P - struct vm_tree_node* U; // -> uncle of N - - N->color = RED; - N->left = NIL; - N->right = NIL; - N->parent = P; - if (P == NULL) { // There is no parent - T->root = N; // N is the new root of the tree T. - return; // insertion complete - } - P->child[dir] = N; // insert N as dir-child of P - // start of the (do while)-loop: - do { - if (P->color == BLACK) { - // Case_I1 (P black): - return; // insertion complete - } - // From now on P is red. - if ((G = P->parent) == NULL) - goto Case_I4; // P red and root - // else: P red and G!=NULL. - dir = childDir(P); // the side of parent G on which node P is located - U = G->child[1-dir]; // uncle - if (U == NIL || U->color == BLACK) // considered black - goto Case_I56; // P red && U black - // Case_I2 (P+U red): - P->color = BLACK; - U->color = BLACK; - G->color = RED; - N = G; // new current node - // iterate 1 black level higher - // (= 2 tree levels) - } while ((P = N->parent) != NULL); - // end of the (do while)-loop - // Leaving the (do while)-loop (after having fallen through from Case_I2). - // Case_I3: N is the root and red. - return; // insertion complete + struct vm_tree_node* G; // -> parent node of P + struct vm_tree_node* U; // -> uncle of N + + N->color = RED; + N->left = NIL; + N->right = NIL; + N->parent = P; + if (P == NULL) { // There is no parent + T->root = N; // N is the new root of the tree T. + return; // insertion complete + } + P->child[dir] = N; // insert N as dir-child of P + // start of the (do while)-loop: + do { + if (P->color == BLACK) { + // Case_I1 (P black): + return; // insertion complete + } + // From now on P is red. + if ((G = P->parent) == NULL) + goto Case_I4; // P red and root + // else: P red and G!=NULL. + dir = childDir(P); // the side of parent G on which node P is located + U = G->child[1-dir]; // uncle + if (U == NIL || U->color == BLACK) // considered black + goto Case_I56; // P red && U black + // Case_I2 (P+U red): + P->color = BLACK; + U->color = BLACK; + G->color = RED; + N = G; // new current node + // iterate 1 black level higher + // (= 2 tree levels) + } while ((P = N->parent) != NULL); + // end of the (do while)-loop + // Leaving the (do while)-loop (after having fallen through from Case_I2). + // Case_I3: N is the root and red. + return; // insertion complete Case_I4: // P is the root and red: - P->color = BLACK; - return; // insertion complete + P->color = BLACK; + return; // insertion complete Case_I56: // P red && U black: - if (N == P->child[1-dir]) - { // Case_I5 (P red && U black && N inner grandchild of G): - RotateDir(P,dir); // P is never the root - N = P; // new current node - P = G->child[dir]; // new parent of N - // fall through to Case_I6 - } - // Case_I6 (P red && U black && N outer grandchild of G): - RotateDirRoot(T,G,1-dir); // G may be the root - P->color = BLACK; - G->color = RED; - return; // insertion complete + if (N == P->child[1-dir]) + { // Case_I5 (P red && U black && N inner grandchild of G): + RotateDir(P,dir); // P is never the root + N = P; // new current node + P = G->child[dir]; // new parent of N + // fall through to Case_I6 + } + // Case_I6 (P red && U black && N outer grandchild of G): + RotateDirRoot(T,G,1-dir); // G may be the root + P->color = BLACK; + G->color = RED; + return; // insertion complete } // end of RBinsert1 void vmt_delete( - struct vm_tree* T, // -> red–black tree - struct vm_tree_node* N) // -> node to be deleted + struct vm_tree* T, // -> red–black tree + struct vm_tree_node* N) // -> node to be deleted { - struct vm_tree_node* P = N->parent; // -> parent node of N - enum vm_tree_direction dir; // side of P on which N is located (∈ { LEFT, RIGHT }) - struct vm_tree_node* S; // -> sibling of N - struct vm_tree_node* C; // -> close nephew - struct vm_tree_node* D; // -> distant nephew - - // P != NULL, since N is not the root. - dir = childDir(N); // side of parent P on which the node N is located - // Replace N at its parent P by NIL: - P->child[dir] = NIL; - goto Start_D; // jump into the loop - - // start of the (do while)-loop: - do { - dir = childDir(N); // side of parent P on which node N is located + struct vm_tree_node* P = N->parent; // -> parent node of N + enum vm_tree_direction dir; // side of P on which N is located (∈ { LEFT, RIGHT }) + struct vm_tree_node* S; // -> sibling of N + struct vm_tree_node* C; // -> close nephew + struct vm_tree_node* D; // -> distant nephew + + // P != NULL, since N is not the root. + dir = childDir(N); // side of parent P on which the node N is located + // Replace N at its parent P by NIL: + P->child[dir] = NIL; + goto Start_D; // jump into the loop + + // start of the (do while)-loop: + do { + dir = childDir(N); // side of parent P on which node N is located Start_D: - S = P->child[1-dir]; // sibling of N (has black height >= 1) - D = S->child[1-dir]; // distant nephew - C = S->child[ dir]; // close nephew - if (S->color == RED) - goto Case_D3; // S red ===> P+C+D black - // S is black: - if (D != NIL && D->color == RED) // not considered black - goto Case_D6; // D red && S black - if (C != NIL && C->color == RED) // not considered black - goto Case_D5; // C red && S+D black - // Here both nephews are == NIL (first iteration) or black (later). - if (P->color == RED) - goto Case_D4; // P red && C+S+D black - // Case_D1 (P+C+S+D black): - S->color = RED; - N = P; // new current node (maybe the root) - // iterate 1 black level - // (= 1 tree level) higher - } while ((P = N->parent) != NULL); - // end of the (do while)-loop - // Case_D2 (P == NULL): - return; // deletion complete + S = P->child[1-dir]; // sibling of N (has black height >= 1) + D = S->child[1-dir]; // distant nephew + C = S->child[ dir]; // close nephew + if (S->color == RED) + goto Case_D3; // S red ===> P+C+D black + // S is black: + if (D != NIL && D->color == RED) // not considered black + goto Case_D6; // D red && S black + if (C != NIL && C->color == RED) // not considered black + goto Case_D5; // C red && S+D black + // Here both nephews are == NIL (first iteration) or black (later). + if (P->color == RED) + goto Case_D4; // P red && C+S+D black + // Case_D1 (P+C+S+D black): + S->color = RED; + N = P; // new current node (maybe the root) + // iterate 1 black level + // (= 1 tree level) higher + } while ((P = N->parent) != NULL); + // end of the (do while)-loop + // Case_D2 (P == NULL): + return; // deletion complete Case_D3: // S red && P+C+D black: - RotateDirRoot(T,P,dir); // P may be the root - P->color = RED; - S->color = BLACK; - S = C; // != NIL - // now: P red && S black - D = S->child[1-dir]; // distant nephew - if (D != NIL && D->color == RED) - goto Case_D6; // D red && S black - C = S->child[ dir]; // close nephew - if (C != NIL && C->color == RED) - goto Case_D5; // C red && S+D black - // Otherwise C+D considered black. - // fall through to Case_D4 - // Case_D4: // P red && S+C+D black: - S->color = RED; - P->color = BLACK; - return; // deletion complete + RotateDirRoot(T,P,dir); // P may be the root + P->color = RED; + S->color = BLACK; + S = C; // != NIL + // now: P red && S black + D = S->child[1-dir]; // distant nephew + if (D != NIL && D->color == RED) + goto Case_D6; // D red && S black + C = S->child[ dir]; // close nephew + if (C != NIL && C->color == RED) + goto Case_D5; // C red && S+D black + // Otherwise C+D considered black. + // fall through to Case_D4 + // Case_D4: // P red && S+C+D black: + S->color = RED; + P->color = BLACK; + return; // deletion complete Case_D4: // P red && S+C+D black: - S->color = RED; - P->color = BLACK; - return; // deletion complete + S->color = RED; + P->color = BLACK; + return; // deletion complete Case_D5: // C red && S+D black: - RotateDir(S,1-dir); // S is never the root - S->color = RED; - C->color = BLACK; - D = S; - S = C; - // now: D red && S black - // fall through to Case_D6 + RotateDir(S,1-dir); // S is never the root + S->color = RED; + C->color = BLACK; + D = S; + S = C; + // now: D red && S black + // fall through to Case_D6 Case_D6: // D red && S black: - RotateDirRoot(T,P,dir); // P may be the root - S->color = P->color; - P->color = BLACK; - D->color = BLACK; - return; // deletion complete + RotateDirRoot(T,P,dir); // P may be the root + S->color = P->color; + P->color = BLACK; + D->color = BLACK; + return; // deletion complete } // end of RBdelete2 enum vm_tree_direction vmn_child_direction( - struct vm_tree_node *n, - struct vm_tree_node *p -) + struct vm_tree_node *n, + struct vm_tree_node *p + ) { - if (p && (compare_key(&p->key, &n->key) > 0)) - { - return LEFT; - } - return RIGHT; + if (p && (compare_key(&p->key, &n->key) > 0)) + { + return LEFT; + } + return RIGHT; } struct vm_tree_node *vmt_search_key( - struct vm_tree *tree, - struct vm_tree_key *key) + struct vm_tree *tree, + struct vm_tree_key *key) { - struct vm_tree_node *N = tree->root; - - while (N && (compare_key(&N->key, key) != 0)) - { - if (compare_key(&N->key, key) > 0) - { - N = N->left; - } - else - { - N = N->right; - } - } - - return N; + struct vm_tree_node *N = tree->root; + + while (N && (compare_key(&N->key, key) != 0)) + { + if (compare_key(&N->key, key) > 0) + { + N = N->left; + } + else + { + N = N->right; + } + } + + return N; } // search for the predecessor node for a given key struct vm_tree_node *vmn_predecessor_key( - struct vm_tree_node *N, - struct vm_tree_key *key) + struct vm_tree_node *N, + struct vm_tree_key *key) { - struct vm_tree_node *P = NULL; - - while (N && (compare_key(&N->key, key) != 0)) - { - if (compare_key(&N->key, key) > 0) - { - N = N->left; - } - else - { - P = N; - N = N->right; - } - } - - return P; + struct vm_tree_node *P = NULL; + + while (N && (compare_key(&N->key, key) != 0)) + { + if (compare_key(&N->key, key) > 0) + { + N = N->left; + } + else + { + P = N; + N = N->right; + } + } + + return P; } // search for the successor node for a given key struct vm_tree_node *vmn_successor_node( - struct vm_tree_node *N) + struct vm_tree_node *N) { - if (N && N->right) - { - return vmn_min(N->right); - } - - struct vm_tree_node *p = N->parent; - while (p && N == p->right) - { - N = p; - p = p->parent; - } - - return p; + if (N && N->right) + { + return vmn_min(N->right); + } + + struct vm_tree_node *p = N->parent; + while (p && N == p->right) + { + N = p; + p = p->parent; + } + + return p; } struct vm_tree_node *vmn_min(struct vm_tree_node *node) { - while (node && node->left) - { - node = node->left; - } + while (node && node->left) + { + node = node->left; + } - return node; + return node; } struct vm_tree_node *vmn_max(struct vm_tree_node *node) { - while (node && node->right) - { - node = node->right; - } + while (node && node->right) + { + node = node->right; + } - return node; + return node; } struct vm_object *vmt_get_object(struct vm_tree *tree, void *address) { - // find the object for a page. - struct vm_tree_key key = {(uintptr_t)address, 1}; - struct vm_tree_node *node = vmt_search_key(tree, &key); - if (node) - { - return node->object; - } - return NULL; + // find the object for a page. + struct vm_tree_key key = {(uintptr_t)address, 1}; + struct vm_tree_node *node = vmt_search_key(tree, &key); + if (node) + { + return node->object; + } + return NULL; } diff --git a/kc/defaults.mk b/kc/defaults.mk index e44781b..ef4e397 100644 --- a/kc/defaults.mk +++ b/kc/defaults.mk @@ -1,4 +1,5 @@ +LDSCRIPT = ../kc.ld CFLAGS += -fPIC -fvisibility=hidden -mgeneral-regs-only diff --git a/lib/api/libc/stdio.h b/lib/api/libc/stdio.h new file mode 100644 index 0000000..81ee462 --- /dev/null +++ b/lib/api/libc/stdio.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include + +typedef struct FILE FILE; + +extern FILE *stdout; +extern FILE *stderr; + +extern int fputc(int c, FILE *f); + +int vfprintf(FILE *f, const char *restrict format, va_list arguments); +int fprintf(FILE *f, const char *restrict format, ...); +int vprintf(const char *restrict format, va_list arguments); +int printf(const char *restrict format, ...); +int vsprintf(char *s, const char *restrict format, va_list arguments); +int sprintf(char *s, const char *restrict format, ...); + diff --git a/lib/api/libc/stdlib.h b/lib/api/libc/stdlib.h new file mode 100644 index 0000000..d1f3d3b --- /dev/null +++ b/lib/api/libc/stdlib.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +size_t mbstowcs(wchar_t *dst, const char *src, size_t length); +void *malloc(size_t size); +void *calloc(size_t count, size_t size); +void free(void *block); + diff --git a/lib/api/libc/string.h b/lib/api/libc/string.h new file mode 100644 index 0000000..c28e17c --- /dev/null +++ b/lib/api/libc/string.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +void *memcpy(void *dest, const void *src, size_t size); +void *memmove(void *dest, const void *src, size_t size); +void *memset(void *dest, int val, size_t size); +int memcmp(const void *str1, const void *str2, size_t count); +size_t strlen(const char *s); +unsigned long long strtoull( + const char *restrict begin, + char **restrict end, + int base); + diff --git a/lib/api/libc/wchar.h b/lib/api/libc/wchar.h new file mode 100644 index 0000000..350c209 --- /dev/null +++ b/lib/api/libc/wchar.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +size_t wcslen(const wchar_t *s); +wchar_t *wcsdup(const wchar_t *s); diff --git a/lib/elf64.c b/lib/elf64.c index b5afc18..38cfc11 100644 --- a/lib/elf64.c +++ b/lib/elf64.c @@ -79,7 +79,7 @@ bool elf64_validate_fd(int fd, unsigned type, unsigned machine) return elf64_validate(&ehdr, type, machine); } -static bool is_loadable(Elf64_Phdr const *phdr) +static bool segment_is_loadable(Elf64_Phdr const *phdr) { return phdr != nullptr && phdr->p_type == PT_LOAD; } @@ -92,7 +92,7 @@ static size_t aligned_size(Elf64_Phdr const *phdr) static size_t segment_memsz(Elf64_Phdr const *phdr) { - return is_loadable(phdr) ? aligned_size(phdr) : 0; + return segment_is_loadable(phdr) ? aligned_size(phdr) : 0; } size_t elf64_size(Elf64_Ehdr const *ehdr, Elf64_Phdr *phdrs) @@ -168,7 +168,7 @@ uintptr_t elf64_dt_val(Elf64_Dyn *dyntab, unsigned long dt_type) static ssize_t load_segment(int fd, Elf64_Phdr *phdr, size_t buffer_size, char buffer[buffer_size]) { - if (!is_loadable(phdr)) + if (!segment_is_loadable(phdr)) { return 0; } diff --git a/lib/heap.c b/lib/heap.c index 8331a17..03d5425 100644 --- a/lib/heap.c +++ b/lib/heap.c @@ -1,23 +1,143 @@ #include #include +#include + +#include "config.h" struct heap_node { - struct heap_node *next; size_t size; + union + { + struct + { + struct heap_node *prev; + struct heap_node *next; + }; + struct + { + char block; + }; + }; }; struct heap_head { - struct heap_node *first; - size_t total_bytes; - size_t ready_bytes; + size_t allocated_size; + size_t maximum_size; + size_t minimum_alloc; + struct heap_node *root; }; +struct heap_head *heap; + +void *allocate_superblock(void) +{ + return mmap(nullptr, HEAP_SEGMENT_SIZE, 0, 0, -1, 0); +} + +static struct heap_node *try_create_heap() +{ + if (heap != nullptr) return heap->root; + + char *superblock = allocate_superblock(); + + heap = (struct heap_head *)superblock; + heap->root = (struct heap_node *)(superblock + sizeof(struct heap_head)); + + heap->allocated_size = HEAP_SEGMENT_SIZE; + heap->maximum_size = HEAP_MAX_SIZE; + heap->minimum_alloc = sizeof(heap->root); + + heap->root->next = nullptr; + heap->root->prev = nullptr; + heap->root->size = HEAP_SEGMENT_SIZE - sizeof(superblock) - sizeof(heap->root->size); + + return heap->root; +} + +static struct heap_node *try_grow_heap(struct heap_node *prev) +{ + if (heap->allocated_size >= heap->maximum_size) + { + return nullptr; + } + + struct heap_node *node = allocate_superblock(); + + if (node == nullptr) + { + return nullptr; + } + + node->size = HEAP_SEGMENT_SIZE - sizeof(node->size); + node->prev = prev; + prev->next = node; + + return node; +} + +static inline size_t get_node_size(size_t size) +{ + return size + sizeof(size); +} + +static inline bool can_divide_node(struct heap_node *parent) +{ + return parent->size <= heap->minimum_alloc + sizeof(struct heap_node); +} + +static inline void *new_node_address(struct heap_node *parent, size_t size) +{ + return &parent->block + parent->size - get_node_size(size); +} + +static inline struct heap_node *divide_node(struct heap_node *parent, size_t size) +{ + struct heap_node *child = new_node_address(parent, size); + + parent->size -= get_node_size(size); + child->size = size; + + return child; +} + +static inline struct heap_node *try_divide_node(struct heap_node *node, size_t size) +{ + return can_divide_node(node) ? divide_node(node, size) : node; +} + +static inline struct heap_node *allocate_node(size_t size) +{ + struct heap_node *node = try_create_heap(); + + while(node != nullptr) + { + if (node->size > size) + { + break; + } + + node = node->next + ? node->next + : try_grow_heap(node); + } + + return node; +} + void *malloc(size_t size) { - (void)size; - return nullptr; + // TODO: min(heap->minimum_alloc, size); + size = size >= heap->minimum_alloc + ? size + : heap->minimum_alloc; + + struct heap_node *node = try_divide_node(allocate_node(size), size); + + return node != nullptr + ? &node->block + : nullptr; } void *calloc(size_t count, size_t size) @@ -32,8 +152,28 @@ void *calloc(size_t count, size_t size) return block; } +static inline struct heap_node *block_to_node(char *block) +{ + return (struct heap_node *)(block - sizeof(size_t)); +} + void free(void *block) { - (void)block; + // TODO: insertion sort by address of node + // TODO: coalesce adjacent nodes + if (block == nullptr) + { + return; + } + + struct heap_node *node = block_to_node(block); + node->prev = nullptr; + node->next = heap->root; + heap->root = node; + + if (node->next != nullptr) + { + node->next->prev = node; + } } diff --git a/lib/kprintf.c b/lib/kprintf.c deleted file mode 100644 index 3d803f6..0000000 --- a/lib/kprintf.c +++ /dev/null @@ -1,653 +0,0 @@ -#include -#include -#include -#include - -enum specifier_type -{ - INVALID_PRINT, - CHARACTER_PRINT, - STRING_PRINT, - INTEGER_PRINT, - COUNT_PRINT -}; - -enum specifier_flags -{ - INVALID_FLAGS, - LEFT_JUSTIFY_FLAG = 0x1, - EXPLICIT_SIGN_FLAG = 0x2, - PAD_SIGN_FLAG = 0x4, - SIGNED_TYPE_FLAG = 0x8, - SIGN_FLAG_MASK = 0x6, - ALTERNATE_FORM_FLAG = 0x10, - ZERO_PAD_FLAG = 0x20, -}; - -enum specifier_integer_base -{ - INVALID_BASE, - BIN_BASE = 2, - OCT_BASE = 8, - DEC_BASE = 10, - HEX_BASE = 16 -}; - -enum specifier_integer_width -{ - INVALID_WIDTH, - BYTE_WIDTH = 8, - SHORT_WIDTH = 16, - INT_WIDTH = 32, - LONG_WIDTH = 64 -}; - -struct method -{ - int (*write_character)(struct method *m, char c); - int (*write_wcharacter)(struct method *m, wchar_t c); - int (*write_string)(struct method *m, const char *s); - int (*write_wstring)(struct method *m, const wchar_t *s); - void *output; - int count; -}; - -struct specifier -{ - enum specifier_type type; - enum specifier_flags flags; - enum specifier_integer_width integer_width; - enum specifier_integer_base integer_base; - int field_width; - int field_precision; - size_t length; -}; - -static struct specifier parse_specifier(const char *format, va_list *arguments) -{ - struct specifier result = - { - INVALID_PRINT, - INVALID_FLAGS, - INVALID_WIDTH, - INVALID_BASE, - 0, - 0, - 0 - }; - - // keep a pointer to the beginning of the specifier - const char *begin = format; - - // step 1: scan for flags - bool flags_parsed = false; - - while (!flags_parsed) - { - switch (*begin) - { - case 0: - result.type = INVALID_PRINT; - return result; - case '-': - result.flags |= LEFT_JUSTIFY_FLAG; - begin++; - break; - case '+': - result.flags |= EXPLICIT_SIGN_FLAG; - begin++; - break; - case ' ': - result.flags |= PAD_SIGN_FLAG; - begin++; - break; - case '#': - result.flags |= ALTERNATE_FORM_FLAG; - begin++; - break; - case '0': - result.flags |= ZERO_PAD_FLAG; - begin++; - break; - default: - flags_parsed = true; - } - } - - // step 2: parse field width and precision - // TODO: parse field width and precision - /* procedure: - * 1. check if next character is * or . - * a. if *, field width is the value pointed to by the next item in - * arguments list - * b. if ., field width will be set to 0, proceed to 3. - * 2. if above check is false, check for numeric character - * a. if is numeric character, parse field width via strtoul, add length - * of numeric string to begin. - * b. if is not a numeric character, skip check for width and precision - * entirely - * 3. check if next character is * or numeric - * a. if *, field precision is the value pointed to by the next item - * in arguments list. - * b. if is numeric character, parse field precision via strtoul, add - * length of numeric string to begin. - */ - bool field_width_parsed = false; - bool field_precision_parsed = false; - - while (!field_width_parsed || !field_precision_parsed) - { - switch (*begin) - { - case '.': - if (!field_width_parsed) - { - result.field_width = 0; - field_width_parsed = true; - begin++; - } - else - { - // the specifier is invalid! - result.type = INVALID_PRINT; - } - break; - case '*': - { - int w = *va_arg(*arguments, int *); - if (!field_width_parsed) - { - result.field_width = w; - field_width_parsed = true; - } - else - { - result.field_precision = w; - field_precision_parsed = true; - } - begin++; - } - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '0': - { - char *next; - unsigned long long w = strtoull(begin, &next, 10); - if (!field_width_parsed) - { - result.field_width = (int)w; - field_width_parsed = true; - } - else if (!field_precision_parsed) - { - result.field_precision = (int)w; - field_precision_parsed = true; - } - if (next > begin) - { - begin = next; - } - } - break; - default: - field_width_parsed = true; - field_precision_parsed = true; - } - } - - // step 3: check for type width arguments - switch (*begin) - { - case 0: // unexpected eos - result.type = INVALID_PRINT; - return result; - case 'h': - { - if (begin[0] == begin[1]) - { - result.integer_width = BYTE_WIDTH; - begin += 2; - } - else - { - result.integer_width = SHORT_WIDTH; - begin++; - } - break; - } - case 'l': - { - // TODO: deal with LLP64? idk. - if (begin[0] == begin[1]) - { - begin += 2; - } - else - { - begin++; - } - result.integer_width = LONG_WIDTH; - break; - } - case 'j': - // TODO: use INTMAX_T_WIDTH - result.integer_width = 64; - begin++; - break; - case 'z': - // TODO: use SIZE_T_WIDTH? - result.integer_width = 64; - begin++; - break; - case 't': - // TODO: use PTRDIFF_T_WIDTH? - result.integer_width = 32; - begin++; - break; - default: - // there is no width argument to be found. - result.integer_width = INT_WIDTH; - break; - } - - // step 4: parse field type - switch (*begin) - { - // unexpected EOS - case 0: - result.type = INVALID_PRINT; - return result; - case 'c': - result.type = CHARACTER_PRINT; - break; - case 's': - result.type = STRING_PRINT; - break; - case 'd': - case 'i': - result.type = INTEGER_PRINT; - result.flags |= SIGNED_TYPE_FLAG; - result.integer_base = DEC_BASE; - break; - case 'u': - result.type = INTEGER_PRINT; - result.integer_base = DEC_BASE; - break; - case 'b': - result.type = INTEGER_PRINT; - result.integer_base = BIN_BASE; - break; - case 'o': - result.type = INTEGER_PRINT; - result.integer_base = OCT_BASE; - break; - case 'x': - result.type = INTEGER_PRINT; - result.integer_base = HEX_BASE; - break; - case 'p': - // TODO: use UINTPTR_T_WIDTH here? - // pointer type overrides all flags - // i can do what i want it says "implementation-defined" in the spec - result.field_precision = 16; - result.type = INTEGER_PRINT; - result.integer_base = HEX_BASE; - result.integer_width = LONG_WIDTH; - result.flags = ALTERNATE_FORM_FLAG|ZERO_PAD_FLAG; - break; - case 'n': - // all flags are invalid/ignored and the current count will be - // stored in the value pointed to by the argument - result.type = COUNT_PRINT; - result.flags = INVALID_FLAGS; - result.integer_width = INVALID_WIDTH; - result.field_width = 0; - result.field_precision = 0; - break; - default: - // this byte of the specifier _must_ be valid. if not, - // the procedure to print should not proceed as it might - // output garbage. - // TODO: specify somehow in the output that the format is bad? - result.type = INVALID_PRINT; - return result; - } - - begin++; - - result.length = begin - format; - return result; -} - -static char *convert_integer( - uint64_t value, - unsigned base, - int zpadding, - char *buffer, - size_t bufsz) -{ - static const char *stringdigits = "0123456789abcdef"; - - // the string will be built from the lower-to-higher value, and the - // result pointer will point to the first character of the string in - // the supplied buffer - - // cannot currently work with a base > 16 - if (base > 16) - { - return NULL; - } - - // make absolutely sure there are no excess bits - - // the string is being built backwards, so the pointer needs to be - // at the last byte of the string - char *result = buffer + bufsz - 1; - - // result >= buffer condition ensures we don't underflow - - do - { - *--result = stringdigits[value % base]; - zpadding--; - value /= base; - } - while (value > 0 && result >= buffer); - - while (zpadding-- > 0 && result >= buffer) - { - *--result = '0'; - } - - return result; -} - -static int print_character( - struct method *m, - struct specifier *spec, - va_list *arguments) -{ - (void)spec; - // all specifier flags and etc. are ignored. - return m->write_character(m, va_arg(*arguments, int)); -} - -static int print_string( - struct method *m, - struct specifier *spec, - va_list *arguments) -{ - // TODO: respect field width - if (spec->integer_width == LONG_WIDTH) - { - return m->write_wstring(m, va_arg(*arguments, const wchar_t *)); - } - return m->write_string(m, va_arg(*arguments, const char *)); -} - -static inline bool is_negative(uint64_t value, unsigned width) -{ - switch (width) - { - case BYTE_WIDTH: - return ((int8_t)value) < 0; - case SHORT_WIDTH: - return ((int16_t)value) < 0; - case INT_WIDTH: - return ((int32_t)value) < 0; - case LONG_WIDTH: - return ((int64_t)value) < 0; - default: - return false; - } -} - -static int print_integer( - struct method *m, - struct specifier *spec, - va_list *arguments) -{ - // 65 bytes is the maximum length that convert_integer will need - // i.e. conversion of uintmax_t to binary plus NUL terminator - // TODO: use UINTMAX_T_WIDTH + 1? - char buffer[65] = {0}; - char *s; - bool negative = false; - uint64_t value = 0; - int r = 0; - unsigned zpad = 0; - - switch (spec->integer_width) - { - case BYTE_WIDTH: - case SHORT_WIDTH: - case INT_WIDTH: - value = va_arg(*arguments, unsigned int); - break; - case LONG_WIDTH: - value = va_arg(*arguments, uint64_t); - break; - default: - return -1; - } - - // check if we need to bother with signs - if (spec->flags & SIGNED_TYPE_FLAG) - { - if ((negative = is_negative(value, spec->integer_width))) - { - value = ~value + 1; - } - - if (negative) - { - r = m->write_character(m, '-'); - } - - else if (!negative && (spec->flags & SIGN_FLAG_MASK)) - { - if (spec->flags & EXPLICIT_SIGN_FLAG) - { - r = m->write_character(m, '+'); - } - else - { - r = m->write_character(m, ' '); - } - } - } - - // zero all the unnecessary bits - value &= (2ULL << (spec->integer_width - 1)) - 1; - - switch (spec->integer_base) - { - case BIN_BASE: - if (spec->flags & ALTERNATE_FORM_FLAG) - { - r = m->write_string(m, "0b"); - } - break; - case OCT_BASE: - if (spec->flags & ALTERNATE_FORM_FLAG) - { - r = m->write_character(m, '0'); - } - break; - case HEX_BASE: - if (spec->flags & ALTERNATE_FORM_FLAG) - { - r = m->write_string(m, "0x"); - } - break; - default: - break; - } - - if (spec->flags & ZERO_PAD_FLAG) - { - zpad = spec->field_precision; - } - s = convert_integer(value, spec->integer_base, zpad, buffer, sizeof(buffer)); - - if (s) - { - m->write_string(m, s); - } - else - { - m->write_string(m, "(INVALID)"); - r = -1; - } - - return r; -} - -static int printf_internal( - struct method *m, - const char *restrict format, - va_list *arguments) -{ - int r = 0; - - while (*format && !r) - { - // case 1: not a format specification - if (*format != '%') - { - r = m->write_character(m, *format++); - continue; - } - - // case 2: looks like a format specification, but isn't - else if (*format == '%' && format[0] == format[1]) - { - r = m->write_character(m, '%'); - format += 2; - continue; - } - - // case 3: is a format specification. parse it - struct specifier spec = parse_specifier(++format, arguments); - - switch (spec.type) - { - case CHARACTER_PRINT: - r = print_character(m, &spec, arguments); - break; - case STRING_PRINT: - r = print_string(m, &spec, arguments); - break; - case INTEGER_PRINT: - r = print_integer(m, &spec, arguments); - break; - default: - r = m->write_string(m, "(INVALID)"); - return -1; - } - format += spec.length; - } - - return r; -} - -static int kfp_write_character(struct method *m, char c) -{ - kfputc((int)c, (FILE *)m->output); - m->count++; - return 0; -} - -static int kfp_write_wcharacter(struct method *m, wchar_t c) -{ - kfputc((int)c, (FILE *)m->output); - m->count++; - return 0; -} - -static int kfp_write_string(struct method *m, const char *c) -{ - while (*c) - { - m->write_character(m, *c++); - } - return 0; -} - -static int kfp_write_wstring(struct method *m, const wchar_t *s) -{ - while (*s) - { - m->write_wcharacter(m, *s++); - } - return 0; -} - -int kvfprintf(FILE *f, const char *restrict format, va_list arguments) -{ - struct method m = { - kfp_write_character, - kfp_write_wcharacter, - kfp_write_string, - kfp_write_wstring, - (void *)f, - 0}; - va_list acopy; - va_copy(acopy, arguments); - int r = printf_internal(&m, format, &acopy); - va_end(acopy); - - if (!r) - { - return m.count; - } - else - { - return -1; - } -} - -int kfprintf(FILE *f, const char *restrict format, ...) -{ - va_list arguments; - va_start(arguments, format); - - int count = kvfprintf(f, format, arguments); - - va_end(arguments); - - return count; -} - -int kvprintf(const char *restrict format, va_list arguments) -{ - va_list acopy; - va_copy(acopy, arguments); - - int count = kvfprintf(kstdout, format, arguments); - - va_end(acopy); - - return count; -} - -int kprintf(const char *restrict format, ...) -{ - va_list arguments; - va_start(arguments, format); - - int count = kvprintf(format, arguments); - - va_end(arguments); - - return count; -} - diff --git a/lib/libc/memcmp.c b/lib/libc/memcmp.c new file mode 100644 index 0000000..2348afe --- /dev/null +++ b/lib/libc/memcmp.c @@ -0,0 +1,16 @@ +/* Public domain. */ +#include + +int +memcmp (const void *str1, const void *str2, size_t count) +{ + const unsigned char *s1 = str1; + const unsigned char *s2 = str2; + + while (count-- > 0) + { + if (*s1++ != *s2++) + return s1[-1] < s2[-1] ? -1 : 1; + } + return 0; +} diff --git a/lib/libc/memcpy.c b/lib/libc/memcpy.c new file mode 100644 index 0000000..58b1e40 --- /dev/null +++ b/lib/libc/memcpy.c @@ -0,0 +1,12 @@ +/* Public domain. */ +#include + +void * +memcpy (void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + while (len--) + *d++ = *s++; + return dest; +} diff --git a/lib/libc/memmove.c b/lib/libc/memmove.c new file mode 100644 index 0000000..fd06bb6 --- /dev/null +++ b/lib/libc/memmove.c @@ -0,0 +1,20 @@ +/* Public domain. */ +#include + +void * +memmove (void *dest, const void *src, size_t len) +{ + char *d = dest; + const char *s = src; + if (d < s) + while (len--) + *d++ = *s++; + else + { + const char *lasts = s + (len-1); + char *lastd = d + (len-1); + while (len--) + *lastd-- = *lasts--; + } + return dest; +} diff --git a/lib/libc/memset.c b/lib/libc/memset.c new file mode 100644 index 0000000..3e7025e --- /dev/null +++ b/lib/libc/memset.c @@ -0,0 +1,11 @@ +/* Public domain. */ +#include + +void * +memset (void *dest, int val, size_t len) +{ + unsigned char *ptr = dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} diff --git a/lib/libc/printf.c b/lib/libc/printf.c new file mode 100644 index 0000000..fa68bcc --- /dev/null +++ b/lib/libc/printf.c @@ -0,0 +1,712 @@ +#include +#include +#include +#include + +enum specifier_type +{ + INVALID_PRINT, + CHARACTER_PRINT, + STRING_PRINT, + INTEGER_PRINT, + COUNT_PRINT +}; + +enum specifier_flags +{ + INVALID_FLAGS, + LEFT_JUSTIFY_FLAG = 0x1, + EXPLICIT_SIGN_FLAG = 0x2, + PAD_SIGN_FLAG = 0x4, + SIGNED_TYPE_FLAG = 0x8, + SIGN_FLAG_MASK = 0x6, + ALTERNATE_FORM_FLAG = 0x10, + ZERO_PAD_FLAG = 0x20, +}; + +enum specifier_integer_base +{ + INVALID_BASE, + BIN_BASE = 2, + OCT_BASE = 8, + DEC_BASE = 10, + HEX_BASE = 16 +}; + +enum specifier_integer_width +{ + INVALID_WIDTH, + BYTE_WIDTH = 8, + SHORT_WIDTH = 16, + INT_WIDTH = 32, + LONG_WIDTH = 64 +}; + +struct method +{ + int (*write_character)(struct method *m, char c); + int (*write_wcharacter)(struct method *m, wchar_t c); + int (*write_string)(struct method *m, const char *s); + int (*write_wstring)(struct method *m, const wchar_t *s); + void *output; + int count; +}; + +struct specifier +{ + enum specifier_type type; + enum specifier_flags flags; + enum specifier_integer_width integer_width; + enum specifier_integer_base integer_base; + int field_width; + int field_precision; + size_t length; +}; + +static struct specifier parse_specifier(const char *format, va_list *arguments) +{ + struct specifier result = + { + INVALID_PRINT, + INVALID_FLAGS, + INVALID_WIDTH, + INVALID_BASE, + 0, + 0, + 0 + }; + + // keep a pointer to the beginning of the specifier + const char *begin = format; + + // step 1: scan for flags + bool flags_parsed = false; + + while (!flags_parsed) + { + switch (*begin) + { + case 0: + result.type = INVALID_PRINT; + return result; + case '-': + result.flags |= LEFT_JUSTIFY_FLAG; + begin++; + break; + case '+': + result.flags |= EXPLICIT_SIGN_FLAG; + begin++; + break; + case ' ': + result.flags |= PAD_SIGN_FLAG; + begin++; + break; + case '#': + result.flags |= ALTERNATE_FORM_FLAG; + begin++; + break; + case '0': + result.flags |= ZERO_PAD_FLAG; + begin++; + break; + default: + flags_parsed = true; + } + } + + // step 2: parse field width and precision + // TODO: parse field width and precision + /* procedure: + * 1. check if next character is * or . + * a. if *, field width is the value pointed to by the next item in + * arguments list + * b. if ., field width will be set to 0, proceed to 3. + * 2. if above check is false, check for numeric character + * a. if is numeric character, parse field width via strtoul, add length + * of numeric string to begin. + * b. if is not a numeric character, skip check for width and precision + * entirely + * 3. check if next character is * or numeric + * a. if *, field precision is the value pointed to by the next item + * in arguments list. + * b. if is numeric character, parse field precision via strtoul, add + * length of numeric string to begin. + */ + bool field_width_parsed = false; + bool field_precision_parsed = false; + + while (!field_width_parsed || !field_precision_parsed) + { + switch (*begin) + { + case '.': + if (!field_width_parsed) + { + result.field_width = 0; + field_width_parsed = true; + begin++; + } + else + { + // the specifier is invalid! + result.type = INVALID_PRINT; + } + break; + case '*': + { + int w = *va_arg(*arguments, int *); + if (!field_width_parsed) + { + result.field_width = w; + field_width_parsed = true; + } + else + { + result.field_precision = w; + field_precision_parsed = true; + } + begin++; + } + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + { + char *next; + unsigned long long w = strtoull(begin, &next, 10); + if (!field_width_parsed) + { + result.field_width = (int)w; + field_width_parsed = true; + } + else if (!field_precision_parsed) + { + result.field_precision = (int)w; + field_precision_parsed = true; + } + if (next > begin) + { + begin = next; + } + } + break; + default: + field_width_parsed = true; + field_precision_parsed = true; + } + } + + // step 3: check for type width arguments + switch (*begin) + { + case 0: // unexpected eos + result.type = INVALID_PRINT; + return result; + case 'h': + { + if (begin[0] == begin[1]) + { + result.integer_width = BYTE_WIDTH; + begin += 2; + } + else + { + result.integer_width = SHORT_WIDTH; + begin++; + } + break; + } + case 'l': + { + // TODO: deal with LLP64? idk. + if (begin[0] == begin[1]) + { + begin += 2; + } + else + { + begin++; + } + result.integer_width = LONG_WIDTH; + break; + } + case 'j': + // TODO: use INTMAX_T_WIDTH + result.integer_width = 64; + begin++; + break; + case 'z': + // TODO: use SIZE_T_WIDTH? + result.integer_width = 64; + begin++; + break; + case 't': + // TODO: use PTRDIFF_T_WIDTH? + result.integer_width = 32; + begin++; + break; + default: + // there is no width argument to be found. + result.integer_width = INT_WIDTH; + break; + } + + // step 4: parse field type + switch (*begin) + { + // unexpected EOS + case 0: + result.type = INVALID_PRINT; + return result; + case 'c': + result.type = CHARACTER_PRINT; + break; + case 's': + result.type = STRING_PRINT; + break; + case 'd': + case 'i': + result.type = INTEGER_PRINT; + result.flags |= SIGNED_TYPE_FLAG; + result.integer_base = DEC_BASE; + break; + case 'u': + result.type = INTEGER_PRINT; + result.integer_base = DEC_BASE; + break; + case 'b': + result.type = INTEGER_PRINT; + result.integer_base = BIN_BASE; + break; + case 'o': + result.type = INTEGER_PRINT; + result.integer_base = OCT_BASE; + break; + case 'x': + result.type = INTEGER_PRINT; + result.integer_base = HEX_BASE; + break; + case 'p': + // TODO: use UINTPTR_T_WIDTH here? + // pointer type overrides all flags + // i can do what i want it says "implementation-defined" in the spec + result.field_precision = 16; + result.type = INTEGER_PRINT; + result.integer_base = HEX_BASE; + result.integer_width = LONG_WIDTH; + result.flags = ALTERNATE_FORM_FLAG|ZERO_PAD_FLAG; + break; + case 'n': + // all flags are invalid/ignored and the current count will be + // stored in the value pointed to by the argument + result.type = COUNT_PRINT; + result.flags = INVALID_FLAGS; + result.integer_width = INVALID_WIDTH; + result.field_width = 0; + result.field_precision = 0; + break; + default: + // this byte of the specifier _must_ be valid. if not, + // the procedure to print should not proceed as it might + // output garbage. + // TODO: specify somehow in the output that the format is bad? + result.type = INVALID_PRINT; + return result; + } + + begin++; + + result.length = begin - format; + return result; +} + +static char *convert_integer( + uint64_t value, + unsigned base, + int zpadding, + char *buffer, + size_t bufsz) +{ + static const char *stringdigits = "0123456789abcdef"; + + // the string will be built from the lower-to-higher value, and the + // result pointer will point to the first character of the string in + // the supplied buffer + + // cannot currently work with a base > 16 + if (base > 16) + { + return NULL; + } + + // make absolutely sure there are no excess bits + + // the string is being built backwards, so the pointer needs to be + // at the last byte of the string + char *result = buffer + bufsz - 1; + + // result >= buffer condition ensures we don't underflow + + do + { + *--result = stringdigits[value % base]; + zpadding--; + value /= base; + } + while (value > 0 && result >= buffer); + + while (zpadding-- > 0 && result >= buffer) + { + *--result = '0'; + } + + return result; +} + +static int print_character( + struct method *m, + struct specifier *spec, + va_list *arguments) +{ + (void)spec; + // all specifier flags and etc. are ignored. + return m->write_character(m, va_arg(*arguments, int)); +} + +static int print_string( + struct method *m, + struct specifier *spec, + va_list *arguments) +{ + // TODO: respect field width + if (spec->integer_width == LONG_WIDTH) + { + return m->write_wstring(m, va_arg(*arguments, const wchar_t *)); + } + return m->write_string(m, va_arg(*arguments, const char *)); +} + +static inline bool is_negative(uint64_t value, unsigned width) +{ + switch (width) + { + case BYTE_WIDTH: + return ((int8_t)value) < 0; + case SHORT_WIDTH: + return ((int16_t)value) < 0; + case INT_WIDTH: + return ((int32_t)value) < 0; + case LONG_WIDTH: + return ((int64_t)value) < 0; + default: + return false; + } +} + +static int print_integer( + struct method *m, + struct specifier *spec, + va_list *arguments) +{ + // 65 bytes is the maximum length that convert_integer will need + // i.e. conversion of uintmax_t to binary plus NUL terminator + // TODO: use UINTMAX_T_WIDTH + 1? + char buffer[65] = {0}; + char *s; + bool negative = false; + uint64_t value = 0; + int r = 0; + unsigned zpad = 0; + + switch (spec->integer_width) + { + case BYTE_WIDTH: + case SHORT_WIDTH: + case INT_WIDTH: + value = va_arg(*arguments, unsigned int); + break; + case LONG_WIDTH: + value = va_arg(*arguments, uint64_t); + break; + default: + return -1; + } + + // check if we need to bother with signs + if (spec->flags & SIGNED_TYPE_FLAG) + { + if ((negative = is_negative(value, spec->integer_width))) + { + value = ~value + 1; + } + + if (negative) + { + r = m->write_character(m, '-'); + } + + else if (!negative && (spec->flags & SIGN_FLAG_MASK)) + { + if (spec->flags & EXPLICIT_SIGN_FLAG) + { + r = m->write_character(m, '+'); + } + else + { + r = m->write_character(m, ' '); + } + } + } + + // zero all the unnecessary bits + value &= (2ULL << (spec->integer_width - 1)) - 1; + + switch (spec->integer_base) + { + case BIN_BASE: + if (spec->flags & ALTERNATE_FORM_FLAG) + { + r = m->write_string(m, "0b"); + } + break; + case OCT_BASE: + if (spec->flags & ALTERNATE_FORM_FLAG) + { + r = m->write_character(m, '0'); + } + break; + case HEX_BASE: + if (spec->flags & ALTERNATE_FORM_FLAG) + { + r = m->write_string(m, "0x"); + } + break; + default: + break; + } + + if (spec->flags & ZERO_PAD_FLAG) + { + zpad = spec->field_precision; + } + s = convert_integer(value, spec->integer_base, zpad, buffer, sizeof(buffer)); + + if (s) + { + m->write_string(m, s); + } + else + { + m->write_string(m, "(INVALID)"); + r = -1; + } + + return r; +} + +static int printf_internal( + struct method *m, + const char *restrict format, + va_list *arguments) +{ + int r = 0; + + while (*format && !r) + { + // case 1: not a format specification + if (*format != '%') + { + r = m->write_character(m, *format++); + continue; + } + + // case 2: looks like a format specification, but isn't + else if (*format == '%' && format[0] == format[1]) + { + r = m->write_character(m, '%'); + format += 2; + continue; + } + + // case 3: is a format specification. parse it + struct specifier spec = parse_specifier(++format, arguments); + + switch (spec.type) + { + case CHARACTER_PRINT: + r = print_character(m, &spec, arguments); + break; + case STRING_PRINT: + r = print_string(m, &spec, arguments); + break; + case INTEGER_PRINT: + r = print_integer(m, &spec, arguments); + break; + default: + r = m->write_string(m, "(INVALID)"); + return -1; + } + format += spec.length; + } + + return r; +} + +static int kfp_write_character(struct method *m, char c) +{ + fputc((int)c, (FILE *)m->output); + m->count++; + return 0; +} + +static int kfp_write_wcharacter(struct method *m, wchar_t c) +{ + fputc((int)c, (FILE *)m->output); + m->count++; + return 0; +} + +static int naive_write_string(struct method *m, const char *c) +{ + while (*c) + { + m->write_character(m, *c++); + } + return 0; +} + +static int naive_write_wstring(struct method *m, const wchar_t *s) +{ + while (*s) + { + m->write_wcharacter(m, *s++); + } + return 0; +} + +static int ss_write_char(struct method *m, const char c) +{ + char *s = (char *)m->output; + *s++ = c; + m->count++; + return 0; +} + +static int ss_write_wchar(struct method *m, const wchar_t c) +{ + wchar_t *s = (wchar_t *)m->output; + *s++ = c; + m->count++; + return 0; +} + +int vfprintf(FILE *f, const char *restrict format, va_list arguments) +{ + struct method m = { + kfp_write_character, + kfp_write_wcharacter, + naive_write_string, + naive_write_wstring, + (void *)f, + 0}; + va_list acopy; + va_copy(acopy, arguments); + int r = printf_internal(&m, format, &acopy); + va_end(acopy); + + if (!r) + { + return m.count; + } + else + { + return -1; + } +} + +int fprintf(FILE *f, const char *restrict format, ...) +{ + va_list arguments; + va_start(arguments, format); + + int count = vfprintf(f, format, arguments); + + va_end(arguments); + + return count; +} + +int vprintf(const char *restrict format, va_list arguments) +{ + va_list acopy; + va_copy(acopy, arguments); + + int count = vfprintf(stdout, format, arguments); + + va_end(acopy); + + return count; +} + +int printf(const char *restrict format, ...) +{ + va_list arguments; + va_start(arguments, format); + + int count = vprintf(format, arguments); + + va_end(arguments); + + return count; +} + +int vsprintf(char *s, const char *restrict format, va_list arguments) +{ + va_list acopy; + va_copy(acopy, arguments); + + struct method m = + { + ss_write_char, + ss_write_wchar, + naive_write_string, + naive_write_wstring, + (void *)s, + 0 + }; + + int r = printf_internal(&m, format, &acopy); + va_end(acopy); + + if (!r) + { + return m.count; + } + else + { + return -1; + } + + return r; +} + +int sprintf(char *s, const char *restrict format, ...) +{ + va_list arguments; + va_start(arguments, format); + + int count = vsprintf(s, format, arguments); + + va_end(arguments); + + return count; +} + + diff --git a/lib/libc/stdio.c b/lib/libc/stdio.c new file mode 100644 index 0000000..70cc685 --- /dev/null +++ b/lib/libc/stdio.c @@ -0,0 +1,30 @@ +#include +#include +#include + +struct FILE +{ + int fd; +}; + +static FILE stdin_stream = { STDIN_FILENO }; +static FILE stdout_stream = { STDOUT_FILENO }; +static FILE stderr_stream = { STDERR_FILENO }; + +FILE *stdin = &stdin_stream; +FILE *stdout = &stdout_stream; +FILE *stderr = &stderr_stream; + +int fputc(int c, FILE *f) +{ + write(f->fd, (const char *)&c, 1); + return 1; +} + +int fputs(const char *s, FILE *f) +{ + size_t slen = strlen(s); + write(f->fd, s, slen); + return (int)slen; +} + diff --git a/lib/libc/string.c b/lib/libc/string.c new file mode 100644 index 0000000..2da2070 --- /dev/null +++ b/lib/libc/string.c @@ -0,0 +1,195 @@ +#include +#include + +int strcmp(const char *s1, const char *s2) +{ + size_t s1len = strlen(s1); + size_t s2len = strlen(s2); + + return memcmp(s1, s2, s1len < s2len ? s1len : s2len); +} + +size_t strlen(const char *s) +{ + size_t l = 0; + + while (s != nullptr && *s++) l++; + + return l; +} + +size_t wcslen(const wchar_t *s) +{ + size_t l = 0; + + while (s != nullptr && *s++) l++; + + return l; +} + +size_t mbstowcs(wchar_t *dst, const char *src, size_t len) +{ + if (dst == nullptr || src == nullptr) + { + return -1; + } + + size_t i; + + for (i = 0; i < len; i++) + { + if ((dst[i] = src[i]) == 0) + { + break; + } + } + + dst[i] = L'\0'; + + return i; +} + +static long long valueof(int c, int base) +{ + long long result = 0; + + if (base < 2 || base > 36) + { + // bases less than 2 or greater than 36 are invalid. + return -1; + } + + // check if value is an ascii numeric character + if (c >= '0' && c <= '9') + { + // simple as + result = (long long)(c ^ 0x30); + } + + // check if value is an uppercase ASCII alphabetical character + else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + // mask in uppercase bit + c |= 0x20; + // value is 10 plus the alphabetical order of the character + result = (long long)(10 + c - 'a'); + } + else + { + // character has no valid interpretation under base 36 + return -1LL; + } + + if (result > (base - 1)) + { + // character is not valid under given base + return -1LL; + } + + return result; +} + +unsigned long long strtoull( + const char *restrict begin, + char **restrict end, + int base) +{ + long result = 0; + const char *current = begin; + + // check for empty string + if (*current == 0) + { + if (end) + { + *end = (char *)begin; + return result; + } + } + + // skip initial whitespace + bool whitespace = true; + while (whitespace) + { + switch (*current) + { + case 0x9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0x20: + current++; + break; + default: + whitespace = false; + } + } + + bool negative = false; + // detect if negative sign is used + if (*current == '-') + { + negative = true; + current++; + } + + // detect base from input + if (base == 0) + { + // base is non-decimal + if (current[0] == 0) + { + if (current[1] == 'b') + { + base = 2; + current += 2; + } + else if (current[1] == 'x') + { + base = 16; + current += 2; + } + else + { + base = 8; + current++; + } + } + // base is decimal otherwise + else + { + base = 10; + } + } + // do the actual conversion now + while (*current) + { + long long value = valueof(*current, base); + + // we've reached the end of the conversion + if (value == -1) + { + break; + } + + result *= base; + result += value; + current++; + } + + // store the end pointer if needed + if (end) + { + *end = (char *)current; + } + + // flip the result if we're meant to + if (negative) + { + result = -result; + } + + return result; +} + diff --git a/lib/memcmp.c b/lib/memcmp.c deleted file mode 100644 index 2348afe..0000000 --- a/lib/memcmp.c +++ /dev/null @@ -1,16 +0,0 @@ -/* Public domain. */ -#include - -int -memcmp (const void *str1, const void *str2, size_t count) -{ - const unsigned char *s1 = str1; - const unsigned char *s2 = str2; - - while (count-- > 0) - { - if (*s1++ != *s2++) - return s1[-1] < s2[-1] ? -1 : 1; - } - return 0; -} diff --git a/lib/memcpy.c b/lib/memcpy.c deleted file mode 100644 index 58b1e40..0000000 --- a/lib/memcpy.c +++ /dev/null @@ -1,12 +0,0 @@ -/* Public domain. */ -#include - -void * -memcpy (void *dest, const void *src, size_t len) -{ - char *d = dest; - const char *s = src; - while (len--) - *d++ = *s++; - return dest; -} diff --git a/lib/memmove.c b/lib/memmove.c deleted file mode 100644 index fd06bb6..0000000 --- a/lib/memmove.c +++ /dev/null @@ -1,20 +0,0 @@ -/* Public domain. */ -#include - -void * -memmove (void *dest, const void *src, size_t len) -{ - char *d = dest; - const char *s = src; - if (d < s) - while (len--) - *d++ = *s++; - else - { - const char *lasts = s + (len-1); - char *lastd = d + (len-1); - while (len--) - *lastd-- = *lasts--; - } - return dest; -} diff --git a/lib/memset.c b/lib/memset.c deleted file mode 100644 index 3e7025e..0000000 --- a/lib/memset.c +++ /dev/null @@ -1,11 +0,0 @@ -/* Public domain. */ -#include - -void * -memset (void *dest, int val, size_t len) -{ - unsigned char *ptr = dest; - while (len-- > 0) - *ptr++ = val; - return dest; -} diff --git a/lib/printf.c b/lib/printf.c deleted file mode 100644 index 4fd14e9..0000000 --- a/lib/printf.c +++ /dev/null @@ -1,648 +0,0 @@ -#include -#include -#include -#include - -enum specifier_type -{ - INVALID_PRINT, - CHARACTER_PRINT, - STRING_PRINT, - INTEGER_PRINT, - COUNT_PRINT -}; - -enum specifier_flags -{ - INVALID_FLAGS, - LEFT_JUSTIFY_FLAG = 0x1, - EXPLICIT_SIGN_FLAG = 0x2, - PAD_SIGN_FLAG = 0x4, - SIGNED_TYPE_FLAG = 0x8, - SIGN_FLAG_MASK = 0x6, - ALTERNATE_FORM_FLAG = 0x10, - ZERO_PAD_FLAG = 0x20, -}; - -enum specifier_integer_base -{ - INVALID_BASE, - BIN_BASE = 2, - OCT_BASE = 8, - DEC_BASE = 10, - HEX_BASE = 16 -}; - -enum specifier_integer_width -{ - INVALID_WIDTH, - BYTE_WIDTH = 8, - SHORT_WIDTH = 16, - INT_WIDTH = 32, - LONG_WIDTH = 64 -}; - -struct method -{ - int (*write_character)(struct method *m, char c); - int (*write_wcharacter)(struct method *m, wchar_t c); - int (*write_string)(struct method *m, const char *s); - int (*write_wstring)(struct method *m, const wchar_t *s); - void *output; - int count; -}; - -struct specifier -{ - enum specifier_type type; - enum specifier_flags flags; - enum specifier_integer_width integer_width; - enum specifier_integer_base integer_base; - int field_width; - int field_precision; - size_t length; -}; - -static struct specifier parse_specifier(const char *format, va_list *arguments) -{ - struct specifier result = - { - INVALID_PRINT, - INVALID_FLAGS, - INVALID_WIDTH, - INVALID_BASE, - 0, - 0, - 0 - }; - - // keep a pointer to the beginning of the specifier - const char *begin = format; - - // step 1: scan for flags - bool flags_parsed = false; - - while (!flags_parsed) - { - switch (*begin) - { - case 0: - result.type = INVALID_PRINT; - return result; - case '-': - result.flags |= LEFT_JUSTIFY_FLAG; - begin++; - break; - case '+': - result.flags |= EXPLICIT_SIGN_FLAG; - begin++; - break; - case ' ': - result.flags |= PAD_SIGN_FLAG; - begin++; - break; - case '#': - result.flags |= ALTERNATE_FORM_FLAG; - begin++; - break; - case '0': - result.flags |= ZERO_PAD_FLAG; - begin++; - break; - default: - flags_parsed = true; - } - } - - // step 2: parse field width and precision - // TODO: parse field width and precision - /* procedure: - * 1. check if next character is * or . - * a. if *, field width is the value pointed to by the next item in - * arguments list - * b. if ., field width will be set to 0, proceed to 3. - * 2. if above check is false, check for numeric character - * a. if is numeric character, parse field width via strtoul, add length - * of numeric string to begin. - * b. if is not a numeric character, skip check for width and precision - * entirely - * 3. check if next character is * or numeric - * a. if *, field precision is the value pointed to by the next item - * in arguments list. - * b. if is numeric character, parse field precision via strtoul, add - * length of numeric string to begin. - */ - bool field_width_parsed = false; - bool field_precision_parsed = false; - - while (!field_width_parsed || !field_precision_parsed) - { - switch (*begin) - { - case '.': - if (!field_width_parsed) - { - result.field_width = 0; - field_width_parsed = true; - begin++; - } - else - { - // the specifier is invalid! - result.type = INVALID_PRINT; - } - break; - case '*': - { - int w = *va_arg(*arguments, int *); - if (!field_width_parsed) - { - result.field_width = w; - field_width_parsed = true; - } - else - { - result.field_precision = w; - field_precision_parsed = true; - } - begin++; - } - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '0': - { - char *next; - unsigned long long w = strtoull(begin, &next, 10); - if (!field_width_parsed) - { - result.field_width = (int)w; - field_width_parsed = true; - } - else if (!field_precision_parsed) - { - result.field_precision = (int)w; - field_precision_parsed = true; - } - if (next > begin) - { - begin = next; - } - } - break; - default: - field_width_parsed = true; - field_precision_parsed = true; - } - } - - // step 3: check for type width arguments - switch (*begin) - { - case 0: // unexpected eos - result.type = INVALID_PRINT; - return result; - case 'h': - { - if (begin[0] == begin[1]) - { - result.integer_width = BYTE_WIDTH; - begin += 2; - } - else - { - result.integer_width = SHORT_WIDTH; - begin++; - } - break; - } - case 'l': - { - // TODO: deal with LLP64? idk. - if (begin[0] == begin[1]) - { - begin += 2; - } - else - { - begin++; - } - result.integer_width = LONG_WIDTH; - break; - } - case 'j': - // TODO: use INTMAX_T_WIDTH - result.integer_width = 64; - begin++; - break; - case 'z': - // TODO: use SIZE_T_WIDTH? - result.integer_width = 64; - begin++; - break; - case 't': - // TODO: use PTRDIFF_T_WIDTH? - result.integer_width = 32; - begin++; - break; - default: - // there is no width argument to be found. - result.integer_width = INT_WIDTH; - break; - } - - // step 4: parse field type - switch (*begin) - { - // unexpected EOS - case 0: - result.type = INVALID_PRINT; - return result; - case 'c': - result.type = CHARACTER_PRINT; - break; - case 's': - result.type = STRING_PRINT; - break; - case 'd': - case 'i': - result.type = INTEGER_PRINT; - result.flags |= SIGNED_TYPE_FLAG; - result.integer_base = DEC_BASE; - break; - case 'u': - result.type = INTEGER_PRINT; - result.integer_base = DEC_BASE; - break; - case 'b': - result.type = INTEGER_PRINT; - result.integer_base = BIN_BASE; - break; - case 'o': - result.type = INTEGER_PRINT; - result.integer_base = OCT_BASE; - break; - case 'x': - result.type = INTEGER_PRINT; - result.integer_base = HEX_BASE; - break; - case 'p': - // TODO: use UINTPTR_T_WIDTH here? - // pointer type overrides all flags - // i can do what i want it says "implementation-defined" in the spec - result.field_precision = 16; - result.type = INTEGER_PRINT; - result.integer_base = HEX_BASE; - result.integer_width = LONG_WIDTH; - result.flags = ALTERNATE_FORM_FLAG|ZERO_PAD_FLAG; - break; - case 'n': - // all flags are invalid/ignored and the current count will be - // stored in the value pointed to by the argument - result.type = COUNT_PRINT; - result.flags = INVALID_FLAGS; - result.integer_width = INVALID_WIDTH; - result.field_width = 0; - result.field_precision = 0; - break; - default: - // this byte of the specifier _must_ be valid. if not, - // the procedure to print should not proceed as it might - // output garbage. - // TODO: specify somehow in the output that the format is bad? - result.type = INVALID_PRINT; - return result; - } - - begin++; - - result.length = begin - format; - return result; -} - -static char *convert_integer( - uint64_t value, - unsigned base, - int zpadding, - char *buffer, - size_t bufsz) -{ - static const char *stringdigits = "0123456789abcdef"; - - // the string will be built from the lower-to-higher value, and the - // result pointer will point to the first character of the string in - // the supplied buffer - - // cannot currently work with a base > 16 - if (base > 16) - { - return NULL; - } - - // make absolutely sure there are no excess bits - - // the string is being built backwards, so the pointer needs to be - // at the last byte of the string - char *result = buffer + bufsz - 1; - - // result >= buffer condition ensures we don't underflow - - do - { - *--result = stringdigits[value % base]; - zpadding--; - value /= base; - } - while (value > 0 && result >= buffer); - - while (zpadding-- > 0 && result >= buffer) - { - *--result = '0'; - } - - return result; -} - -static int print_character( - struct method *m, - struct specifier *spec, - va_list *arguments) -{ - (void)spec; - // all specifier flags and etc. are ignored. - return m->write_character(m, va_arg(*arguments, int)); -} - -static int print_string( - struct method *m, - struct specifier *spec, - va_list *arguments) -{ - // TODO: respect field width - if (spec->integer_width == LONG_WIDTH) - { - return m->write_wstring(m, va_arg(*arguments, const wchar_t *)); - } - return m->write_string(m, va_arg(*arguments, const char *)); -} - -static inline bool is_negative(uint64_t value, unsigned width) -{ - switch (width) - { - case BYTE_WIDTH: - return ((int8_t)value) < 0; - case SHORT_WIDTH: - return ((int16_t)value) < 0; - case INT_WIDTH: - return ((int32_t)value) < 0; - case LONG_WIDTH: - return ((int64_t)value) < 0; - default: - return false; - } -} - -static int print_integer( - struct method *m, - struct specifier *spec, - va_list *arguments) -{ - // 65 bytes is the maximum length that convert_integer will need - // i.e. conversion of uintmax_t to binary plus NUL terminator - // TODO: use UINTMAX_T_WIDTH + 1? - char buffer[65] = {0}; - char *s; - bool negative = false; - uint64_t value = 0; - int r = 0; - unsigned zpad = 0; - - switch (spec->integer_width) - { - case BYTE_WIDTH: - case SHORT_WIDTH: - case INT_WIDTH: - value = va_arg(*arguments, unsigned int); - break; - case LONG_WIDTH: - value = va_arg(*arguments, uint64_t); - break; - default: - return -1; - } - - // check if we need to bother with signs - if (spec->flags & SIGNED_TYPE_FLAG) - { - if ((negative = is_negative(value, spec->integer_width))) - { - value = ~value + 1; - } - - if (negative) - { - r = m->write_character(m, '-'); - } - - else if (!negative && (spec->flags & SIGN_FLAG_MASK)) - { - if (spec->flags & EXPLICIT_SIGN_FLAG) - { - r = m->write_character(m, '+'); - } - else - { - r = m->write_character(m, ' '); - } - } - } - - // zero all the unnecessary bits - value &= (2ULL << (spec->integer_width - 1)) - 1; - - switch (spec->integer_base) - { - case BIN_BASE: - if (spec->flags & ALTERNATE_FORM_FLAG) - { - r = m->write_string(m, "0b"); - } - break; - case OCT_BASE: - if (spec->flags & ALTERNATE_FORM_FLAG) - { - r = m->write_character(m, '0'); - } - break; - case HEX_BASE: - if (spec->flags & ALTERNATE_FORM_FLAG) - { - r = m->write_string(m, "0x"); - } - break; - default: - break; - } - - if (spec->flags & ZERO_PAD_FLAG) - { - zpad = spec->field_precision; - } - s = convert_integer(value, spec->integer_base, zpad, buffer, sizeof(buffer)); - - if (s) - { - m->write_string(m, s); - } - else - { - m->write_string(m, "(INVALID)"); - r = -1; - } - - return r; -} - -static int printf_internal( - struct method *m, - const char *restrict format, - va_list *arguments) -{ - int r = 0; - - while (*format && !r) - { - // case 1: not a format specification - if (*format != '%') - { - r = m->write_character(m, *format++); - continue; - } - - // case 2: looks like a format specification, but isn't - else if (*format == '%' && format[0] == format[1]) - { - r = m->write_character(m, '%'); - format += 2; - continue; - } - - // case 3: is a format specification. parse it - struct specifier spec = parse_specifier(++format, arguments); - - switch (spec.type) - { - case CHARACTER_PRINT: - r = print_character(m, &spec, arguments); - break; - case STRING_PRINT: - r = print_string(m, &spec, arguments); - break; - case INTEGER_PRINT: - r = print_integer(m, &spec, arguments); - break; - default: - r = m->write_string(m, "(INVALID)"); - return -1; - } - format += spec.length; - } - - return r; -} - -static int kfp_write_character(struct method *m, char c) -{ - fputc((int)c, (FILE *)m->output); - m->count++; - return 0; -} - -static int kfp_write_wcharacter(struct method *m, wchar_t c) -{ - (void)m; - (void)c; - return -1; -} - -static int kfp_write_string(struct method *m, const char *s) -{ - m->count += fputs(s, (FILE *)m->output); - return 0; -} - -static int kfp_write_wstring(struct method *m, const wchar_t *s) -{ - (void)m; - (void)s; - return -1; -} - -int vfprintf(FILE *f, const char *restrict format, va_list arguments) -{ - struct method m = { - kfp_write_character, - kfp_write_wcharacter, - kfp_write_string, - kfp_write_wstring, - (void *)f, - 0}; - va_list acopy; - va_copy(acopy, arguments); - int r = printf_internal(&m, format, &acopy); - va_end(acopy); - - if (!r) - { - return m.count; - } - else - { - return -1; - } -} - -int fprintf(FILE *f, const char *restrict format, ...) -{ - va_list arguments; - va_start(arguments, format); - - int count = vfprintf(f, format, arguments); - - va_end(arguments); - - return count; -} - -int vprintf(const char *restrict format, va_list arguments) -{ - va_list acopy; - va_copy(acopy, arguments); - - int count = vfprintf(stdout, format, arguments); - - va_end(acopy); - - return count; -} - -int printf(const char *restrict format, ...) -{ - va_list arguments; - va_start(arguments, format); - - int count = vprintf(format, arguments); - - va_end(arguments); - - return count; -} - diff --git a/lib/stdio.c b/lib/stdio.c deleted file mode 100644 index 70cc685..0000000 --- a/lib/stdio.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include - -struct FILE -{ - int fd; -}; - -static FILE stdin_stream = { STDIN_FILENO }; -static FILE stdout_stream = { STDOUT_FILENO }; -static FILE stderr_stream = { STDERR_FILENO }; - -FILE *stdin = &stdin_stream; -FILE *stdout = &stdout_stream; -FILE *stderr = &stderr_stream; - -int fputc(int c, FILE *f) -{ - write(f->fd, (const char *)&c, 1); - return 1; -} - -int fputs(const char *s, FILE *f) -{ - size_t slen = strlen(s); - write(f->fd, s, slen); - return (int)slen; -} - diff --git a/lib/string.c b/lib/string.c deleted file mode 100644 index 2da2070..0000000 --- a/lib/string.c +++ /dev/null @@ -1,195 +0,0 @@ -#include -#include - -int strcmp(const char *s1, const char *s2) -{ - size_t s1len = strlen(s1); - size_t s2len = strlen(s2); - - return memcmp(s1, s2, s1len < s2len ? s1len : s2len); -} - -size_t strlen(const char *s) -{ - size_t l = 0; - - while (s != nullptr && *s++) l++; - - return l; -} - -size_t wcslen(const wchar_t *s) -{ - size_t l = 0; - - while (s != nullptr && *s++) l++; - - return l; -} - -size_t mbstowcs(wchar_t *dst, const char *src, size_t len) -{ - if (dst == nullptr || src == nullptr) - { - return -1; - } - - size_t i; - - for (i = 0; i < len; i++) - { - if ((dst[i] = src[i]) == 0) - { - break; - } - } - - dst[i] = L'\0'; - - return i; -} - -static long long valueof(int c, int base) -{ - long long result = 0; - - if (base < 2 || base > 36) - { - // bases less than 2 or greater than 36 are invalid. - return -1; - } - - // check if value is an ascii numeric character - if (c >= '0' && c <= '9') - { - // simple as - result = (long long)(c ^ 0x30); - } - - // check if value is an uppercase ASCII alphabetical character - else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) - { - // mask in uppercase bit - c |= 0x20; - // value is 10 plus the alphabetical order of the character - result = (long long)(10 + c - 'a'); - } - else - { - // character has no valid interpretation under base 36 - return -1LL; - } - - if (result > (base - 1)) - { - // character is not valid under given base - return -1LL; - } - - return result; -} - -unsigned long long strtoull( - const char *restrict begin, - char **restrict end, - int base) -{ - long result = 0; - const char *current = begin; - - // check for empty string - if (*current == 0) - { - if (end) - { - *end = (char *)begin; - return result; - } - } - - // skip initial whitespace - bool whitespace = true; - while (whitespace) - { - switch (*current) - { - case 0x9: - case 0xa: - case 0xb: - case 0xc: - case 0xd: - case 0x20: - current++; - break; - default: - whitespace = false; - } - } - - bool negative = false; - // detect if negative sign is used - if (*current == '-') - { - negative = true; - current++; - } - - // detect base from input - if (base == 0) - { - // base is non-decimal - if (current[0] == 0) - { - if (current[1] == 'b') - { - base = 2; - current += 2; - } - else if (current[1] == 'x') - { - base = 16; - current += 2; - } - else - { - base = 8; - current++; - } - } - // base is decimal otherwise - else - { - base = 10; - } - } - // do the actual conversion now - while (*current) - { - long long value = valueof(*current, base); - - // we've reached the end of the conversion - if (value == -1) - { - break; - } - - result *= base; - result += value; - current++; - } - - // store the end pointer if needed - if (end) - { - *end = (char *)current; - } - - // flip the result if we're meant to - if (negative) - { - result = -result; - } - - return result; -} - diff --git a/rules.mk b/rules.mk index 756184e..87b5739 100644 --- a/rules.mk +++ b/rules.mk @@ -15,23 +15,24 @@ COMPILE.info = $(info $$(CC) $@) %.os: $(LINK.info) - $(LINK.os) + @$(LINK.os) %.debug.os: %.os - $(OBJCOPY) --only-keep-debug $< $@ + $(OBJCOPY.info) + @$(OBJCOPY) --only-keep-debug $< $@ @$(OBJCOPY) --strip-debug $< %.o: %.c $(COMPILE.info) - $(COMPILE.c) -MMD -MP + @$(COMPILE.c) -MMD -MP %.o: %.cpp $(COMPILE.info) - $(COMPILE.cpp) -MMD -MP + @$(COMPILE.cpp) -MMD -MP %.o: %.S $(COMPILE.info) - $(COMPILE.S) -MMD -MP + @$(COMPILE.S) -MMD -MP clean: $(info $$(RM) $(CLEANLIST)) diff --git a/service/Makefile b/service/Makefile index b78cac3..e9c74d5 100644 --- a/service/Makefile +++ b/service/Makefile @@ -4,12 +4,12 @@ LDFLAGS += -z max-page-size=4096 --export-dynamic -pic \ -z separate-code -pie -Bsymbolic -shared \ -z noexecstack -g -VPATH := ../lib arch/x86_64/ lib +VPATH := ../lib ../lib/libc arch/x86_64/ lib TARGET := kjarna.os .DEFAULT: $(TARGET) -CPPFLAGS += -I../api/posix -I../api/libc -I../api -I. +CPPFLAGS += -I../api/posix -I../lib/api -I../api -I. CFLAGS += -fPIC -fvisibility=hidden -mgeneral-regs-only diff --git a/service/config.h b/service/config.h index b438f0b..8d3132e 100644 --- a/service/config.h +++ b/service/config.h @@ -2,5 +2,8 @@ #define KJARNA_REVISION 2 -const char * const HAL_PATH = "/service/kjarna/kernel.os"; +extern const char * const HAL_PATH; + +#define HEAP_SEGMENT_SIZE 0x4000 +#define HEAP_MAX_SIZE 0x80000000 diff --git a/service/main.c b/service/main.c index e2c705b..788cffc 100644 --- a/service/main.c +++ b/service/main.c @@ -6,6 +6,7 @@ #include "config.h" #include +const char * const HAL_PATH = "/service/kjarna/kernel.os"; static int open_kernel(void) { @@ -44,6 +45,8 @@ int main(int argc, char **argv) printf("kjarna %d\n", KJARNA_REVISION); int kernel_fd = open_kernel(); + while (1); + (void)kernel_fd; return 0; } -- cgit v1.3.1-1-g115d