aboutsummaryrefslogtreecommitdiff
path: root/kernel/util
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/util
Created student weenix repository
Diffstat (limited to 'kernel/util')
-rw-r--r--kernel/util/debug.c237
-rw-r--r--kernel/util/debug.py77
-rw-r--r--kernel/util/init.c142
-rw-r--r--kernel/util/list.c53
-rw-r--r--kernel/util/list.py32
-rw-r--r--kernel/util/math.c411
-rw-r--r--kernel/util/printf.c996
-rw-r--r--kernel/util/string.c509
-rw-r--r--kernel/util/time.c194
-rw-r--r--kernel/util/timer.c121
10 files changed, 2772 insertions, 0 deletions
diff --git a/kernel/util/debug.c b/kernel/util/debug.c
new file mode 100644
index 0000000..47c8345
--- /dev/null
+++ b/kernel/util/debug.c
@@ -0,0 +1,237 @@
+#include "main/apic.h"
+#include "main/io.h"
+#include "util/printf.h"
+#include "util/string.h"
+
+/*
+ * Debug message behavior.
+ *
+ * To disable a dbg mode add ',-name' to this variable. To enable one add
+ * ',name'. For example to have everything except 'mm' and 'pagealloc' you would
+ * set DBG to 'all,-mm,-pagealloc'. To have only 'test', 'testpass', 'testfail'
+ * you would set DBG to '-all,test,testpass,testfail'.
+ *
+ * We generally recommend that you leave this set to 'all' with some of the
+ * less useful message types disabled. To see all available message types, and
+ * to potentially add to them see 'kernel/include/util/debug.h'
+ *
+ * Note that due to the way this is interpreted either 'all' or '-all' should
+ * always be the first thing in this variable. Note that this setting can be
+ * changed at runtime by modifying the dbg_modes global variable.
+ */
+#define INIT_DBG_MODES "-all"
+
+/* Below is a truly terrible poll-driven serial driver that we use for debugging
+ * purposes - it outputs to COM1, but
+ * this can be easily changed. It does not use interrupts, and cannot read input
+ * */
+/* This port is COM1 */
+#define PORT 0x3f8
+/* Corresponding interrupt vector */
+#define PORT_INTR 0x0d
+
+uint64_t dbg_modes;
+
+typedef struct dbg_mode
+{
+ const char *d_name;
+ uint64_t d_mode;
+ const char *d_color;
+} dbg_mode_t;
+
+void dbg_init()
+{
+ outb(PORT + 3, 0x80); /* Enable DLAB (set baud rate divisor) */
+ outb(PORT + 0, 0x03); /* Set divisor to 3 (lo byte) 38400 baud */
+ outb(PORT + 1, 0x00); /* (hi byte) */
+ outb(PORT + 3, 0x03); /* 8 bits, no parity, one stop bit */
+ outb(PORT + 2, 0xC7); /* Enable FIFO, clear them, with 14-byte threshold */
+
+ dbg_add_modes(INIT_DBG_MODES);
+}
+
+static dbg_mode_t dbg_tab[] = {DBG_TAB};
+
+const char *dbg_color(uint64_t d_mode)
+{
+ dbg_mode_t *mode;
+ for (mode = dbg_tab; mode->d_mode != 0UL; mode++)
+ {
+ if (mode->d_mode & d_mode)
+ {
+ return mode->d_color;
+ }
+ }
+ /* If we get here, something went seriously wrong */
+ panic("Unknown debug mode 0x%lx\n", d_mode);
+}
+
+static void dbg_puts(char *c)
+{
+ while (*c != '\0')
+ {
+ /* Wait until the port is free */
+ while (!(inb(PORT + 5) & 0x20))
+ ;
+ outb(PORT, (uint8_t)*c++);
+ }
+}
+
+#define BUFFER_SIZE 1024
+
+void dbg_print(char *fmt, ...)
+{
+ va_list args;
+ char buf[BUFFER_SIZE];
+ size_t count;
+
+ va_start(args, fmt);
+ count = (size_t)vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ if (count >= sizeof(buf))
+ {
+ dbg_puts(
+ "WARNING: The following message has been truncated due to "
+ "buffer size limitations.\n");
+ }
+ dbg_puts(buf);
+}
+
+void dbg_printinfo(dbg_infofunc_t func, const void *data)
+{
+ char buf[BUFFER_SIZE];
+ func(data, buf, BUFFER_SIZE);
+ dbg_puts(buf);
+}
+
+#ifndef NDEBUG
+/**
+ * searches for <code>name</code> in the list of known
+ * debugging modes specified above and, if it
+ * finds <code>name</code>, adds the corresponding
+ * debugging mode to a list
+ */
+void dbg_add_mode(const char *name)
+{
+ long cancel;
+ dbg_mode_t *mode;
+
+ if (*name == '-')
+ {
+ cancel = 1;
+ name++;
+ }
+ else
+ {
+ cancel = 0;
+ }
+
+ for (mode = dbg_tab; mode->d_name != NULL; mode++)
+ {
+ if (strcmp(name, mode->d_name) == 0)
+ {
+ break;
+ }
+ }
+ if (mode->d_name == NULL)
+ {
+ dbg_print("Warning: Unknown debug option: \"%s\"\n", name);
+ return;
+ }
+
+ if (cancel)
+ {
+ dbg_modes &= ~mode->d_mode;
+ }
+ else
+ {
+ dbg_modes |= mode->d_mode;
+ }
+}
+
+/**
+ * Cycles through each comma-delimited debugging option and
+ * adds it to the debugging modes by calling dbg_add_mode
+ */
+void dbg_add_modes(const char *modes)
+{
+ char env[256];
+ char *name;
+
+ strncpy(env, modes, sizeof(env));
+ /* Maybe it would be good if we did this without strtok, but I'm too lazy */
+ for (name = strtok(env, ","); name; name = strtok(NULL, ","))
+ {
+ dbg_add_mode(name);
+ }
+}
+
+size_t dbg_modes_info(const void *data, char *buf, size_t size)
+{
+ KASSERT(NULL == data);
+ KASSERT(0 < size);
+
+ size_t osize = size;
+
+ dbg_mode_t *mode;
+ for (mode = dbg_tab; mode->d_name != NULL; ++mode)
+ {
+ if (dbg_modes & mode->d_mode && mode->d_mode != DBG_ALL)
+ {
+ int len;
+ if ((len = snprintf(buf, size, "%s,", mode->d_name)) >= (int)size)
+ {
+ break;
+ }
+ else
+ {
+ buf += len;
+ size -= len;
+ }
+ }
+ }
+
+ if (size == osize)
+ {
+ buf[0] = '\0';
+ return 0;
+ }
+ else
+ {
+ /* remove trailing comma */
+ buf[-1] = '\0';
+ return osize - size + 1;
+ }
+}
+#endif
+
+/* This is meant as a good point to automatically set a breakpoint which will
+ * stop just after a panic has occured and printed its message. */
+noreturn static void dbg_panic_halt()
+{
+ __asm__ volatile("cli; hlt");
+ __builtin_unreachable();
+}
+
+#define PANIC_BUFSIZE 2048
+
+noreturn void dbg_panic(const char *file, int line, const char *func,
+ const char *fmt, ...)
+{
+ char buf[PANIC_BUFSIZE];
+ va_list args;
+ va_start(args, fmt);
+
+ DEBUG_ENTER
+ dbg_print("C%ld P%ld panic in %s:%u %s(): ", curcore.kc_id,
+ curproc ? curproc->p_pid : -1L, file, line, func);
+ vsnprintf(buf, PANIC_BUFSIZE, fmt, args);
+ dbg_print("%s", buf);
+ dbg_print("\nC%ld Halting.\n\n", apic_current_id());
+ DEBUG_EXIT
+
+ va_end(args);
+
+ dbg_panic_halt();
+}
diff --git a/kernel/util/debug.py b/kernel/util/debug.py
new file mode 100644
index 0000000..7d1ce0d
--- /dev/null
+++ b/kernel/util/debug.py
@@ -0,0 +1,77 @@
+import gdb
+
+import weenix
+import weenix.info
+
+
+class InfoCommand(weenix.Command):
+ """usage: info <infofunc> [<data>]
+ <infofunc> the info function to be called
+ <data> the first argument to <infofunc>, if unspecified NULL is used
+ Prints the string generated by one of the kernel's info functions."""
+
+ def __init__(self):
+ weenix.Command.__init__(self, "info", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
+
+ def invoke(self, arg, tty):
+ args = gdb.string_to_argv(arg)
+ if len(args) < 1 or len(args) > 2:
+ gdb.write("{0}\n".format(self.__doc__))
+ raise gdb.GdbError("invalid arguments")
+ gdb.write(weenix.info.string(args[0], args[1] if (len(args) > 1) else None))
+
+
+InfoCommand()
+
+
+class DbgCommand(weenix.Command):
+ """usage: dbg [<modes>]
+ <modes> any number of whitespace seperated debug modes
+ When no arguments are given prints a list of all active debug
+ modes. If any debug modes are listed they are added to the
+ current debug modes. If a listed mode is prefixed with a
+ '-' it is removed instead of added."""
+
+ def __init__(self):
+ weenix.Command.__init__(self, "dbg", gdb.COMMAND_DATA)
+
+ def _modes(self):
+ i = 0
+ l = list()
+ while gdb.parse_and_eval("dbg_tab[{0}]".format(i))["d_name"] != 0:
+ mode = gdb.parse_and_eval("dbg_tab[{0}]".format(i))
+ i += 1
+ l.append(mode["d_name"].string())
+ return l
+
+ def invoke(self, arg, tty):
+ if len(arg.strip()) == 0:
+ info = weenix.info.string("dbg_modes_info")
+ if len(info) == 0:
+ gdb.write("No active modes.\n")
+ else:
+ gdb.write("{0}\n".format(weenix.info.string("dbg_modes_info")))
+ else:
+ modes = self._modes()
+ for mode in arg.split():
+ name = mode[1:] if (mode.startswith("-")) else mode
+ if not name in modes:
+ gdb.write(
+ 'warning: skipping non-existant mode "{0}"\n'.format(name)
+ )
+ else:
+ weenix.eval_func("dbg_add_mode", '"{0}"'.format(mode))
+
+ def complete(self, line, word):
+ l = self._modes()
+ l = filter(lambda x: x.startswith(word), l)
+ for used in line.split():
+ if used.startswith("-"):
+ used = used[1:]
+ l = filter(lambda x: x != used, l)
+ l.sort()
+
+ return l
+
+
+DbgCommand()
diff --git a/kernel/util/init.c b/kernel/util/init.c
new file mode 100644
index 0000000..d1bc0d8
--- /dev/null
+++ b/kernel/util/init.c
@@ -0,0 +1,142 @@
+#include "kernel.h"
+
+#include "mm/kmalloc.h"
+
+#include "util/debug.h"
+#include "util/init.h"
+#include "util/list.h"
+#include "util/string.h"
+
+static int _init_search_count = 0;
+
+struct init_function
+{
+ init_func_t if_func;
+ const char *if_name;
+ list_link_t if_link;
+
+ int if_search;
+ int if_called;
+ list_t if_deps;
+};
+
+struct init_depends
+{
+ const char *id_name;
+ list_link_t id_link;
+};
+
+static void _init_call(list_t *funcs, struct init_function *func)
+{
+ list_iterate(&func->if_deps, dep, struct init_depends, id_link)
+ {
+ struct init_function *found = NULL;
+ list_iterate(funcs, f, struct init_function, if_link)
+ {
+ if (strcmp(dep->id_name, f->if_name) == 0)
+ {
+ found = f;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ panic("'%s' dependency for '%s' does not exist", dep->id_name,
+ func->if_name);
+ }
+
+ if (func->if_search == found->if_search)
+ {
+ panic("circular dependency between '%s' and '%s'", func->if_name,
+ found->if_name);
+ }
+
+ dbg(DBG_INIT, "'%s' depends on '%s': ", func->if_name, found->if_name);
+ if (!found->if_called)
+ {
+ dbgq(DBG_INIT, "calling\n");
+ found->if_search = func->if_search;
+ _init_call(funcs, found);
+ }
+ else
+ {
+ dbgq(DBG_INIT, "already called\n");
+ }
+ }
+
+ KASSERT(!func->if_called);
+
+ dbg(DBG_INIT, "Calling %s (0x%p)\n", func->if_name, func->if_func);
+ func->if_func();
+ func->if_called = 1;
+}
+
+void init_call_all()
+{
+ list_t funcs;
+ char *buf, *end;
+
+ list_init(&funcs);
+ buf = (char *)&kernel_start_init;
+ end = (char *)&kernel_end_init;
+
+ while (buf < end)
+ {
+ struct init_function *curr = kmalloc(sizeof(*curr));
+ KASSERT(NULL != curr);
+
+ list_insert_tail(&funcs, &curr->if_link);
+ list_init(&curr->if_deps);
+
+ KASSERT(NULL != *(uintptr_t *)buf);
+ curr->if_func = (init_func_t) * (uintptr_t *)buf;
+ curr->if_name = buf + sizeof(curr->if_func);
+ curr->if_search = 0;
+ curr->if_called = 0;
+
+ buf += sizeof(curr->if_func) + strlen(curr->if_name) + 1;
+
+ while ((NULL == *(uintptr_t *)buf) && (buf < end))
+ {
+ struct init_depends *dep = kmalloc(sizeof(*dep));
+ KASSERT(NULL != dep);
+
+ list_insert_tail(&curr->if_deps, &dep->id_link);
+
+ dep->id_name = buf + sizeof(curr->if_func);
+ buf += sizeof(curr->if_func) + strlen(dep->id_name) + 1;
+ }
+ }
+
+ KASSERT(buf == end);
+
+ dbg(DBG_INIT, "Initialization functions and dependencies:\n");
+ list_iterate(&funcs, func, struct init_function, if_link)
+ {
+ dbgq(DBG_INIT, "%s (0x%p): ", func->if_name, func->if_func);
+ list_iterate(&func->if_deps, dep, struct init_depends, id_link)
+ {
+ dbgq(DBG_INIT, "%s ", dep->id_name);
+ }
+ dbgq(DBG_INIT, "\n");
+ }
+
+ list_iterate(&funcs, func, struct init_function, if_link)
+ {
+ if (!func->if_called)
+ {
+ func->if_search = ++_init_search_count;
+ _init_call(&funcs, func);
+ }
+ }
+
+ list_iterate(&funcs, func, struct init_function, if_link)
+ {
+ list_iterate(&func->if_deps, dep, struct init_depends, id_link)
+ {
+ kfree(dep);
+ }
+ kfree(func);
+ }
+}
diff --git a/kernel/util/list.c b/kernel/util/list.c
new file mode 100644
index 0000000..81a1beb
--- /dev/null
+++ b/kernel/util/list.c
@@ -0,0 +1,53 @@
+
+#include <util/debug.h>
+#include <util/list.h>
+
+inline void list_init(list_t *list) { list->l_next = list->l_prev = list; }
+
+inline void list_link_init(list_link_t *link)
+{
+ link->l_next = link->l_prev = NULL;
+}
+
+inline long list_link_is_linked(const list_link_t *link)
+{
+ return link->l_next && link->l_prev;
+}
+
+inline long list_empty(const list_t *list) { return list->l_next == list; }
+
+inline void list_assert_sanity(const list_t *list)
+{
+ KASSERT(list->l_next && list->l_next->l_prev && list->l_prev &&
+ list->l_prev->l_next);
+}
+
+inline void list_insert_before(list_link_t *link, list_link_t *to_insert)
+{
+ list_link_t *prev = to_insert;
+ list_link_t *next = link;
+ prev->l_next = next;
+ prev->l_prev = next->l_prev;
+ next->l_prev->l_next = prev;
+ next->l_prev = prev;
+}
+
+inline void list_insert_head(list_t *list, list_link_t *link)
+{
+ list_insert_before((list)->l_next, link);
+}
+
+inline void list_insert_tail(list_t *list, list_link_t *link)
+{
+ list_insert_before(list, link);
+}
+
+inline void list_remove(list_link_t *link)
+{
+ list_link_t *ll = link;
+ list_link_t *prev = ll->l_prev;
+ list_link_t *next = ll->l_next;
+ prev->l_next = next;
+ next->l_prev = prev;
+ ll->l_next = ll->l_prev = NULL;
+}
diff --git a/kernel/util/list.py b/kernel/util/list.py
new file mode 100644
index 0000000..4eeed03
--- /dev/null
+++ b/kernel/util/list.py
@@ -0,0 +1,32 @@
+import gdb
+
+import weenix
+import weenix.list
+
+
+class ListCommand(weenix.Command):
+ """usage: list <list> [<type> <member>]
+ <list> the list_t to be printed
+ <type> the type of the values stored on the list
+ <member> type's list link member used to make the list
+ Prints all items on a list_t, if <type> and <member> are not given
+ then the addresses of the list links are printed, otherwise the items
+ are printed assuming that they have the given type."""
+
+ def __init__(self):
+ weenix.Command.__init__(self, "list", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
+
+ def invoke(self, arg, tty):
+ args = gdb.string_to_argv(arg)
+ if len(args) == 1:
+ for i, item in enumerate(weenix.list.load(args[0])):
+ gdb.write("{0:>3}: {1:8}\n".format(i, item.link_addr()))
+ elif len(args) == 3:
+ for i, item in enumerate(weenix.list.load(args[0], args[1], args[2])):
+ gdb.write("{0:>3}: {1}\n".format(i, item.item()))
+ else:
+ gdb.write("{0}\n".format(self.__doc__))
+ raise gdb.GdbError("invalid arguments")
+
+
+ListCommand()
diff --git a/kernel/util/math.c b/kernel/util/math.c
new file mode 100644
index 0000000..93900a2
--- /dev/null
+++ b/kernel/util/math.c
@@ -0,0 +1,411 @@
+// todo port to 64 bit
+#if 0
+/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
+ ****************************************************************************
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: math.c
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes:
+ *
+ * Date: Aug 2003
+ *
+ * Environment: Xen Minimal OS
+ * Description: Library functions for 64bit arith and other
+ * from freebsd, files in sys/libkern/ (qdivrem.c, etc)
+ *
+ ****************************************************************************
+ * $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
+ ****************************************************************************
+ *-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
+*/
+
+#include "kernel.h"
+#include "types.h"
+
+/*
+ * Depending on the desired operation, we view a `long long' (aka quad_t) in
+ * one or more of the following formats.
+ */
+union uu {
+ int64_t q; /* as a (signed) quad */
+ int64_t uq; /* as an unsigned quad */
+ long sl[2]; /* as two signed longs */
+ unsigned long ul[2]; /* as two unsigned longs */
+};
+/* XXX RN: Yuck hardcoded endianess :) */
+#define _QUAD_HIGHWORD 1
+#define _QUAD_LOWWORD 0
+/*
+ * Define high and low longwords.
+ */
+#define H _QUAD_HIGHWORD
+#define L _QUAD_LOWWORD
+
+/*
+ * Total number of bits in a quad_t and in the pieces that make it up.
+ * These are used for shifting, and also below for halfword extraction
+ * and assembly.
+ */
+#define CHAR_BIT 8 /* number of bits in a char */
+#define QUAD_BITS (sizeof(int64_t) * CHAR_BIT)
+#define LONG_BITS (sizeof(long) * CHAR_BIT)
+#define HALF_BITS (sizeof(long) * CHAR_BIT / 2)
+
+/*
+ * Extract high and low shortwords from longword, and move low shortword of
+ * longword to upper half of long, i.e., produce the upper longword of
+ * ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.)
+ *
+ * These are used in the multiply code, to split a longword into upper
+ * and lower halves, and to reassemble a product as a quad_t, shifted left
+ * (sizeof(long)*CHAR_BIT/2).
+ */
+#define HHALF(x) ((x) >> HALF_BITS)
+#define LHALF(x) ((x) & ((1UL << HALF_BITS) - 1))
+#define LHUP(x) ((x) << HALF_BITS)
+
+/*
+ * Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed),
+ * section 4.3.1, pp. 257--259.
+ */
+#define B (1UL << HALF_BITS) /* digit base */
+
+/* Combine two `digits' to make a single two-digit number. */
+#define COMBINE(a, b) (((unsigned long)(a) << HALF_BITS) | (b))
+
+/* select a type for digits in base B: use unsigned short if they fit */
+/* #if ULONG_MAX == 0xffffffff && USHORT_MAX >= 0xffff
+typedef unsigned short digit;
+#else */
+typedef unsigned long digit;
+/* #endif */
+
+
+/*
+ * Shift p[0]..p[len] left `sh' bits, ignoring any bits that
+ * `fall out' the left (there never will be any such anyway).
+ * We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS.
+ */
+static void
+shl(register digit *p, register int len, register int sh)
+{
+ register int i;
+
+ for (i = 0; i < len; i++)
+ p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh));
+ p[i] = LHALF(p[i] << sh);
+}
+
+/*
+ * __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v.
+ *
+ * We do this in base 2-sup-HALF_BITS, so that all intermediate products
+ * fit within u_long. As a consequence, the maximum length dividend and
+ * divisor are 4 `digits' in this base (they are shorter if they have
+ * leading zeros).
+ */
+uint64_t
+__qdivrem(uint64_t uq, uint64_t vq, uint64_t *arq)
+{
+ union uu tmp;
+ digit *u, *v, *q;
+ register digit v1, v2;
+ unsigned long qhat, rhat, t;
+ int m, n, d, j, i;
+ digit uspace[5], vspace[5], qspace[5];
+
+ /*
+ * Take care of special cases: divide by zero, and u < v.
+ */
+ if (vq == 0) {
+ /* divide by zero. */
+ static volatile const unsigned int zero = 0;
+
+ tmp.ul[H] = tmp.ul[L] = 1 / zero;
+ if (arq)
+ *arq = uq;
+ return tmp.q;
+ }
+ if (uq < vq) {
+ if (arq)
+ *arq = uq;
+ return 0;
+ }
+ u = &uspace[0];
+ v = &vspace[0];
+ q = &qspace[0];
+
+ /*
+ * Break dividend and divisor into digits in base B, then
+ * count leading zeros to determine m and n. When done, we
+ * will have:
+ * u = (u[1]u[2]...u[m+n]) sub B
+ * v = (v[1]v[2]...v[n]) sub B
+ * v[1] != 0
+ * 1 < n <= 4 (if n = 1, we use a different division algorithm)
+ * m >= 0 (otherwise u < v, which we already checked)
+ * m + n = 4
+ * and thus
+ * m = 4 - n <= 2
+ */
+ tmp.uq = uq;
+ u[0] = 0;
+ u[1] = HHALF(tmp.ul[H]);
+ u[2] = LHALF(tmp.ul[H]);
+ u[3] = HHALF(tmp.ul[L]);
+ u[4] = LHALF(tmp.ul[L]);
+ tmp.uq = vq;
+ v[1] = HHALF(tmp.ul[H]);
+ v[2] = LHALF(tmp.ul[H]);
+ v[3] = HHALF(tmp.ul[L]);
+ v[4] = LHALF(tmp.ul[L]);
+ for (n = 4; v[1] == 0; v++) {
+ if (--n == 1) {
+ unsigned long rbj; /* r*B+u[j] (not root boy jim) */
+ digit q1, q2, q3, q4;
+
+ /*
+ * Change of plan, per exercise 16.
+ * r = 0;
+ * for j = 1..4:
+ * q[j] = floor((r*B + u[j]) / v),
+ * r = (r*B + u[j]) % v;
+ * We unroll this completely here.
+ */
+ t = v[2]; /* nonzero, by definition */
+ q1 = u[1] / t;
+ rbj = COMBINE(u[1] % t, u[2]);
+ q2 = rbj / t;
+ rbj = COMBINE(rbj % t, u[3]);
+ q3 = rbj / t;
+ rbj = COMBINE(rbj % t, u[4]);
+ q4 = rbj / t;
+ if (arq)
+ *arq = rbj % t;
+ tmp.ul[H] = COMBINE(q1, q2);
+ tmp.ul[L] = COMBINE(q3, q4);
+ return tmp.q;
+ }
+ }
+
+ /*
+ * By adjusting q once we determine m, we can guarantee that
+ * there is a complete four-digit quotient at &qspace[1] when
+ * we finally stop.
+ */
+ for (m = 4 - n; u[1] == 0; u++)
+ m--;
+ for (i = 4 - m; --i >= 0;)
+ q[i] = 0;
+ q += 4 - m;
+
+ /*
+ * Here we run Program D, translated from MIX to C and acquiring
+ * a few minor changes.
+ *
+ * D1: choose multiplier 1 << d to ensure v[1] >= B/2.
+ */
+ d = 0;
+ for (t = v[1]; t < B / 2; t <<= 1)
+ d++;
+ if (d > 0) {
+ shl(&u[0], m + n, d); /* u <<= d */
+ shl(&v[1], n - 1, d); /* v <<= d */
+ }
+ /*
+ * D2: j = 0.
+ */
+ j = 0;
+ v1 = v[1]; /* for D3 -- note that v[1..n] are constant */
+ v2 = v[2]; /* for D3 */
+ do {
+ register digit uj0, uj1, uj2;
+
+ /*
+ * D3: Calculate qhat (\^q, in TeX notation).
+ * Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and
+ * let rhat = (u[j]*B + u[j+1]) mod v[1].
+ * While rhat < B and v[2]*qhat > rhat*B+u[j+2],
+ * decrement qhat and increase rhat correspondingly.
+ * Note that if rhat >= B, v[2]*qhat < rhat*B.
+ */
+ uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */
+ uj1 = u[j + 1]; /* for D3 only */
+ uj2 = u[j + 2]; /* for D3 only */
+ if (uj0 == v1) {
+ qhat = B;
+ rhat = uj1;
+ goto qhat_too_big;
+ } else {
+ unsigned long nn = COMBINE(uj0, uj1);
+ qhat = nn / v1;
+ rhat = nn % v1;
+ }
+ while (v2 * qhat > COMBINE(rhat, uj2)) {
+qhat_too_big:
+ qhat--;
+ if ((rhat += v1) >= B)
+ break;
+ }
+ /*
+ * D4: Multiply and subtract.
+ * The variable `t' holds any borrows across the loop.
+ * We split this up so that we do not require v[0] = 0,
+ * and to eliminate a final special case.
+ */
+ for (t = 0, i = n; i > 0; i--) {
+ t = u[i + j] - v[i] * qhat - t;
+ u[i + j] = LHALF(t);
+ t = (B - HHALF(t)) & (B - 1);
+ }
+ t = u[j] - t;
+ u[j] = LHALF(t);
+ /*
+ * D5: test remainder.
+ * There is a borrow if and only if HHALF(t) is nonzero;
+ * in that (rare) case, qhat was too large (by exactly 1).
+ * Fix it by adding v[1..n] to u[j..j+n].
+ */
+ if (HHALF(t)) {
+ qhat--;
+ for (t = 0, i = n; i > 0; i--) { /* D6: add back. */
+ t += u[i + j] + v[i];
+ u[i + j] = LHALF(t);
+ t = HHALF(t);
+ }
+ u[j] = LHALF(u[j] + t);
+ }
+ q[j] = qhat;
+ } while (++j <= m); /* D7: loop on j. */
+
+ /*
+ * If caller wants the remainder, we have to calculate it as
+ * u[m..m+n] >> d (this is at most n digits and thus fits in
+ * u[m+1..m+n], but we may need more source digits).
+ */
+ if (arq) {
+ if (d) {
+ for (i = m + n; i > m; --i)
+ u[i] = (u[i] >> d) |
+ LHALF(u[i - 1] << (HALF_BITS - d));
+ u[i] = 0;
+ }
+ tmp.ul[H] = COMBINE(uspace[1], uspace[2]);
+ tmp.ul[L] = COMBINE(uspace[3], uspace[4]);
+ *arq = tmp.q;
+ }
+
+ tmp.ul[H] = COMBINE(qspace[1], qspace[2]);
+ tmp.ul[L] = COMBINE(qspace[3], qspace[4]);
+ return tmp.q;
+}
+
+
+/*
+ * Divide two signed quads.
+ * ??? if -1/2 should produce -1 on this machine, this code is wrong
+ */
+int64_t __divdi3(int64_t a, int64_t b)
+{
+ uint64_t ua, ub, uq;
+ int neg;
+
+ if (a < 0)
+ ua = -(uint64_t)a, neg = 1;
+ else
+ ua = a, neg = 0;
+ if (b < 0)
+ ub = -(uint64_t)b, neg ^= 1;
+ else
+ ub = b;
+ uq = __qdivrem(ua, ub, (uint64_t *)0);
+ return (neg ? -uq : uq);
+}
+
+/*
+ * Divide two unsigned quads.
+ */
+uint64_t
+__udivdi3(uint64_t a, uint64_t b)
+{
+ return __qdivrem(a, b, (uint64_t *)0);
+}
+
+
+/*
+ * Return remainder after dividing two unsigned quads.
+ */
+uint64_t
+__umoddi3(uint64_t a, uint64_t b)
+{
+ uint64_t r;
+
+ (void)__qdivrem(a, b, &r);
+ return r;
+}
+
+/*
+ * Return ceil(log_2(x))
+ * We shift our input right until we get zero. The number of times we had to
+ * shift before getting zero gives us the ceiling of log2(x), except for powers
+ * of 2, in which case it gives us 1 + log2(x). Thus, we check whether it's a
+ * power of two, and special case that.
+ * author: dap
+ */
+int log2(int x)
+{
+ int current = x;
+ /* y keeps track of 2^(result) to see if our input was a power of 2 */
+ int y = 1;
+ int result = 0;
+ while (current) {
+ current >>= 1;
+ ++result;
+ y <<= 1;
+ }
+ y >>= 1;
+ if (y == x)
+ return result - 1;
+
+ return result;
+}
+
+#endif
diff --git a/kernel/util/printf.c b/kernel/util/printf.c
new file mode 100644
index 0000000..6daf8ce
--- /dev/null
+++ b/kernel/util/printf.c
@@ -0,0 +1,996 @@
+/*
+ ****************************************************************************
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: printf.c
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ *
+ * Date: Aug 2003, Aug 2005
+ *
+ * Environment: Xen Minimal OS
+ * Description: Library functions for printing
+ * (freebsd port, mainly sys/subr_prf.c)
+ *
+ ****************************************************************************
+ *
+ *-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
+ */
+
+#include "ctype.h"
+#include "kernel.h"
+#include "limits.h"
+
+#include "util/debug.h"
+#include "util/string.h"
+
+/**
+ * simple_strtoul - convert a string to an unsigned long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = 0, value;
+
+ if (!base)
+ {
+ base = 10;
+ if (*cp == '0')
+ {
+ base = 8;
+ cp++;
+ if ((*cp == 'x') && isxdigit(cp[1]))
+ {
+ cp++;
+ base = 16;
+ }
+ }
+ }
+ while (isxdigit(*cp) &&
+ (value = isdigit(*cp) ? *cp - '0' : toupper(*cp) - 'A' + 10) <
+ base)
+ {
+ result = result * base + value;
+ cp++;
+ }
+ if (endp)
+ {
+ *endp = (char *)cp;
+ }
+ return result;
+}
+
+/**
+ * simple_strtol - convert a string to a signed long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long simple_strtol(const char *cp, char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ {
+ return -simple_strtoul(cp + 1, endp, base);
+ }
+ return simple_strtoul(cp, endp, base);
+}
+
+/**
+ * simple_strtoull - convert a string to an unsigned long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+unsigned long long simple_strtoull(const char *cp, char **endp,
+ unsigned int base)
+{
+ unsigned long long result = 0, value;
+
+ if (!base)
+ {
+ base = 10;
+ if (*cp == '0')
+ {
+ base = 8;
+ cp++;
+ if ((*cp == 'x') && isxdigit(cp[1]))
+ {
+ cp++;
+ base = 16;
+ }
+ }
+ }
+ while (isxdigit(*cp) &&
+ (value = isdigit(*cp) ? *cp - '0'
+ : (islower(*cp) ? toupper(*cp) : *cp) - 'A' +
+ 10) < base)
+ {
+ result = result * base + value;
+ cp++;
+ }
+ if (endp)
+ {
+ *endp = (char *)cp;
+ }
+ return result;
+}
+
+/**
+ * simple_strtoll - convert a string to a signed long long
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+long long simple_strtoll(const char *cp, char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ {
+ return -simple_strtoull(cp + 1, endp, base);
+ }
+ return simple_strtoull(cp, endp, base);
+}
+
+static int skip_atoi(const char **s)
+{
+ int i = 0;
+
+ while (isdigit(**s))
+ i = i * 10 + *((*s)++) - '0';
+ return i;
+}
+
+#define ZEROPAD 1 /* pad with zero */
+#define SIGN 2 /* unsigned/signed long */
+#define PLUS 4 /* show plus */
+#define SPACE 8 /* space if plus */
+#define LEFT 16 /* left justified */
+#define SPECIAL 32 /* 0x */
+#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
+
+static char *number(char *buf, char *end, long long num, int base, int size,
+ int precision, int type)
+{
+ char c, sign, tmp[66];
+ const char *digits;
+ const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ int i;
+
+ digits = (type & LARGE) ? large_digits : small_digits;
+ if (type & LEFT)
+ {
+ type &= ~ZEROPAD;
+ }
+ if (base < 2 || base > 36)
+ {
+ return buf;
+ }
+ c = (type & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (type & SIGN)
+ {
+ if (num < 0)
+ {
+ sign = '-';
+ num = -num;
+ size--;
+ }
+ else if (type & PLUS)
+ {
+ sign = '+';
+ size--;
+ }
+ else if (type & SPACE)
+ {
+ sign = ' ';
+ size--;
+ }
+ }
+ if (type & SPECIAL)
+ {
+ if (base == 16)
+ {
+ size -= 2;
+ }
+ else if (base == 8)
+ {
+ size--;
+ }
+ }
+ i = 0;
+ if (num == 0)
+ {
+ tmp[i++] = '0';
+ }
+ else
+ {
+ /* XXX KAF: force unsigned mod and div. */
+ /* XXX kernel does not support long long division */
+ unsigned long long num2 = (unsigned long long)num;
+ unsigned int base2 = (unsigned int)base;
+ while (num2 != 0)
+ {
+ tmp[i++] = digits[num2 % base2];
+ num2 /= base2;
+ }
+ }
+ if (i > precision)
+ {
+ precision = i;
+ }
+ size -= precision;
+ if (!(type & (ZEROPAD + LEFT)))
+ {
+ while (size-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = ' ';
+ }
+ ++buf;
+ }
+ }
+ if (sign)
+ {
+ if (buf <= end)
+ {
+ *buf = sign;
+ }
+ ++buf;
+ }
+ if (type & SPECIAL)
+ {
+ if (base == 8)
+ {
+ if (buf <= end)
+ {
+ *buf = '0';
+ }
+ ++buf;
+ }
+ else if (base == 16)
+ {
+ if (buf <= end)
+ {
+ *buf = '0';
+ }
+ ++buf;
+ if (buf <= end)
+ {
+ *buf = digits[33];
+ }
+ ++buf;
+ }
+ }
+ if (!(type & LEFT))
+ {
+ while (size-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = c;
+ }
+ ++buf;
+ }
+ }
+ while (i < precision--)
+ {
+ if (buf <= end)
+ {
+ *buf = '0';
+ }
+ ++buf;
+ }
+ while (i-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = tmp[i];
+ }
+ ++buf;
+ }
+ while (size-- > 0)
+ {
+ if (buf <= end)
+ {
+ *buf = ' ';
+ }
+ ++buf;
+ }
+ return buf;
+}
+
+/**
+ * vsnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * Call this function if you are already dealing with a va_list.
+ * You probably want snprintf instead.
+ */
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ int len;
+ unsigned long long num;
+ int i, base;
+ char *str, *end, c;
+ const char *s;
+
+ int flags; /* flags to number() */
+
+ int field_width; /* width of output field */
+ int precision; /* min. # of digits for integers; max
+ number of chars for from string */
+ int qualifier; /* 'h', 'l', or 'L' for integer fields */
+ /* 'z' support added 23/7/1999 S.H. */
+ /* 'z' changed to 'Z' --davidm 1/25/99 */
+
+ str = buf;
+ end = buf + size - 1;
+
+ if (end < buf - 1)
+ {
+ end = ((void *)-1);
+ size = end - buf + 1;
+ }
+
+ for (; *fmt; ++fmt)
+ {
+ if (*fmt != '%')
+ {
+ if (str <= end)
+ {
+ *str = *fmt;
+ }
+ ++str;
+ continue;
+ }
+
+ /* process flags */
+ flags = 0;
+ repeat:
+ ++fmt; /* this also skips first '%' */
+ switch (*fmt)
+ {
+ case '-':
+ flags |= LEFT;
+ goto repeat;
+ case '+':
+ flags |= PLUS;
+ goto repeat;
+ case ' ':
+ flags |= SPACE;
+ goto repeat;
+ case '#':
+ flags |= SPECIAL;
+ goto repeat;
+ case '0':
+ flags |= ZEROPAD;
+ goto repeat;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (isdigit(*fmt))
+ {
+ field_width = skip_atoi(&fmt);
+ }
+ else if (*fmt == '*')
+ {
+ ++fmt;
+ /* it's the next argument */
+ field_width = va_arg(args, int);
+ if (field_width < 0)
+ {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+
+ /* get the precision */
+ precision = -1;
+ if (*fmt == '.')
+ {
+ ++fmt;
+ if (isdigit(*fmt))
+ {
+ precision = skip_atoi(&fmt);
+ }
+ else if (*fmt == '*')
+ {
+ ++fmt;
+ /* it's the next argument */
+ precision = va_arg(args, int);
+ }
+ if (precision < 0)
+ {
+ precision = 0;
+ }
+ }
+
+ /* get the conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z')
+ {
+ qualifier = *fmt;
+ ++fmt;
+ if (qualifier == 'l' && *fmt == 'l')
+ {
+ qualifier = 'L';
+ ++fmt;
+ }
+ }
+ if (*fmt == 'q')
+ {
+ qualifier = 'L';
+ ++fmt;
+ }
+
+ /* default base */
+ base = 10;
+
+ switch (*fmt)
+ {
+ case 'c':
+ if (!(flags & LEFT))
+ {
+ while (--field_width > 0)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ }
+ c = (unsigned char)va_arg(args, int);
+ if (str <= end)
+ {
+ *str = c;
+ }
+ ++str;
+ while (--field_width > 0)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ continue;
+
+ case 's':
+ s = va_arg(args, char *);
+ if (!s)
+ {
+ s = "<NULL>";
+ }
+
+ len = strnlen(s, precision);
+
+ if (!(flags & LEFT))
+ {
+ while (len < field_width--)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ }
+ for (i = 0; i < len; ++i)
+ {
+ if (str <= end)
+ {
+ *str = *s;
+ }
+ ++str;
+ ++s;
+ }
+ while (len < field_width--)
+ {
+ if (str <= end)
+ {
+ *str = ' ';
+ }
+ ++str;
+ }
+ continue;
+
+ case 'p':
+ if (field_width == -1)
+ {
+ field_width = 2 * sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ str = number(str, end, (unsigned long)va_arg(args, void *), 16,
+ field_width, precision, flags);
+ continue;
+
+ case 'n':
+ /* FIXME:
+ * What does C99 say about the overflow case here? */
+ if (qualifier == 'l')
+ {
+ long *ip = va_arg(args, long *);
+ *ip = (str - buf);
+ }
+ else if (qualifier == 'Z')
+ {
+ size_t *ip = va_arg(args, size_t *);
+ *ip = (str - buf);
+ }
+ else
+ {
+ int *ip = va_arg(args, int *);
+ *ip = (str - buf);
+ }
+ continue;
+
+ case '%':
+ if (str <= end)
+ {
+ *str = '%';
+ }
+ ++str;
+ continue;
+
+ /* integer number formats - set up the flags and "break" */
+ case 'o':
+ base = 8;
+ break;
+
+ case 'X':
+ flags |= LARGE;
+ base = 16;
+ break;
+ case 'x':
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+ flags |= SIGN;
+ case 'u':
+ break;
+
+ default:
+ if (str <= end)
+ {
+ *str = '%';
+ }
+ ++str;
+ if (*fmt)
+ {
+ if (str <= end)
+ {
+ *str = *fmt;
+ }
+ ++str;
+ }
+ else
+ {
+ --fmt;
+ }
+ continue;
+ }
+ if (qualifier == 'L')
+ {
+ num = va_arg(args, long long);
+ }
+ else if (qualifier == 'l')
+ {
+ num = va_arg(args, unsigned long);
+ if (flags & SIGN)
+ {
+ num = (signed long)num;
+ }
+ }
+ else if (qualifier == 'Z')
+ {
+ num = va_arg(args, size_t);
+ }
+ else if (qualifier == 'h')
+ {
+ num = (unsigned short)va_arg(args, int);
+ if (flags & SIGN)
+ {
+ num = (signed short)num;
+ }
+ }
+ else
+ {
+ num = va_arg(args, unsigned int);
+ if (flags & SIGN)
+ {
+ num = (signed int)num;
+ }
+ }
+
+ str = number(str, end, num, base, field_width, precision, flags);
+ }
+ if (str <= end)
+ {
+ *str = '\0';
+ }
+ else if (size > 0)
+ {
+ /* don't write out a null byte if the buf size is zero */
+ *end = '\0';
+ }
+ /* the trailing null byte doesn't count towards the total
+ * ++str;
+ */
+ return str - buf;
+}
+
+/**
+ * snprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
+ */
+int snprintf(char *buf, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+ return i;
+}
+
+size_t iprintf(char **str, size_t *size, char *fmt, ...)
+{
+ va_list args;
+ int len;
+
+ va_start(args, fmt);
+ len = vsnprintf(*str, *size, fmt, args);
+ va_end(args);
+
+ /* The way the "iprintf system" works, we're never going to catch
+ * an error anywhere else. The size of the buffer will appear to have
+ * increased, and it will appear to start farther to the left -> bad!
+ * (However, kernel vsnprintf should never fail...) */
+ KASSERT(len >= 0);
+
+ len = MIN(len, (int)(*size - 1));
+
+ *str += len;
+ *size -= len;
+
+ return *size - 1;
+}
+
+/**
+ * vsscanf - Unformat a buffer into a list of arguments
+ * @buf: input buffer
+ * @fmt: format of buffer
+ * @args: arguments
+ */
+int vsscanf(const char *buf, const char *fmt, va_list args)
+{
+ const char *str = buf;
+ char *next;
+ char digit;
+ int num = 0;
+ int qualifier;
+ int base;
+ int field_width;
+ int is_sign = 0;
+
+ while (*fmt && *str)
+ {
+ /* skip any white space in format */
+ /* white space in format matchs any amount of
+ * white space, including none, in the input.
+ */
+ if (isspace(*fmt))
+ {
+ while (isspace(*fmt))
+ ++fmt;
+ while (isspace(*str))
+ ++str;
+ }
+
+ /* anything that is not a conversion must match exactly */
+ if (*fmt != '%' && *fmt)
+ {
+ if (*fmt++ != *str++)
+ {
+ break;
+ }
+ continue;
+ }
+
+ if (!*fmt)
+ {
+ break;
+ }
+ ++fmt;
+
+ /* skip this conversion.
+ * advance both strings to next white space
+ */
+ if (*fmt == '*')
+ {
+ while (!isspace(*fmt) && *fmt)
+ fmt++;
+ while (!isspace(*str) && *str)
+ str++;
+ continue;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (isdigit(*fmt))
+ {
+ field_width = skip_atoi(&fmt);
+ }
+
+ /* get conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z' ||
+ *fmt == 'z')
+ {
+ qualifier = *fmt++;
+ if (qualifier == *fmt)
+ {
+ if (qualifier == 'h')
+ {
+ qualifier = 'H';
+ fmt++;
+ }
+ else if (qualifier == 'l')
+ {
+ qualifier = 'L';
+ fmt++;
+ }
+ }
+ }
+ base = 10;
+ is_sign = 0;
+
+ if (!*fmt || !*str)
+ {
+ break;
+ }
+
+ switch (*fmt++)
+ {
+ case 'c':
+ {
+ char *s = (char *)va_arg(args, char *);
+ if (field_width == -1)
+ {
+ field_width = 1;
+ }
+ do
+ {
+ *s++ = *str++;
+ } while (--field_width > 0 && *str);
+ num++;
+ }
+ continue;
+ case 's':
+ {
+ char *s = (char *)va_arg(args, char *);
+ if (field_width == -1)
+ {
+ field_width = INT_MAX;
+ }
+ /* first, skip leading white space in buffer */
+ while (isspace(*str))
+ str++;
+
+ /* now copy until next white space */
+ while (*str && !isspace(*str) && field_width--)
+ {
+ *s++ = *str++;
+ }
+ *s = '\0';
+ num++;
+ }
+ continue;
+ case 'n':
+ /* return number of characters read so far */
+ {
+ int *i = (int *)va_arg(args, int *);
+ *i = str - buf;
+ }
+ continue;
+ case 'o':
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ base = 16;
+ break;
+ case 'i':
+ base = 0;
+ is_sign = 1;
+ break;
+ case 'd':
+ is_sign = 1;
+ break;
+ case 'u':
+ break;
+ case '%':
+ /* looking for '%' in str */
+ if (*str++ != '%')
+ {
+ return num;
+ }
+ continue;
+ default:
+ /* invalid format; stop here */
+ return num;
+ }
+
+ /* have some sort of integer conversion.
+ * first, skip white space in buffer.
+ */
+ while (isspace(*str))
+ str++;
+
+ digit = *str;
+ if (is_sign && digit == '-')
+ {
+ digit = *(str + 1);
+ }
+
+ if (!digit || (base == 16 && !isxdigit(digit)) ||
+ (base == 10 && !isdigit(digit)) ||
+ (base == 8 && (!isdigit(digit) || digit > '7')) ||
+ (base == 0 && !isdigit(digit)))
+ {
+ break;
+ }
+
+ switch (qualifier)
+ {
+ case 'H': /* that's 'hh' in format */
+ if (is_sign)
+ {
+ signed char *s = (signed char *)va_arg(args, signed char *);
+ *s = (signed char)simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned char *s =
+ (unsigned char *)va_arg(args, unsigned char *);
+ *s = (unsigned char)simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'h':
+ if (is_sign)
+ {
+ short *s = (short *)va_arg(args, short *);
+ *s = (short)simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned short *s =
+ (unsigned short *)va_arg(args, unsigned short *);
+ *s = (unsigned short)simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'l':
+ if (is_sign)
+ {
+ long *l = (long *)va_arg(args, long *);
+ *l = simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned long *l =
+ (unsigned long *)va_arg(args, unsigned long *);
+ *l = simple_strtoul(str, &next, base);
+ }
+ break;
+ case 'L':
+ if (is_sign)
+ {
+ long long *l = (long long *)va_arg(args, long long *);
+ *l = simple_strtoll(str, &next, base);
+ }
+ else
+ {
+ unsigned long long *l = (unsigned long long *)va_arg(
+ args, unsigned long long *);
+ *l = simple_strtoull(str, &next, base);
+ }
+ break;
+ case 'Z':
+ case 'z':
+ {
+ size_t *s = (size_t *)va_arg(args, size_t *);
+ *s = (size_t)simple_strtoul(str, &next, base);
+ }
+ break;
+ default:
+ if (is_sign)
+ {
+ int *i = (int *)va_arg(args, int *);
+ *i = (int)simple_strtol(str, &next, base);
+ }
+ else
+ {
+ unsigned int *i =
+ (unsigned int *)va_arg(args, unsigned int *);
+ *i = (unsigned int)simple_strtoul(str, &next, base);
+ }
+ break;
+ }
+ num++;
+
+ if (!next)
+ {
+ break;
+ }
+ str = next;
+ }
+ return num;
+}
+
+/**
+ * sscanf - Unformat a buffer into a list of arguments
+ * @buf: input buffer
+ * @fmt: formatting of buffer
+ * @...: resulting arguments
+ */
+int sscanf(const char *buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsscanf(buf, fmt, args);
+ va_end(args);
+ return i;
+}
diff --git a/kernel/util/string.c b/kernel/util/string.c
new file mode 100644
index 0000000..2d47075
--- /dev/null
+++ b/kernel/util/string.c
@@ -0,0 +1,509 @@
+#include "ctype.h"
+#include "errno.h"
+
+int memcmp(const void *cs, const void *ct, size_t count)
+{
+ int ret;
+ /* Compare bytes at %esi and %edi up to %ecx bytes OR until
+ * the bytes are not equal */
+ /* If not equal, set zf = 0 and stop */
+ /* Find out zf and sf and use them to return 0,1, or -1 */
+ __asm__ volatile(
+ "xor %%eax, %%eax\n\t" /* Zero output */
+ "cld\n\t" /* Make sure direction is forwards */
+ "repe\n\t"
+ "cmpsb\n\t"
+ "setnz %%al\n\t" /* If it is not zero, put 1 in low part */
+ "sets %%ah" /* If sign set (means 2nd arg larger),
+ * put 1 in high part */
+ : "=a"(ret)
+ : "S"(cs), "D"(ct), "c"(count)
+ : "cc" /* Overwrite flags */
+ );
+ return ((ret & 1) ? ((ret >> 8) ? -1 : 1) : 0);
+}
+
+void *memcpy(void *dest, const void *src, size_t count)
+{
+ /* Move %ecx bytes from %esi to %edi */
+ __asm__ volatile(
+ "cld\n\t" /* Make sure direction is forwards */
+ "rep\n\t"
+ "movsb"
+ : /* No output */
+ : "S"(src), "D"(dest), "c"(count)
+ : "cc" /* We overwrite condition codes - i.e., flags */
+ );
+ return dest;
+}
+
+void *memset(void *s, int c, size_t count)
+{
+ /* Fill %ecx bytes at %edi with %eax (actually %al) */
+ __asm__ volatile(
+ "cld\n\t" /* Make sure direction is forwards */
+ "rep\n\t"
+ "stosb"
+ : /* No output */
+ : "a"(c), "D"(s), "c"(count)
+ : "cc" /* Overwrite flags */
+ );
+ return s;
+}
+
+int strncmp(const char *cs, const char *ct, size_t count)
+{
+ register signed char __res = 0;
+
+ while (count)
+ {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ {
+ break;
+ }
+ count--;
+ }
+
+ return __res;
+}
+
+int strcmp(const char *cs, const char *ct)
+{
+ register signed char __res;
+
+ while (1)
+ {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ {
+ break;
+ }
+ }
+
+ return __res;
+}
+
+char *strcpy(char *dest, const char *src)
+{
+ char *tmp = dest;
+
+ while ((*dest++ = *src++) != '\0') /* nothing */
+ ;
+ return tmp;
+}
+
+char *strncpy(char *dest, const char *src, size_t count)
+{
+ char *tmp = dest;
+
+ while (count)
+ {
+ if ((*dest = *src) != 0)
+ src++;
+ dest++;
+ count--;
+ }
+
+ return tmp;
+}
+
+size_t strnlen(const char *s, size_t count)
+{
+ const char *sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc)
+ {
+ /* nothing */}
+ return sc - s;
+}
+
+char *strcat(char *dest, const char *src)
+{
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
+}
+
+size_t strlen(const char *s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; ++sc)
+ {
+ /* nothing */}
+ return sc - s;
+}
+
+char *strchr(const char *s, int c)
+{
+ for (; *s != (char)c; ++s)
+ {
+ if (*s == '\0')
+ {
+ return NULL;
+ }
+ }
+ return (char *)s;
+}
+
+char *strrchr(const char *s, int c)
+{
+ char *r = NULL;
+ for (; *s; ++s)
+ {
+ if (*s == (char)c)
+ {
+ r = (char *)s;
+ }
+ }
+ return r;
+}
+
+char *strstr(const char *s1, const char *s2)
+{
+ int l1, l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ {
+ return (char *)s1;
+ }
+ l1 = strlen(s1);
+ while (l1 >= l2)
+ {
+ l1--;
+ if (!memcmp(s1, s2, l2))
+ {
+ return (char *)s1;
+ }
+ s1++;
+ }
+ return NULL;
+}
+
+/*
+ * The following three functions were ripped out of OpenSolaris. Legally, they
+ * might have to be in a separate file. Leaving it here out of laziness.
+ * Got this from /onnv-gate/usr/src/common/uti/string.c.
+ */
+
+char *strpbrk(const char *string, const char *brkset)
+{
+ const char *p;
+
+ do
+ {
+ for (p = brkset; *p != '\0' && *p != *string; ++p)
+ ;
+ if (*p != '\0')
+ {
+ return (char *)string;
+ }
+ } while (*string++);
+
+ return NULL;
+}
+
+size_t strspn(const char *string, const char *charset)
+{
+ const char *p, *q;
+
+ for (q = string; *q != '\0'; ++q)
+ {
+ for (p = charset; *p != '\0' && *p != *q; ++p)
+ ;
+ if (*p == '\0')
+ {
+ break;
+ }
+ }
+
+ return q - string;
+}
+
+char *strtok(char *string, const char *sepset)
+{
+ char *p, *q, *r;
+ static char *savept;
+
+ /*
+ * Set `p' to our current location in the string.
+ */
+ p = (string == NULL) ? savept : string;
+ if (p == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * Skip leading separators; bail if no tokens remain.
+ */
+ q = p + strspn(p, sepset);
+ if (*q == '\0')
+ {
+ return NULL;
+ }
+
+ /*
+ * Mark the end of the token and set `savept' for the next iteration.
+ */
+ if ((r = strpbrk(q, sepset)) == NULL)
+ {
+ savept = NULL;
+ }
+ else
+ {
+ *r = '\0';
+ savept = ++r;
+ }
+
+ return q;
+}
+
+/* created with the help of:
+ * perl -p -e 's/#define\s+(\w+)\s+\d+\s+\/\* ([^\t\*]+)\s*\*\/\s*$/case $1:
+ * return "$2";\n/' < /usr/include/sys/errno.h
+ */
+char *strerror(long errnum)
+{
+ switch (errnum)
+ {
+ case EPERM:
+ return "Not super-user";
+ case ENOENT:
+ return "No such file or directory";
+ case ESRCH:
+ return "No such process";
+ case EINTR:
+ return "interrupted system call";
+ case EIO:
+ return "I/O error";
+ case ENXIO:
+ return "No such device or address";
+ case E2BIG:
+ return "Arg list too long";
+ case ENOEXEC:
+ return "Exec format error";
+ case EBADF:
+ return "Bad file number";
+ case ECHILD:
+ return "No children";
+ case EAGAIN:
+ return "Resource temporarily unavailable";
+ case ENOMEM:
+ return "Not enough core";
+ case EACCES:
+ return "Permission denied";
+ case EFAULT:
+ return "Bad address";
+ case ENOTBLK:
+ return "Block device required";
+ case EBUSY:
+ return "Mount device busy";
+ case EEXIST:
+ return "File exists";
+ case EXDEV:
+ return "Cross-device link";
+ case ENODEV:
+ return "No such device";
+ case ENOTDIR:
+ return "Not a directory";
+ case EISDIR:
+ return "Is a directory";
+ case EINVAL:
+ return "Invalid argument";
+ case ENFILE:
+ return "File table overflow";
+ case EMFILE:
+ return "Too many open files";
+ case ENOTTY:
+ return "Inappropriate ioctl for device";
+ case ETXTBSY:
+ return "Text file busy";
+ case EFBIG:
+ return "File too large";
+ case ENOSPC:
+ return "No space left on device";
+ case ESPIPE:
+ return "Illegal seek";
+ case EROFS:
+ return "Read only file system";
+ case EMLINK:
+ return "Too many links";
+ case EPIPE:
+ return "Broken pipe";
+ case EDOM:
+ return "Math arg out of domain of func";
+ case ERANGE:
+ return "Math result not representable";
+ case ENOMSG:
+ return "No message of desired type";
+ case EIDRM:
+ return "Identifier removed";
+ case ECHRNG:
+ return "Channel number out of range";
+ case EL2NSYNC:
+ return "Level 2 not synchronized";
+ case EL3HLT:
+ return "Level 3 halted";
+ case EL3RST:
+ return "Level 3 reset";
+ case ELNRNG:
+ return "Link number out of range";
+ case EUNATCH:
+ return "Protocol driver not attached";
+ case ENOCSI:
+ return "No CSI structure available";
+ case EL2HLT:
+ return "Level 2 halted";
+ case EDEADLK:
+ return "Deadlock condition.";
+ case ENOLCK:
+ return "No record locks available.";
+ case ECANCELED:
+ return "Operation canceled";
+ case ENOTSUP:
+ return "Operation not supported";
+ case EDQUOT:
+ return "Disc quota exceeded";
+ case EBADE:
+ return "invalid exchange";
+ case EBADR:
+ return "invalid request descriptor";
+ case EXFULL:
+ return "exchange full";
+ case ENOANO:
+ return "no anode";
+ case EBADRQC:
+ return "invalid request code";
+ case EBADSLT:
+ return "invalid slot";
+ case EBFONT:
+ return "bad font file fmt";
+ case ENOSTR:
+ return "Device not a stream";
+ case ENODATA:
+ return "no data (for no delay io)";
+ case ETIME:
+ return "timer expired";
+ case ENOSR:
+ return "out of streams resources";
+ case ENONET:
+ return "Machine is not on the network";
+ case ENOPKG:
+ return "Package not installed";
+ case EREMOTE:
+ return "The object is remote";
+ case ENOLINK:
+ return "the link has been severed";
+ case EADV:
+ return "advertise error";
+ case ESRMNT:
+ return "srmount error";
+ case ECOMM:
+ return "Communication error on send";
+ case EPROTO:
+ return "Protocol error";
+ case EMULTIHOP:
+ return "multihop attempted";
+ case EBADMSG:
+ return "trying to read unreadable message";
+ case ENAMETOOLONG:
+ return "path name is too long";
+ case EOVERFLOW:
+ return "value too large to be stored in data type";
+ case ENOTUNIQ:
+ return "given log. name not unique";
+ case EBADFD:
+ return "f.d. invalid for this operation";
+ case EREMCHG:
+ return "Remote address changed";
+ case ELIBACC:
+ return "Can't access a needed shared lib.";
+ case ELIBBAD:
+ return "Accessing a corrupted shared lib.";
+ case ELIBSCN:
+ return ".lib section in a.out corrupted.";
+ case ELIBMAX:
+ return "Attempting to link in too many libs.";
+ case ELIBEXEC:
+ return "Attempting to exec a shared library.";
+ case EILSEQ:
+ return "Illegal byte sequence.";
+ case ENOSYS:
+ return "Unsupported file system operation";
+ case ELOOP:
+ return "Symbolic link loop";
+ case ERESTART:
+ return "Restartable system call";
+ case ESTRPIPE:
+ return "if pipe/FIFO, don't sleep in stream head";
+ case ENOTEMPTY:
+ return "directory not empty";
+ case EUSERS:
+ return "Too many users (for UFS)";
+ case ENOTSOCK:
+ return "Socket operation on non-socket";
+ case EDESTADDRREQ:
+ return "Destination address required";
+ case EMSGSIZE:
+ return "Message too long";
+ case EPROTOTYPE:
+ return "Protocol wrong type for socket";
+ case ENOPROTOOPT:
+ return "Protocol not available";
+ case EPROTONOSUPPORT:
+ return "Protocol not supported";
+ case ESOCKTNOSUPPORT:
+ return "Socket type not supported";
+ case EPFNOSUPPORT:
+ return "Protocol family not supported";
+ case EAFNOSUPPORT:
+ return "Address family not supported by protocol family";
+ case EADDRINUSE:
+ return "Address already in use";
+ case EADDRNOTAVAIL:
+ return "Can't assign requested address";
+ case ENETDOWN:
+ return "Network is down";
+ case ENETUNREACH:
+ return "Network is unreachable";
+ case ENETRESET:
+ return "Network dropped connection because of reset";
+ case ECONNABORTED:
+ return "Software caused connection abort";
+ case ECONNRESET:
+ return "Connection reset by peer";
+ case ENOBUFS:
+ return "No buffer space available";
+ case EISCONN:
+ return "Socket is already connected";
+ case ENOTCONN:
+ return "Socket is not connected";
+ case ESHUTDOWN:
+ return "Can't send after socket shutdown";
+ case ETOOMANYREFS:
+ return "Too many references: can't splice";
+ case ETIMEDOUT:
+ return "Connection timed out";
+ case ECONNREFUSED:
+ return "Connection refused";
+ case EHOSTDOWN:
+ return "Host is down";
+ case EHOSTUNREACH:
+ return "No route to host";
+ case EALREADY:
+ return "operation already in progress";
+ case EINPROGRESS:
+ return "operation now in progress";
+ case ESTALE:
+ return "Stale NFS file handle";
+ default:
+ return 0;
+ }
+}
diff --git a/kernel/util/time.c b/kernel/util/time.c
new file mode 100644
index 0000000..11ff8de
--- /dev/null
+++ b/kernel/util/time.c
@@ -0,0 +1,194 @@
+#include "util/time.h"
+#include "drivers/cmos.h"
+#include "main/apic.h"
+#include "proc/sched.h"
+#include "util/printf.h"
+#include "util/timer.h"
+#include <drivers/screen.h>
+
+#define TIME_APIC_TICK_FREQUENCY 16
+// this is pretty wrong...
+#define MICROSECONDS_PER_APIC_TICK (16 * 1000 / TIME_APIC_TICK_FREQUENCY)
+
+volatile uint64_t jiffies;
+uint64_t timer_tickcount CORE_SPECIFIC_DATA;
+uint64_t kernel_preempted_count CORE_SPECIFIC_DATA;
+uint64_t user_preempted_count CORE_SPECIFIC_DATA;
+uint64_t not_preempted_count CORE_SPECIFIC_DATA;
+uint64_t idle_count CORE_SPECIFIC_DATA;
+
+// (freq / 16) interrupts per millisecond
+static long timer_tick_handler(regs_t *regs)
+{
+ timer_tickcount++;
+
+#ifdef __VGABUF__
+ if (timer_tickcount % 128 == 0)
+ screen_flush();
+#endif
+
+ if (curcore.kc_id == 0)
+ {
+ jiffies = timer_tickcount;
+ __timers_fire();
+ }
+
+#ifdef __KPREEMPT__ // if (preemption_enabled()) {
+ (regs->r_cs & 0x3) ? user_preempted_count++ : kernel_preempted_count++;
+ apic_eoi();
+ if (regs->r_cs & 0x3 && curthr->kt_cancelled)
+ kthread_exit((void *)-1);
+ sched_yield();
+ return 1;
+
+#endif
+#ifndef __KPREEMPT__ //} else {
+ curthr ? not_preempted_count++ : idle_count++;
+ return 0;
+#endif //}
+
+ return 0;
+}
+
+void time_init()
+{
+ timer_tickcount = 0;
+ intr_register(INTR_APICTIMER, timer_tick_handler);
+ apic_enable_periodic_timer(TIME_APIC_TICK_FREQUENCY);
+}
+
+void time_spin(uint64_t ms)
+{
+ uint64_t ticks_to_wait = ms * TIME_APIC_TICK_FREQUENCY / 16;
+ uint64_t target = timer_tickcount + ticks_to_wait;
+ dbg(DBG_SCHED, "spinning for %lu ms (%lu APIC ticks)\n", ms, ticks_to_wait);
+ while (timer_tickcount < target)
+ ;
+}
+
+void time_sleep(uint64_t ms)
+{
+ // TODO make curthr runnable and place on runqueue
+ time_spin(ms);
+}
+
+inline time_t core_uptime()
+{
+ return (MICROSECONDS_PER_APIC_TICK * timer_tickcount) / 1000;
+}
+
+static int mdays[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+time_t do_time()
+{
+ rtc_time_t tm = rtc_get_time();
+ // dbg(DBG_SCHED, "rtc_time (Y-M-D:hh:mm:ss): %d-%d-%d:%d:%d:%d\n", tm.year,
+ // tm.month, tm.day, tm.hour, tm.minute, tm.second);
+
+ int yday = mdays[tm.month - 1] + tm.day - 1;
+ if (tm.month >= 3 && (((tm.year % 4 == 0) && (tm.year % 100 != 0)) ||
+ (tm.year % 400 == 0)))
+ {
+ yday += 1;
+ }
+ tm.year -= 1900;
+
+ /* oof */
+ time_t unix_time =
+ tm.second + tm.minute * 60 + tm.hour * 3600 + yday * 86400 +
+ (tm.year - 70) * 31536000 + ((tm.year - 69) / 4) * 86400 -
+ ((tm.year - 1) / 100) * 86400 + ((tm.year + 299) / 400) * 86400;
+
+ return unix_time;
+}
+
+static size_t human_readable_format(char *buf, size_t size, uint64_t ticks)
+{
+ uint64_t milliseconds = core_uptime();
+ uint64_t minutes = milliseconds / (60 * 1000);
+ milliseconds -= minutes * 60 * 1000;
+ uint64_t seconds = milliseconds / 1000;
+ milliseconds -= seconds * 1000;
+ return (size_t)snprintf(buf, size, "%lu min, %lu sec, %lu ms", minutes,
+ seconds, milliseconds);
+}
+
+static size_t percentage(char *buf, size_t size, uint64_t numerator,
+ uint64_t denominator)
+{
+ // 2 decimal points, no floats
+ uint64_t new_numerator = numerator * 10000;
+ if (new_numerator < numerator)
+ {
+ return (size_t)snprintf(buf, size, "N/A");
+ }
+ uint64_t result = denominator ? new_numerator / denominator : 0;
+ return (size_t)snprintf(buf, size, "%lu.%02lu%%", result / 100,
+ result % 100);
+}
+
+size_t time_stats(char *buf, size_t len)
+{
+ size_t off = 0;
+ off += snprintf(buf + off, len - off, "core uptime:\t");
+ off += human_readable_format(buf + off, len - off, timer_tickcount);
+ off += snprintf(buf + off, len - off, "\nidle time:\t");
+ off += human_readable_format(buf + off, len - off, idle_count);
+ off += snprintf(buf + off, len - off, "\t");
+ off += percentage(buf + off, len - off, idle_count, timer_tickcount);
+
+ KASSERT(not_preempted_count + user_preempted_count +
+ kernel_preempted_count + idle_count - timer_tickcount <=
+ 2);
+
+ off += snprintf(buf + off, len - off, "\n\ntotal tick count = %lu",
+ timer_tickcount);
+ off += snprintf(buf + off, len - off, "\nidle count = %lu",
+ idle_count);
+ off += snprintf(buf + off, len - off, "\t");
+ off += percentage(buf + off, len - off, idle_count, timer_tickcount);
+ off += snprintf(buf + off, len - off, "\nkernel preempted count = %lu",
+ kernel_preempted_count);
+ off += snprintf(buf + off, len - off, "\t");
+ off += percentage(buf + off, len - off, kernel_preempted_count,
+ timer_tickcount);
+ off += snprintf(buf + off, len - off, "\nuser preempted count = %lu",
+ user_preempted_count);
+ off += snprintf(buf + off, len - off, "\t");
+ off +=
+ percentage(buf + off, len - off, user_preempted_count, timer_tickcount);
+ off += snprintf(buf + off, len - off, "\nnot preempted count = %lu",
+ not_preempted_count);
+ off += snprintf(buf + off, len - off, "\t");
+ off +=
+ percentage(buf + off, len - off, not_preempted_count, timer_tickcount);
+
+ return off;
+}
+
+static void do_wakeup(uint64_t arg)
+{
+ kthread_t *thr = (kthread_t *)arg;
+
+ if (thr->kt_wchan)
+ {
+ sched_broadcast_on(thr->kt_wchan);
+ }
+}
+
+long do_usleep(useconds_t usec)
+{
+ ktqueue_t waitq;
+ sched_queue_init(&waitq);
+
+ timer_t timer;
+ timer_init(&timer);
+ timer.function = do_wakeup;
+ timer.data = (uint64_t)curthr;
+ timer.expires = jiffies + (usec / MICROSECONDS_PER_APIC_TICK);
+
+ timer_add(&timer);
+ long ret = sched_cancellable_sleep_on(&waitq);
+ timer_del(&timer);
+ return ret;
+} \ No newline at end of file
diff --git a/kernel/util/timer.c b/kernel/util/timer.c
new file mode 100644
index 0000000..f1be4a2
--- /dev/null
+++ b/kernel/util/timer.c
@@ -0,0 +1,121 @@
+#include "util/timer.h"
+#include "proc/spinlock.h"
+#include "util/time.h"
+
+static timer_t *timer_running = NULL;
+static uint64_t timer_next_expiry = -1;
+static list_t timers_primary = LIST_INITIALIZER(timers_primary);
+static list_t timers_secondary = LIST_INITIALIZER(timers_secondary);
+static int timers_firing = 0;
+
+void timer_init(timer_t *timer)
+{
+ timer->expires = -1;
+ list_link_init(&timer->link);
+}
+
+void timer_add(timer_t *timer) { timer_mod(timer, timer->expires); }
+
+int __timer_del(timer_t *timer)
+{
+ int ret = 0;
+ if (list_link_is_linked(&timer->link))
+ {
+ list_remove(&timer->link);
+ ret = 1;
+ }
+ return ret;
+}
+
+int timer_del(timer_t *timer)
+{
+ int ret = __timer_del(timer);
+
+ return ret;
+}
+
+void __timer_add(timer_t *timer)
+{
+ KASSERT(!list_link_is_linked(&timer->link));
+ list_t *list = timers_firing ? &timers_secondary : &timers_primary;
+ list_insert_head(list, &timer->link);
+}
+
+int timer_mod(timer_t *timer, int expires)
+{
+
+ timer->expires = expires;
+ int ret = __timer_del(timer);
+ __timer_add(timer);
+ timer_next_expiry = MIN(timer_next_expiry, timer->expires);
+
+ return ret;
+}
+
+int timer_pending(timer_t *timer)
+{
+ int ret = list_link_is_linked(&timer->link);
+ return ret;
+}
+
+int timer_del_sync(timer_t *timer)
+{
+ /* Not great performance wise... */
+ while (timer_running == timer)
+ {
+ sched_yield();
+ }
+
+ int ret = __timer_del(timer);
+
+ return ret;
+}
+
+/* Note: using a linked-list rather than some priority is terribly inefficient
+ * Also this implementation is just bad. Sorry.
+ */
+int ready = 0;
+void __timers_fire()
+{
+ if (curthr && !preemption_enabled())
+ {
+ return;
+ }
+
+ timers_firing = 1;
+
+ //dbg(DBG_PRINT, "next expiry: %d\n", timer_next_expiry);
+ if (jiffies < timer_next_expiry)
+ {
+ timers_firing = 0;
+ return;
+ }
+
+ uint64_t min_expiry = 0;
+
+ list_iterate(&timers_primary, timer, timer_t, link)
+ {
+ if (jiffies >= timer->expires)
+ {
+ list_remove(&timer->link);
+ timer_running = timer;
+ timer->function(timer->data);
+ timer_running = NULL;
+ }
+ else
+ {
+ min_expiry = MIN(min_expiry, timer->expires);
+ }
+ }
+
+ /* migrate from the backup list to the primary list */
+ list_iterate(&timers_secondary, timer, timer_t, link)
+ {
+ min_expiry = MIN(min_expiry, timer->expires);
+ list_remove(&timer->link);
+ list_insert_head(&timers_primary, &timer->link);
+ }
+
+ timer_next_expiry = min_expiry;
+ timers_firing = 0;
+}