This commit is contained in:
2025-10-01 10:31:39 +02:00
parent 04b67a0ad5
commit 4f1ffac3f9
20 changed files with 396 additions and 188 deletions

View File

@@ -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))

View File

@@ -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
);
}

View File

@@ -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);

View File

@@ -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<<i)) && error_names[i].data) {
printf(str_attach("{str} | "), error_names[i]);
static const struct str error_flag_zero_str[page_fault_error_count] = {
[present] = str_attach("Caused by a non-present page"),
[write] = str_attach("Caused by a read access"),
[user] = str_attach("Caused while CPL != 3"),
};
for (size_t i = 0; i < page_fault_error_count; i++) {
if ((err & (1<<i)) && error_flag_str[i].data) {
printf(str_attach(" - {str}\n"), error_flag_str[i]);
} else if (error_flag_zero_str[i].data) {
printf(str_attach(" - {str}\n"), error_flag_zero_str[i]);
}
}
printf(str_attach("0]\n"));
printf(str_attach("\n"));
print_interrupt_frame(frame);
@@ -161,18 +173,6 @@ void interrupt_default(struct interrupt_frame* frame)
kernel.nested_exception_counter = 0;
}
__attribute__((interrupt))
void interrupt_handler_1(struct interrupt_frame* frame)
{
if (kernel.nested_exception_counter++ > 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
* ====

View File

@@ -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

View File

@@ -2,35 +2,43 @@
#include <stddef.h>
#include <stdint.h>
#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");
}

View File

@@ -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,

View File

@@ -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);
//}
//
///* =========================================== */

View File

@@ -2,6 +2,7 @@
#include <stdint.h>
#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);

View File

@@ -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);

View File

@@ -2,7 +2,7 @@
#include "bitmap.h"
#include <limits.h>
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) {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -1,11 +1,21 @@
#pragma once
#include <limits.h>
#include <stddef.h>
#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);

View File

@@ -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))

View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -1,14 +1,19 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
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)

View File

@@ -5,9 +5,15 @@
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include "kernel/tty.h"
#include "str.h"
/*
* Math functions
* ==============
*/
/*
* Error handling
* ==============

View File

@@ -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);

View File

@@ -0,0 +1,25 @@
#include "str.h"
#include <stdint.h>
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;
}