aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-05-24 13:24:27 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-05-24 18:13:43 +0300
commitbc600ecc3bdf0f189861dfb840f70c2339a7a853 (patch)
treed9840b1dc1b865442a028c03ad7109ac67894f6e /common
parent6a7073e5f262db9a4578ff00b5b28e34335564ce (diff)
downloadkmi-bc600ecc3bdf0f189861dfb840f70c2339a7a853.tar.gz
kmi-bc600ecc3bdf0f189861dfb840f70c2339a7a853.zip
rename common to src
+ I keep starting to type src and wondering why autocomplete won't work, I guess src is just uncounciously a better name
Diffstat (limited to 'common')
-rw-r--r--common/bits.c62
-rw-r--r--common/canary.c42
-rw-r--r--common/debug.c786
-rw-r--r--common/dispatch.c35
-rw-r--r--common/dmem.c154
-rw-r--r--common/elf.c156
-rw-r--r--common/fdt.c15
-rw-r--r--common/initrd.c167
-rw-r--r--common/ipi.c35
-rw-r--r--common/irq.c67
-rw-r--r--common/main.c63
-rw-r--r--common/mem.c59
-rw-r--r--common/mem_nodes.c40
-rw-r--r--common/mem_regions.c587
-rw-r--r--common/nodes.c221
-rw-r--r--common/panic.c25
-rw-r--r--common/pmem.c587
-rw-r--r--common/proc.c56
-rw-r--r--common/sp_tree.c295
-rw-r--r--common/string.c463
-rw-r--r--common/tcb.c310
-rw-r--r--common/timer.c204
-rw-r--r--common/uapi/cap.c94
-rw-r--r--common/uapi/conf.c115
-rw-r--r--common/uapi/dispatch.c89
-rw-r--r--common/uapi/ipc.c348
-rw-r--r--common/uapi/irq.c29
-rw-r--r--common/uapi/mem.c158
-rw-r--r--common/uapi/proc.c188
-rw-r--r--common/uapi/timers.c120
-rw-r--r--common/vmem.c378
31 files changed, 0 insertions, 5948 deletions
diff --git a/common/bits.c b/common/bits.c
deleted file mode 100644
index 6462d21..0000000
--- a/common/bits.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file bits.c
- * Bit manipulation helper implementations, currently just byte swaps.
- */
-
-#include <kmi/types.h>
-#include <kmi/attrs.h>
-#include <kmi/bits.h>
-#include <kmi/builtin.h>
-
-#undef __bswap16
-__weak uint16_t __bswap16(const uint16_t u)
-{
- return (u & 0xff00) >> 8 | (u & 0x00ff) << 8;
-}
-
-#undef __bswap32
-__weak uint32_t __bswap32(const uint32_t u)
-{
- return (u & 0xff000000) >> 24 | (u & 0x00ff0000) >> 8 |
- (u & 0x0000ff00) << 8 | (u & 0x000000ff) << 24;
-}
-
-#undef __bswap64
-__weak uint64_t __bswap64(const uint64_t u)
-{
- return (u & 0xff00000000000000ULL) >> 56 |
- (u & 0x00ff000000000000ULL) >> 40 |
- (u & 0x0000ff0000000000ULL) >> 24 |
- (u & 0x000000ff00000000ULL) >> 8 |
- (u & 0x00000000ff000000ULL) << 8 |
- (u & 0x0000000000ff0000ULL) << 24 |
- (u & 0x000000000000ff00ULL) << 40 |
- (u & 0x00000000000000ffULL) << 56;
-}
-
-#undef ffs
-__weak int ffs(int v)
-{
- /* http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightParallel */
- if (v == 0)
- return 0;
-
- /* silence ubsan warning */
- if (v == INT_MIN)
- return 32;
-
- int c = 32;
- v &= -v;
-
- if (v) c--;
- if (v & 0x0000FFFF) c -= 16;
- if (v & 0x00FF00FF) c -= 8;
- if (v & 0x0F0F0F0F) c -= 4;
- if (v & 0x33333333) c -= 2;
- if (v & 0x55555555) c -= 1;
-
- return c + 1;
-}
diff --git a/common/canary.c b/common/canary.c
deleted file mode 100644
index 4f1f8ba..0000000
--- a/common/canary.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-#include <kmi/canary.h>
-#include <kmi/mem.h>
-
-/**
- * @file canary.c
- * Kernel stack canary implementation.
- */
-
-/** Typedef for canary value type. */
-typedef uint32_t canary_t;
-
-/** Canary magic value. */
-static const canary_t canary = 0xb00b1e5;
-
-/**
- * Helper for calculating the canary location of \p t.
- *
- * @param t \ref tcb to calculate canary position of.
- * @return Pointer to canary location, that is bottom of stack.
- * \todo This assumes that all stacks grow downwards. Unlikely to ever be
- * ported to a platform that doesn't abide by this, but keep it in mind anyway.
- */
-static canary_t *get_canary(struct tcb *t)
-{
- size_t s = order_size(KERNEL_STACK_PAGE_ORDER);
- return (canary_t *)align_down((uintptr_t)t, s);
-}
-
-void set_canary(struct tcb *t)
-{
- canary_t *c = get_canary(t);
- *c = canary;
-}
-
-bool check_canary(struct tcb *t)
-{
- canary_t *c = get_canary(t);
- return *c != canary;
-}
diff --git a/common/debug.c b/common/debug.c
deleted file mode 100644
index fcec537..0000000
--- a/common/debug.c
+++ /dev/null
@@ -1,786 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file debug.c
- * Handle printing to serial. Note that the serial drivers are only included
- * when running a debug build to save space in release mode.
- */
-
-#include <kmi/types.h>
-#include <kmi/debug.h>
-#include <kmi/bits.h>
-#include <kmi/vmem.h>
-#include <kmi/pmem.h>
-#include <arch/vmem.h>
-#include <libfdt.h>
-#include <stdarg.h>
-
-#if defined(DEBUG)
-
-/** Debug context structure. */
-struct dbg_info {
- /** Address of serial device in virtual memory. Always access registers
- * through this address rather than \p addr. */
- pm_t base;
- /** Shift count between registers of serial device. */
- size_t shift;
- /** Type of serial device. */
- enum serial_dev dev;
- /** Physical address of serial device. */
- pm_t addr;
-};
-
-/** Static debugging information. */
-static struct dbg_info dbg_info = (struct dbg_info){ 0 };
-
-/* forward declarations. */
-static struct dbg_info __dbg_from_fdt(const void *fdt);
-
-void init_dbg(const void *fdt)
-{
- dbg_info = __dbg_from_fdt(fdt);
-}
-
-void setup_dmap_dbg()
-{
- /* noop with the new 8250 driver, though might still be useful in the
- * future if I add in some other kind of simple uart */
-}
-
-void setup_io_dbg(struct vmem *b)
-{
- dbg_info.base = map_io_dbg(b);
-}
-
-vm_t map_io_dbg(struct vmem *b)
-{
- return setup_kernel_io(b, dbg_info.addr);
-}
-
-/** 8250 data register index. */
-#define UART_8250_DATA 0
-/** 8250 irq register idex. */
-#define UART_8250_IRQ 1
-/** 8250 ird_id register index. */
-#define UART_8250_IRQ_ID 2
-/** 8250 lcr register index. */
-#define UART_8250_LCR 3
-/** 8250 mcr register index. */
-#define UART_8250_MCR 4
-/** 8250 lsr register index. */
-#define UART_8250_LSR 5
-/** 8250 msr register index. */
-#define UART_8250_MSR 6
-/** 8250 scr register index. */
-#define UART_8250_SCR 7
-
-/* if there arises a need for more supported serial drivers, I should probably
- * try to implement some kind of basic driver subsystem, but this is good enough
- * for now. */
-
-/** Line status data ready. */
-#define LSR_DR (1 << 0)
-
-/** Line status overrun error. */
-#define LSR_OE (1 << 1)
-
-/** Line status parity error. */
-#define LSR_PE (1 << 2)
-
-/** Line status framing error. */
-#define LSR_FE (1 << 3)
-
-/** Line status break interrupt. */
-#define LSR_BI (1 << 4)
-
-/** Line status transmitter holding register. */
-#define LSR_THRE (1 << 5)
-
-/** Line status transmitter empty. */
-#define LSR_TEMT (1 << 6)
-
-/** Line status error in RCVR FIFO. */
-#define LSR_ERR (1 << 7)
-
-/**
- * 8250 transmitter empty.
- *
- * @return \c 0 if not empty, non-zero otherwise.
- */
-static int __8250_tx_empty()
-{
- volatile uint8_t *lsr = (uint8_t *)dbg_info.base +
- (UART_8250_LSR << dbg_info.shift);
- return (*lsr) & LSR_THRE;
-}
-
-/**
- * Put character out onto 8250 serial lines.
- *
- * @param c Character to output.
- */
-static void __8250_putchar(char c)
-{
- if (!dbg_info.base)
- return;
-
- while (__8250_tx_empty() == 0)
- ;
-
- volatile uint8_t *data = (uint8_t *)dbg_info.base +
- (UART_8250_DATA << dbg_info.shift);
- *data = c;
-}
-
-/**
- * Put one character out on the serial lines.
- * Automatically handles newlines.
- *
- * @param c Character to put.
- */
-static void __putchar(char c)
-{
- if (c == '\n')
- __putchar('\r');
-
- switch (dbg_info.dev) {
- case UART_8250: __8250_putchar(c); return;
- }
-}
-
-/**
- * Convert serial device name (from FDT) to serial device enumerator.
- *
- * @param dev_name Device name string.
- * @return Corresponding enumerator value.
- */
-static enum serial_dev __serial_dev_enum(const char *dev_name)
-{
- /* qemu, for example */
- if (strncmp("ns16550", dev_name, 7) == 0)
- return UART_8250;
-
- /* mentioned in dtc documentation as an example */
- if (strncmp("ns8250", dev_name, 7) == 0)
- return UART_8250;
-
- /* starfive visionfive 2, for example (hopefully works) */
- if (strncmp("snps,dw-apb-uart", dev_name, 16) == 0)
- return UART_8250;
-
- return -1;
-}
-
-/**
- * Get debugging info from FDT.
- *
- * @param fdt Global FDT pointer.
- * @return Filled \ref dbg_info structure.
- */
-static struct dbg_info __dbg_from_fdt(const void *fdt)
-{
- int chosen_offset = fdt_path_offset(fdt, "/chosen");
- const char *stdout =
- fdt_getprop(fdt, chosen_offset, "stdout-path", NULL);
-
- /* discard options */
- size_t baselen = strlen(stdout);
- const char *options = strchr(stdout, ':');
- if (options)
- baselen = options - stdout;
-
- int stdout_offset = fdt_path_offset_namelen(fdt, stdout, baselen);
-
- /* get serial device type */
- const char *dev_name = (const char *)fdt_getprop(fdt, stdout_offset,
- "compatible", NULL);
-
- enum serial_dev dev = __serial_dev_enum(dev_name);
-
- /* get serial device address */
- const void *reg_ptr = fdt_getprop(fdt, stdout_offset, "reg", NULL);
- struct cell_info ci = get_cellinfo(fdt, stdout_offset);
- pm_t dbg_ptr = (pm_t)fdt_load_reg_addr(ci, reg_ptr, 0);
-
- /* get serial device offset if present */
- size_t shift = 0;
- const void *shift_ptr = fdt_getprop(fdt, stdout_offset, "reg-shift",
- NULL);
-
- if (shift_ptr)
- shift = (size_t)fdt_load_int32_ptr(shift_ptr);
-
- /* while in direct map, base == addr, and this changes only when we jump
- * into virtually mapped io */
- return (struct dbg_info){ dbg_ptr, shift, dev, dbg_ptr };
-}
-
-/** Printf formatting left align flag. */
-#define LEFT_FLAG (1 << 0)
-
-/** Printf formatting explicit sign flag. */
-#define SIGN_FLAG (1 << 1)
-
-/** Printf formatting hash sign flag. */
-#define HASH_FLAG (1 << 2)
-
-/** Printf formatting zero padding flag. */
-#define ZERO_FLAG (1 << 3)
-
-/** Printf formatting ' flag. */
-#define FMT_FLAG (1 << 4)
-
-/** Printf formatting space flag. */
-#define SPACE_FLAG (1 << 5)
-
-/** Printf formatting long specifier flag. */
-#define LONG_FLAG (1 << 6)
-
-/** Printf formatting long long specifier flag. */
-#define LLONG_FLAG (1 << 7)
-
-/** Printf formatting short flag. */
-#define SHORT_FLAG (1 << 8)
-
-/** Printf formatting char flag. */
-#define CHAR_FLAG (1 << 9)
-
-/** Printf precision flag. */
-#define PRECS_FLAG (1 << 11)
-
-/** Printf unsigned flag. */
-#define UNSIGN_FLAG (1 << 12)
-
-/** Printf width flag. */
-#define WIDTH_FLAG (1 << 13)
-
-/** Printf padding flag. */
-#define PAD_FLAG (1 << 14)
-
-/** Printf continue flag. */
-#define CONT 1
-
-/** Printf stop flag. */
-#define STOP 0
-
-/**
- * Check if character is ASCII decimal digit.
- *
- * @param c Character to check.
- * @return \c true if character is ASCII decimal digit, \c false otherwise.
- */
-static bool __is_digit(char c)
-{
- return (c >= '0') && (c <= '9');
-}
-
-/**
- * Convert string to corresponding number (assuming int).
- *
- * @param s Number string.
- * @return Corresponding number.
- */
-static int __atoi(const char *s)
-{
- unsigned int i = 0;
- while (__is_digit(*s)) {
- i = i * 10 + (unsigned int)(*(s++) - '0');
- }
-
- return i;
-}
-
-/**
- * Calculate signed char from value using type interpretation.
- *
- * @param x Type to interpret value as.
- * @param value Value to interpret.
- * @param base Base to interpret value in.
- */
-#define handle_type(x, value, base) \
- c = (x)value % (x)base; \
- value = (x)value / (x)base;
-/**
- * Convert number to string length.
- *
- * @param value Number to print.
- * @param base Base to print in.
- * @param flags Flags to output.
- * @param print Print number as well.
- * @return Length of corresponding string.
- */
-static size_t __integral_val(ssize_t value, size_t base, size_t flags,
- bool print)
-{
- /* assume ascii numbers, which is why 'signed char' is probably fine */
- size_t ret = 0;
- signed char c = 0;
-
-
- if (!is_set(flags, UNSIGN_FLAG)) {
- /* signed values, only with i format */
- if (is_set(flags, LLONG_FLAG)) {
- handle_type(signed long long, value, base);
- } else if (is_set(flags, LONG_FLAG)) {
- handle_type(signed long, value, base);
- } else if (is_set(flags, SHORT_FLAG)) {
- handle_type(signed short, value, base);
- } else if (is_set(flags, CHAR_FLAG)) {
- handle_type(signed char, value, base);
- } else {
- handle_type(signed int, value, base);
- }
-
- /* convert negative results into actual characters */
- c = c < 0 ? -c : c;
-
- } else {
- /* unsigned values, everything else */
- if (is_set(flags, LLONG_FLAG)) {
- handle_type(unsigned long long, value, base);
- } else if (is_set(flags, LONG_FLAG)) {
- handle_type(unsigned long, value, base);
- } else if (is_set(flags, SHORT_FLAG)) {
- handle_type(unsigned short, value, base);
- } else if (is_set(flags, CHAR_FLAG)) {
- handle_type(unsigned char, value, base);
- } else {
- handle_type(unsigned int, value, base);
- }
- }
-
- if (base == 16)
- c += c > 9 ? 'a' - 10 : '0';
- else
- c += '0';
-
- if (value != 0)
- ret = __integral_val(value, base, flags, print);
-
- if (print)
- __putchar(c);
-
- return ret + 1;
-}
-
-/**
- * Print prefix corresponding to \c base.
- *
- * @param base Base to integer.
- * @return Length of prefix as string.
- */
-static size_t __print_prefix(size_t base)
-{
- size_t i = 0;
-
- const char *hex = "0x";
- const char *oct = "0";
- const char *bin = "0b";
- const char *empty = "";
-
- const char *prefix;
-
- if (base == 16)
- prefix = hex;
- else if (base == 8)
- prefix = oct;
- else if (base == 2)
- prefix = bin;
- else
- prefix = empty;
-
- for (; *prefix; ++i)
- __putchar(*prefix++);
-
- return i;
-}
-
-/**
- * Print padding.
- *
- * @param pad Number of characters to print.
- * @param pad_char Character to use as padding.
- * @return Number of characters printed.
- */
-static size_t __print_padding(size_t pad, char pad_char)
-{
- size_t i = 0;
- for (; i < pad; ++i) {
- __putchar(pad_char);
- }
-
- return i;
-}
-
-/**
- * Print signed value.
- *
- * @param value Value to print.
- * @param flags Flags to printing.
- * @return Number of characters written.
- */
-static size_t __print_sign(ssize_t value, size_t flags)
-{
- if (is_set(flags, LLONG_FLAG))
- value = (signed long long)value;
- else if (is_set(flags, LONG_FLAG))
- value = (signed long)value;
- else if (is_set(flags, SHORT_FLAG))
- value = (signed short)value;
- else if (is_set(flags, CHAR_FLAG))
- value = (signed char)value;
- else
- value = (signed int)value;
-
- if (value < 0) {
- __putchar('-');
- return 1;
- } else if (flags & SIGN_FLAG) {
- __putchar('+');
- return 1;
- }
-
- return 0;
-}
-
-/**
- * Length of integral value as string.
- *
- * @param value Value to convert to string.
- * @param base Base to interpret value as.
- * @param flags Formatting flags.
- * @return \see __integral_val().
- */
-#define __integral_len(value, base, flags) __integral_val((value), (base), \
- (flags), false)
-
-/**
- * Print integral value as string.
- *
- * @param value Value to convert to string.
- * @param base Base to interpret value as.
- * @param flags Formatting flags.
- * @return \see __integral_val().
- */
-#define __integral_print(value, base, flags) __integral_val((value), (base), \
- (flags), true)
-
-/**
- * Print integral value.
- *
- * @param value Value to print.
- * @param base Base to print value in.
- * @param flags Flags to printing.
- * @param width Minimum width of printing.
- * @return Number of characters written.
- */
-static size_t __print_integral(ssize_t value, size_t base, size_t flags,
- size_t width)
-{
- size_t ret = 0;
- size_t raw_len = __integral_len(value, base, flags);
- ssize_t pad = is_set(flags, PAD_FLAG) ? width - raw_len : 0;
-
- /* depending on which flags are set, the prefix, sign and right justify has to
- * be ordereder differently. */
- if (is_set(flags, ZERO_FLAG)) {
- if (!is_set(flags, UNSIGN_FLAG))
- ret += __print_sign(value, flags);
-
- if (is_set(flags, HASH_FLAG))
- ret += __print_prefix(base);
-
- if (pad > 0 && !is_set(flags, LEFT_FLAG))
- ret += __print_padding(pad, '0');
-
- } else if (is_set(flags, SPACE_FLAG)) {
- if (pad > 0 && !is_set(flags, LEFT_FLAG))
- ret += __print_padding(pad, ' ');
-
- if (!is_set(flags, UNSIGN_FLAG))
- ret += __print_sign(value, flags);
-
- if (is_set(flags, HASH_FLAG))
- ret += __print_prefix(base);
- } else {
- if (!is_set(flags, UNSIGN_FLAG))
- ret += __print_sign(value, flags);
-
- if (is_set(flags, HASH_FLAG))
- ret += __print_prefix(base);
- }
-
- /* print value itself */
- ret += __integral_print(value, base, flags);
-
- /* left-justify */
- if (is_set(flags, ZERO_FLAG)) {
- if (pad > 0 && is_set(flags, LEFT_FLAG))
- ret += __print_padding(pad, '0');
- } else if (is_set(flags, SPACE_FLAG)) {
- if (pad > 0 && is_set(flags, LEFT_FLAG))
- ret += __print_padding(pad, ' ');
- }
-
- return ret;
-}
-
-void dbg(const char *fmt, ...)
-{
- /* largely inspired by
- * https://github.com/mpaland/printf/blob/master/printf.c
- */
-
- /* Note that X is binary formatting, because who uses uppercase hex? */
-
- va_list vl;
- va_start(vl, fmt);
-
- size_t chars_written = 0;
-
- while (*fmt) {
- if (*fmt != '%') {
- __putchar(*fmt++);
- chars_written++;
- continue;
- }
-
- fmt++;
- if (*fmt == '%') {
- /* literal percent sign */
- __putchar('%');
- chars_written++;
- fmt++;
- continue;
- }
-
- /* check flags */
- size_t flags = 0;
- int a = STOP;
- do {
- switch (*fmt) {
- case ' ':
- set_bit(flags, SPACE_FLAG);
- fmt++;
- a = CONT;
- break;
-
- case '-':
- set_bit(flags, LEFT_FLAG);
- fmt++;
- a = CONT;
- break;
-
- case '+':
- set_bit(flags, SIGN_FLAG);
- fmt++;
- a = CONT;
- break;
-
- case '#':
- set_bit(flags, HASH_FLAG);
- fmt++;
- a = CONT;
- break;
-
- case '0':
- set_bit(flags, ZERO_FLAG);
- fmt++;
- a = CONT;
- break;
-
- case '\'':
- set_bit(flags, FMT_FLAG);
- fmt++;
- a = CONT;
- break;
-
- default:
- a = STOP;
- break;
- }
- } while (a != STOP);
-
- /* check width */
- size_t width = 0;
- if (__is_digit(*fmt)) {
- width = __atoi(fmt++);
- set_bit(flags, WIDTH_FLAG | PAD_FLAG | SPACE_FLAG);
- } else if (*fmt == '*') {
- int w = va_arg(vl, int);
- if (w < 0) {
- width = -w;
- set_bit(flags, LEFT_FLAG);
- } else {
- width = w;
- }
- set_bit(flags, WIDTH_FLAG | PAD_FLAG | SPACE_FLAG);
- fmt++;
- }
-
- /* check precision */
- size_t precision = 0;
- if (*fmt == '.') {
- fmt++;
- set_bit(flags, PRECS_FLAG | PAD_FLAG | ZERO_FLAG);
- if (__is_digit(*fmt)) {
- precision = __atoi(fmt++);
- } else if (*fmt == '*') {
- precision = va_arg(vl, int);
- fmt++;
- }
- }
-
- /* check length */
- switch (*fmt) {
- case 'l':
- fmt++;
- if (*fmt == 'l') {
- set_bit(flags, LLONG_FLAG);
- fmt++;
- } else {
- set_bit(flags, LONG_FLAG);
- }
- break;
-
- case 'h':
- fmt++;
- if (*fmt == 'h') {
- set_bit(flags, CHAR_FLAG);
- fmt++;
- } else {
- set_bit(flags, SHORT_FLAG);
- }
- break;
-
- case 'j':
- fmt++;
- if (sizeof(intmax_t) == sizeof(long))
- set_bit(flags, LONG_FLAG);
- else
- set_bit(flags, LLONG_FLAG);
- break;
-
- case 'z':
- fmt++;
- if (sizeof(size_t) == sizeof(long))
- set_bit(flags, LONG_FLAG);
- else
- set_bit(flags, LLONG_FLAG);
- break;
-
- case 't':
- fmt++;
- if (sizeof(ptrdiff_t) == sizeof(long))
- set_bit(flags, LONG_FLAG);
- else
- set_bit(flags, LLONG_FLAG);
- break;
- }
-
- /* read actual specifier */
- size_t base = 10;
- size_t value = 0;
- int i = -1;
- const char *s = 0;
- void *p = 0;
- int *n = 0;
- char c = 0;
-
- switch (*fmt) {
- case 'd':
- case 'i':
- case 'u':
- case 'x':
- case 'X':
- case 'o':
- case 'b':
- /* integer handling */
- switch (*fmt) {
- case 'x':
- base = 16;
- break;
- case 'X':
- base = 2;
- break;
- case 'o':
- base = 8;
- break;
- default:
- base = 10;
- break;
- }
-
- if (base == 10)
- clear_bit(flags, HASH_FLAG);
-
- /* precision takes precedence */
- if (is_set(flags, PRECS_FLAG))
- width = precision;
-
- /* formatting doesn't apply to decimal integers
- * */
- if (*fmt != 'i' && *fmt != 'd') {
- clear_bit(flags, SIGN_FLAG);
- set_bit(flags, UNSIGN_FLAG);
- }
-
- if (is_set(flags, LLONG_FLAG))
- value = va_arg(vl, long long);
- else if (is_set(flags, LONG_FLAG))
- value = va_arg(vl, long);
- else
- value = va_arg(vl, int);
-
- chars_written +=
- __print_integral(value, base, flags, width);
- fmt++;
- break;
-
- case 'c':
- c = va_arg(vl, int);
- __putchar(c);
- chars_written++;
- fmt++;
- break;
-
- case 's':
- s = va_arg(vl, const char *);
-
- if (is_set(flags, PRECS_FLAG))
- i = precision;
-
- for (; *s && i--;) {
- __putchar(*s++);
- chars_written++;
- }
- fmt++;
- break;
-
- case 'p':
- p = va_arg(vl, void *);
- set_bit(flags, UNSIGN_FLAG | HASH_FLAG);
-
- if (sizeof(void *) == sizeof(long))
- set_bit(flags, LONG_FLAG);
- else
- set_bit(flags, LLONG_FLAG);
-
- chars_written +=
- __print_integral((ssize_t)p, 16, flags, width);
- fmt++;
- break;
-
- case 'n':
- n = va_arg(vl, int *);
- *n = chars_written;
- fmt++;
- break;
- }
- }
-
- va_end(vl);
-}
-
-#endif /* DEBUG */
diff --git a/common/dispatch.c b/common/dispatch.c
deleted file mode 100644
index 2786678..0000000
--- a/common/dispatch.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-#include <kmi/uapi.h>
-#include <kmi/ipi.h>
-
-/**
- * @file dispatch.c
- *
- * Interface between assembly and C for dispatching syscalls/IPIs.
- * In kmi, IPIs are a higher level concept and is not expected to be handled by
- * the underlying architecture. Instead, we only require that there is some way
- * to trigger an interrupt in some other core, and check if the interrupt was an
- * IPI or syscall in C.
- */
-
-/**
- * Syscall/IPI handler.
- * If a syscall was triggered, parameters have the meaning they are given.
- * Otherwise, they are meaningless and unused.
- *
- * @param a Syscall number.
- * @param b Argument 0.
- * @param c Argument 1.
- * @param d Argument 2.
- * @param e Argument 3.
- * @param f Argument 4.
- *
- * Returns value of taken action.
- */
-void dispatch(sys_arg_t a, sys_arg_t b, sys_arg_t c,
- sys_arg_t d, sys_arg_t e, sys_arg_t f)
-{
- handle_syscall(a, b, c, d, e, f, cur_tcb());
-}
diff --git a/common/dmem.c b/common/dmem.c
deleted file mode 100644
index bbabe09..0000000
--- a/common/dmem.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file dmem.c
- * Handle device memory, i.e. anything outside the physical RAM.
- *
- * \todo Handle NUMA.
- */
-
-#include <kmi/assert.h>
-#include <kmi/dmem.h>
-
-/** Region before RAM. */
-static struct mem_region_root pre_ram = { 0 };
-
-/** Region after RAM. */
-static struct mem_region_root post_ram = { 0 };
-
-pm_t __pre_base = 0;
-pm_t __pre_top = 0;
-pm_t __post_base = 0;
-pm_t __post_top = 0;
-
-stat_t init_devmem(pm_t ram_base, pm_t ram_top)
-{
- pm_t mem_top = (pm_t)-1;
-
- __pre_base = 0;
- __pre_top = ram_base - 1;
-
- __post_base = ram_top;
- __post_top = mem_top;
-
- size_t pre_pages = __pages(__pre_top);
- size_t post_pages = __pages(__post_top) - __pages(__post_base);
-
- init_region(&pre_ram, __pre_base, pre_pages);
- init_region(&post_ram, __post_base, post_pages);
-
- return OK;
-}
-
-/**
- * Device virtual memory worker callback for \ref map_fill_region().
- *
- * @param b Virtual memory to work in.
- * @param offset Hint for \ref alloc_page().
- * @param vaddr Current virtual address.
- * @param flags Flags of region.
- * @param order Suggested page order.
- * @param data Pointer to \ref stat_t.
- * @return \see alloc_uvmem_wrapper().
- *
- * \see alloc_uvmem_wrapper().
- */
-static stat_t dev_alloc_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
- vmflags_t flags, enum mm_order order,
- void *data)
-{
- stat_t *status = (stat_t *)data;
- /** \todo remember to do something with this status info */
- *status = map_vpage(b, *offset, vaddr, flags, order);
- *offset += order_size(order);
- return OK;
-}
-
-/**
- * Device virtual memory freeing worker callback for \ref map_fill_region().
- *
- * @param b Virtual memory to work in.
- * @param offset Hint for \ref alloc_page().
- * @param vaddr Current virtual address.
- * @param flags Flags of region.
- * @param order Suggested page order.
- * @param data Pointer to \ref stat_t.
- * @return \see alloc_uvmem_wrapper().
- *
- * \see alloc_uvmem_wrapper().
- */
-static stat_t dev_free_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
- vmflags_t flags, enum mm_order order, void *data)
-{
- UNUSED(offset);
- UNUSED(flags);
- pm_t paddr = 0;
- enum mm_order v_order = 0;
- stat_vpage(b, vaddr, &paddr, &v_order, 0);
- if (order != v_order)
- return INFO_TRGN;
-
- stat_t *status = (stat_t *)data;
- *status = unmap_vpage(b, vaddr);
- return OK;
-}
-
-vm_t alloc_devmem(struct tcb *t, pm_t dev_start, size_t bytes, vmflags_t flags)
-{
- hard_assert(t && is_proc(t), ERR_INVAL);
-
- vm_t region = 0;
- if (dev_start < __pre_top)
- region = alloc_region(&pre_ram, bytes, 0, flags);
-
- if (dev_start > __post_base)
- region = alloc_region(&post_ram, bytes, 0, flags);
-
- if (!region)
- return NULL;
-
- stat_t status = OK;
- const vm_t w = map_fill_region(t->proc.vmem, &dev_alloc_wrapper,
- dev_start, region,
- bytes, flags, &status);
-
- if (status)
- return NULL;
-
- return w;
-}
-
-stat_t free_devmem(struct tcb *t, vm_t dev_start)
-{
- hard_assert(t && is_proc(t), ERR_INVAL);
-
- pm_t dev_paddr = 0;
- stat_vpage(t->proc.vmem, dev_start, &dev_paddr, 0, 0);
-
- if (dev_paddr >= __pre_top && dev_paddr <= __post_base)
- return ERR_ADDR;
-
- struct mem_region *m = 0;
- if (dev_paddr < __pre_top)
- m = find_used_region(&pre_ram, dev_paddr);
-
- if (dev_paddr > __post_base)
- m = find_used_region(&post_ram, dev_paddr);
-
- if (!m)
- return ERR_NF;
-
- size_t region_size = __addr(m->end - m->start);
- stat_t status = OK;
- map_fill_region(t->proc.vmem, &dev_free_wrapper, dev_paddr, dev_start,
- region_size, 0, &status);
-
- if (dev_paddr < __pre_top)
- free_region(&pre_ram, dev_paddr);
-
- if (dev_paddr > __post_base)
- free_region(&post_ram, dev_paddr);
-
- return status;
-}
diff --git a/common/elf.c b/common/elf.c
deleted file mode 100644
index da47502..0000000
--- a/common/elf.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file elf.c
- * Handle elf executables, set up requested memory mappings etc.
- */
-
-#include <kmi/elf.h>
-#include <kmi/vmem.h>
-#include <kmi/bits.h>
-#include <kmi/string.h>
-#include <kmi/assert.h>
-
-/**
- * Convert ELF flags to page flags.
- *
- * @param elf_flags ELF flags to convert.
- * @return Corresponding page flags.
- */
-static uint8_t __elf_to_uvflags(uint8_t elf_flags)
-{
- uint8_t uvflags = VM_V | VM_U;
- if (elf_flags & PF_X)
- uvflags |= VM_X;
-
- if (elf_flags & PF_W)
- uvflags |= VM_W;
-
- if (elf_flags & PF_R)
- uvflags |= VM_R;
-
- return uvflags;
-}
-
-/**
- * Map ELF executable.
- *
- * @param t Thread space to work in.
- * @param bin Address of binary to map.
- * @param ei_c ELF identity class.
- * @param phstart Program header start.
- * @param phnum Number of program header entries.
- * @param phsize Size of page header entry.
- */
-static void __map_exec(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart,
- size_t phnum, size_t phsize)
-{
- hard_assert(t && is_proc(t), RETURN_VOID);
-
- /** \todo take alignment into consideration? */
- /** \todo take overlapping memory regions into account, probably mostly
- * by keeping track of previously allocated area and seeing if the
- * segment fits into it */
- /** \todo check if p_memsz is larger than p_filesz, the segment should be
- * filled with zeroes. */
- /** \todo in general, make this a lot more clean. */
- /* useful bit of info: all segments are sorted in ascending order of p_vaddr */
- vm_t runner = phstart;
- vmflags_t default_flags = VM_V | VM_R | VM_W | VM_X | VM_U;
- for (size_t i = 0; i < phnum; ++i, runner += phsize) {
- if (program_header_prop(ei_c, runner, p_type) != PT_LOAD)
- continue;
-
- vm_t va = program_header_prop(ei_c, runner, p_vaddr);
- size_t vsz = program_header_prop(ei_c, runner, p_memsz);
-
- vm_t start = alloc_fixed_region(&t->sp_r, va, vsz, &vsz,
- default_flags);
- if (!start)
- return; /* out of memory or something */
-
- uint8_t elf_flags = program_header_prop(ei_c, runner, p_flags);
- uint8_t uvflags = __elf_to_uvflags(elf_flags);
-
- map_allocd_region(t->proc.vmem, start, vsz, default_flags, 0);
- memset((void *)start, 0, vsz);
-
- vm_t vo = bin + program_header_prop(ei_c, runner, p_offset);
- vm_t vfz = program_header_prop(ei_c, runner, p_filesz);
- memcpy((void *)va, (void *)vo, vfz);
-
- /* skip while testing
- * \todo: also fix, this modifies only the first region. Create new
- * function?
- *
- pm_t paddr = 0;
- stat_vpage(t->b_r, va, &paddr, 0, 0);
- mod_vpage(t->b_r, va, paddr, uvflags);
- */
- }
-}
-
-/**
- * Map ELF dynamic object.
- *
- * @param t Thread space to work in.
- * @param bin Address of binary to map.
- * @param ei_c ELF identity class.
- * @param phstart Program header start.
- * @param phnum Number of program header entries.
- * @param phsize Size of page header entry.
- * @return Base of dynamic mapping.
- */
-static vm_t __map_dyn(struct tcb *t, vm_t bin, uint8_t ei_c, vm_t phstart,
- size_t phnum, size_t phsize)
-{
- /** \todo this path should only be taken when no PT_INTERP is defined, as
- * making sure ld is loaded should be done in userspace. Maybe a bit
- * hacky, I know.*/
-}
-
-/**
- * Map binary and optional interpreter.
- *
- * \todo Implement interpeter handling.
- *
- * @param t Thread space to work in.
- * @param ei_c ELF identity class. (Of binary, should do one for interp?)
- * @param elf ELF binary.
- * @param interp ELF interpeter.
- * @return Entry address.
- */
-static vm_t __prepare_proc(struct tcb *t, uint8_t ei_c, vm_t elf, vm_t interp)
-{
- short e_type = elf_header_prop(ei_c, elf, e_type);
- if (e_type != ET_DYN && e_type != ET_EXEC)
- return 0;
-
- vm_t phstart = ptradd(elf, elf_header_prop(ei_c, elf, e_phoff));
- size_t phnum = elf_header_prop(ei_c, elf, e_phnum);
- size_t phsize = elf_header_prop(ei_c, elf, e_phentsize);
-
- vm_t entry = elf_header_prop(ei_c, elf, e_entry);
- if (e_type == ET_EXEC) {
- __map_exec(t, elf, ei_c, phstart, phnum, phsize);
- return entry;
- } else {
- vm_t o = __map_dyn(t, elf, ei_c, phstart, phnum, phsize);
- return o + entry;
- }
-}
-
-/* sets up all memory regions etc, returns the entry address */
-vm_t load_elf(struct tcb *t, vm_t elf, vm_t interp)
-{
- struct elf_ident *i = (struct elf_ident *)elf;
- if (i->ei_magic != cpu_to_be32(EI_MAGIC))
- return 0;
-
- if (i->ei_class != ELFCLASS32 && i->ei_class != ELFCLASS64)
- return 0;
-
- /* more sanity checks? */
- return __prepare_proc(t, i->ei_class, elf, interp);
-}
diff --git a/common/fdt.c b/common/fdt.c
deleted file mode 100644
index 677be7b..0000000
--- a/common/fdt.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file fdt.c
- * Helper functions for handling the global FDT.
- */
-
-#include <libfdt.h>
-
-struct cell_info get_cellinfo(const void *fdt, const int offset)
-{
- return (struct cell_info){ fdt_size_cells(fdt, offset),
- fdt_address_cells(fdt, offset) };
-}
diff --git a/common/initrd.c b/common/initrd.c
deleted file mode 100644
index caff8b3..0000000
--- a/common/initrd.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file initrd.c
- * Handle initrd, implement cpio with newc format.
- */
-
-#include <kmi/initrd.h>
-#include <kmi/vmem.h>
-#include <kmi/string.h>
-#include <kmi/utils.h>
-#include <kmi/attrs.h>
-#include <kmi/debug.h>
-#include <libfdt.h>
-
-/** GNU cpio, POSIX 'newc' format header. */
-struct __packed cpio_header {
- /** Magic bytes. */
- char c_magic[6];
-
- /** File inode. */
- char c_ino[8];
-
- /** File type and permissions. */
- char c_mode[8];
-
- /** User ID. */
- char c_uid[8];
-
- /** Group ID. */
- char c_gid[8];
-
- /** Number of links to this file. */
- char c_nlink[8];
-
- /** Modification time. */
- char c_mtime[8];
-
- /** Size of file. */
- char c_filesize[8];
-
- /** Device major. */
- char c_devmajor[8];
-
- /** Device minor. */
- char c_devminor[8];
-
- /** Block device major. */
- char c_rdevmajor[8];
-
- /** Block device minor. */
- char c_rdevminor[8];
-
- /** Length of filename. */
- char c_namesize[8];
-
- /** CRC check. */
- char c_check[8];
-};
-
-/**
- * Get next file in archive.
- * Does not check for out of bounds.
- *
- * @param cp Pointer to current file header.
- * @return Pointer to next file header.
- */
-static struct cpio_header *__next_entry(struct cpio_header *cp)
-{
- size_t blen = align_up(
- sizeof(struct cpio_header) + convnum(cp->c_namesize, 8, 16), 4);
- size_t tlen = align_up(convnum(cp->c_filesize, 8, 16), 4);
-
- return (struct cpio_header *)(((char *)cp) + blen + tlen);
-}
-
-/**
- * Get file with name in archive.
- *
- * @param c Pointer to initrd.
- * @param fname Filename to look for.
- * @param fname_len Length of filename.
- * @return Pointer to corresponding file header if found, \c NULL otherwise.
- */
-static struct cpio_header *__find_file(const char *c, const char *fname,
- size_t fname_len)
-{
- struct cpio_header *cp = (struct cpio_header *)c;
- for (; cp; cp = __next_entry(cp)) {
- size_t namelen = convnum(cp->c_namesize, 8, 16);
- if (namelen == 0)
- return NULL;
-
- if (namelen < fname_len)
- continue;
-
- char *name = (char *)(cp + 1);
- if (fname[0] != '/')
- name += namelen - (fname_len + 1); /* match ending */
-
- if (strncmp(name, fname, fname_len) == 0)
- return cp;
- }
-
- return NULL;
-}
-
-/** Name of \c init program. */
-static char init_n[] = "init";
-
-/** Length of \c init name. */
-static size_t init_nlen = ARRAY_SIZE(init_n) - 1; /* ignore trailing NULL */
-
-pm_t get_initrdtop(const void *fdt)
-{
- int chosen_offset = fdt_path_offset(fdt, "/chosen");
- struct cell_info ci = get_cellinfo(fdt, chosen_offset);
-
- void *initrd_end_ptr = (void *)fdt_getprop(fdt, chosen_offset,
- "linux,initrd-end", NULL);
-
- /* fdt is only aware of physical memory pointers */
- return (pm_t)__va(fdt_load_int_ptr(ci.addr_cells, initrd_end_ptr));
-}
-
-pm_t get_initrdbase(const void *fdt)
-{
- const int chosen_offset = fdt_path_offset(fdt, "/chosen");
- const struct cell_info ci = get_cellinfo(fdt, chosen_offset);
-
- void *initrd_base_ptr = (void *)fdt_getprop(fdt, chosen_offset,
- "linux,initrd-start", NULL);
-
- return (pm_t)__va(fdt_load_int_ptr(ci.addr_cells, initrd_base_ptr));
-}
-
-
-size_t get_init_size(const void *fdt)
-{
- char *c = (char *)get_initrdbase(fdt);
- struct cpio_header *cp = __find_file(c, init_n, init_nlen);
- return convnum(cp->c_filesize, 8, 16);
-}
-
-vm_t get_init_base(const void *fdt)
-{
- char *c = (char *)get_initrdbase(fdt);
- struct cpio_header *cp = __find_file(c, init_n, init_nlen);
- size_t name_len = convnum(cp->c_namesize, 8, 16);
- return ((vm_t)cp) + align_up(sizeof(struct cpio_header) + name_len, 4);
-}
-
-stat_t move_init(const void *fdt, void *target)
-{
- const char *c = (const char *)get_initrdbase(fdt);
-
- const struct cpio_header *cp = __find_file(c, init_n, init_nlen);
- size_t name_len = convnum(cp->c_namesize, 8, 16);
- size_t file_len = convnum(cp->c_filesize, 8, 16);
-
- char *fp = (char *)cp;
- fp += align_up(sizeof(struct cpio_header) + name_len, 4);
-
- memmove(target, fp, file_len);
- return OK;
-}
diff --git a/common/ipi.c b/common/ipi.c
deleted file mode 100644
index 59f9fd1..0000000
--- a/common/ipi.c
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-#include <kmi/ipi.h>
-#include <arch/cpu.h>
-
-#include <arch/proc.h>
-
-/**
- * @file ipi.c
- *
- * IPI function implementations.
- */
-
-bool clear_ipi(struct tcb *t)
-{
- bool r = t->ipi;
- t->ipi = false;
- return r;
-}
-
-void send_ipi(struct tcb *t)
-{
- t->ipi = true;
- cpu_send_ipi(t->cpu_id);
-}
-
-void handle_ipi()
-{
- struct tcb *t = cur_tcb();
- adjust_ipi(t);
-
- /** @todo use rpc stack */
- set_return(t, t->callback);
-}
diff --git a/common/irq.c b/common/irq.c
deleted file mode 100644
index db90d34..0000000
--- a/common/irq.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2023, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file irq.c
- * Common IRQ handling stuff implementations.
- */
-
-#include <kmi/irq.h>
-#include <kmi/pmem.h>
-#include <kmi/debug.h>
-#include <kmi/assert.h>
-#include <kmi/string.h>
-#include <arch/irq.h>
-
-/** Hold maximum IRQ id supported by system. */
-static size_t max_irq;
-
-/** Hold map of IRD ID -> thread id. @todo process id? */
-static id_t *irq_map;
-
-void init_irq(void *fdt)
-{
- /** @todo check arch max irq and adjust accordingly */
- irq_map = (id_t *)alloc_page(MM_O0);
- memset(irq_map, 0, order_size(MM_O0));
- max_irq = order_size(MM_O0) / sizeof(irq_map[0]);
-
- setup_irq(fdt);
-}
-
-stat_t register_irq(struct tcb *t, irq_t id)
-{
- if (id >= max_irq)
- return ERR_INVAL;
-
- if (irq_map[id])
- return ERR_EXT;
-
- irq_map[id] = t->tid;
- return activate_irq(id);
-}
-
-stat_t unregister_irq(struct tcb *t, irq_t id)
-{
- id_t tid = irq_map[id];
- if (tid != t->tid)
- return ERR_PERM;
-
- irq_map[id] = 0;
- return deactivate_irq(id);
-}
-
-void handle_irq()
-{
- irq_t id = get_irq();
- hard_assert(id < max_irq, RETURN_VOID);
-
- id_t tid = irq_map[id];
-
- if (!tid) {
- bug("unregistered irq: %llu\n", (unsigned long long)id);
- return;
- }
-
- /** @todo switch to thread */
-}
diff --git a/common/main.c b/common/main.c
deleted file mode 100644
index 4647fed..0000000
--- a/common/main.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file main.c
- * Entry point for actual kernel setup.
- */
-
-#include <kmi/mem_nodes.h>
-#include <kmi/initrd.h>
-#include <kmi/timer.h>
-#include <kmi/attrs.h>
-#include <kmi/proc.h>
-#include <kmi/debug.h>
-#include <kmi/vmem.h>
-#include <kmi/irq.h>
-#include <arch/arch.h>
-#include <arch/proc.h>
-#include <arch/smp.h>
-#include <libfdt.h>
-
-/**
- * Boot entry of kernel actual.
- *
- * Sets up all kernel subsystems and jumps into \c init program, does not
- * return.
- *
- * @param fdt Global FDT pointer in physical memory.
- * @param ram_base RAM base.
- * @return Should not.
- */
-void __main main(void *fdt, uintptr_t ram_base)
-{
- set_ram_base(ram_base);
-
- /* convert physical address to virtual address */
- fdt = __va(fdt);
-
- /* dbg uses direct mapping at this point */
- init_dbg(fdt);
- setup_dmap_dbg();
- dbg_fdt(fdt);
-
- setup_arch(fdt);
-
- init_pmem(fdt);
- /* setup temporary virtual memory */
- struct vmem *b = init_vmem(fdt);
-
- /* start up debugging in kernel IO */
- setup_io_dbg(b);
-
- init_irq(fdt);
- init_timer(fdt);
- init_proc(fdt);
-
- /* try to bring up other cores on system */
- smp_bringup(b, fdt);
-
- /* start running init program */
- void *initrd = (void *)get_initrdbase(fdt);
- run_init(cur_tcb(), fdt, initrd);
-}
diff --git a/common/mem.c b/common/mem.c
deleted file mode 100644
index e3f98e7..0000000
--- a/common/mem.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file mem.c
- * Generic memory handling, used both by physical and virtual memory.
- */
-
-#include <kmi/types.h>
-#include <kmi/mem.h>
-#include <kmi/vmem.h>
-#include <libfdt.h>
-
-size_t __mm_shifts[10];
-size_t __mm_widths[10];
-size_t __mm_sizes[10];
-size_t __mm_page_shift;
-enum mm_order __mm_max_order;
-
-/**
- * RAM base address. Not sure if it should be provided through a macro
- * like __mm_*.
- */
-pm_t ram_base;
-
-enum mm_order nearest_order(size_t size)
-{
- for (enum mm_order order = max_order(); order >= MM_MIN; --order)
- if (order_size(order) >= size)
- return order;
-
- return MM_O0;
-}
-
-void init_mem(size_t max_order, size_t bits[10], size_t page_shift)
-{
- __mm_max_order = max_order;
- __mm_page_shift = page_shift;
-
- __mm_shifts[0] = page_shift;
- __mm_widths[0] = 1 << bits[0];
- __mm_sizes[0] = 1 << __mm_page_shift;
-
- for (enum mm_order i = MM_O1; i <= max_order(); ++i) {
- __mm_widths[i] = 1 << bits[i];
- __mm_shifts[i] = __mm_shifts[i - 1] + bits[i - 1];
- __mm_sizes[i] = 1UL << __mm_shifts[i];
- }
-}
-
-void set_ram_base(uintptr_t base)
-{
- ram_base = base;
-}
-
-uintptr_t get_ram_base()
-{
- return ram_base;
-}
diff --git a/common/mem_nodes.c b/common/mem_nodes.c
deleted file mode 100644
index 358a68d..0000000
--- a/common/mem_nodes.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file mem_nodes.c
- * Memory node wrapper around the node subsystem, used by \ref
- * common/mem_regions.c.
- *
- * Each region of memory is allocated through a \ref mem_region node, which is
- * allocated through the node subsystem.
- */
-
-#include <kmi/vmem.h>
-#include <kmi/pmem.h>
-#include <kmi/mem.h>
-#include <kmi/string.h>
-#include <kmi/mem_nodes.h>
-
-/** Memory node subsystem instance. */
-static struct node_root root;
-
-void init_mem_nodes()
-{
- init_nodes(&root, sizeof(struct mem_region));
-}
-
-void destroy_mem_nodes()
-{
- destroy_nodes(&root);
-}
-
-struct mem_region *get_mem_node()
-{
- return (struct mem_region *)get_node(&root);
-}
-
-void free_mem_node(struct mem_region *m)
-{
- free_node(&root, (void *)m);
-}
diff --git a/common/mem_regions.c b/common/mem_regions.c
deleted file mode 100644
index 9468774..0000000
--- a/common/mem_regions.c
+++ /dev/null
@@ -1,587 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file mem_regions.c
- * Memory region handling, used by both device memory and user virtual memory
- * subsystems.
- */
-
-#include <kmi/mem_regions.h>
-#include <kmi/mem_nodes.h>
-#include <kmi/pmem.h>
-#include <kmi/bits.h>
-#include <kmi/mem.h>
-
-/**
- * Readability wrapper for marking region used.
- *
- * @param r Region flags to set.
- */
-#define mark_region_used(r) set_bit(r, MR_USED)
-
-/**
- * Readability wrapper for marking region unused.
- *
- * @param r Region flags to clear.
- */
-#define mark_region_unused(r) clear_bit(r, MR_USED)
-
-/* pretty major slowdown when we get to some really massive numbers, not
- * entirely sure why. Will need to check up on this at some point, have I
- * somehow managed to come up with a _very_ bad situation for my sp_trees?
- *
- * EDIT: apparently, yeah. Max depth of 106 with a million entries, interesting.
- * I guess since in this scenario all sizes are 1, and I just shove everything
- * to the right? Maybe?
- *
- * EDIT upon EDIT: yeah, when taking the start position of the region into
- * account we get a much more sensible max depth of 39 for 5 million entries.
- * Seems I have found a weakness in sp_trees :D
- *
- * Duplicate entries don't work well with any trees, I think. Good to know,
- * maybe not even anything with sp_trees but more a weakness of binary trees in
- * general?
- */
-
-/**
- * Insert free memory region.
- *
- * @param r Memory region root to insert \c m into.
- * @param m Free memory region to insert.
- * @return \c m.
- */
-static struct mem_region *__insert_free_region(struct mem_region_root *r,
- struct mem_region *m)
-{
- struct sp_node *n = sp_root(&r->free_regions), *p = NULL;
- vm_t start = m->start;
- size_t size = m->end - m->start;
- enum sp_dir d = LEFT;
-
- m->sp_n = (struct sp_node){ 0 };
-
- while (n) {
- struct mem_region *t = mem_container(n);
- size_t nsize = t->end - t->start;
- p = n;
-
- if (size < nsize) {
- n = sp_left(n);
- d = LEFT;
- }
-
- else if (size > nsize) {
- n = sp_right(n);
- d = RIGHT;
- }
-
- else if (start < t->start) {
- n = sp_left(n);
- d = LEFT;
- }
-
- else {
- n = sp_right(n);
- d = RIGHT;
- }
- }
-
- if (sp_root(&r->free_regions))
- sp_insert(&sp_root(&r->free_regions), p, &m->sp_n, d);
- else
- sp_root(&r->free_regions) = &m->sp_n;
-
- return m;
-}
-
-/**
- * Insert used memory region.
- *
- * @param r Memory region root to insert \c m into.
- * @param m Memory region to insert.
- * @return \c m.
- */
-static struct mem_region *__insert_used_region(struct mem_region_root *r,
- struct mem_region *m)
-{
- struct sp_node *n = sp_root(&r->used_regions), *p = NULL;
- vm_t start = m->start;
- enum sp_dir d = LEFT;
-
- m->sp_n = (struct sp_node){ 0 };
-
- while (n) {
- struct mem_region *t = mem_container(n);
-
- p = n;
-
- if (start < t->start) {
- n = sp_left(n);
- d = LEFT;
- }
-
- else {
- /* we should never encounter a situation where start =
- * t->start */
- n = sp_right(n);
- d = RIGHT;
- }
- }
-
- if (sp_root(&r->used_regions))
- sp_insert(&sp_root(&r->used_regions), p, &m->sp_n, d);
- else
- sp_root(&r->used_regions) = &m->sp_n;
-
- return m;
-}
-
-stat_t init_region(struct mem_region_root *r, vm_t start, size_t arena_size)
-{
- /* convert bytes to pages */
- start = __page(start);
- arena_size = __page(arena_size);
- struct mem_region *m = get_mem_node();
- m->start = start;
- m->end = start + arena_size;
- __insert_free_region(r, m);
-
- return OK;
-}
-
-/**
- * Destroy memory region and all its children.
- *
- * @param n \ref sp_node of memory region to destroy.
- */
-static void __destroy_region(struct sp_node *n)
-{
- if (!n)
- return;
-
- if (sp_left(n))
- __destroy_region(sp_left(n));
-
- if (sp_right(n))
- __destroy_region(sp_right(n));
-
- struct mem_region *m = mem_container(n);
- free_mem_node(m);
-}
-
-stat_t destroy_region(struct mem_region_root *r)
-{
- __destroy_region(sp_root(&r->free_regions));
- __destroy_region(sp_root(&r->used_regions));
- /** \todo error checking? */
- return OK;
-}
-
-/* interestingly this is now the main bottleneck :D
- *
- * eh, it's not a massive thing I guess, maybe the code could be a bit quicker
- * but I mean 10 000 000 memory allocations in 20 s is good enough for now
- * */
-struct mem_region *find_used_region(struct mem_region_root *r, vm_t start)
-{
- /** @todo check that start is aligned to page boundary? */
- vm_t ref = __page(start);
- struct sp_node *n = sp_root(&r->used_regions);
- while (n) {
- struct mem_region *t = mem_container(n);
- if (ref == t->start)
- return t;
-
- if (ref < t->start)
- n = sp_left(n);
- else
- n = sp_right(n);
- }
-
- return 0;
-}
-
-/**
- * Create memory region.
- *
- * @param start Start of region.
- * @param end End of region.
- * @param prev Previous region.
- * @param next Next region.
- * @return Created region.
- */
-static struct mem_region *__create_region(vm_t start, vm_t end,
- struct mem_region *prev,
- struct mem_region *next)
-{
- struct mem_region *m = get_mem_node();
- m->start = start;
- m->end = end;
- m->prev = prev;
- m->next = next;
- return m;
-}
-
-/**
- * Get first order size smaller than \c s in bytes.
- *
- * @param s Size to look for.
- * @return Size of first order smaller than \c s.
- */
-static size_t po_align(size_t s)
-{
- for (size_t o = __mm_max_order; o > 0; --o) {
- if (s >= order_size(o))
- return order_size(o);
- }
-
- return 0;
-}
-
-struct mem_region *find_closest_used_region(struct mem_region_root *r,
- vm_t start)
-{
- struct mem_region *closest = 0;
- size_t md = (size_t)(-1);
- struct sp_node *n = sp_root(&r->used_regions);
- if (!n)
- return mem_container(sp_root(&r->free_regions));
-
- while (n) {
- struct mem_region *t = mem_container(n);
- size_t d = ABS((ssize_t)start - (ssize_t)t->start);
-
- if (d == 0) /* exact match */
- return t;
-
- if (d < md) { /* closest so far */
- closest = t;
- md = d;
- }
-
- if (start < t->start)
- n = sp_left(n);
- else
- n = sp_right(n);
- }
-
- return closest;
-}
-
-/* should probably document this a bit better but in short, look for the "best"
- * free block, meaning one that is hopefully aligned so as to allow us to later
- * map it to higher order pages. If no block is found such that that is
- * possible, also keep track of the smallest block that we found that the region
- * still fits in, unaligned. If none of these criteria are met, a NULL is
- * returned. Note that this does not check *all* possible memory blocks, only
- * going up in increasing size so as to save time. */
-struct mem_region *find_free_region(struct mem_region_root *r, size_t size,
- size_t *align)
-{
- *align = 0;
- size_t offset = __page(po_align(__addr(size)));
- struct mem_region *quick_best = 0;
- struct sp_node *n = sp_root(&r->free_regions);
- while (n) {
- struct mem_region *t = mem_container(n);
- vm_t start = align_up(t->start, offset);
-
- size_t qsize = t->end - t->start;
- size_t bsize = t->end - start;
-
- if (!quick_best && size <= qsize)
- quick_best = t;
-
- if (size <= bsize) {
- *align = start - t->start;
- return t;
- }
-
- n = sp_right(n);
- }
-
- return quick_best;
-}
-
-struct mem_region *find_first_region(struct mem_region_root *r)
-{
- /* get used region with smallest address, likely also close to the start
- * of the linked list */
- struct mem_region *m = find_closest_used_region(r, 0);
- while (m->prev) {
- m = m->prev;
- }
-
- return m;
-}
-
-/**
- * Carve out new used memory region from free memory region.
- *
- * @param r Memory region root to work in.
- * @param m Free memory region to carve used memory region out of.
- * @param pages Number of base order pages to give used region.
- * @param align Alignment of used region. In this case, start of used region
- * @param pid Process ID to associate with region if shared. 0 if private.
- * from start of free region.
- * @param flags Flags of used region.
- * @return Start address of used region.
- */
-static vm_t __partition_region(struct mem_region_root *r, struct mem_region *m,
- size_t pages, size_t align, vmflags_t flags,
- id_t pid)
-{
- sp_remove(&sp_root(&r->free_regions), &m->sp_n);
-
- vm_t pre_start = m->start;
- vm_t pre_end = pre_start + align;
-
- vm_t start = pre_end;
- vm_t end = start + pages;
-
- vm_t post_start = end;
- vm_t post_end = m->end;
-
- if (pre_start != pre_end) {
- struct mem_region *n =
- __create_region(pre_start, pre_end, m->prev, m);
- m->prev = n;
- if (n->prev)
- n->prev->next = n;
-
- __insert_free_region(r, n);
- }
-
- if (post_start != post_end) {
- struct mem_region *n =
- __create_region(post_start, post_end, m, m->next);
- m->next = n;
- if (n->next)
- n->next->prev = n;
-
- __insert_free_region(r, n);
- }
-
- m->end = end;
- m->start = start;
- m->flags = flags;
- m->pid = pid;
- mark_region_used(m->flags);
- __insert_used_region(r, m);
- return __addr(start);
-}
-
-/* apparently Linux doesn't necessarily give a shit about mmap hints, so I'll
- * just ignore them for now. Note that alloc_region should only be used when
- * mmap is called with MAP_ANON, all other situations should be handled in some
- * fs server */
-vm_t alloc_shared_region(struct mem_region_root *r, size_t size,
- size_t *actual_size,
- vmflags_t flags, id_t pid)
-{
- size_t asize = align_up(size, BASE_PAGE_SIZE);
- if (actual_size)
- *actual_size = asize;
-
- size_t pages = __page(asize);
-
- /* find best fitting, alignment etc. */
- size_t align = 0;
- struct mem_region *m = find_free_region(r, pages, &align);
- if (!m)
- return 0;
-
- return __partition_region(r, m, pages, align, flags, pid);
-}
-
-vm_t alloc_region(struct mem_region_root *r, size_t size, size_t *actual_size,
- vmflags_t flags)
-{
- return alloc_shared_region(r, size, actual_size, flags, 0);
-}
-
-vm_t alloc_fixed_region(struct mem_region_root *r, vm_t start, size_t size,
- size_t *actual_size, vmflags_t flags)
-{
- size_t asize = align_up(size, BASE_PAGE_SIZE);
- if (actual_size)
- *actual_size = asize;
-
- size_t pages = __page(asize);
- start = __page(start);
-
- struct mem_region *m = find_closest_used_region(r, start);
- if (!m)
- return 0;
-
- /* locate actual region where start is between the region start and end */
- while (!((m->start <= start) && (start < m->end))) {
- if (start > m->start)
- m = m->next;
- else
- m = m->prev;
- }
-
- /* if region is already in use, forget it */
- if (is_region_used(m))
- return 0;
-
- /* region is too small */
- if (start + pages > m->end)
- return 0;
-
- /* actually start marking region used */
- return __partition_region(r, m, pages, start - m->start, flags, 0);
-}
-
-/**
- * Try to coalesce two adjacent memory regions, iterating left.
- *
- * @param r Memory region root to work in.
- * @param m Memory region to start trying to coalesce.
- */
-static void __try_coalesce_prev(struct mem_region_root *r, struct mem_region *m)
-{
- while (m) {
- if (!m || is_region_used(m))
- return;
-
- struct mem_region *p = m->prev;
- if (!p || is_region_used(p))
- return;
-
- m->start = p->start;
- m->prev = p->prev;
-
- if (m->prev)
- m->prev->next = m;
-
- sp_remove(&sp_root(&r->free_regions), &p->sp_n);
- free_mem_node(p);
-
- m = m->prev;
- }
-}
-
-/**
- * Try to coalesce two adjacent memory region, iterating right.
- *
- * @param r Memory region root to work in.
- * @param m Memory region to start trying to coalesce.
- */
-static void __try_coalesce_next(struct mem_region_root *r, struct mem_region *m)
-{
- while (m) {
- if (!m || is_region_used(m))
- return;
-
- struct mem_region *n = m->next;
- if (!n || is_region_used(n))
- return;
-
- m->end = n->end;
- m->next = n->next;
-
- if (m->next)
- m->next->prev = m;
-
- sp_remove(&sp_root(&r->free_regions), &n->sp_n);
- free_mem_node(n);
-
- m = m->next;
- }
-}
-
-/**
- * Try coalescing memory regions.
- *
- * @param r Memory region root to work in.
- * @param m Memory region to start trying to coalesce.
- */
-static void __try_coalesce_regions(struct mem_region_root *r,
- struct mem_region *m)
-{
- __try_coalesce_prev(r, m);
- __try_coalesce_next(r, m);
-}
-
-stat_t free_region(struct mem_region_root *r, vm_t start)
-{
- /* addr not aligned to page boundary, corrupted or incorrect pointer */
- if (!is_aligned(start, BASE_PAGE_SIZE))
- return ERR_ALIGN;
-
- struct mem_region *m = find_used_region(r, start);
- if (!m)
- return ERR_NF;
-
- return free_known_region(r, m);
-}
-
-stat_t free_known_region(struct mem_region_root *r, struct mem_region *m)
-{
- sp_remove(&sp_root(&r->used_regions), &m->sp_n);
- mark_region_unused(m->flags);
-
- __try_coalesce_regions(r, m);
- __insert_free_region(r, m);
- return OK;
-}
-
-void set_alt_region_addr(struct mem_region_root *r, vm_t va, vm_t alt_va)
-{
- struct mem_region *m = find_used_region(r, va);
- if (!m)
- return;
-
- /* not shared region */
- if (m->pid == 0)
- return;
-
- m->alt_va = alt_va;
-}
-
-/* assuming start is chosen to start on an aligned border, this should choose
- * the 'optimal' fit for the mapping.
- *
- * NOTE: not actually optimal, this doesn't bother to go through possible
- * permutations etc. which would be slow and I don't want to implement it.
- */
-vm_t map_fill_region(struct vmem *b, region_callback_t *mem_handler,
- pm_t offset, vm_t start, size_t bytes, vmflags_t flags,
- void *data)
-{
- pm_t runner = __page(start);
- size_t pages = __pages(bytes);
- enum mm_order top = __mm_max_order;
-
- /* actual start might not be the same as the user specified start */
- start = __addr(runner);
-
- for (; pages; top--) {
- size_t o_size = order_size(top);
- size_t o_pages = __pages(o_size);
-
- /* NULL does pass this check, so technically all NULL pages are
- * aligned, but they're caught in the while expr so this should
- * work even if someone tries to map NULL */
- if (!is_aligned(runner, o_pages))
- continue;
-
- while (pages >= o_pages) {
- stat_t res = mem_handler(b, &offset, __addr(runner),
- flags, top, data);
- if (res > 0)
- break;
-
- if (res < 0)
- return 0;
-
- pages -= o_pages;
- runner += o_pages;
- }
- }
-
- return start;
-}
diff --git a/common/nodes.c b/common/nodes.c
deleted file mode 100644
index 5edb783..0000000
--- a/common/nodes.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file nodes.c
- * The node subsystem. Each client has to initialize their own node system,
- * after which they can request nodes of a size specified at init.
- *
- * Node allocation is implemented through a similar system used by jemalloc
- * (https://github.com/jemalloc/jemalloc), but instead of having a number of
- * different sized buckets there is only the one size specified by the user.
- * This cuts down on complexity and improves performance, at a somewhat major
- * flexibility cost. Still, this kernel generally only allocates nodes of the
- * same size again and again, and this approach seems sensible.
- *
- *
- * Quick overview of the allocator, all nodes live in memory pages. Each memory
- * page has a small header at the front, with some metadata about number of free
- * and used node slots. When a memory page is filled, a new one is allocated by
- * the physical memory subsystem and the pages are linked together in a common
- * list. At the same time, a second linked list is maintained which maintains
- * which pages have empty slots. When a node is freed, the page it belonged to
- * is added to the free list (if it didn't already exist there) and when a new
- * node is requested, the free list is looked through first.
- *
- * \todo More in-depth documentation about the node algorithm.
- */
-
-#include <kmi/mem.h>
-#include <kmi/pmem.h>
-#include <kmi/bits.h>
-#include <kmi/nodes.h>
-#include <kmi/string.h>
-
-/* the structure of each node_region is approximately
- *
- * struct node_region | bitmap | array of node_size nodes
- *
- * where array starts on a multiple of node_size to ensure alignment and
- * bitmap is a bitmap of whether node at index is free or used (1 being used, 0
- * being free)
- */
-
-/**
- * Get start of node region from pointer.
- *
- * @param r Pointer to node inside node region.
- * @return Corresponding node region.
- */
-#define node_region(r) \
- ((struct node_region *)((uintptr_t)(r) & ~(BASE_PAGE_SIZE - 1)))
-
-/**
- * Create new node region.
- *
- * @return Pointer to created region.
- */
-static struct node_region *__create_region()
-{
- struct node_region *r = (struct node_region *)alloc_page(BASE_PAGE);
- memset(r, FREE, BASE_PAGE_SIZE);
- return r;
-}
-
-void init_nodes(struct node_root *r, size_t node_size)
-{
- r->head = __create_region();
- r->av_head = r->head;
- r->node_size = node_size;
- r->bitmap = sizeof(struct node_region);
-
- /* ideal values */
- size_t max_nodes = BASE_PAGE_SIZE / node_size;
- /* make sure not to truncate division */
- size_t bitmap_size = (max_nodes / 8) + 1;
- uintptr_t first_node = r->bitmap + bitmap_size;
- /* actual values */
- r->first_node = align_up(first_node, node_size);
- r->max_nodes = max_nodes - (r->first_node / node_size);
-}
-
-void destroy_nodes(struct node_root *r)
-{
- struct node_region *nr = r->head;
- while (nr) {
- struct node_region *d = nr;
- nr = nr->prev;
- free_page(BASE_PAGE, (pm_t)d);
- }
-}
-
-/**
- * Find free node in node region.
- *
- * @param r Node root to work in.
- * @param nr Node region to look in.
- * @return Pointer to free node.
- */
-static void *__find_free_node(struct node_root *r, struct node_region *nr)
-{
- uint8_t *bitmap = r->bitmap + (uint8_t *)nr;
- for (size_t i = 0; i < r->max_nodes; ++i) {
- if (bitmap_is_set(bitmap, i))
- continue;
-
- bitmap_set(bitmap, i);
- return (i * r->node_size) + (r->first_node + (uint8_t *)nr);
- }
-
- return 0;
-}
-
-/**
- * Pop free list head.
- *
- * @param r Node region root to work in.
- */
-static void __pop_av_head(struct node_root *r)
-{
- struct node_region *t = r->av_head;
- r->av_head = r->av_head->next;
- if (r->av_head)
- r->av_head->av_prev = 0;
-
- t->av_next = 0;
- t->av_prev = 0;
-}
-
-void *get_node(struct node_root *r)
-{
- if (!r)
- return 0;
-
- if (!r->av_head) {
- r->av_head = __create_region();
-
- r->av_head->prev = r->head;
- r->head->next = r->av_head;
-
- r->head = r->av_head;
- }
-
- void *p = __find_free_node(r, r->av_head);
- if (++r->av_head->used_nodes == r->max_nodes)
- __pop_av_head(r);
-
- return p;
-}
-
-/**
- * Push free list head.
- *
- * @param r Node region root to work in.
- * @param nr Node region to push.
- */
-static void __push_av_head(struct node_root *r, struct node_region *nr)
-{
- nr->av_prev = 0;
- nr->av_next = r->av_head;
- if (r->av_head)
- r->av_head->av_prev = nr;
-
- r->av_head = nr;
-}
-
-/**
- * Free a node region.
- *
- * @param r Node region root to work in.
- * @param nr Node region to free.
- */
-static void __free_region(struct node_root *r, struct node_region *nr)
-{
- struct node_region *av_n = nr->av_next;
- struct node_region *av_p = nr->av_prev;
-
- if (av_n)
- av_n->av_prev = av_p;
-
- if (av_p)
- av_p->av_next = av_n;
-
- if (nr == r->av_head)
- __pop_av_head(r);
-
- struct node_region *n = nr->next;
- struct node_region *p = nr->prev;
-
- if (n)
- n->prev = p;
-
- if (p)
- p->next = n;
-
- if (nr == r->head) {
- if (r->head->prev) {
- r->head->next = 0;
- r->head = r->head->prev;
- } else
- return;
- }
-
- free_page(BASE_PAGE, (pm_t)nr);
-}
-
-void free_node(struct node_root *r, void *p)
-{
- struct node_region *nr = node_region(p);
- uint8_t *bitmap = r->bitmap + (uint8_t *)nr;
- size_t i =
- ((uintptr_t)p - (r->first_node + (uintptr_t)nr)) / r->node_size;
- bitmap_clear(bitmap, i);
-
- if (--nr->used_nodes == 0) {
- __free_region(r, nr);
- return;
- }
-
- else if (!nr->av_next && !nr->av_prev)
- __push_av_head(r, nr);
-}
diff --git a/common/panic.c b/common/panic.c
deleted file mode 100644
index 03c0de8..0000000
--- a/common/panic.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2023, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file panic.c
- * Kernel panic handler implementation.
- */
-
-#include <kmi/power.h>
-#include <kmi/debug.h>
-
-void kernel_panic(void *pc, void *addr, long cause)
-{
- /* could be useful to print out register values as well? */
- error("kernel paniced at pc: %p with address %p and cause %lx\n",
- pc, addr, cause);
-
- info("attempting to reboot\n");
-
- poweroff(COLD_REBOOT);
-
- /* spin if poweroff failed for some reason */
- error("reboot failed, spinning in place\n");
- while (1);
-}
diff --git a/common/pmem.c b/common/pmem.c
deleted file mode 100644
index 4da3f4c..0000000
--- a/common/pmem.c
+++ /dev/null
@@ -1,587 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file pmem.c
- * Physical memory subsystem. Allocates physical memory pages, with support for
- * different ordered pages, depending on the underlying architecture.
- *
- * Effectively, each page order (4096, 2M, 1G...) has a free list of bitmaps for
- * each page size. When an order runs out of free nodes, it just 'allocates' a
- * node from a higher order list, and gives out those maps. This turned out to
- * be around 50% faster than the previous method, with about half the necessary
- * code. Could still probably be cleaned up a little bit, in particular I don't
- * really care for probe_pmap() vs populate_pmap() but I suppose it's fine for
- * now.
- */
-
-#include <kmi/mem_nodes.h>
-#include <kmi/pmem.h>
-#include <kmi/dmem.h>
-#include <kmi/debug.h>
-#include <kmi/initrd.h>
-#include <kmi/string.h> /* memset */
-#include <kmi/bits.h> /* is_nset etc */
-#include <libfdt.h>
-
-/* \todo add memory page counting?
- * To make sure memory is not overcommited at clone, for example.
- */
-
-/**
- * Loop over orders in reverse, starting with highest, giving the iterator the
- * name \p iter.
- *
- * @param iter Name of iterator.
- */
-#define reverse_foreach_order(iter) \
- for (enum mm_order iter = max_order(); iter != MM_MIN; --iter)
-
-/** Page bitmap node */
-struct mm_bmap {
- /** How many bits this node has. This is generally the same as \c bits in
- * mm_bucket, but could be used for trailing nodes with some irregular
- * number of bits. */
- size_t size;
-
- /** How many pages are currently used */
- size_t used;
-
- /** Next node in freelist */
- struct mm_bmap *next;
-
- /** Previous node in freelist */
- struct mm_bmap *prev;
-
- /** Actual bitmap */
- uint8_t bits[];
-};
-
-/** Bucket of bitmaps for some order of pages */
-struct mm_bucket {
- /** How many bits per (regular) bitmap, see \c size in mm_bmap */
- size_t bits;
-
- /** Size in bytes of a page of this order */
- size_t page_size;
-
- /** Current head of freelist */
- struct mm_bmap *head;
-
- /** Bitmaps in contiguous array, to make populating easier. */
- struct mm_bmap bmap[];
-};
-
-/** Physical map. */
-struct mm_pmap {
- /** Base address of our map. Note that this should be the virtual base
- * address of the physical ram. */
- pm_t base;
- /** Buckets, one per order up to maximum order. */
- struct mm_bucket *buckets[MM_NUM];
-};
-
-/** Static physical map address. \note If I support NUMA, this should probably not be
- * static, rather one physical map per NUMA region. */
-static struct mm_pmap *pmap = 0;
-
-/**
- * Zero out memory if \p populate is true.
- * Helper for populate_pmap(), makes it a bit more easy to follow when we're
- * just calculating the size of out physical map versus actually building it.
- *
- * @param populate Whether to write anything.
- * @param cont Where to write.
- * @param size How many bytes to write.
- * @return Address following last written byte.
- */
-static pm_t __zero_if(bool populate, pm_t cont, size_t size)
-{
- if (populate)
- memset((void *)cont, 0, size);
-
- return cont + size;
-}
-
-/**
- * Get size in bytes of (regular) bitmap node in this bucket.
- *
- * @param bucket Bucket.
- * @return Size in bytes of (regular) bitmap node.
- */
-static size_t __get_set_size(struct mm_bucket *bucket)
-{
- return sizeof(bucket->bmap[0]) + bucket->bits / 8;
-}
-
-/**
- * Get pointer to bitmap node at index \p set.
- *
- * @param bucket Bucket.
- * @param set Index of bitmap node to get.
- * @return Pointer to bitmap node.
- */
-static struct mm_bmap *__get_set(struct mm_bucket *bucket, size_t set)
-{
- uintptr_t bmap = (uintptr_t)bucket->bmap;
- return (struct mm_bmap *)(bmap + __get_set_size(bucket) * set);
-}
-
-/**
- * Get index from pointer to \p bmap within \p bucket.
- *
- * @param bucket Bucket.
- * @param bmap Bitmap node.
- * @return Index of \p bmap within \p bucket.
- */
-static size_t __get_set_index(struct mm_bucket *bucket, struct mm_bmap *bmap)
-{
- size_t s = (uintptr_t)bmap - (uintptr_t)bucket->bmap;
- return s / __get_set_size(bucket);
-}
-
-/**
- * Attach bitmap \p bmap to freelist within \p bucket.
- *
- * @param bucket Bucket.
- * @param bmap Bitmap node.
- */
-static void __attach_set(struct mm_bucket *bucket, struct mm_bmap *bmap)
-{
- /* already attached */
- if (bmap->next)
- return;
-
- bmap->next = bucket->head;
- bucket->head = bmap;
- if (bmap->next)
- bmap->next->prev = bmap;
-}
-
-/**
- * Remove bitmap \p bmap from freelist within \p bucket.
- *
- * @param bucket Bucket.
- * @param bmap Bitmap node.
- */
-static void __detach_set(struct mm_bucket *bucket, struct mm_bmap *bmap)
-{
- if (bucket->head == bmap)
- bucket->head = bmap->next;
-
- if (bmap->next)
- bmap->next->prev = bmap->prev;
-
- if (bmap->prev)
- bmap->prev->next = bmap->next;
-}
-
-/**
- * Calculate address of page within bucket.
- *
- * @param bucket Bucket.
- * @param s Index of bitmap node.
- * @param b Bit within bitmap.
- * @return Address of corresponding page.
- */
-static pm_t __page_addr(struct mm_bucket *bucket, size_t s, size_t b)
-{
- return pmap->base
- + s * bucket->page_size * bucket->bits
- + b * bucket->page_size;
-}
-
-/**
- * Calculate which bitmap node index and bit within bitmap an address
- * corresponds to.
- *
- * @param bucket Bucket.
- * @param a Address.
- * @param s Corresponding bitmap node index.
- * @param b Bitmap node bit index.
- */
-static void __get_bit(struct mm_bucket *bucket, pm_t a, size_t *s, size_t *b)
-{
- a -= pmap->base;
- size_t p = a / bucket->page_size;
- *s = p / bucket->bits;
- *b = p % bucket->bits;
-}
-
-void free_page(enum mm_order order, pm_t addr)
-{
- struct mm_bucket *bucket = pmap->buckets[order];
- if (!bucket)
- return;
-
- size_t set = 0, bit = 0;
- __get_bit(bucket, addr, &set, &bit);
-
- struct mm_bmap *bmap = __get_set(bucket, set);
- bmap->used--;
-
- bitmap_clear(bmap->bits, bit);
- __attach_set(bucket, bmap);
-
- if (bmap->used == 0) {
- __detach_set(bucket, bmap);
-
- if (bmap->size == order_width(order + 1))
- free_page(order + 1, __page_addr(bucket, set, bit));
- }
-}
-
-pm_t alloc_page(enum mm_order order)
-{
- struct mm_bucket *bucket = pmap->buckets[order];
- if (!bucket)
- return 0;
-
- struct mm_bmap *bmap = bucket->head;
- if (!bmap) {
- pm_t a = alloc_page(order + 1);
- if (!a)
- return 0;
-
- size_t set = 0, bit = 0;
- __get_bit(bucket, a, &set, &bit);
-
- bmap = __get_set(bucket, set);
- bmap->used = 0;
- bitmap_clear_all(bmap->bits, bmap->size);
- __attach_set(bucket, bmap);
- return alloc_page(order);
- }
-
- bmap->used++;
-
- size_t set = __get_set_index(bucket, bmap);
- size_t bit = bitmap_find_first_unset(bmap->bits, bmap->size);
- bitmap_set(bmap->bits, bit);
-
- if (bmap->used == bmap->size)
- __detach_set(bucket, bmap);
-
- return __page_addr(bucket, set, bit);
-}
-
-void mark_used(enum mm_order order, pm_t addr)
-{
- struct mm_bucket *bucket = pmap->buckets[order];
- if (!bucket)
- return;
-
- size_t set = 0, bit = 0;
- __get_bit(bucket, addr, &set, &bit);
-
- struct mm_bmap *bmap = __get_set(bucket, set);
- if (bmap->used == 0) {
- bitmap_clear_all(bmap->bits, bmap->size);
- __attach_set(bucket, bmap);
- mark_used(order + 1, addr);
- }
-
- /* a page already in use can just be left alone. This MIGHT hide some
- * bugs in case two separate things overlap in memory during
- * initialization, but that scenario should probably be handled outside
- * of this function anyway. */
- if (bitmap_is_set(bmap->bits, bit))
- return;
-
- bmap->used++;
- bitmap_set(bmap->bits, bit);
-
- if (bmap->used == bmap->size)
- __detach_set(bucket, bmap);
-}
-
-/**
- * Helper for probing/populating a bucket.
- *
- * @param n How many pages in total to account for.
- * @todo currently may cut off some pages if \p n is larger than but not a multiple of order
- * width.
- * @param cont Where to place bucket.
- * @param order Order of bucket to populate.
- * @param first First bucket being populated. Top bucket, owns all pages to
- * start with.
- * @param populate Whether to actually write bucket to memory.
- * @return Address right after where last byte of bycket would be.
- */
-static pm_t __maybe_populate_bucket(size_t n, pm_t cont, enum mm_order order,
- bool first, bool populate)
-{
- struct mm_bucket *bucket = (struct mm_bucket *)cont;
- /* todo max order? */
- size_t bits = order_width(order + 1);
- if (bits == 0)
- bits = n;
-
- if (populate) {
- bucket->bits = bits;
- bucket->page_size = order_size(order);
- bucket->head = NULL;
- }
-
- size_t set_size = sizeof(struct mm_bmap) + bits / 8;
-
- cont += sizeof(struct mm_bucket);
-
- size_t sets = n / bits;
- for (size_t i = 0; i < sets; ++i) {
- struct mm_bmap *bmap = (struct mm_bmap *)cont;
- if (populate) {
- memset(bmap, 0, set_size);
- bmap->size = bits;
- }
-
- if (first && populate)
- __attach_set(bucket, bmap);
-
- n -= bits;
- cont += set_size;
- }
-
- if (n) {
- struct mm_bmap *bmap = (struct mm_bmap *)cont;
- if (populate)
- bmap->size = n;
-
- if (first && populate)
- __attach_set(bucket, bmap);
-
- cont += set_size;
- }
-
- return cont;
-}
-
-/**
- * Probe how many bytes the physical map would take up, optionally populate
- * empty physical map if \p populate is given.
- *
- * I realize it sounds like two
- * different functions, and that it might be a good idea to split in twine,
- * but my thinking was that using the same algorithm with a
- * flag to enable writing to memory would decrease chances that I would
- * calculate the size differently from what is actually needed. We need an
- * accurate estimate of the pmap size to know whether we can place it somewhere
- * and not overwrite something else.
- *
- * @param ram_base Address in kernel space where the physical RAM starts.
- * @param ram_size Size of RAM in bytes.
- * @param start Where to start building pmap.
- * @param populate Whether to actually write anything out to memory.
- * @return Size of pmap in bytes.
- */
-static pm_t __maybe_populate_pmap(pm_t ram_base, size_t ram_size, pm_t start,
- bool populate)
-{
- pm_t cont = start;
-
- pmap = (struct mm_pmap *)start;
- cont = __zero_if(populate, cont, sizeof(*pmap));
- if (populate)
- pmap->base = ram_base;
-
- bool first = true;
- reverse_foreach_order(iter) {
- size_t num = ram_size / order_size(iter);
- if (num == 0)
- continue;
-
- if (populate)
- pmap->buckets[iter] = (struct mm_bucket *)cont;
-
- cont = __maybe_populate_bucket(num, cont, iter, first,
- populate);
- first = false;
- }
-
- return cont - start;
-}
-
-pm_t populate_pmap(pm_t ram_base, size_t ram_size, pm_t start)
-{
- return __maybe_populate_pmap(ram_base, ram_size, start, true);
-}
-
-pm_t probe_pmap(pm_t ram_base, size_t ram_size, pm_t start)
-{
- return __maybe_populate_pmap(ram_base, ram_size, start, false);
-}
-
-/**
- * Helper function for marking area used.
- *
- * @param base Base address of area.
- * @param top Top address of top.
- */
-static void __mark_area_used(pm_t base, pm_t top)
-{
- if (top < base) {
- bug("top < base: %lx < %lx\n", top, base);
- return;
- }
-
- size_t area_left = top - base;
- pm_t runner = base;
- while (area_left >= BASE_PAGE_SIZE) {
- mark_used(BASE_PAGE, runner);
- runner += BASE_PAGE_SIZE;
- area_left -= BASE_PAGE_SIZE;
- }
-
- if (area_left != 0)
- mark_used(BASE_PAGE, runner);
-}
-
-/**
- * Mark reserved memory region used, to avoid it getting accidentally allocated.
- *
- * @param fdt Global FDT pointer.
- */
-static void __mark_reserved_mem(void *fdt)
-{
- int rmem_offset = fdt_path_offset(fdt, "/reserved-memory");
- struct cell_info ci = get_cellinfo(fdt, rmem_offset);
-
- int node = 0;
- fdt_for_each_subnode(node, fdt, rmem_offset) {
- uint8_t *rmem_reg =
- (uint8_t *)fdt_getprop(fdt, node, "reg", NULL);
-
- pm_t base = (pm_t)fdt_load_reg_addr(ci, rmem_reg, 0);
-
- /** @todo make sure the top of a reserved memory area doesn't go
- * against our assumptions in FW_MAX_SIZE? */
- pm_t top = (pm_t)fdt_load_reg_size(ci, rmem_reg, 0) + base;
- __mark_area_used((pm_t)__va(base), (pm_t)__va(top));
- info("marked [%lx - %lx] reserved\n",
- (pm_t)__va(base), (pm_t)__va(top));
- }
-}
-
-/**
- * Read top of RAM from FDT.
- *
- * @param fdt Global FDT pointer.
- * @return Physical address of top of RAM.
- */
-static pm_t __get_ramtop(void *fdt)
-{
- int mem_offset = fdt_path_offset(fdt, "/memory");
- const void *mem_reg = fdt_getprop(fdt, mem_offset, "reg", NULL);
-
- /* here we actually want the root offset because /memory itself doesn't
- * have children, I guess? */
- struct cell_info ci = get_cellinfo(fdt, fdt_path_offset(fdt, "/"));
- pm_t base = (pm_t)fdt_load_reg_addr(ci, mem_reg, 0);
- return (pm_t)fdt_load_reg_size(ci, mem_reg, 0) + base;
-}
-
-/**
- * Read top of FDT.
- *
- * @param fdt Global FDT pointer.
- * @return Physical address of top of FDT.
- */
-static pm_t __get_fdttop(void *fdt)
-{
- const char *b = (const char *)fdt;
- return (pm_t)(b + fdt_totalsize(fdt));
-}
-
-/**
- * Return base of FDT.
- *
- * Technically pretty useless, but here mainly for cohesion.
- *
- * @param fdt Global FDT pointer.
- * @return \c fdt.
- */
-static pm_t __get_fdtbase(void *fdt)
-{
- /* lol */
- return (pm_t)fdt;
-}
-
-void init_pmem(void *fdt)
-{
- /** @todo should I keep the info outputs? I suppose it's nice to see
- * if any assumption is being broken in the serial log, but in that case
- * I should really try adding more of them to other parts of the
- * codebase as well, the pmem subsystem isn't really especially complex.
- */
- info("initializing pmem\n");
-
- size_t max_order = 0;
- size_t base_bits = 0;
- size_t bits[NUM_ORDERS] = { 0 };
- stat_pmem_conf(fdt, &max_order, &base_bits, bits);
- init_mem(max_order, bits, base_bits);
-
- pm_t ram_size = __get_ramtop(fdt) - get_ram_base();
- pm_t ram_base = (pm_t)__va(get_ram_base());
-
- info("using ram range [%lx - %lx]\n",
- ram_base, ram_base + ram_size);
-
- /** @todo could probably improve error messages on failing to get fdt
- * values */
- pm_t initrd_base = get_initrdbase(fdt);
- pm_t initrd_top = get_initrdtop(fdt);
- info("found initrd at [%lx - %lx]\n", initrd_base, initrd_top);
-
- pm_t fdt_top = __get_fdttop(fdt);
- pm_t fdt_base = __get_fdtbase(fdt);
- info("found fdt at [%lx - %lx]\n", fdt_base, fdt_top);
-
- /* find probably most suitable contiguous region of ram for our physical
- * ram map */
- /** @todo this really should check that there's enough space in RAM
- * instead of just forcing the pmap to be populated */
- pm_t pmap_base = align_up(MAX(initrd_top, fdt_top), BASE_PAGE_SIZE);
- info("choosing to place pmem map at %lx\n", pmap_base);
-
- size_t probe_size = probe_pmap(ram_base, ram_size, pmap_base);
- info("pmem map probe size returned %lu\n", probe_size);
-
- size_t actual_size = populate_pmap(ram_base, ram_size, pmap_base);
- info("pmem map actual size %lu\n", actual_size);
-
- if (probe_size != actual_size) {
- bug("probe_size (%#lx) != actual_size (%#lx)\n", probe_size,
- actual_size);
- }
-
- /* mark init stack, this should be unmapped once we get to executing
- * processes */
- __mark_area_used(VM_STACK_BASE, VM_STACK_TOP);
- info("marked stack [%lx - %lx] used\n", VM_STACK_BASE, VM_STACK_TOP);
-
- /* mark kernel */
- /* this could be made more explicit, I suppose. */
- __mark_area_used(VM_KERN, VM_KERN + PM_KERN_SIZE);
- info("marked kernel [%lx - %lx] used\n", VM_KERN,
- VM_KERN + PM_KERN_SIZE);
-
- /* mark fdt and initrd */
- __mark_area_used(initrd_base, initrd_top);
- info("marked initrd [%lx - %lx] used\n", initrd_base, initrd_top);
-
- __mark_area_used(fdt_base, fdt_top);
- info("marked fdt [%lx - %lx] used\n", fdt_base, fdt_top);
-
- /* mark pmap */
- __mark_area_used(pmap_base, pmap_base + actual_size);
- info("marked pmap [%lx - %lx] used\n", pmap_base,
- pmap_base + actual_size);
-
- /* mark reserved mem */
- __mark_reserved_mem(fdt);
-
- init_mem_nodes();
-
- init_devmem((pm_t)__pa(ram_base), (pm_t)__pa(ram_base + ram_size));
-}
diff --git a/common/proc.c b/common/proc.c
deleted file mode 100644
index e6c2769..0000000
--- a/common/proc.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file proc.c
- * Process handling, might be merged into \ref common/tcb.c.
- */
-
-#include <kmi/elf.h>
-#include <kmi/proc.h>
-#include <kmi/conf.h>
-#include <kmi/string.h>
-#include <kmi/initrd.h>
-#include <arch/arch.h>
-#include <arch/proc.h>
-#include <arch/cpu.h>
-
-stat_t prepare_proc(struct tcb *t, vm_t bin, vm_t interp)
-{
- vm_t entry = load_elf(t, bin, interp);
- if (!entry)
- return ERR_INVAL;
-
- alloc_stack(t);
- set_thread(t);
- set_return(t, entry);
- return OK;
-}
-
-stat_t init_proc(void *fdt)
-{
- init_tcbs();
-
- /** \todo cleanup or something */
- struct tcb *t = create_proc(NULL);
- if (!t)
- return ERR_OOMEM;
-
- /* we're the first cpu, so we always have ID 0 */
- t->cpu_id = 0;
-
- /* force tcb for core */
- tcb_assign(t);
-
- /* set current tcb */
- use_tcb(t);
-
- /* init process has all capabilities */
- set_caps(t->caps, 0, CAP_CAPS | CAP_PROC | CAP_CALL | CAP_POWER);
-
- /* allocate stacks after ELF file to make sure nothing of importance
- * clashes */
- return prepare_proc(t, get_init_base(fdt), 0);
- /** \todo start one thread per core, with special handling for init in
- * that each thread starts at the entry point of init? */
-}
diff --git a/common/sp_tree.c b/common/sp_tree.c
deleted file mode 100644
index 9f69158..0000000
--- a/common/sp_tree.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file sp_tree.c
- * Implementation of my sp_trees. An sp_tree is a mix of rb-trees and avl-trees,
- * with slightly faster insertion but worse tree depth on average.
- *
- * See https://github.com/Kimplul/sptree
- *
- * \todo Document sp_tree algorithm better.
- */
-
-#include <kmi/sp_tree.h>
-
-/**
- * Basic BST left turn.
- * Drop node \c n down to the left side of the right node, letting it take
- * the place of \c n.
- *
- * @param n Node to turn left.
- */
-static void __sp_turn_left(struct sp_node *n)
-{
- struct sp_node *l = sp_left(n);
- struct sp_node *p = sp_paren(n);
-
- sp_paren(l) = sp_paren(n);
- sp_left(n) = sp_right(l);
- sp_paren(n) = l;
- sp_right(l) = n;
-
- if (p && sp_left(p) == n)
- sp_left(p) = l;
- else if (p)
- sp_right(p) = l;
-
- if (sp_left(n))
- sp_lparen(n) = n;
-}
-
-/**
- * Basic BST right turn.
- * Drop node \c n down to the right side of the left node, letting it take the
- * place of \c n.
- *
- * Does not check if right node exists.
- *
- * @param n Node to turn right.
- */
-static void __sp_turn_right(struct sp_node *n)
-{
- struct sp_node *r = sp_right(n);
- struct sp_node *p = sp_paren(n);
-
- sp_paren(r) = sp_paren(n);
- sp_right(n) = sp_left(r);
- sp_paren(n) = r;
- sp_left(r) = n;
-
- if (p && sp_left(p) == n)
- sp_left(p) = r;
- else if (p)
- sp_right(p) = r;
-
- if (sp_right(n))
- sp_rparen(n) = n;
-}
-
-/**
- * Calculate approximate balance of node, based on height hints.
- *
- * @param n Node to calculate balance for.
- * @return Balance of node.
- */
-static int_fast16_t __sp_balance(struct sp_node *n)
-{
- int_fast16_t l = 0;
- int_fast16_t r = 0;
-
- if (sp_left(n))
- l = sp_left(n)->hint + 1;
-
- if (sp_right(n))
- r = sp_right(n)->hint + 1;
-
- return l - r;
-}
-
-/**
- * Get highest hint.
- *
- * @param n Node to calculate highest hint for.
- * @return Highest hint.
- */
-static int_fast16_t __sp_max_hint(struct sp_node *n)
-{
- int_fast16_t l = 0;
- int_fast16_t r = 0;
-
- if (sp_left(n))
- l = sp_left(n)->hint + 1;
-
- if (sp_right(n))
- r = sp_right(n)->hint + 1;
-
- if (l > r)
- return l;
- else
- return r;
-}
-
-/**
- * Balance tree, moving up from c n.
- *
- * @param root Root of tree.
- * @param n Node to start balancing operation from.
- */
-static void __sp_update(struct sp_node **root, struct sp_node *n)
-{
- while (n) {
- int b = __sp_balance(n);
- int prev_hint = n->hint;
- struct sp_node *p = sp_paren(n);
-
- if (b < -1) {
- /* leaning to the right */
- if (n == *root)
- *root = sp_right(n);
-
- __sp_turn_right(n);
- }
-
- else if (b > 1) {
- /* leaning to the left */
- if (n == *root)
- *root = sp_left(n);
-
- __sp_turn_left(n);
- }
-
- n->hint = __sp_max_hint(n);
- if (n->hint == 0 || n->hint != prev_hint)
- n = p;
- else
- return;
- }
-}
-
-void sp_insert(struct sp_node **root, struct sp_node *p, struct sp_node *n,
- enum sp_dir d)
-{
- if (!*root) {
- *root = n;
- return;
- }
-
- if (d == LEFT)
- sp_left(p) = n;
- else
- sp_right(p) = n;
-
- sp_paren(n) = p;
- __sp_update(root, n);
-}
-
-/**
- * Replace node \c n with \c l, pulling of the righthand side of \n.
- *
- * @param n Node to replace.
- * @param r Node to replace with.
- */
-static void __sp_replace_right(struct sp_node *n, struct sp_node *r)
-{
- struct sp_node *p = sp_paren(n);
- struct sp_node *rp = sp_paren(r);
-
- if (sp_left(rp) == r) {
- sp_left(rp) = sp_right(r);
- if (sp_right(r))
- sp_rparen(r) = rp;
- }
-
- if (sp_paren(rp) == n)
- sp_paren(rp) = r;
-
- sp_paren(r) = p;
- sp_left(r) = sp_left(n);
-
- if (sp_right(n) != r) {
- sp_right(r) = sp_right(n);
- sp_rparen(n) = r;
- }
-
- if (p && sp_left(p) == n)
- sp_left(p) = r;
- else if (p)
- sp_right(p) = r;
-
- if (sp_left(n))
- sp_lparen(n) = r;
-}
-
-/**
- * Replace node \c n with node \c l, pulling up the lefthand side of \c n.
- *
- * @param n Node to replace.
- * @param l Node to replace with.
- */
-static void __sp_replace_left(struct sp_node *n, struct sp_node *l)
-{
- struct sp_node *p = sp_paren(n);
- struct sp_node *lp = sp_paren(l);
-
- if (sp_right(lp) == l) {
- sp_right(lp) = sp_left(l);
- if (sp_left(l))
- sp_lparen(l) = lp;
- }
-
- if (sp_paren(lp) == n)
- sp_paren(lp) = l;
-
- sp_paren(l) = p;
- sp_right(l) = sp_right(n);
-
- if (sp_left(n) != l) {
- sp_left(l) = sp_left(n);
- sp_lparen(n) = l;
- }
-
- if (p && sp_left(p) == n)
- sp_left(p) = l;
- else if (p)
- sp_right(p) = l;
-
- if (sp_right(n))
- sp_rparen(n) = l;
-}
-
-void sp_remove(struct sp_node **root, struct sp_node *del)
-{
- if (sp_right(del)) {
- struct sp_node *least = sp_first(sp_right(del));
-
- if (del == *root)
- *root = least;
-
- __sp_replace_right(del, least);
- __sp_update(root, sp_right(least));
- return;
- }
-
- if (sp_left(del)) {
- struct sp_node *most = sp_last(sp_left(del));
-
- if (del == *root)
- *root = most;
-
- __sp_replace_left(del, most);
- __sp_update(root, sp_left(most));
- return;
- }
-
- if (del == *root) {
- *root = 0;
- return;
- }
-
- /* empty node */
- struct sp_node *paren = sp_paren(del);
-
- if (sp_left(paren) == del)
- sp_left(paren) = 0;
- else
- sp_right(paren) = 0;
-
- __sp_update(root, paren);
-}
-
-struct sp_node *sp_first(struct sp_node *n)
-{
- if (!sp_left(n))
- return n;
-
- return sp_first(sp_left(n));
-}
-
-struct sp_node *sp_last(struct sp_node *n)
-{
- if (!sp_right(n))
- return n;
-
- return sp_last(sp_right(n));
-}
diff --git a/common/string.c b/common/string.c
deleted file mode 100644
index 8c88e65..0000000
--- a/common/string.c
+++ /dev/null
@@ -1,463 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file string.c
- * Implementations of some string.h stdlib functions.
- */
-
-#include <kmi/string.h>
-#include <kmi/types.h>
-#include <kmi/attrs.h>
-
-/* we need to undef the macros in string.h, otherwise the names get mangled */
-#undef strcpy
-__weak char *strcpy(char * restrict dst, const char * restrict src)
-{
- const char *s1 = src;
- char *s2 = dst;
-
- while (*s1)
- *(s2++) = *(s1++);
-
- return dst;
-}
-
-#undef strncpy
-__weak char *strncpy(char * restrict dst, const char * restrict src, size_t num)
-{
- const char *s1 = src;
- char *s2 = dst;
-
- /* copy s1 into s2 */
- while (num-- && *s1)
- *(s2++) = *(s1++);
-
- /* the previous loop always overshoots by one */
- num++;
-
- /* pad with zeroes if num is not yet zero */
- while (num--)
- *(s2++) = 0;
-
- return dst;
-}
-
-#undef strcat
-__weak char *strcat(char * restrict dst, const char * restrict src)
-{
- const char *s1 = src;
- size_t l1 = strlen(s1);
- char *s2 = dst + l1;
-
- while (*s1)
- *(s2++) = *(s1++);
-
- /* append null character */
- *s2 = 0;
-
- return dst;
-}
-
-#undef strncat
-__weak char *strncat(char * restrict dst, const char * restrict src, size_t num)
-{
- const char *s1 = src;
- size_t l1 = strlen(s1);
- char *s2 = dst + l1;
-
- while (num-- && *s1)
- *(s2++) = *(s1++);
-
- /* append null character */
- *s2 = 0;
-
- return dst;
-}
-
-#undef strcmp
-__weak int strcmp(const char *str1, const char *str2)
-{
- const char *s1 = (const char *)str1;
- const char *s2 = (const char *)str2;
-
- while ((*(s1++) == *(s2++)) && *s1 && *s2)
- ;
-
- return (int)(s1[-1] - s2[-1]);
-}
-
-#undef strncmp
-__weak int strncmp(const char *str1, const char *str2, size_t num)
-{
- const char *s1 = (const char *)str1;
- const char *s2 = (const char *)str2;
-
- while ((*(s1++) == *(s2++)) && *s1 && *s2 && --num)
- ;
-
- return (int)(s1[-1] - s2[-1]);
-}
-
-#undef strchr
-__weak char *strchr(const char *str, int chr)
-{
- const char *s1 = str;
- ssize_t num = strlen(s1);
-
- while (num-- && *(s1++) != chr)
- ;
-
- if (num < 0)
- return 0;
-
- return (char *)(s1 - 1);
-}
-
-#undef strtok
-__weak char *strtok(char * restrict str, const char * restrict delims)
-{
- static char *cont = 0;
- const char *s1 = str;
-
- if (!s1)
- s1 = cont;
-
- if (!s1)
- return 0;
-
- s1 = strpbrk(s1, delims);
-
- if (!s1)
- cont = 0;
- else
- cont = (char *)s1 + 1;
-
- return (char *)s1;
-}
-
-/* should probably test out these functions somehwere, blergh */
-#undef strstr
-__weak char *strstr(const char *str1, const char *str2)
-{
- /* boyer-moore-horspool */
- char table[256] = { 0 };
- size_t sl = strlen(str1);
- size_t pl = strlen(str2);
-
- const unsigned char *s1 = (const unsigned char *)str1;
- const unsigned char *haystack = (const unsigned char *)s1;
- const unsigned char *needle = (const unsigned char *)str2;
-
- for (size_t i = 0; i < 256; ++i)
- table[i] = pl;
-
- /* generate deltas */
- for (size_t i = 0; i < pl - 1; ++i)
- table[needle[i]] = pl - i - 1;
-
- size_t skip = 0;
- while (sl - skip >= pl) {
- s1 = &haystack[skip];
-
- if (!memcmp(s1, needle, pl))
- return (char *)s1;
-
- skip += table[haystack[skip + pl - 1]];
- }
-
- return 0;
-}
-
-#undef strrchr
-__weak char *strrchr(const char *str, int chr)
-{
- ssize_t num = strlen(str);
- const char *s1 = (str + num) - 1;
-
- while (num-- && *(s1--) != chr)
- ;
-
- if (num < 0)
- return 0;
-
- return (char *)(s1 + 1);
-}
-
-#undef strpbrk
-__weak char *strpbrk(const char *str1, const char *str2)
-{
- size_t i = strcspn(str1, str2);
-
- if (!i)
- return 0;
-
- return (char *)(str1 + i);
-}
-
-#undef strcspn
-__weak size_t strcspn(const char *str1, const char *str2)
-{
- char table[256] = { 0 };
- const unsigned char *s1 = (const unsigned char *)str1;
- const unsigned char *s2 = (const unsigned char *)s1;
- const unsigned char *t1 = (const unsigned char *)str2;
-
- /* populate table */
- while (*(t1++))
- table[*t1] = 1;
-
- for (;;) {
- if (table[*(s2++)])
- break;
- }
-
- /* the for loop overshoots by one */
- return (size_t)(s2 - s1) - 1;
-}
-
-#undef strspn
-__weak size_t strspn(const char *str1, const char *str2)
-{
- char table[256] = { 0 };
- const unsigned char *s1 = (const unsigned char *)str1;
- const unsigned char *s2 = (const unsigned char *)s1;
- const unsigned char *t1 = (const unsigned char *)str2;
-
- /* populate table */
- while (*(t1++))
- table[*t1] = 1;
-
- for (;;) {
- if (!table[*(s2++)])
- break;
- }
-
- /* the for loop overshoots by one */
- return (size_t)(s2 - s1) - 1;
-}
-
-#undef strlen
-__weak size_t strlen(const char *str)
-{
- const char *s1 = str;
- while (*(s1++))
- ;
-
- /* the loop overshoots by one */
- return (size_t)(s1 - str) - 1;
-}
-
-/* not a macro */
-__weak size_t strnlen(const char *str, size_t num)
-{
- const char *s1 = str;
- while (num-- && *(s1++))
- ;
-
- return (size_t)(s1 - str) - 1;
-}
-
-#undef memset
-__weak void *memset(void *ptr, int value, size_t num)
-{
- char *p = ptr;
- char c = value;
-
- while (num--)
- *(p++) = c;
-
- return ptr;
-}
-
-#undef memchr
-__weak void *memchr(const void *ptr, int val, size_t num)
-{
- const char *p1 = (char *)ptr;
- ssize_t n = num;
- char c = (char)val;
-
- while (n-- && *(p1++) != c)
- ;
-
- if (n < 0)
- return 0;
-
- return (void *)(p1 - 1);
-}
-
-#undef memcpy
-__weak void *memcpy(void * restrict dst, const void * restrict src, size_t num)
-{
- const char *m1 = (const char *)src;
- char *m2 = (char *)dst;
-
- while (num--)
- *(m2++) = *(m1++);
-
- return dst;
-}
-
-#undef memmove
-__weak void *memmove(void *dst, const void *src, size_t num)
-{
- const char *m1 = (const char *)src;
- char *m2 = (char *)dst;
-
- m1 += num;
- m2 += num;
-
- /* doesn't really take into account aliasing yet */
- while (num--)
- *(--m2) = *(--m1);
-
- return dst;
-}
-
-#undef memcmp
-__weak int memcmp(const void *ptr1, const void *ptr2, size_t num)
-{
- const char *p1 = (const char *)ptr1;
- const char *p2 = (const char *)ptr2;
-
- while ((*(p1++) == *(p2++)) && --num)
- ;
-
- return (int)(p1[-1] - p2[-1]);
-}
-
-/**
- * Convert ASCII hex character to integer.
- * Allows both upper- and lowercase letters.
- *
- * @param c Character to convert.
- * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc.
- * \c -1 if conversion failed.
- */
-static int __hexval(char c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
-
- if (c >= 'a' && c <= 'f')
- return 10 + c - 'a';
-
- if (c >= 'A' && c <= 'F')
- return 10 + c - 'A';
-
- return -1;
-}
-
-/**
- * Convert string assumed to represent hex
- * value to corresponding pointer.
- *
- * @param s String to convert to value.
- * @return Corresponding pointer value.
- */
-static uintptr_t __hexuintptr(const char *s)
-{
- uintptr_t res = 0;
- int val = 0;
- while ((val = __hexval(*(s++))) != -1) {
- res *= 16;
- res += val;
- }
-
- return res;
-}
-
-/**
- * Convert ASCII decimal character to integer.
- *
- * @param c Character to convert.
- * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc.
- * \c -1 if conversion failed.
- */
-static int __decval(char c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
-
- return -1;
-}
-
-/**
- * Convert string assumed to represent decimal
- * value to corresponding pointer.
- *
- * @param s String to convert to value.
- * @return Corresponding pointer value.
- */
-static uintptr_t __decuintptr(const char *s)
-{
- uintptr_t res = 0;
- int val = 0;
- while ((val = __decval(*(s++))) != -1) {
- res *= 10;
- res += val;
- }
-
- return res;
-}
-
-/**
- * Convert ASCII octal character to integer.
- *
- * @param c Character to convert.
- * @return Corresponding integer value. That is, '1' => 1, '2' => 2, etc.
- * \c -1 if conversion failed.
- */
-static int __octval(char c)
-{
- if (c >= '0' && c <= '7')
- return c - '0';
-
- return -1;
-}
-
-/**
- * Convert string assumed to represent octal
- * value to corresponding pointer.
- *
- * @param s String to convert to value.
- * @return Corresponding pointer value.
- */
-static uintptr_t __octuintptr(const char *s)
-{
- uintptr_t res = 0;
- int val = 0;
- while ((val = __octval(*(s++))) != -1) {
- res *= 8;
- res += val;
- }
-
- return res;
-}
-
-uintptr_t strtouintptr(const char *s)
-{
- if (!s)
- return 0;
-
- if (s[0] == 0)
- return 0;
-
- if (s[0] == '0') {
- if (s[1] == 0)
- return 0;
-
- if (s[1] == 'x' || s[1] == 'X')
- return __hexuintptr(s + 2);
-
- return __octuintptr(s + 1);
- }
-
- if (s[0] == '-')
- return -__decuintptr(s + 1);
-
- if (s[0] == '+')
- return __decuintptr(s + 1);
-
- return __decuintptr(s);
-}
diff --git a/common/tcb.c b/common/tcb.c
deleted file mode 100644
index c0f82cc..0000000
--- a/common/tcb.c
+++ /dev/null
@@ -1,310 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file tcb.c
- * Thread control block handling implementation.
- */
-
-#include <kmi/tcb.h>
-#include <kmi/mem.h>
-#include <kmi/conf.h>
-#include <kmi/pmem.h>
-#include <kmi/vmem.h>
-#include <kmi/nodes.h>
-#include <kmi/types.h>
-#include <kmi/assert.h>
-#include <kmi/string.h>
-#include <kmi/canary.h>
-
-#include <arch/cpu.h>
-#include <arch/vmem.h>
-#include <arch/proc.h>
-
-/* arguably exessively many globals... */
-/** Thread ID to start looking from when allocating new ID. */
-static id_t start_tid = 0;
-
-/** Total number of possible thread IDs. */
-static id_t num_tids;
-
-/** Pointer to array of \ref tcb structures. Length of the array is \c num_tids.*/
-static struct tcb **tcbs;
-
-/**
- * Array of thread control block associated with each cpu.
- *
- * \todo If we ever support systems with massive amounts of cpus, this should probably
- * be allocated at runtime.
- */
-static struct tcb *__cpu_tcb[MAX_CPUS] = { 0 };
-
-void init_tcbs()
-{
- /* MM_O1 is 2MiB on riscv64, so 262144 different possible thread ids.
- * Should be enough, if we're really strapped for memory I might try
- * something smaller but this is fine for now. */
- tcbs = (struct tcb **)alloc_page(MM_O1);
- num_tids = order_size(MM_O1) / sizeof(struct tcb *);
- catastrophic_assert(is_powerof2(num_tids));
- memset(tcbs, 0, order_size(MM_O1));
-}
-
-void destroy_tcbs()
-{
- free_page(MM_O1, (pm_t)tcbs);
-}
-
-/**
- * Allocate a new thread ID.
- *
- * @param t Thread to allocate new ID to.
- * @return Allocated ID.
- */
-static id_t __alloc_tid(struct tcb *t)
-{
- id_t stop_tid = start_tid - 1;
- /** \todo this would need some locking or something... */
- for (id_t i = start_tid;; ++i) {
- if (i <= 0)
- i = 1;
-
- /* we're completely full */
- if (i == stop_tid)
- return ERR_NF;
-
- if (get_tcb(i) || i == 0)
- continue;
-
- tcbs[i & (num_tids - 1)] = t;
- start_tid = i + 1;
- return i;
- }
-
- return ERR_NF;
-}
-
-/**
- * Setup thread stack.
- *
- * @param t Thread to setup stack for.
- * @param bytes Minimum size of stack.
- * @return Base of allocated stack.
- */
-static vm_t __setup_thread_stack(struct tcb *t, size_t bytes)
-{
- return alloc_uvmem(t, bytes, VM_V | VM_R | VM_W | VM_U);
-}
-
-stat_t alloc_stack(struct tcb *t)
-{
- /* get parent process */
- struct tcb *p = get_tcb(t->eid);
-
- t->thread_stack = __setup_thread_stack(p, __thread_stack_size);
- if (!t->thread_stack)
- return ERR_OOMEM;
-
- /** \todo this only allows for a global stack size, what if a user wants
- * per thread stack sizes? */
- t->thread_stack_top = t->thread_stack + __thread_stack_size;
- return OK;
-}
-
-struct tcb *create_thread(struct tcb *p)
-{
- hard_assert(tcbs, 0);
-
- vm_t bottom = alloc_page(KERNEL_STACK_PAGE_ORDER);
- /* move tcb to top of kernel stack, keeping alignment in check
- * (hopefully) */
- /** \todo check alignment */
- struct tcb *t = (struct tcb *)align_down(
- bottom + order_size(MM_O0) - sizeof(struct tcb), sizeof(long));
- memset(t, 0, sizeof(struct tcb));
-
- id_t tid = __alloc_tid(t);
- tcbs[tid] = t;
- t->tid = tid;
- t->dead = false;
-
- if (likely(p)) {
- t->pid = p->pid;
- /** @todo I'm assuming two threads can share the same vmem
- * structure, this works on riscv but in the event that other
- * systems don't we can easily turn this into a clone_uvmem. */
- t->proc.vmem = p->proc.vmem;
- } else {
- init_uvmem(t, UVMEM_START, UVMEM_END);
- t->proc.vmem = create_vmem();
- t->pid = t->tid;
- t->rid = t->tid;
- p = t;
- }
-
- t->eid = t->pid;
- t->rid = p->rid;
- t->rpc.vmem = create_vmem();
- setup_rpc_stack(t);
- reference_proc(p);
-
- t->regs = (vm_t)t;
-
- set_canary(t);
- return t;
-}
-
-/**
- * Copy process.
- *
- * @param p Parent process.
- * @param n New process.
- * @return \ref OK.
- */
-static stat_t __copy_proc(struct tcb *p, struct tcb *n)
-{
- /** @todo setup rpc stack stuff */
- /** @todo I think keeping track of userspace stack stuff is unnecessary,
- * unless we want unlimited stack size but that sounds dumb. Anycase, we
- * need to duplicate stack info, whatever we do. */
- n->exec = p->exec;
- n->callback = p->callback;
- n->thread_stack = p->thread_stack;
- n->thread_stack_top = p->thread_stack_top;
-
- clone_regs(n, p);
- copy_caps(n->caps, p->caps);
- return clone_mem_regions(n, p);
-}
-
-struct tcb *create_proc(struct tcb *p)
-{
- hard_assert(tcbs, 0);
-
- /* create a new thread outside the current process */
- struct tcb *n = create_thread(NULL);
- if (!n)
- return 0;
-
- if (p)
- __copy_proc(p, n); /* we have a parent thread */
-
- return n;
-}
-
-/**
- * Destroy data associated with thread.
- *
- * @param t Thread whose data to destroy.
- * @return \ref OK.
- */
-static stat_t __destroy_thread_data(struct tcb *t)
-{
- /* free rpc vmem */
- destroy_vmem(t->rpc.vmem);
-
- /* free associated kernel stack and the structure itself */
- vm_t bottom = align_down((vm_t)t, order_size(MM_O0));
- free_page(MM_O0, (pm_t)bottom);
-
- /** \todo free stacks */
-
- return OK;
-}
-
-stat_t destroy_thread(struct tcb *t)
-{
- hard_assert(tcbs, ERR_NOINIT);
- hard_assert(!is_proc(t), ERR_INVAL);
-
- /* remove thread id from list */
- /** @todo what about if thread is in rpc? should it rather just be
- * marked dead? */
- tcbs[t->tid] = 0;
-
- /* remove reference to root process */
- unreference_proc(get_rproc(t));
-
- return __destroy_thread_data(t);
-}
-
-stat_t destroy_proc(struct tcb *p)
-{
- hard_assert(tcbs, ERR_NOINIT);
- hard_assert(is_proc(p), ERR_INVAL);
-
- p->dead = true;
- /* unreference ourselves */
- unreference_proc(p);
-
- catastrophic_assert(destroy_uvmem(p));
- return __destroy_thread_data(p);
-}
-
-void reference_proc(struct tcb *p)
-{
- hard_assert(is_proc(p), RETURN_VOID);
- p->refcount++;
-}
-
-void unreference_proc(struct tcb *p)
-{
- hard_assert(is_proc(p), RETURN_VOID);
- p->refcount--;
- if (p->dead && p->refcount == 0) {
- dbg("thread %d is completely destroyed\n", p->tid);
- /** @todo actually destroy */
- }
-}
-
-/* weak to allow optimisation on risc-v, but provide fallback for future */
-__weak struct tcb *cur_tcb()
-{
- return cpu_tcb(cpu_id());
-}
-
-struct tcb *cpu_tcb(id_t cpu_id)
-{
- return __cpu_tcb[cpu_id];
-}
-
-struct tcb *cur_proc()
-{
- struct tcb *t = cur_tcb();
- return get_tcb(t->pid);
-}
-
-struct tcb *eff_proc()
-{
- struct tcb *t = cur_tcb();
- return get_tcb(t->eid);
-}
-
-void use_tcb(struct tcb *t)
-{
- cpu_assign(t);
-
- __cpu_tcb[t->cpu_id] = t;
-
- use_vmem(t->proc.vmem);
-}
-
-struct tcb *get_tcb(id_t tid)
-{
- hard_assert(tcbs, 0);
-
- if (tid <= 0)
- return NULL;
-
- return tcbs[tid & (num_tids - 1)];
-}
-
-void set_return(struct tcb *t, vm_t v)
-{
- t->exec = v;
-}
-
-bool running(struct tcb *t)
-{
- return cpu_tcb(t->cpu_id) == t;
-}
diff --git a/common/timer.c b/common/timer.c
deleted file mode 100644
index 272ba02..0000000
--- a/common/timer.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file timer.c
- * Timer handling implementation. Currently we only expect an architecture to
- * support a single timer per core.
- *
- * By keeping all timers in a binary search
- * tree ordered by time, we can just set the single timer to interrupt us when
- * the next timer is due and with thread info call the thread that set the
- * timer. From what I can tell, this is largely what Linux does.
- *
- * \todo Figure out if there are any advantages to having multiple concurrent
- * timers.
- */
-
-#include <kmi/sp_tree.h>
-#include <kmi/string.h>
-#include <kmi/nodes.h>
-#include <kmi/utils.h>
-#include <kmi/timer.h>
-#include <kmi/debug.h>
-#include <arch/timer.h>
-#include <arch/cpu.h>
-
-/** Timer resolution. */
-static ticks_t ticks_per_sec = 0;
-
-/** Array of timer maps for each cpu. */
-static struct sp_root cpu_timers[MAX_CPUS] = { 0 };
-
-/** Timer node subsystem instance. */
-static struct node_root node_root;
-
-/** Node in timer map. */
-struct timer_node {
- /** Sp tree node. */
- struct sp_node sp_n;
-
- /** Corresponding timer. */
- struct timer timer;
-};
-
-/**
- * Get \ref timer_node from \ref sp_node.
- *
- * @param ptr \ref sp_node whose parent \ref timer_node to get.
- * @return Corresponding \ref timer_node.
- */
-#define timer_container(ptr) container_of(ptr, struct timer_node, sp_n)
-
-/**
- * Get \ref timer_node from \ref timer.
- *
- * @param ptr \ref timer whose parent \ref timer_node to get.
- * @return Corresponding \ref timer_node.
- */
-#define timer_node_container(ptr) container_of(ptr, struct timer_node, timer)
-
-/**
- * Get timer map of current cpu.
- *
- * @return Root of current cpu's timer map.
- */
-static struct sp_root *__cpu_timers()
-{
- return &cpu_timers[cpu_id()];
-}
-
-void init_timer(const void *fdt)
-{
- ticks_per_sec = stat_timer(fdt);
- info("ticks_per_sec: %" PRIu64 "\n", ticks_per_sec);
- info("current ticks: %" PRIu64 "\n", current_ticks());
- init_nodes(&node_root, sizeof(struct timer_node));
-}
-
-/**
- * Insert timer into current cpu's timer map.
- *
- * \note \c ti.cid might change during the insertion if there already is a node
- * with identical \c cid to avoid collisions. Very unlikely though.
- *
- * @param ti Timer node to insert.
- * @return \c cid of timer node.
- */
-static id_t __insert_timer(struct timer_node *ti)
-{
- struct sp_root *root = __cpu_timers();
- struct sp_node *n = sp_root(root), *p = NULL;
- enum sp_dir d = LEFT;
- while (n) {
- struct timer_node *t = container_of(n, struct timer_node, sp_n);
- if (ti->timer.cid == t->timer.cid) {
- /* if there's an identical ID, we'll just increment our
- * ID until we get and ID that doesn't exist yet. There
- * is a very small possibility that this will set a
- * timer that's very slightly ahead of some other timer
- * to be handled after the one that's very close, but
- * the timescales that we're dealing with are probably
- * tiny enough that this won't matter, even if it
- * occurs. */
- ti->timer.cid++;
- }
-
- p = n;
-
- if (ti->timer.cid < t->timer.cid) {
- n = sp_left(n);
- d = LEFT;
- } else {
- n = sp_right(n);
- d = RIGHT;
- }
- }
-
- if (sp_root(root))
- sp_insert(&sp_root(root), p, &ti->sp_n, d);
- else
- sp_root(root) = &ti->sp_n;
-
- return ti->timer.cid;
-}
-
-/**
- * Create timer at absolute timepoint.
- *
- * @param tid Requesting thread ID.
- * @param ticks Absolute timepoint.
- * @return \c cid of created timer.
- */
-static id_t __new_timer(id_t tid, ticks_t ticks)
-{
- struct timer_node *ti = (struct timer_node *)get_node(&node_root);
- ti->timer.ticks = ticks;
- /* preliminary ID, may change after actual insertion */
- ti->timer.cid = ticks;
- ti->timer.tid = tid;
- return __insert_timer(ti);
-}
-
-/* these are likely not perfectly accurate timers due to some random delay from
- * function calls etc, but probably good enough. */
-id_t new_rel_timer(id_t tid, ticks_t ticks)
-{
- return new_abs_timer(tid, ticks + current_ticks());
-}
-
-id_t new_abs_timer(id_t tid, ticks_t ticks)
-{
- id_t id = __new_timer(tid, ticks);
- set_timer(ticks);
- return id;
-}
-
-struct timer *newest_timer()
-{
- struct sp_node *t = sp_first(sp_root(__cpu_timers()));
- return &timer_container(t)->timer;
-}
-
-struct timer *find_timer(id_t cid)
-{
- struct sp_node *n = sp_root(__cpu_timers());
- while (n) {
- struct timer_node *t = timer_container(n);
- if (t->timer.cid == cid)
- return &t->timer;
-
- if (t->timer.cid < cid)
- n = sp_left(n);
- else
- n = sp_right(n);
- }
-
- return 0;
-}
-
-stat_t remove_timer(struct timer *t)
-{
- if (!t)
- return ERR_INVAL;
-
- struct sp_node *n = &timer_node_container(t)->sp_n;
- sp_remove(&sp_root(__cpu_timers()), n);
-
- return OK;
-}
-
-ticks_t nsecs_to_ticks(tunit_t nsecs)
-{
- ticks_t t = (nsecs * ticks_per_sec) / 1000000000;
- return t == 0 ? 1 : t;
-}
-
-/* call to this function from exception handlers */
-void handle_timer()
-{
- struct timer *t = newest_timer();
- remove_timer(t);
-
- /** \todo handle timer thread ID */
-}
diff --git a/common/uapi/cap.c b/common/uapi/cap.c
deleted file mode 100644
index 157ef47..0000000
--- a/common/uapi/cap.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-#include <kmi/uapi.h>
-#include <kmi/caps.h>
-#include <kmi/tcb.h>
-
-/**
- * @file cap.c
- * Sycalls handlers for thread capabilities.
- */
-
-/**
- * Check that values are legal for thread ID and offset.
- *
- * @param tid Thread ID of thread whose caps should be fetched.
- * @param off Offset of capabilities.
- * @return Pointer to capabilities of \p tid, \c 0 otherwise.
- */
-static capflags_t *__get_tcb_caps(id_t tid, size_t off)
-{
- if (!cap_off_ok(off))
- return NULL;
-
- struct tcb *t = get_tcb(tid);
- if (!t)
- return NULL;
-
- return &t->caps;
-}
-
-/**
- * Set capabilities.
- *
- * @param t Current tcb.
- * @param tid Thread ID whose capabilities to set.
- * @param off Offset of capability, multiple of \c bits(cap).
- * @param caps Mask of capabilities to set.
- * @return \ref OK on success, \ref ERR_INVAL on invalid input.
- */
-SYSCALL_DEFINE3(set_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off,
- sys_arg_t caps)
-{
- if (!is_set(t->caps, CAP_CAPS))
- return_args1(t, ERR_PERM);
-
- capflags_t *c;
- if (!(c = __get_tcb_caps(tid, off)))
- return_args1(t, ERR_INVAL);
-
- set_caps(*c, off, caps);
- return_args1(t, OK);
-}
-
-/**
- * Get capabilities.
- *
- * @param t Current tcb.
- * @param tid Thread ID whose capabilities to get.
- * @param off Offset of capability, multiple of \c bits(cap).
- * @return \ref OK, capabilities.
- */
-SYSCALL_DEFINE2(get_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off)
-{
- capflags_t *c;
- if (!(c = __get_tcb_caps(tid, off)))
- return_args1(t, ERR_INVAL);
-
- return_args2(t, OK, get_caps(*c, off));
-}
-
-/**
- * Clear capabilities.
- *
- * @param t Current tcb.
- * @param tid Thread ID whose capabilities to clear.
- * @param off Offset of capability, multiple of \c bits(cap).
- * @param caps Mask of capabilities to clear.
- * @return ERR_LERM if invalid permissions, ERR_INVAL if \p tid doesn't exist,
- * otherwise OK.
- */
-SYSCALL_DEFINE3(clear_cap)(struct tcb *t, sys_arg_t tid, sys_arg_t off,
- sys_arg_t caps)
-{
- if (!is_set(t->caps, CAP_CAPS))
- return_args1(t, ERR_PERM);
-
- capflags_t *c;
- if (!(c = __get_tcb_caps(tid, off)))
- return_args1(t, ERR_INVAL);
-
- clear_caps(*c, off, caps);
- return_args1(t, OK);
-}
diff --git a/common/uapi/conf.c b/common/uapi/conf.c
deleted file mode 100644
index 894b98a..0000000
--- a/common/uapi/conf.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file conf.c
- * Runtime configuration sycall implementations.
- *
- * At the moment there are not runtime configuration parameters.
- */
-
-#include <kmi/power.h>
-#include <kmi/sizes.h>
-#include <kmi/uapi.h>
-#include <kmi/conf.h>
-
-#include <arch/proc.h>
-
-/** \todo stack size should really be set on a per-thread basis, and are the
- * conf*-syscalls even necessary? */
-size_t __thread_stack_size = SZ_2M;
-size_t __rpc_stack_size = SZ_512K;
-
-/** IDs for configuration parameters. */
-/** @todo should probably be moved somewhere so it can be shared with userspace */
-enum conf_param {
- CONF_THREAD_STACK = 0,
- CONF_RPC_STACK,
-};
-
-/**
- * Configuration parameter read syscall handler.
- *
- * \todo Implement parameters.
- *
- * @param t Current tcb.
- * @param param Parameter to read.
- * @return \ref OK and parameter value.
- */
-SYSCALL_DEFINE1(conf_get)(struct tcb *t, sys_arg_t param)
-{
- if (!has_cap(t->caps, CAP_CONF))
- return_args1(t, ERR_PERM);
-
- long val = 0;
- switch (param) {
- case CONF_THREAD_STACK:
- val = __thread_stack_size;
- break;
-
- case CONF_RPC_STACK:
- val = __rpc_stack_size;
- break;
-
- default:
- return_args1(t, ERR_NF);
- }
-
- return_args2(t, OK, val);
-}
-
-/**
- * Configuration parameter write syscall handler.
- *
- * \todo Implement parameters.
- *
- * @param t Current tcb.
- * @param param Parameter to write.
- * @param val Value to set \c param to.
- * @return \ref OK and \c 0.
- */
-SYSCALL_DEFINE2(conf_set)(struct tcb *t, sys_arg_t param, sys_arg_t val)
-{
- if (!has_cap(t->caps, CAP_CONF))
- return_args1(t, ERR_PERM);
-
- size_t size = 0;
- switch (param) {
- case CONF_THREAD_STACK:
- __thread_stack_size = align_up(val, BASE_PAGE_SIZE);
- break;
-
- case CONF_RPC_STACK:
- size = align_up(val, BASE_PAGE_SIZE);
- if (size > max_rpc_size())
- return_args1(t, ERR_MISC);
-
- __rpc_stack_size = size;
- break;
- }
-
- return_args1(t, OK);
-}
-
-/**
- * Poweroff syscall handler.
- *
- * @param t Current tcb.
- * @param type Type of poweroff.
- * @return \ref ERR_INVAL and \c 0 if incorrect poweroff \c type give, otherwise
- * does not return.
- */
-SYSCALL_DEFINE1(poweroff)(struct tcb *t, sys_arg_t type)
-{
- if (!(has_cap(t->caps, CAP_POWER)))
- return_args1(t, ERR_PERM);
-
- switch (type) {
- case SHUTDOWN:
- case COLD_REBOOT:
- case WARM_REBOOT:
- return_args2(t, OK, poweroff(type));
- };
-
- return_args1(t, ERR_INVAL);
-}
diff --git a/common/uapi/dispatch.c b/common/uapi/dispatch.c
deleted file mode 100644
index 5b523cc..0000000
--- a/common/uapi/dispatch.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file dispatch.c
- * Syscall dispatch.
- */
-
-#include <kmi/canary.h>
-#include <kmi/debug.h>
-#include <kmi/uapi.h>
-
-/* not sure why doxygen requires these two definitions to state their return
- * values, when they don't actually return anything but eh */
-
-/**
- * Noop syscall handler.
- *
- * @param t Current tcb.
- *
- * @return \ref OK and \c 0.
- */
-SYSCALL_DEFINE0(noop)(struct tcb *t)
-{
- info("sys_noop\n");
- set_args1(t, OK);
-}
-
-/**
- * Putch syscall handler.
- *
- * @param t Current tcb.
- * @param a Character to put.
- *
- * @return \ref OK and 0.
- */
-SYSCALL_DEFINE1(putch)(struct tcb *t, sys_arg_t a)
-{
- dbg("%c", (char)a);
- set_args1(t, OK);
-}
-
-void handle_syscall(sys_arg_t syscall, sys_arg_t a, sys_arg_t b,
- sys_arg_t c, sys_arg_t d, sys_arg_t e, struct tcb *t)
-{
- adjust_syscall(t);
-
- switch (syscall) {
- case SYS_NOOP: sys_noop(t, a, b, c, d, e); break;
- case SYS_PUTCH: sys_putch(t, a, b, c, d, e); break;
- case SYS_REQ_MEM: sys_req_mem(t, a, b, c, d, e); break;
- case SYS_REQ_PAGE: sys_req_page(t, a, b, c, d, e); break;
- case SYS_REQ_PMEM: sys_req_pmem(t, a, b, c, d, e); break;
- case SYS_REQ_FIXMEM: sys_req_fixmem(t, a, b, c, d, e); break;
- case SYS_REQ_SHAREDMEM: sys_req_sharedmem(t, a, b, c, d, e); break;
- case SYS_FREE_MEM: sys_free_mem(t, a, b, c, d, e); break;
- case SYS_TIMEBASE: sys_timebase(t, a, b, c, d, e); break;
- case SYS_TICKS: sys_ticks(t, a, b, c, d, e); break;
- case SYS_REQ_REL_TIMER: sys_req_rel_timer(t, a, b, c, d, e); break;
- case SYS_REQ_ABS_TIMER: sys_req_abs_timer(t, a, b, c, d, e); break;
- case SYS_IPC_SERVER: sys_ipc_server(t, a, b, c, d, e); break;
- case SYS_IPC_REQ: sys_ipc_req(t, a, b, c, d, e); break;
- case SYS_IPC_FWD: sys_ipc_fwd(t, a, b, c, d, e); break;
- case SYS_IPC_KICK: sys_ipc_kick(t, a, b, c, d, e); break;
- case SYS_IPC_RESP: sys_ipc_resp(t, a, b, c, d, e); break;
- case SYS_IPC_NOTIFY: sys_ipc_notify(t, a, b, c, d, e); break;
- case SYS_CREATE: sys_create(t, a, b, c, d, e); break;
- case SYS_FORK: sys_fork(t, a, b, c, d, e); break;
- case SYS_EXEC: sys_exec(t, a, b, c, d, e); break;
- case SYS_SPAWN: sys_spawn(t, a, b, c, d, e); break;
- case SYS_KILL: sys_kill(t, a, b, c, d, e); break;
- case SYS_SWAP: sys_swap(t, a, b, c, d, e); break;
- case SYS_CONF_SET: sys_conf_set(t, a, b, c, d, e); break;
- case SYS_CONF_GET: sys_conf_get(t, a, b, c, d, e); break;
- case SYS_SET_CAP: sys_set_cap(t, a, b, c, d, e); break;
- case SYS_GET_CAP: sys_get_cap(t, a, b, c, d, e); break;
- case SYS_CLEAR_CAP: sys_clear_cap(t, a, b, c, d, e); break;
- case SYS_POWEROFF: sys_poweroff(t, a, b, c, d, e); break;
- case SYS_IRQ_REQ: sys_irq_req(t, a, b, c, d, e); break;
- default:
- error("Syscall %zu outside allowed range [0 - %i]\n", syscall,
- SYS_NUM - 1);
- set_args1(t, ERR_INVAL);
- };
-
- if (check_canary(t)) {
- bug("Syscall %zu overwrote stack canary\n", syscall);
- }
-}
diff --git a/common/uapi/ipc.c b/common/uapi/ipc.c
deleted file mode 100644
index cb2ce28..0000000
--- a/common/uapi/ipc.c
+++ /dev/null
@@ -1,348 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file ipc.c
- * Interprocess communication syscall implementations.
- */
-
-#include <kmi/uapi.h>
-#include <kmi/tcb.h>
-#include <kmi/ipi.h>
-#include <kmi/conf.h>
-
-/** Structure for maintaining the required context data for an rpc call. */
-struct call_ctx {
- /** Execution continuation point. */
- vm_t exec;
-
- /** Register save area. */
- vm_t regs;
-
- /** Position in rpc stack. */
- vm_t rpc_stack;
-
- /** Effective process ID. */
- id_t eid;
-
- /** Current process ID. */
- id_t pid;
-
- /** Whether this context should be skipped when responding. */
- bool kick;
-};
-
-/**
- * Represents difference between where rpc stack was before rpc call and during.
- * Used to figure out which areas should be marked inaccessible.
- */
-struct stack_diff {
- /** Current stack start. Keep in mind that stacks grow down. */
- vm_t start;
- /** Difference end, i.e. previous stack start. */
- vm_t end;
-};
-
-/** Enumerator for IPC kind. Used by do_ipc(). */
-enum ipc_kind {
- IPC_REQ, IPC_FWD, IPC_KICK
-};
-
-/**
- * Now that we know we're doing an rpc, clone virtual memories and do
- * the slow stuff.
- *
- * @param t Thread to migrate.
- * @param r Process to migrate to.
- * @param s RPC stack regions to mark inaccessible.
- */
-static void finalize_rpc(struct tcb *t, struct tcb *r, vm_t s)
-{
- clone_uvmem(r->proc.vmem, t->rpc.vmem);
- set_return(t, r->callback);
- reference_proc(r);
- t->pid = r->rid;
-
- /* make sure updates are visible when swapping to the new virtual memory */
- mark_rpc_invalid(t, s);
- use_vmem(t->rpc.vmem);
-}
-
-/**
- * Optimistically assume we're going to take the rpc and do some preparations
- * for it. Most notably, write the arguments as early as possible to
- * free up registers for the compiler to play with.
- *
- * @param t Thread to migrate.
- * @param a RPC arguments.
- * @param kind Kind of IPC we're doing. Essentially toggles kick boolean.
- * @return RPC stack difference that should be passed to finalize_rpc().
- */
-static vm_t enter_rpc(struct tcb *t, struct sys_ret a,
- enum ipc_kind kind)
-{
- vm_t rpc_stack = rpc_position(t);
-
- struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1;
- ctx->regs = t->regs;
- t->regs = (vm_t)ctx;
-
- /* try to get rid of args as fast as possible to free up registers for
- * later use */
- set_args(t, 6, a);
-
- ctx->exec = t->exec;
- ctx->pid = t->pid;
- ctx->eid = t->eid;
- ctx->rpc_stack = rpc_stack;
-
- /* only rpcs can be kicked forward */
- ctx->kick = kind == IPC_KICK && is_rpc(t);
-
- /** @todo if we run out of rpc_stack space we should just stop, likely
- * return a status? except it shouldn't happen after we've run
- * enough_rpc_stack(). */
- vm_t new_stack = rpc_stack - BASE_PAGE_SIZE;
-
- /** @todo what if each stack is only some number of pages, and if a proc
- * goes over the limit is is seen as programming error? Possibly user
- * configurable number as well, might actually use the config subsystem
- * :D
- * In such a case it would probably be smarter to mark all pages
- * inaccessible at first, and then mark the first page accessible. If
- * the process needs more stack space it'll cause a paging exception,
- * we'll handle it separately and if the process isn't going over the
- * limit just give it more.
- * */
- t->rpc_stack = new_stack;
- set_stack(t, new_stack);
- return new_stack;
-}
-
-/**
- * Jump back to process where rpc came from, assuming such a thing exists.
- *
- * @param t Thread to do return migration on.
- * @param a Arguments to pass along.
- */
-static void leave_rpc(struct tcb *t, struct sys_ret a)
-{
- vm_t rpc_stack = t->rpc_stack + BASE_PAGE_SIZE;
- struct call_ctx *ctx = (struct call_ctx *)(rpc_stack) - 1;
- vm_t top = ctx->rpc_stack;
-
- /* find first instance of not kicked context */
- while (ctx->kick) {
- rpc_stack = ctx->rpc_stack + BASE_PAGE_SIZE;
- ctx = (struct call_ctx *)(rpc_stack) - 1;
- unreference_proc(get_tcb(ctx->pid));
- }
-
- t->regs = ctx->regs;
- /* again, get rid of args as fast as possible */
- set_args(t, 6, a);
-
- set_return(t, ctx->exec);
- /* if we're returning from a failed rpc, this should essentially be a
- * no-op */
- mark_rpc_valid(t, top);
- t->rpc_stack = ctx->rpc_stack;
- t->pid = ctx->pid;
- t->eid = ctx->eid;
-
- if (is_rpc(t))
- use_vmem(t->rpc.vmem);
- else
- use_vmem(t->proc.vmem);
-}
-
-/**
- * Check that there's enough stack left for an rpc invocation.
- *
- * @param t Thread whose migration to check.
- * @return \c true if there's enough stack left to safely do migration,
- * \c false otherwise.
- */
-static bool enough_rpc_stack(struct tcb *t)
-{
- /* get top of call stack */
- vm_t top = rpc_position(t);
-
- /* if we can still fit an rpc stack into the call stack, we can safely
- * do the migration. */
- return top - BASE_PAGE_SIZE - __rpc_stack_size >= RPC_STACK_BASE;
-}
-
-/**
- * IPC server notification syscall handler.
- *
- * @param t Current tcb.
- * @param callback Address of server callback.
- * @return \ref OK and \c 0.
- */
-SYSCALL_DEFINE1(ipc_server)(struct tcb *t, sys_arg_t callback)
-{
- get_cproc(t)->callback = callback;
- return_args1(t, OK);
-}
-
-/**
- * Actual IPC syscall handler.
- *
- * @param t Current tcb.
- * @param pid Process to request RPC to.
- * @param d0 IPC argument 0.
- * @param d1 IPC argument 1.
- * @param d2 IPC argument 2.
- * @param d3 IPC argument 3.
- * @param kind Which kind of IPC to perform.
- *
- * Returns \ref ERR_OOMEM if there isn't enough IPC stack left, \ref ERR_INVAL
- * if the the target process doesn't exist, \ref ERR_NOINIT if the target
- * process hasn't defined a callback. Otherwise \ref OK and whatever the target
- * process sends back.
- *
- * @todo should all static functions have double underscores? I seem to be
- * inconsistent.
- */
-static void do_ipc(struct tcb *t,
- sys_arg_t pid,
- sys_arg_t d0,
- sys_arg_t d1,
- sys_arg_t d2,
- sys_arg_t d3,
- enum ipc_kind kind)
-{
- if (unlikely(!enough_rpc_stack(t)))
- return_args1(t, ERR_OOMEM);
-
- vm_t s = enter_rpc(t, SYS_RET6(t->eid, t->tid, d0, d1, d2, d3), kind);
-
- struct tcb *r = get_tcb(pid);
- if (unlikely(!r)) {
- leave_rpc(t, SYS_RET1(ERR_INVAL));
- return;
- }
-
- r = get_rproc(r);
- if (unlikely(r->dead)) {
- leave_rpc(t, SYS_RET1(ERR_INVAL));
- return;
- }
-
- if (unlikely(!r->callback)) {
- leave_rpc(t, SYS_RET1(ERR_NOINIT));
- return;
- }
-
- if (kind != IPC_REQ)
- t->eid = t->pid;
-
- finalize_rpc(t, r, s);
- /* I tested out passing the return values as arguments to
- * ret_userspace_fast, but apparently that causes enough stack shuffling
- * to be slower overall. */
- ret_userspace_fast();
-}
-/**
- * IPC request syscall handler.
- *
- * @param t Current tcb.
- * @param pid Process to request RPC to.
- * @param d0 IPC argument 0.
- * @param d1 IPC argument 1.
- * @param d2 IPC argument 2.
- * @param d3 IPC argument 3.
- * @return When succesful: OK, thread id of the caller and the arguments as-is.
- */
-SYSCALL_DEFINE5(ipc_req)(struct tcb *t, sys_arg_t pid,
- sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3)
-{
- do_ipc(t, pid, d0, d1, d2, d3, IPC_REQ);
-}
-
-/**
- * IPC forwarding syscall handler.
- *
- * @param t Current tcb.
- * @param pid Process to request RPC to.
- * @param d0 IPC argument 0.
- * @param d1 IPC argument 1.
- * @param d2 IPC argument 2.
- * @param d3 IPC argument 3.
- * @return When succesful: OK, thread id of the caller and the arguments as-is.
- */
-SYSCALL_DEFINE5(ipc_fwd)(struct tcb *t, sys_arg_t pid,
- sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3)
-{
- do_ipc(t, pid, d0, d1, d2, d3, IPC_FWD);
-}
-
-/**
- * IPC kicking syscall handler.
- *
- * @param t Current tcb.
- * @param pid Process to request RPC to.
- * @param d0 IPC argument 0.
- * @param d1 IPC argument 1.
- * @param d2 IPC argument 2.
- * @param d3 IPC argument 3.
- * @return When succesful: OK, thread id of the caller and the arguments as-is.
- */
-SYSCALL_DEFINE5(ipc_kick)(struct tcb *t, sys_arg_t pid,
- sys_arg_t d0, sys_arg_t d1, sys_arg_t d2,
- sys_arg_t d3)
-{
- do_ipc(t, pid, d0, d1, d2, d3, IPC_KICK);
-}
-
-/**
- * IPC response syscall handler.
- *
- * @param t Current tcb.
- * @param d0 IPC return value 0.
- * @param d1 IPC return value 1.
- * @param d2 IPC return value 2.
- * @param d3 IPC return value 3.
- * @return \c d0 and \c d1.
- */
-SYSCALL_DEFINE4(ipc_resp)(struct tcb *t, sys_arg_t d0, sys_arg_t d1,
- sys_arg_t d2,
- sys_arg_t d3)
-{
- /* if we're not in an rpc, the user messed something up. */
- /** @todo choose or come up with more fitting error value. */
- if (unlikely(!is_rpc(t)))
- return_args1(t, ERR_MISC);
-
- leave_rpc(t, SYS_RET6(OK, t->tid, d0, d1, d2, d3));
-}
-
-/**
- * Notify syscall handler.
- *
- * \todo Implement.
- *
- * @param t Current tcb.
- * @param tid Thread ID to notify.
- * @return \ref OK and 0.
- */
-SYSCALL_DEFINE1(ipc_notify)(struct tcb *t, sys_arg_t tid){
- if (!has_cap(t->caps, CAP_CALL))
- return_args1(t, ERR_PERM);
-
- struct tcb *r = get_tcb(tid);
- if (r->notify_state == NOTIFY_QUEUED)
- return_args1(t, OK);
-
- if (r->notify_state == NOTIFY_RUNNING) {
- t->notify_state = NOTIFY_QUEUED;
- return_args1(t, OK);
- }
-
- r->notify_state = NOTIFY_QUEUED;
- if (running(r))
- send_ipi(r);
-
- return_args1(t, OK);
-}
diff --git a/common/uapi/irq.c b/common/uapi/irq.c
deleted file mode 100644
index 6687826..0000000
--- a/common/uapi/irq.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2023, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file irq.c
- * IRQ request syscall handling implementation.
- */
-
-#include <kmi/uapi.h>
-#include <kmi/irq.h>
-
-/**
- * Actual IRQ handling request syscall handler.
- *
- * @param t Current tcb.
- * @param id IRQ id to request to handle.
- *
- * @return OK on success, non-zero otherwise.
- */
-SYSCALL_DEFINE1(irq_req)(struct tcb *t, sys_arg_t id)
-{
- if (!has_cap(t->caps, CAP_IRQ))
- return_args1(t, ERR_PERM);
-
- if (!t->callback)
- return_args1(t, ERR_NF);
-
- return_args1(t, register_irq(t, id));
-}
diff --git a/common/uapi/mem.c b/common/uapi/mem.c
deleted file mode 100644
index d54390a..0000000
--- a/common/uapi/mem.c
+++ /dev/null
@@ -1,158 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file mem.c
- * Memory handling syscall implementations.
- * \todo Should we return more error information?
- */
-
-#include <kmi/uapi.h>
-#include <kmi/utils.h>
-#include <kmi/vmem.h>
-#include <kmi/dmem.h>
-
-/**
- * Memory request syscall handler.
- *
- * @param t Current tcb.
- * @param size Minimum size of allocation.
- * @param flags Flags of allocation.
- * @return \ref OK and start of allocation when succesful,
- * \ref ERR_OOMEM and \c NULL otherwise.
- */
-SYSCALL_DEFINE2(req_mem)(struct tcb *t, sys_arg_t size, sys_arg_t flags)
-{
- struct tcb *r = get_cproc(t);
- vm_t start = 0;
- /** @todo expose flags to users */
- if (!(start = alloc_uvmem(r, size, flags)))
- return_args1(t, ERR_OOMEM);
-
- return_args2(t, OK, start);
-}
-
-/**
- * Allocate single page to program.
- *
- * @param t Current tcb.
- * @param size Size of the allocation.
- * @param flags Flags of allocation.
- * @return \ref ERR_OOMEM if unsucessful, otherwise \ref OK, virtual address,
- * actual size, physical address, in that order.
- */
-SYSCALL_DEFINE2(req_page)(struct tcb *t, sys_arg_t size, sys_arg_t flags)
-{
- struct tcb *r = get_cproc(t);
- vm_t start = 0; pm_t paddr = 0; size_t asize = size;
- if (!(start = alloc_uvpage(r, asize, flags, &asize, &paddr)))
- return_args1(t, ERR_OOMEM);
-
- return_args4(t, OK, start, asize, paddr);
-}
-
-/**
- * Fixed memory request syscall handler.
- *
- * @param t Current tcb.
- * @param fixed Address which should be included in allocation.
- * @param size Minimum size of allocation after \c start.
- * @param flags Flags of allocation.
- * @return \ref OK and start of allocation when succesful,
- * \ref ERR_OOMEM and \c NULL otherwise.
- */
-SYSCALL_DEFINE3(req_fixmem)(struct tcb *t, sys_arg_t fixed, sys_arg_t size,
- sys_arg_t flags)
-{
- struct tcb *r = get_cproc(t);
- vm_t start = 0;
- if (!(start = alloc_fixed_uvmem(r, fixed, size, flags)))
- return_args1(t, ERR_OOMEM);
-
- return_args2(t, OK, start);
-}
-
-/**
- * Free memory syscall handler.
- *
- * @param t Current tcb.
- * @param start Start of allocation to free.
- * @return \ref OK and \c 0 when succesful, \ref ERR_NF and \c 0 otherwise.
- */
-SYSCALL_DEFINE1(free_mem)(struct tcb *t, sys_arg_t start)
-{
- struct tcb *r = get_cproc(t);
- vm_t vm_start = (vm_t)start;
-
- stat_t status = OK;
- /* try freeing normal user memory first, if that fails, try device
- * memory, otherwise just assume the address is borked. */
- if (!(status = free_uvmem(r, vm_start)))
- return_args1(t, OK);
-
- if (!(status = free_devmem(r, vm_start)))
- return_args1(t, OK);
-
- return_args1(t, status);
-}
-
-/**
- * Request physical memory syscall handler.
- *
- * @param t Current tcb.
- * @param paddr Physical address to map.
- * @param size Minimum size of allocation.
- * @param flags Flags of allocation.
- * @return \ref OK and start of allocation when succesful,
- * \ref ERR_OOMEM and \c NULL otherwise.
- */
-SYSCALL_DEFINE3(req_pmem)(struct tcb *t, sys_arg_t paddr, sys_arg_t size,
- sys_arg_t flags)
-{
- /* this will require some pondering, but essentially this syscall should
- * only be used for device access, so any addresses requested should be
- * outside the RAM area, and I'll probably have to implement some method
- * that keeps track of used regions outside of RAM. We'll see.
- */
- struct tcb *r = get_cproc(t);
- vm_t start = 0;
- if (!(start = alloc_devmem(r, paddr, size, flags)))
- return_args1(t, ERR_OOMEM);
-
- return_args2(t, OK, start);
-}
-
-/**
- * Request shared memory syscall handler.
- *
- * @param t Current tcb.
- * @param tid Thread to share memory with.
- * @param size Minimum size of allocation.
- * @param sflags Flags of allocation for \p t.
- * @param cflags Flags of allocation for \p tid.
- * @return \ref OK and start of \p t allocation and start of \p tid allocation,
- * in that order, \ref ERR_OOMEM otherwise.
- *
- * @todo should we also take the thread who should get the other end of the
- * memory?
- */
-SYSCALL_DEFINE4(req_sharedmem)(struct tcb *t, sys_arg_t tid,
- sys_arg_t size, sys_arg_t sflags,
- sys_arg_t cflags)
-{
- /** @todo check capability for shared memory */
- struct tcb *u = get_tcb(tid);
- if (!u)
- return_args1(t, ERR_INVAL);
-
- struct tcb *s = get_cproc(t);
- struct tcb *c = get_rproc(u);
-
- vm_t sstart, cstart;
- if (alloc_shared_uvmem(s, c, size, sflags, cflags, &sstart, &cstart))
- return_args1(t, ERR_OOMEM);
-
- return_args3(t, OK, sstart, cstart);
-}
-
-/** \todo add some way to specify who gets to access the shared memory? */
diff --git a/common/uapi/proc.c b/common/uapi/proc.c
deleted file mode 100644
index e7fa49d..0000000
--- a/common/uapi/proc.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file proc.c
- * Process/thread handling syscall implementations.
- */
-
-#include <kmi/elf.h>
-#include <kmi/uapi.h>
-#include <kmi/proc.h>
-#include <kmi/bits.h>
-#include <kmi/mem_regions.h>
-
-/**
- * Create syscall handler.
- *
- * @param t Current tcb.
- * @param func Function to jump to at thread creation.
- * @param d0 Argument 0.
- * @param d1 Argument 1.
- * @param d2 Argument 2.
- * @param d3 Argument 3.
- *
- * @return ERR_OOMEM if thread creation was unsuccessful, otherwise OK and the
- * thread id.
- */
-SYSCALL_DEFINE5(create)(struct tcb *t, sys_arg_t func,
- sys_arg_t d0, sys_arg_t d1, sys_arg_t d2, sys_arg_t d3)
-{
- struct tcb *c = create_thread(t);
- if (!c)
- return_args1(t, ERR_OOMEM);
-
- alloc_stack(c);
-
- set_args5(c, c->tid, d0, d1, d2, d3);
- set_return(c, func);
-
- return_args2(t, OK, c->tid);
-}
-
-/**
- * Fork syscall handler.
- *
- * \todo Not entirely sure how I should handle forks/execs etc, mostly whether I
- * should allow forks/execs to be called directly or only though the process
- * manager. Probably though the process manager, although that will add in a
- * slight bit of delay.
- *
- * I suppose I could add in a runtime parameter
- * that would allow them to be called directly, and then the process manager
- * would have to periodically ask the kernel about all threads it is aware of
- * via sys_sync. Dunno.
- *
- * @param t Current tcb.
- * @return \ref OK and 0.
- */
-SYSCALL_DEFINE0(fork)(struct tcb *t)
-{
- struct tcb *c = get_cproc(t);
- if (!(has_cap(c->caps, CAP_PROC)))
- return_args1(t, ERR_PERM);
-
- struct tcb *n = create_proc(get_eproc(t));
- if (!n)
- return_args1(t, ERR_OOMEM);
-
- /* prepare args for when we eventually swap to the new proc, giving
- * parent ID as third return value */
- set_args3(n, OK, 0, get_eproc(t)->pid);
-
- return_args2(t, OK, n->pid);
-}
-
-/**
- * Exec syscall handler.
- *
- * @param t Current tcb.
- * @param bin Binary to execute.
- * @param interp Optional interpreter binary.
- *
- * @return \see prepare_proc().
- */
-SYSCALL_DEFINE2(exec)(struct tcb *t, sys_arg_t bin, sys_arg_t interp)
-{
- /** @todo probably make sure thread is root thread of process? */
- /* mark binary to be kept */
- struct mem_region *b = find_used_region(&t->sp_r, bin);
- if (!b)
- return_args1(t, ERR_INVAL);
-
- set_bit(b->flags, MR_KEEP);
-
- struct mem_region *i = 0;
- if (interp) {
- /* mark interpreter to be kept */
- i = find_used_region(&t->sp_r, interp);
- if (!i)
- return_args1(t, ERR_INVAL);
-
- set_bit(i->flags, MR_KEEP);
- }
-
- /* free everything except regions to be kept */
- clear_uvmem(t);
-
- /* restore to normal */
- clear_bit(b->flags, MR_KEEP);
- if (interp)
- clear_bit(b->flags, MR_KEEP);
-
- return_args1(t, prepare_proc(t, bin, interp));
-}
-
-/**
- * Spawn syscall handler.
- *
- * @param t Current tcb.
- * @param bin Binary to execute.
- * @param interp Optional interpreter binary.
- *
- * @return \see prepare_proc() and process id of the new process.
- */
-SYSCALL_DEFINE2(spawn)(struct tcb *t, sys_arg_t bin, sys_arg_t interp)
-{
- struct tcb *c = get_proc(t);
- if (!(has_cap(c->caps, CAP_PROC)))
- return_args1(t, ERR_PERM);
-
- struct tcb *n = create_proc(NULL);
- if (!n)
- return_args1(t, ERR_OOMEM);
-
- return_args2(t, prepare_proc(n, bin, interp), n->pid);
-}
-
-/**
- * Kill syscall handler.
- *
- * @param t Current tcb.
- * @param tid Thread to kill.
- * \todo Implement.
- *
- * @return ERR_PERM if not capable to kill, otherwise OK.
- */
-SYSCALL_DEFINE1(kill)(struct tcb *t, sys_arg_t tid)
-{
- struct tcb *c = get_cproc(t);
- if (!(has_cap(c->caps, CAP_PROC)))
- return_args1(t, ERR_PERM);
-
- /** @todo implement */
- /** @todo remember to unregister IRQ handlers */
-
- return_args1(t, OK);
-}
-
-/**
- * Swap syscall handler.
- *
- * \todo Implement.
- * \todo Should swap return the registers of the new thread that would be used
- * for message passing?
- *
- * @param t Current tcb.
- * @param tid Thread ID to swap to.
- *
- * @return \ref OK.
- */
-SYSCALL_DEFINE1(swap)(struct tcb *t, sys_arg_t tid){
- struct tcb *c = get_cproc(t);
- if (!(has_cap(c->caps, CAP_PROC)))
- return_args1(t, ERR_PERM);
-
- struct tcb *s = get_tcb(tid);
- if (!s)
- return_args1(t, ERR_INVAL);
-
- /* switch over to new thread */
- use_tcb(s);
-
- /* set return value for current thread */
- set_args1(t, OK);
-
- /* get register state for new thread */
- return_args(s, get_args(s));
-}
diff --git a/common/uapi/timers.c b/common/uapi/timers.c
deleted file mode 100644
index b119fcc..0000000
--- a/common/uapi/timers.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file timers.c
- * Timer syscall implementations.
- */
-
-#include <kmi/timer.h>
-#include <kmi/uapi.h>
-
-#include <arch/timer.h>
-
-/**
- * Convert arch-specific register values \p ticks and \p mult to \ref ticks_t.
- *
- * If we're on a 32bit system, one register can't contain a tick value,
- * so we use two registers and combine them into one value and let the compiler
- * handle the rest.
- *
- * @param ticks Register width tick value.
- * @param mult Register width repeat value.
- * @return Corresponding \ref ticks_t value.
- */
-static ticks_t scaled_ticks(sys_arg_t ticks, sys_arg_t mult)
-{
-#if defined(_LP64)
- UNUSED(mult);
- return ticks;
-#else
- return (ticks_t)ticks * (ticks_t)mult;
-#endif
-}
-
-/**
- * Timebase syscall handler.
- *
- * @param t Current tcb.
- * @return \ref OK and timebase in second argument if 64bit, otherwise high 32
- * bits of timebase in second argument and low 32 bits in third argument.
- */
-SYSCALL_DEFINE0(timebase)(struct tcb *t)
-{
- ticks_t tm = secs_to_ticks(1);
-#if defined(_LP64)
- return_args2(t, OK, tm);
-#else
- return_args3(t, OK, tm >> 32, tm);
-#endif
-}
-
-/**
- * Current ticks syscall handler.
- *
- * Note that most platforms allow user level read access to hardware timers, and
- * should be preferred over this syscall on such platforms. Still, for
- * completeness sake.
- *
- * @param t Current tcb.
- * @return \ref OK and the current ticks when on 64bit systems, otherwise high
- * 32 bits of ticks in second argument and low 32 bits in third.
- */
-SYSCALL_DEFINE0(ticks)(struct tcb *t)
-{
- ticks_t tm = current_ticks();
-#if defined(_LP64)
- return_args2(t, OK, tm);
-#else
- return_args3(t, OK, tm >> 32, tm);
-#endif
-}
-
-/**
- * Relative timer request syscall handler.
- *
- * \note On 64bit systems, \p mult is ignored as \p ticks register is large
- * enough to contain essentially any timepoint we want. A couple thousand years
- * when the clock runs at 5GHz, if I'm not completely mistaken.
- *
- * @param t Current tcb.
- * @param ticks Number of ticks from now.
- * @param mult Multiply \c ticks by this value.
- * @return \ref OK and \c cid of created timer.
- */
-SYSCALL_DEFINE2(req_rel_timer)(struct tcb *t, sys_arg_t ticks, sys_arg_t mult)
-{
- return_args2(t, OK, new_rel_timer(t->tid, scaled_ticks(ticks, mult)));
-}
-
-/**
- * Absolute timer request syscall handler.
- *
- * @param t Current tcb.
- * @param ticks Absolute timepoint relative to some start point defined at boot.
- * @param mult Multiply \c ticks by this value.
- * @return \ref OK and \c cid of created timer.
- * \see req_rel_timer().
- */
-SYSCALL_DEFINE2(req_abs_timer)(struct tcb *t, sys_arg_t ticks, sys_arg_t mult)
-{
- return_args2(t, OK, new_abs_timer(t->tid, scaled_ticks(ticks, mult)));
-}
-
-/**
- * Free timer request syscall handler.
- *
- * @param t Current tcb.
- * @param cid \c cid of timer to free.
- * @return \ref ERR_NF and \c 0if no timer could be found with \c cid, \ref OK
- * and 0 otherwise.
- */
-SYSCALL_DEFINE1(free_timer)(struct tcb *t, sys_arg_t cid)
-{
- struct timer *timer = find_timer(cid);
- if (!timer)
- return_args1(t, ERR_NF);
-
- remove_timer(timer);
- return_args1(t, OK);
-}
diff --git a/common/vmem.c b/common/vmem.c
deleted file mode 100644
index f52f43e..0000000
--- a/common/vmem.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/* SPDX-License-Identifier: copyleft-next-0.3.1 */
-/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */
-
-/**
- * @file vmem.c
- * Virtual memory handling, mainly userspace virtual memory.
- */
-
-#include <kmi/mem_regions.h>
-#include <kmi/assert.h>
-#include <kmi/string.h>
-#include <kmi/debug.h>
-#include <kmi/bits.h>
-#include <kmi/vmem.h>
-#include <arch/vmem.h>
-
-stat_t init_uvmem(struct tcb *t, vm_t base, vm_t top)
-{
- return init_region(&t->sp_r, base, top);
-}
-
-/**
- * Clone process memory region.
- *
- * @param d Destination tcb.
- * @param s Source tcb.
- * @param m Memory region to clone.
- * @return \ref ERR_MISC if clone failed, otherwise \ref OK.
- *
- * @todo check shared memory regions.
- */
-static stat_t __clone_mapped_region(struct tcb *d, struct tcb *s,
- struct mem_region *m)
-{
- vm_t start = m->start * order_size(BASE_PAGE);
- vm_t end = m->end * order_size(BASE_PAGE);
-
- size_t size = end - start, actual_size = 0;
- vm_t va = alloc_fixed_region(&d->sp_r, start, size,
- &actual_size, m->flags);
-
- catastrophic_assert(va == start);
-
- if (!copy_allocd_region(d->proc.vmem, va, size, m->flags, s->proc.vmem))
- return ERR_MISC;
-
- return OK;
-}
-
-/**
- * Unmap and free private memory region.
- *
- * @param t Current thread.
- * @param m Memory region to free.
- * @return \see unmap_freed_region().
- */
-static stat_t __free_mapped_private_region(struct tcb *t, struct mem_region *m)
-{
- stat_t status = OK;
- pm_t start = __addr(m->start);
- pm_t end = __addr(m->end);
- if (!unmap_freed_region(t->proc.vmem, start, end - start, m->flags,
- &status))
- return ERR_MISC;
-
- return status;
-}
-
-/**
- * Check whether process associated with shared memory is still using it.
- *
- * @param pid Process to check.
- * @param start Start of memory region
- * @return \ref true if it is still in use, \ref false otherwise.
- */
-static bool __proc_has_region(id_t pid, vm_t start)
-{
- /** @todo this has a slight potential to have a race condition, where
- * both threads want to free the same shared region at the same time. */
- struct tcb *p = get_tcb(pid);
- if (!p)
- return false;
-
- struct mem_region *m = find_used_region(&p->sp_r, start);
- if (!m)
- return false;
-
- return true;
-}
-
-/**
- * Unmap shared region and free associated physical pages if they're not being
- * used by the other process.
- *
- * @param t Current thread.
- * @param m Memory region to free.
- * @return \see unmap_vpage().
- */
-static stat_t __free_mapped_shared_region(struct tcb *t, struct mem_region *m)
-{
- vm_t start = __addr(m->start);
- bool in_use = __proc_has_region(m->pid, m->alt_va);
-
- size_t osize = order_size(BASE_PAGE);
- size_t pages = m->start - m->end;
-
- stat_t status = OK;
- for (size_t i = 0; i < pages; ++i) {
- vm_t va = start + i * osize;
-
- pm_t pa = 0;
- stat_vpage(t->proc.vmem, va, &pa, 0, 0);
- status = unmap_vpage(t->proc.vmem, va);
-
- if (!in_use)
- free_page(pa, BASE_PAGE);
- }
-
- return status;
-}
-
-/**
- * Convenience function for freeing mapped regions.
- *
- * @param t Thread to work in.
- * @param m Memory region to free.
- * @return \ref OK
- */
-static stat_t __free_mapped_region(struct tcb *t, struct mem_region *m)
-{
- if (m->pid != 0)
- return __free_mapped_shared_region(t, m);
-
- return __free_mapped_private_region(t, m);
-}
-
-stat_t clear_uvmem(struct tcb *t)
-{
- struct mem_region *m = find_first_region(&t->sp_r);
- while (m) {
- if (!is_region_kept(m))
- __free_mapped_region(t, m);
-
- m = m->next;
- }
-
- return OK;
-}
-
-stat_t purge_uvmem(struct tcb *t)
-{
- struct mem_region *m = find_first_region(&t->sp_r);
- while (m) {
- __free_mapped_region(t, m);
- m = m->next;
- }
-
- return OK;
-}
-
-stat_t destroy_uvmem(struct tcb *t)
-{
- /* force clear all regions */
- purge_uvmem(t);
- /* destroy region tree itself */
- return destroy_region(&t->sp_r);
-}
-
-stat_t clone_mem_regions(struct tcb *d, struct tcb *s)
-{
- /** @todo implement some way to only iterate used regions, this loops
- * through all regions which is likely a slight bit slower. */
- struct mem_region *m = find_first_region(&s->sp_r);
- while (m) {
- if (is_region_used(m))
- __clone_mapped_region(d, s, m);
-
- m = m->next;
- }
-
- return OK;
-}
-
-vm_t alloc_uvmem(struct tcb *t, size_t size, vmflags_t flags)
-{
- /* t exists and is the process tcb of the current process */
- hard_assert(t && is_proc(t), ERR_INVAL);
-
- stat_t status = OK;
- const vm_t v = alloc_region(&t->sp_r, size, &size, flags);
- const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status);
- return w;
-}
-
-vm_t alloc_uvpage(struct tcb *t, size_t size, vmflags_t flags, size_t *asize,
- pm_t *paddr)
-{
- hard_assert(t && is_proc(t), ERR_INVAL);
-
- enum mm_order order = nearest_order(size);
- size_t actual_size = order_size(order);
- stat_t status = OK;
-
- const vm_t v = alloc_region(&t->sp_r, size, &size, flags);
- const vm_t w = __addr(__page(v));
-
- pm_t addr = alloc_page(order);
- /** @todo should free region */
- if (!addr)
- return NULL;
-
- status = map_vpage(t->proc.vmem, addr, w, flags, order);
- if (status)
- return NULL;
-
- if (asize)
- *asize = actual_size;
-
- if (paddr)
- *paddr = addr;
-
- return w;
-}
-
-vm_t alloc_fixed_uvmem(struct tcb *t, vm_t start, size_t size, vmflags_t flags)
-{
- hard_assert(t && is_proc(t), ERR_INVAL);
-
- stat_t status = OK;
- const vm_t v = alloc_fixed_region(&t->sp_r, start, size, &size, flags);
- const vm_t w = map_allocd_region(t->proc.vmem, v, size, flags, &status);
- return w;
-}
-
-/* free_shared_uvmem shouldn't be needed, likely to work with free_uvmem */
-stat_t alloc_shared_uvmem(struct tcb *s, struct tcb *c,
- size_t size, vmflags_t sflags, vmflags_t cflags,
- vm_t *sstart, vm_t *cstart)
-{
- hard_assert(sstart, ERR_INVAL);
- hard_assert(cstart, ERR_INVAL);
- hard_assert(s && is_proc(s), ERR_INVAL);
- hard_assert(c && is_proc(c), ERR_INVAL);
-
- size_t ssize, csize;
- vm_t sv = alloc_shared_region(&s->sp_r, size, &ssize, sflags, c->rid);
- vm_t cv = alloc_shared_region(&c->sp_r, size, &csize, cflags, s->rid);
-
- /* not exactly optimal but good enough for now, I can start worrying
- * about hyperoptimizations whenever. */
- set_alt_region_addr(&s->sp_r, sv, cv);
- set_alt_region_addr(&c->sp_r, cv, sv);
-
- if (csize != ssize) {
- /** @todo cleanup, better errors? */
- return ERR_INVAL;
- }
-
- stat_t cstatus = OK, sstatus = OK;
- size_t osize = order_size(BASE_PAGE);
- size_t pages = ssize / osize;
- for (size_t i = 0; i < pages; ++i) {
- pm_t p = alloc_page(BASE_PAGE);
- sstatus = map_vpage(s->proc.vmem, p, sv + i * osize, sflags,
- BASE_PAGE);
- cstatus = map_vpage(c->proc.vmem, p, cv + i * osize, cflags,
- BASE_PAGE);
- }
-
- *sstart = sv;
- *cstart = cv;
-
- if (sstatus)
- return sstatus;
-
- if (cstatus)
- return cstatus;
-
- return OK;
-}
-
-stat_t free_uvmem(struct tcb *r, vm_t va)
-{
- /** \todo assume tcb is root tcb? */
- struct mem_region *m = find_used_region(&r->sp_r, va);
- if (!m)
- return ERR_NF;
-
- stat_t status = __free_mapped_region(r, m);
- if (status)
- return ERR_MISC;
-
- return free_known_region(&r->sp_r, m);
-}
-
-stat_t alloc_uvmem_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
- vmflags_t flags, enum mm_order order, void *data)
-{
- *offset = alloc_page(order);
- if (!*offset)
- return INFO_TRGN; /* try again */
-
- stat_t *status = (stat_t *)data, ret;
- ret = map_vpage(b, *offset, vaddr, flags, order);
- if (status)
- *status = ret;
-
- return ret;
-}
-
-stat_t alloc_shared_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
- vmflags_t flags, enum mm_order order, void *data)
-{
- if (order != MM_O0)
- return INFO_TRGN;
-
- *offset = alloc_page(MM_O0);
-
- stat_t *status = (stat_t *)data, ret;
- ret = map_vpage(b, *offset, vaddr, flags, order);
- if (status)
- *status = ret;
-
- return ret;
-}
-
-stat_t copy_allocd_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
- vmflags_t flags, enum mm_order order, void *data)
-{
- struct vmem *s = (struct vmem *)data;
-
- pm_t paddr = 0;
- enum mm_order v_order = 0;
- stat_vpage(s, vaddr, &paddr, &v_order, 0);
- /** @todo what if we could combine multiple pages into one in the new
- * process? */
- if (order > v_order)
- return INFO_TRGN;
-
- pm_t new_page = alloc_page(order);
- if (!new_page)
- return INFO_TRGN;
-
- map_vpage(b, new_page, vaddr, flags, order);
- memcpy((void *)new_page, (void *)(paddr + *offset), order_size(order));
-
- if (v_order > order)
- *offset += order_size(order);
- else
- *offset = 0;
-
- return OK;
-}
-
-stat_t free_uvmem_wrapper(struct vmem *b, pm_t *offset, vm_t vaddr,
- vmflags_t flags, enum mm_order order, void *data)
-{
- UNUSED(flags);
- UNUSED(offset);
-
- pm_t paddr = 0;
- enum mm_order v_order = 0;
- stat_vpage(b, vaddr, &paddr, &v_order, 0);
- if (order != v_order)
- return INFO_TRGN;
-
- /** @todo we might need to cause an ipi to flush the tlb for other
- * cores */
-
- stat_t *status = (stat_t *)data, ret;
- ret = unmap_vpage(b, vaddr);
- if (status)
- *status = ret;
-
- free_page(order, paddr);
-
- return ret;
-}