From 4f1ffac3f91f12f726fd23f413e9e90d4beaa558 Mon Sep 17 00:00:00 2001 From: Ole Morud Date: Wed, 1 Oct 2025 10:31:39 +0200 Subject: [PATCH] more WIP --- Makefile | 12 ++--- src/kernel/gdt.c | 68 +++++++++++++++++++++++++ src/kernel/gdt.h | 59 ++-------------------- src/kernel/interrupts.c | 82 +++++++++++++++---------------- src/kernel/interrupts.h | 2 +- src/kernel/kernel.c | 101 ++++++++++++++++++++++++++------------ src/kernel/kernel_state.h | 19 ++++++- src/kernel/pic.c | 44 ++++++++--------- src/kernel/pic.h | 24 +-------- src/kernel/tty.c | 4 ++ src/lib/bitmap.c | 15 +++++- src/lib/bitmap_test.c | 19 +++++++ src/lib/include/bitmap.h | 29 ++++++++++- src/lib/include/libc.h | 10 ++++ src/lib/include/macros.h | 5 ++ src/lib/include/printf.h | 1 + src/lib/include/str.h | 18 ++++++- src/lib/libc.c | 6 +++ src/lib/printf.c | 41 +++++++++++++++- src/lib/str.c | 25 ++++++++++ 20 files changed, 396 insertions(+), 188 deletions(-) diff --git a/Makefile b/Makefile index 5156a16..7ca0817 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ SOURCE_DIR := src BUILD_DIR := build CONTAINER_CMD := podman run -v "$(shell pwd)":"/scratch" \ + --rm \ --workdir="/scratch" \ --network=none \ -e TERM \ @@ -16,7 +17,7 @@ CONTAINER_CMD := podman run -v "$(shell pwd)":"/scratch" \ cc-i686:latest QEMU := qemu-system-i386 -QEMU_FLAGS := -d int -no-reboot +QEMU_FLAGS := -no-reboot -serial stdio # -d int CC := $(CONTAINER_CMD) i686-elf-gcc LD := $(CONTAINER_CMD) i686-elf-ld @@ -29,7 +30,7 @@ OBJECTS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.o) $( DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.d)) CFLAGS := -ffreestanding -nostdlib -std=c2x -MMD -I$(SOURCE_DIR)/lib/include -I$(SOURCE_DIR) -no-pie -CFLAGS += -O1 -g3 +CFLAGS += -O0 -g CFLAGS += -Wall -Wextra -Werror CFLAGS += -fstack-protector-strong -g3 CFLAGS += -Wno-unused-function @@ -81,10 +82,9 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.S Makefile TEST_BUILD_DIR := $(BUILD_DIR)/tests -#TEST_SOURCES := $(shell find $(SOURCE_DIR) -name '*_test.c') -#TEST_DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(TEST_BUILD_DIR)/%, $(TEST_SOURCES:.c=.d)) -#TEST_OUTPUT := $(patsubst $(SOURCE_DIR)/%, $(TEST_BUILD_DIR)/%, $(TEST_SOURCES:.c=)) - +TEST_SOURCES := $(shell find $(SOURCE_DIR) -name '*_test.c') +TEST_DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(TEST_BUILD_DIR)/%, $(TEST_SOURCES:.c=.d)) +TEST_OUTPUT := $(patsubst $(SOURCE_DIR)/%, $(TEST_BUILD_DIR)/%, $(TEST_SOURCES:.c=)) tests: $(TEST_OUTPUT) $(info TEST_SOURCES is $(TEST_SOURCES)) diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c index 402cad5..4df881d 100644 --- a/src/kernel/gdt.c +++ b/src/kernel/gdt.c @@ -52,3 +52,71 @@ struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content) // ); //} +void gdt_flush_granular(uint32_t code, + uint32_t data, + uint32_t extra_segment, + uint32_t general_segment_1, + uint32_t general_segment_2, + uint32_t stack_segment) +{ + __asm__ volatile ( + // Far jump to reload the CS register + "push %[cs]\n" + "push $reload_CS_granular\n" + "retf\n" + + "reload_CS_granular:\n" + "movw %[ds], %%ax\n" + "movw %%ax, %%ds\n" + "movw %[es], %%ax\n" + "movw %%ax, %%es\n" + "movw %[fs], %%ax\n" + "movw %%ax, %%fs\n" + "movw %[gs], %%ax\n" + "movw %%ax, %%gs\n" + "movw %[ss], %%ax\n" + "movw %%ax, %%ss\n" + : // No output operands + : [cs] "m"(code), + [ds] "m"(data), + [es] "m"(extra_segment), + [fs] "m"(general_segment_1), + [gs] "m"(general_segment_2), + [ss] "m"(stack_segment) + : "ax" // Clobbered register + ); +} + +void gdt_load(struct gdt_table_entry base[], uint16_t gdt_size, segment_t data, segment_t code) +{ + /* the lgdt instruction requires a packed alignment */ + struct __attribute__((packed)) { + uint16_t size; + uint32_t base; + } gdt = { + .size = gdt_size, + .base = (uint32_t)base, + }; + + __asm__ volatile ( + "lgdt %[gdt]\n" + // Far jump to reload the CS register + "push %[cs]\n" + "push $reload_CS\n" + "retf\n" + + "reload_CS:\n" + // Load data segment into AX and then move it to all segment registers + "movw %[ds], %%ax\n" + "movw %%ax, %%ds\n" + "movw %%ax, %%es\n" + "movw %%ax, %%fs\n" + "movw %%ax, %%gs\n" + "movw %%ax, %%ss\n" + : // No output operands + : [gdt] "m"(gdt), + [ds] "m"(data), + [cs] "m"(code) + : "ax" // Clobbered register + ); +} diff --git a/src/kernel/gdt.h b/src/kernel/gdt.h index 36fd607..4d56799 100644 --- a/src/kernel/gdt.h +++ b/src/kernel/gdt.h @@ -56,64 +56,11 @@ enum gdt_flags : uint8_t { struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content); // only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro -static inline void gdt_flush_granular(uint32_t code, +void gdt_flush_granular(uint32_t code, uint32_t data, uint32_t extra_segment, uint32_t general_segment_1, uint32_t general_segment_2, - uint32_t stack_segment) -{ - __asm__ volatile ( - // Far jump to reload the CS register - "jmp %[cs], $reload_CS\n" - "reload_CS:\n" - "movw %[ds], %%ax\n" - "movw %%ax, %%ds\n" - "movw %[es], %%ax\n" - "movw %%ax, %%es\n" - "movw %[fs], %%ax\n" - "movw %%ax, %%fs\n" - "movw %[gs], %%ax\n" - "movw %%ax, %%gs\n" - "movw %[ss], %%ax\n" - "movw %%ax, %%ss\n" - : // No output operands - : [cs] "i"(code), - [ds] "i"(data), - [es] "i"(extra_segment), - [fs] "i"(general_segment_1), - [gs] "i"(general_segment_2), - [ss] "i"(stack_segment) - : "ax" // Clobbered register - ); -} -static inline void gdt_load(struct gdt_table_entry base[], uint16_t gdt_size, segment_t data, segment_t code) -{ - /* the lgdt instruction requires a packed alignment */ - struct __attribute__((packed)) { - uint16_t size; - uint32_t base; - } gdt = { - .size = gdt_size, - .base = (uint32_t)base, - }; + uint32_t stack_segment); - __asm__ volatile ( - "lgdt %[gdt]\n" - // Far jump to reload the CS register - "jmp %[cs], $reload_CS\n" - "reload_CS:\n" - // Load data segment into AX and then move it to all segment registers - "movw %[ds], %%ax\n" - "movw %%ax, %%ds\n" - "movw %%ax, %%es\n" - "movw %%ax, %%fs\n" - "movw %%ax, %%gs\n" - "movw %%ax, %%ss\n" - : // No output operands - : [gdt] "m"(gdt), - [ds] "i"(data), - [cs] "i"(code) - : "ax" // Clobbered register - ); -} +void gdt_load(struct gdt_table_entry base[], uint16_t gdt_size, segment_t data, segment_t code); diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 977125c..afccc29 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -17,16 +17,15 @@ struct __attribute__((packed)) interrupt_frame { uword_t ss; }; -static void print_interrupt_frame(struct interrupt_frame* f) +void print_interrupt_frame(struct interrupt_frame* f) { printf(str_attach( "Interrupt frame:\n" - "================\n" - "ip: {x32}\n" - "cs: {x32} == {cs}\n" - "flags: {x32}\n" - "sp: {x32}\n" - "ss: {x32}\n"), + " - ip: {x32}\n" + " - cs: {x32} ({cs})\n" + " - flags: {x32}\n" + " - sp: {x32}\n" + " - ss: {x32}\n"), f->ip, f->cs, f->flags, @@ -106,38 +105,51 @@ void exception_handler_page_fault(struct interrupt_frame* frame, int err) } terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED); printf(str_attach( - "page fault :(, err: 0x{x32}: ["), + "=============\n" + "PAGE FAULT :(\n" + "=============\n" + "Error: 0x{x32}: \n"), err); /* page fault error bits */ enum { - present = 1<<0, - write = 1<<1, - user = 1<<2, - reserved_write = 1<<3, - instruction_fetch = 1<<4, - protection_key = 1<<5, - shadow_stack = 1<<6, - software_guard_ex = 1<<15, + present = 0, + write = 1, + user = 2, + reserved_write = 3, + instruction_fetch = 4, + protection_key = 5, + shadow_stack = 6, + software_guard_ex = 15, + + page_fault_error_count }; - const struct str error_names[] = { - [0] = str_attach("present"), - [1] = str_attach("write"), - [2] = str_attach("user"), - [3] = str_attach("reserved_write"), - [4] = str_attach("instruction_fetch"), - [5] = str_attach("protection_key"), - [6] = str_attach("shadow_stack"), - [15] = str_attach("software_guard_ex"), + static const struct str error_flag_str[page_fault_error_count] = { + [present] = str_attach("Caused by a page-protection violation"), + [write] = str_attach("Caused by a write access"), + [user] = str_attach("Caused while CPL == 3 (not neccessarily a privilige escalation)"), + [reserved_write] = str_attach("One or more page directory entries contain reserved bits which are set to 1"), + [instruction_fetch] = str_attach("Caused by an instruction fetch"), + [protection_key] = str_attach("Caused by a protection-key violation"), + [shadow_stack] = str_attach("Caused by a shadow stack access"), + [software_guard_ex] = str_attach("Caused by an SGX violation"), }; - for (size_t i = 0; i < sizeof error_names / sizeof *error_names; i++) { - if ((err & (1< EXCEPTION_DEPTH_MAX) { - panic(str_attach("fatal: too many nested exceptions\n")); - } - (void)frame; - printf(str_attach("interrupt_handler_1 called!\n")); - - kernel.nested_exception_counter = 0; -} - /* * IRQs * ==== diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index f10a34b..28a1efc 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -28,7 +28,7 @@ __attribute__((interrupt, noreturn)) void interrupt_default(struct interrupt_frame* frame); __attribute__((interrupt)) -void interrupt_handler_1(struct interrupt_frame* frame); +void interrupt_handler_syscall(struct interrupt_frame* frame); /** * IRQs diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 2423032..6de62b3 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -2,35 +2,43 @@ #include #include +#include "file_system.h" #include "gdt.h" -#include "tss.h" #include "idt.h" #include "interrupts.h" -#include "types.h" #include "kernel_state.h" -#include "pic.h" - #include "page.h" +#include "pic.h" +#include "serial.h" +#include "tss.h" +#include "types.h" // Future user-space #include "libc.h" #include "tty.h" #include "str.h" #include "bitmap.h" +#include "syscall.h" -uint32_t page_directory[1024] __attribute__((aligned(4096))); -uint32_t page_table[1024] __attribute__((aligned(4096))); -_Static_assert(((uint32_t)page_table & 0xfff) == 0); -_Static_assert(((uint32_t)page_directory & 0xfff) == 0); +#define syscall(number, b, c, d) \ + __asm__ volatile( \ + "int $0x80\n" \ + : \ + : "a"(number), \ + "b"(b), \ + "c"(c), \ + "d"(d) \ + : "memory" \ + ); static void user_mode_code(void*) { - printf(str_attach("hello from user-space before interrupt :)\n")); - __asm__ volatile ("int $0x80"); - + //printf(str_attach("hello from user-space before interrupt :)\n")); + //__asm__ volatile ("int $0x80"); + int output; + syscall(SYSCALL_PRINT, &str_attach("hello from ring 3\n"), 0, 0); #if 0 - printf(str_attach("hello from user-space before exception :)\n")); - /* test division by 0 exception */ + syscall(SYSCALL_PRINT, &str_attach("trying to divide by zero :)\n"), 0, 0); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdiv-by-zero" volatile int a = 5/0; @@ -38,9 +46,13 @@ static void user_mode_code(void*) #pragma GCC diagnostic pop // should not happen - printf(str_attach("hello from userspace after interrupt and exception!\n")); + syscall(SYSCALL_PRINT, &str_attach("hello from userspace after interrupt and exception!\n"), 0, 0); #endif - + + syscall(SYSCALL_EXIT, 0, 0, 0); + + while (1) + ; } static void ring3_mode(segment_t udata_segment, segment_t ucode_segment, func_t callback) @@ -60,9 +72,9 @@ static void ring3_mode(segment_t udata_segment, segment_t ucode_segment, func_t "push %[callback]\n" "iret" : - : [udata] "i"(udata_segment), - [ucode] "i"(ucode_segment), - [callback] "i"(callback) + : [udata] "m"(udata_segment), + [ucode] "m"(ucode_segment), + [callback] "m"(callback) : "eax" ); } @@ -78,6 +90,13 @@ void kernel_main(void) __asm__ volatile("cli"); + /* Set up the serial port + * ====================== + */ + if (serial_init(SERIAL_COM1, 3) != 0) { + panic(str_attach("serial_init failed")); + } + /* Set up the GDT * ============== */ kernel.gdt[SEGMENT_NULL] = gdt_encode_entry((struct gdt_entry_content){0}); @@ -190,7 +209,7 @@ void kernel_main(void) kernel.idt[IDT_DESC_PIC2 + 7] = mint(irq_handler_15); /* Interrupts */ - kernel.idt[IDT_DESC_INTERRUPT_SYSCALL] = mint(interrupt_handler_1); + kernel.idt[IDT_DESC_INTERRUPT_SYSCALL] = mint(interrupt_handler_syscall); #undef mtrap #undef mint #undef m_idt_default @@ -208,7 +227,17 @@ void kernel_main(void) /* enable interrupts */ __asm__ volatile("sti"); - printf(str_attach("setting up paging...\n")); + /* + * File system setup + * ================= + * */ + printf(str_attach("Testing file system...\n")); + { + int ok = test_file_system(); + if (ok != 0) { + panic(str_attach("test_file_system() returned non-zero value")); + } + } /** * Paging setup @@ -216,24 +245,35 @@ void kernel_main(void) * We align by 1<<12 because page directory and page table entries store * addresses from bit 12-31 * - * For now give user access to pages to avoid a page fault + * For now give user access to pages to avoid a page fault, since it's not + * implemented properly */ - for (size_t i = 0; i < sizeof page_table / sizeof *page_table; i++) { - page_directory[i] = PDE_WRITE; - } + printf(str_attach("setting up paging...\n")); - for (size_t i = 0; i < sizeof page_table / sizeof *page_table; i++) { - page_table[i] = PTE_ADDRESS(i) | PTE_WRITE | PTE_PRESENT | PTE_USER; - } + static uint32_t page_directory[1024] __attribute__((aligned(4096))); + static uint32_t page_table_0[1024] __attribute__((aligned(4096))); + _Static_assert(((uint32_t)page_directory & 0xfff) == 0); + _Static_assert(((uint32_t)page_table_0 & 0xfff) == 0); - page_directory[0] = ((uint32_t)page_table) | PDE_WRITE | PDE_PRESENT | PDE_USER_ACCESS; + for (size_t i = 0; i < sizeof page_directory / sizeof *page_directory; i++) { + page_directory[i] = PDE_WRITE; /* no present bit */ + } + page_directory[1023] = (uint32_t)page_directory; + + for (size_t i = 0; i < sizeof page_table_0 / sizeof *page_table_0; i++) { + // for now this page table allows user-space code to access + // kernel-space memory (PTE_USER) + page_table_0[i] = PTE_ADDRESS(i) | PTE_WRITE | PTE_PRESENT | PTE_USER; + } + page_directory[0] = ((uint32_t)page_table_0) | PDE_WRITE | PDE_PRESENT | PDE_USER_ACCESS; cr3_set((uint32_t)page_directory); - cr0_flags_set(CR0_PAGING); + cr0_flags_set(CR0_PAGING | CR0_PROTECTED_MODE); printf(str_attach("done!\n")); printf(str_attach("starting code in ring 3...\n")); + /* Finally go to ring 3 */ ring3_mode(segment(SEGMENT_USER_DATA, SEGMENT_GDT, 3), segment(SEGMENT_USER_CODE, SEGMENT_GDT, 3), @@ -241,9 +281,6 @@ void kernel_main(void) printf(str_attach("back to kernel mode...\n")); - while (1) - /* busy loop */; - __asm__ volatile ("hlt"); } diff --git a/src/kernel/kernel_state.h b/src/kernel/kernel_state.h index 4573796..9b05694 100644 --- a/src/kernel/kernel_state.h +++ b/src/kernel/kernel_state.h @@ -70,7 +70,24 @@ enum idt_desc_index : size_t { /* IRQ offsets */ IDT_DESC_PIC1 = 32, - IDT_DESC_PIC2 = IDT_DESC_PIC1 + 8, + IDT_DESC_TIMER = IDT_DESC_PIC1 + 0, + IDT_DESC_KEYBOARD = IDT_DESC_PIC1 + 1, + IDT_DESC_CASCADE = IDT_DESC_PIC1 + 2, + IDT_DESC_COM2 = IDT_DESC_PIC1 + 3, + IDT_DESC_COM1 = IDT_DESC_PIC1 + 4, + IDT_DESC_LPT2 = IDT_DESC_PIC1 + 5, + IDT_DESC_FLOPPY_DISK = IDT_DESC_PIC1 + 6, + IDT_DESC_SPURIOUS = IDT_DESC_PIC1 + 7, + + IDT_DESC_PIC2 = 40, + IDT_DESC_CMOS = IDT_DESC_PIC2 + 0, + IDT_DESC_FREE1 = IDT_DESC_PIC2 + 1, + IDT_DESC_FREE2 = IDT_DESC_PIC2 + 2, + IDT_DESC_FREE3 = IDT_DESC_PIC2 + 3, + IDT_DESC_PS2_MOUSE = IDT_DESC_PIC2 + 4, + IDT_DESC_FPU = IDT_DESC_PIC2 + 5, + IDT_DESC_ATA1 = IDT_DESC_PIC2 + 6, + IDT_DESC_ATA2 = IDT_DESC_PIC2 + 7, /* Software Interrupts */ IDT_DESC_INTERRUPT_SYSCALL = 128, diff --git a/src/kernel/pic.c b/src/kernel/pic.c index 7729370..eab27cb 100644 --- a/src/kernel/pic.c +++ b/src/kernel/pic.c @@ -67,25 +67,25 @@ void pic8259_clear_irq_mask(uint16_t mask) * https://wiki.osdev.org/8259_PIC#ISR_and_IRR * =========================================== * */ -static uint16_t get_irq_reg(int ocw3) -{ - /* OCW3 to PIC CMD to get the register values. PIC2 is chained, and - represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */ - outb(PIC1_COMMAND, ocw3); - outb(PIC2_COMMAND, ocw3); - return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND); -} - -/* Returns the combined value of the cascaded PICs irq request register */ -uint16_t pic8259_get_irr(void) -{ - return get_irq_reg(OCW3_READ_IRR); -} - -/* Returns the combined value of the cascaded PICs in-service register */ -uint16_t pic8259_get_isr(void) -{ - return get_irq_reg(OCW3_READ_ISR); -} - -/* =========================================== */ +//static uint16_t get_irq_reg(int ocw3) +//{ +// /* OCW3 to PIC CMD to get the register values. PIC2 is chained, and +// represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */ +// outb(PIC1_COMMAND, ocw3); +// outb(PIC2_COMMAND, ocw3); +// return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND); +//} +// +///* Returns the combined value of the cascaded PICs irq request register */ +//uint16_t pic8259_get_irr(void) +//{ +// return get_irq_reg(OCW3_READ_IRR); +//} +// +///* Returns the combined value of the cascaded PICs in-service register */ +//uint16_t pic8259_get_isr(void) +//{ +// return get_irq_reg(OCW3_READ_ISR); +//} +// +///* =========================================== */ diff --git a/src/kernel/pic.h b/src/kernel/pic.h index 0f4ab7d..85c4111 100644 --- a/src/kernel/pic.h +++ b/src/kernel/pic.h @@ -2,6 +2,7 @@ #include #include "str.h" +#include "serial.h" enum pic8259_port : uint16_t { PIC1_COMMAND = 0x20, @@ -141,29 +142,6 @@ enum ocw3_command : uint8_t { OCW3_SET_SPECIAL_MASK = (1<<3) | OCW3_BIT_ESMM | OCW3_BIT_SMM, }; -static inline void outb(uint16_t port, uint8_t signal) -{ - __asm__ volatile ("outb %[signal], %[port]" - : - : [signal] "a" (signal), - [port] "Nd" (port) - : "memory"); -#ifdef ADD_IO_WAIT - /* do an I/O operation on an unused port to wait 1-4 microseconds */ - outb(0x80, 0); -#endif -} - -static inline uint8_t inb(uint16_t port) -{ - uint8_t ret; - __asm__ volatile ( "inb %w1, %b0" - : "=a"(ret) - : "Nd"(port) - : "memory"); - return ret; -} - void pic8259_remap(int offset1, int offset2); void pic8259_set_irq_mask(uint16_t mask); diff --git a/src/kernel/tty.c b/src/kernel/tty.c index 9e1a0aa..8c57a97 100644 --- a/src/kernel/tty.c +++ b/src/kernel/tty.c @@ -8,6 +8,8 @@ #include "tty.h" #include "libc.h" +#include "serial.h" + static struct terminal_state t = { .row = 0, .column = 0, @@ -62,6 +64,8 @@ void terminal_scroll(int n) void terminal_putchar(int c) { + serial_putchar(SERIAL_COM1, c); + /* clear the cursor marker */ terminal_putentryat(' ', t.color, t.column, t.row); diff --git a/src/lib/bitmap.c b/src/lib/bitmap.c index fa13621..04f56ee 100644 --- a/src/lib/bitmap.c +++ b/src/lib/bitmap.c @@ -2,7 +2,7 @@ #include "bitmap.h" #include -static constexpr struct bitmap B = {0}; +static constexpr struct bitmap B = {0}; /* <-- just used on the line below */ static constexpr size_t bits_per_index = sizeof (B.data[0]) * CHAR_BIT; #define mask(n) ((1<<(n))-1) @@ -16,6 +16,19 @@ static inline int trailing_zeroes(int n) return __builtin_ctz(n); } +int bitmap_find(const struct bitmap* bitmap, int val) +{ + for (size_t i = 0; i < bitmap->bit_count / bits_per_index; i++) { + auto x = val == 0 + ? ~bitmap->data[i] + : bitmap->data[i]; + if (x != 0) { + return i * bits_per_index + trailing_zeroes(x); + } + } + return -1; +} + int bitmap_set_range(struct bitmap* bitmap, size_t begin, size_t end) { if (end < begin) { diff --git a/src/lib/bitmap_test.c b/src/lib/bitmap_test.c index 870a537..5dd0e69 100644 --- a/src/lib/bitmap_test.c +++ b/src/lib/bitmap_test.c @@ -292,5 +292,24 @@ int main() } while (0); + test_begin("test bitmap_find()"); + do { + uint32_t data[4] = {0}; + struct bitmap b = BITMAP_ATTACH(data, sizeof data); + constexpr size_t pos = 42; + int ok = bitmap_set(&b, pos); + if (ok != 0) { + test_fail("bitmap_set() failed while testing bitmap_find()"); + break; + } + int found = bitmap_find(&b, 1); + if (found != pos) { + test_fail("bitmap_find() failed, expected %zu, got %i", pos, found); + break; + } + + test_ok("bitmap_find() ok! pos: %zu, found: %i", pos, found); + } while (0); + return g_status; } diff --git a/src/lib/include/bitmap.h b/src/lib/include/bitmap.h index 771cde9..91aa60f 100644 --- a/src/lib/include/bitmap.h +++ b/src/lib/include/bitmap.h @@ -14,10 +14,12 @@ struct bitmap { * buf_size: size of buffer in bytes */ #define BITMAP_ATTACH(buf, buf_size) (struct bitmap){.bit_count = buf_size * CHAR_BIT, .data = buf} +/* + * Set the bit value at `index` + * */ static inline int bitmap_set(struct bitmap* bitmap, size_t index) { constexpr size_t bits_per_index = sizeof (bitmap->data[0]) * CHAR_BIT; - _Static_assert(bits_per_index == 32); if (unlikely(index > bitmap->bit_count)) { return -1; } @@ -25,6 +27,9 @@ static inline int bitmap_set(struct bitmap* bitmap, size_t index) return 0; } +/* + * Unset the bit value at `index` + * */ static inline int bitmap_unset(struct bitmap* bitmap, size_t index) { constexpr size_t bits_per_index = sizeof (bitmap->data[0]) * CHAR_BIT; @@ -35,7 +40,10 @@ static inline int bitmap_unset(struct bitmap* bitmap, size_t index) return 0; } -static inline int bitmap_get(struct bitmap* bitmap, size_t index) +/* + * Get the bit value at `index` + * */ +static inline int bitmap_get(const struct bitmap* bitmap, size_t index) { constexpr size_t bits_per_index = sizeof (bitmap->data[0]) * CHAR_BIT; if (unlikely(index > bitmap->bit_count)) { @@ -44,6 +52,23 @@ static inline int bitmap_get(struct bitmap* bitmap, size_t index) return !!(bitmap->data[index / bits_per_index] & (1ULL << (index % bits_per_index))); } +/* + * Finds the index of a bit with value `val`. + * Returns -1 if none are found + * */ +int bitmap_find(const struct bitmap* bitmap, int val); + +/* + * Sets the range begin..end, end exclusive + */ int bitmap_set_range(struct bitmap* bitmap, size_t begin, size_t end); + +/* + * Clears the range begin..end, end exclusive + */ int bitmap_clear_range(struct bitmap* bitmap, size_t begin, size_t end); + +/* + * Returns true if a range is empty + */ int bitmap_range_empty(struct bitmap* bitmap, size_t begin, size_t end); diff --git a/src/lib/include/libc.h b/src/lib/include/libc.h index 20ebbce..a212ab1 100644 --- a/src/lib/include/libc.h +++ b/src/lib/include/libc.h @@ -1,11 +1,21 @@ #pragma once +#include #include #include "str.h" __attribute__((noreturn)) void panic(struct str s); +/* bit_ceil returns the smallest power of 2 greater than `n` */ +static inline uint32_t bit_ceil32(uint32_t n) +{ + /* __builtin_clz is undefined for n = 0 */ + return n + ? 1 << (sizeof(n)*CHAR_BIT - __builtin_clz(n)) + : 1; +} + void* memmove(void *dest, const void *src, size_t n); void* memset(void *s, int c, size_t n); void* memcpy(void *dest, const void *src, size_t n); diff --git a/src/lib/include/macros.h b/src/lib/include/macros.h index 6f2fffa..a32e3ee 100644 --- a/src/lib/include/macros.h +++ b/src/lib/include/macros.h @@ -4,3 +4,8 @@ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) +#define MEMBER_TYPE(parent_type, member) \ + typeof(((parent_type){0}).member) + +#define MEMBER_SIZE(type, member) \ + (sizeof (((type){0}).member)) diff --git a/src/lib/include/printf.h b/src/lib/include/printf.h index e69de29..6f70f09 100644 --- a/src/lib/include/printf.h +++ b/src/lib/include/printf.h @@ -0,0 +1 @@ +#pragma once diff --git a/src/lib/include/str.h b/src/lib/include/str.h index 03096a9..e7c571b 100644 --- a/src/lib/include/str.h +++ b/src/lib/include/str.h @@ -1,14 +1,19 @@ #pragma once #include +#include struct str { const char* data; const size_t len; }; -#define str_attach(cstr) (struct str){.data = cstr, .len = sizeof(cstr)-1} +#define str_attach(cstr) ((struct str){.data = cstr, .len = sizeof(cstr)-1}) +/* + * Returns a slice of `s`, `end`-exclusive + * str_slice("hello", 1, 4) would return "ell" + * */ static inline struct str str_slice(struct str s, size_t begin, size_t end) { return (struct str) { @@ -17,5 +22,16 @@ static inline struct str str_slice(struct str s, size_t begin, size_t end) }; } +/** + * If a and b are not equal, str_compare returns the index where a and b + * diverges. Otherwise returns -1 + */ +bool str_equals(struct str a, struct str b); + +/** + * If a and b are equals, str_compare returns true, otherwise returns false + */ +int str_compare(struct str a, struct str b); + #define CSTR_(x) #x #define CSTR(x) CSTR_(x) diff --git a/src/lib/libc.c b/src/lib/libc.c index 5d048a0..94d68f9 100644 --- a/src/lib/libc.c +++ b/src/lib/libc.c @@ -5,9 +5,15 @@ #include #include +#include #include "kernel/tty.h" #include "str.h" +/* + * Math functions + * ============== + */ + /* * Error handling * ============== diff --git a/src/lib/printf.c b/src/lib/printf.c index c29352a..ea4ebf0 100644 --- a/src/lib/printf.c +++ b/src/lib/printf.c @@ -121,12 +121,22 @@ static int print_str(struct printf_state* s, int padding, char pad_char) return str.len; } +static inline bool isspace(char ch) +{ + return ch == '\n' || ch == ' ' || ch == '\t'; +} + +static inline bool isprint(char ch) +{ + return (ch >= 32 && ch < 127) || isspace(ch); +} + static int print_char(struct printf_state* s) { // char is promoted to int when passed through va_arg const int ch = va_arg(s->ap, int); - if (ch >= 32 && ch < 127) { + if (isprint(ch)) { terminal_putchar(ch); return 1; } else { @@ -134,6 +144,30 @@ static int print_char(struct printf_state* s) } } +static int print_cstr(struct printf_state* s, int padding, char pad_char) +{ + // TODO: implement padding + (void)padding; + (void)pad_char; + + const char* cstr = va_arg(s->ap, const char*); + char ch; + int written = 0; + while ((ch = *(cstr++))) { + if (isprint(ch)) { + terminal_putchar(ch); + } else { + terminal_putchar('{'); + written += print_long(ch, str_attach("0123456789ABCDEF"), false, 0, 0); + terminal_putchar('}'); + written += 2; + } + written += 1; + } + return written; +} + +/* cs register */ static int print_cs(struct printf_state* s) { int n; @@ -161,7 +195,7 @@ static int print_cs(struct printf_state* s) } unsigned int ring = (cs>>5) & 0b11; - n = printf(str_attach("DPL({uint})"), ring); + n = printf(str_attach("DPL:{uint}"), ring); if (n == -1) { return -1; } @@ -206,6 +240,9 @@ static int parse_format_cmd(struct printf_state* s) case 'x32': return print_x32(s, pad, pad_char); + case 'cstr': + return print_cstr(s, pad, pad_char); + case 'str': return print_str(s, pad, pad_char); diff --git a/src/lib/str.c b/src/lib/str.c index e69de29..6b7ce73 100644 --- a/src/lib/str.c +++ b/src/lib/str.c @@ -0,0 +1,25 @@ + +#include "str.h" +#include + +int str_compare(struct str a, struct str b) +{ + const size_t min_len = a.len > b.len + ? b.len + : a.len; + for (size_t i = 0; i < min_len; i++) { + if (a.data[i] != b.data[i]) { + return i; + } + } + return -1; +} + +bool str_equals(struct str a, struct str b) +{ + if (a.len != b.len) { + return false; + } + + return str_compare(a, b) == -1; +}