diff options
author | nthnluu <nate1299@me.com> | 2024-01-28 21:20:27 -0500 |
---|---|---|
committer | nthnluu <nate1299@me.com> | 2024-01-28 21:20:27 -0500 |
commit | c63f340d90800895f007de64b7d2d14624263331 (patch) | |
tree | 2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/util |
Created student weenix repository
Diffstat (limited to 'kernel/util')
-rw-r--r-- | kernel/util/debug.c | 237 | ||||
-rw-r--r-- | kernel/util/debug.py | 77 | ||||
-rw-r--r-- | kernel/util/init.c | 142 | ||||
-rw-r--r-- | kernel/util/list.c | 53 | ||||
-rw-r--r-- | kernel/util/list.py | 32 | ||||
-rw-r--r-- | kernel/util/math.c | 411 | ||||
-rw-r--r-- | kernel/util/printf.c | 996 | ||||
-rw-r--r-- | kernel/util/string.c | 509 | ||||
-rw-r--r-- | kernel/util/time.c | 194 | ||||
-rw-r--r-- | kernel/util/timer.c | 121 |
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; +} |