Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions kernel/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ override CFLAGS += \
-mno-sse \
-mno-sse2 \
-mno-red-zone \
-mcmodel=kernel
-mcmodel=kernel \
-DSYS_SELF_TEST

# TODO: add these flags: -Wconversion -Wsign-conversion -Wcast-align \
# -Wpointer-arith -Wshadow
Expand All @@ -70,7 +71,8 @@ override CPPFLAGS := \
-I. \
$(CPPFLAGS) \
-MMD \
-MP
-MP \
-DSYS_SELF_TEST

# Internal linker flags that should not be changed by the user.
override LDFLAGS += \
Expand Down
15 changes: 15 additions & 0 deletions kernel/include/kclib/assert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef ASSERT_H
#define ASSERT_H

typedef bool (*kassert_handler) (bool, const char*);

bool kassert_halt (bool condition);
bool kassert_handle (bool condition, const char* message, kassert_handler handler);

// Requires serial to be initialised!

bool kassert_print_halt (bool condition, const char* message);
bool kassert_print_no_block (bool condition, const char* message);
bool kassert_print_handle (bool condition, const char* message, kassert_handler handler);

#endif
16 changes: 16 additions & 0 deletions kernel/include/kernel/tests.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef TESTS_H
#define TESTS_H

typedef bool (*test_t) (void);
typedef struct reg_test_t reg_test_t;

struct reg_test_t {
test_t handler;
const char* description;
reg_test_t* next;
};

void register_test (test_t t_handler, const char* t_failmessage);
void run_tests (void);

#endif
58 changes: 58 additions & 0 deletions kernel/src/kclib/assert.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <kclib/assert.h>
#include <kclib/stdio.h>

/*!
* Assert condition is true, if not, then busy wait
* @param condition condition to be checked
*/
inline bool kassert_halt (bool condition) {
if (condition) return true;
for (;;)
;
}

/*!
* Assert condition is true, if not, then forward to handler
* @param condition condition to be checked
* @param message message to be forwarded to handler
* @param handler handler to forward to
*/
inline bool kassert_handle (bool condition, const char* message, kassert_handler handler) {
if (condition) return true;
return handler (condition, message);
}

/*!
* Assert condition is true, if not, then print to serial and busy wait
* @param condition condition to be checked
* @param message message to be printed
*/
inline bool kassert_print_halt (bool condition, const char* message) {
if (condition) return true;
kserial_printf ("KASSERT (halt) failed for: %s\n", message);
for (;;)
;
}

/*!
* Assert condition is true, if not, then print to serial and continue execution
* @param condition condition to be checked
* @param message message to be printed
*/
inline bool kassert_print_no_block (bool condition, const char* message) {
if (condition) return true;
kserial_printf ("KASSERT (non-block) failed for: %s\n", message);
return false;
}

/*!
* Assert condition is true, if not, then print to serial and forward to handler
* @param condition condition to be checked
* @param message message to be printed and forwarded to handler
* @param handler handler to forward to
*/
inline bool kassert_print_handle (bool condition, const char* message, kassert_handler handler) {
if (condition) return true;
kserial_printf ("KASSERT (handle) failed for: %s\n", message);
return handler (condition, message);
}
10 changes: 10 additions & 0 deletions kernel/src/kernel/entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <kernel/serial.h>
#include <kernel/stack.h>
#include <kernel/syscall.h>
#include <kernel/tests.h>
#include <liballoc/liballoc.h>
#include <stddef.h>
#include <stdint.h>
Expand Down Expand Up @@ -139,6 +140,15 @@ __attribute__ ((noreturn)) void _start_stage2 (void) {
jump_to_usermode (entry_point, user_stack_base, &current->p_user);
}

#ifdef SYS_SELF_TEST
uint64_t test_fork_result = do_syscall (SYSCALL_SYS_FORK, 0, 0, 0);
if (test_fork_result == 0) {
write_serial_str ("Sys self test enabled!\n");
run_tests ();
do_syscall (SYSCALL_SYS_EXIT, 0, 0, 0);
}
#endif

for (;;)
;
}
Expand Down
62 changes: 62 additions & 0 deletions kernel/src/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,73 @@ static uint64_t sys_getpid (uint64_t arg1, uint64_t arg2, uint64_t arg3) {
return current ? current->p_id : 0ull;
}

#ifdef SYS_SELF_TEST

#include <kernel/tests.h>

static bool test_process_queue_torture (void) {
const int count = 50;
process_queue q = {.head = nullptr, .tail = nullptr};
process** procs = kmalloc (sizeof (process*) * count);
if (!procs) return false;

// Fill procs
for (int i = 0; i < count; i++) {
procs[i] = kmalloc (sizeof (process));
if (!procs[i]) return false;
procs[i]->p_id = 1000 + i;
if (enqueue_process (&q, procs[i]) != 0) return false;
}

// Partial dequeue and re-enqueue
for (int i = 0; i < count / 2; i++) {
process* out = nullptr;
if (dequeue_process (&q, &out) != 0 || out != procs[i]) return false;
}

for (int i = 0; i < count / 2; i++)
if (enqueue_process (&q, procs[i]) != 0) return false;

// Final drain and verification
for (int i = count / 2; i < count; i++) {
process* out = nullptr;
if (dequeue_process (&q, &out) != 0 || out != procs[i]) return false;
}
for (int i = 0; i < count / 2; i++) {
process* out = nullptr;
if (dequeue_process (&q, &out) != 0 || out != procs[i]) return false;
}

// Cleanup
for (int i = 0; i < count; i++)
kfree (procs[i]);
kfree (procs);

return q.head == nullptr && q.tail == nullptr;
}

static bool test_process_queue_empty (void) {
process_queue q = {.head = nullptr, .tail = nullptr};
process* out = (process*)0xdeadbeef;

if (dequeue_process (&q, &out) != 0) return false;
if (out != nullptr) return false;

return true;
}

#endif

void init_process (void) {
ready_queue.head = ready_queue.tail = nullptr;
next_free_pid = 2ll;

register_syscall (SYSCALL_SYS_EXIT, sys_exit);
register_syscall (SYSCALL_SYS_FORK, sys_fork);
register_syscall (SYSCALL_SYS_GETPID, sys_getpid);

#ifdef SYS_SELF_TEST
register_test (test_process_queue_empty, "Dequeuing from empty queue");
register_test (test_process_queue_torture, "Process queue torture");
#endif
}
45 changes: 45 additions & 0 deletions kernel/src/kernel/tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <kclib/assert.h>
#include <kclib/stdio.h>
#include <kclib/string.h>
#include <kernel/tests.h>
#include <liballoc/liballoc.h>
#include <stdint.h>

reg_test_t* tests_head = nullptr;
reg_test_t* tests_tail = nullptr;
uint64_t test_count = 0;

void register_test (test_t t_handler, const char* t_failmessage) {
reg_test_t* new_test = kmalloc (sizeof (reg_test_t));
kassert_print_halt (new_test != nullptr,
"Tried to register a test but no memory was available.\n");

new_test->handler = t_handler;
new_test->description = kstrdup (t_failmessage);
new_test->next = nullptr;

if (tests_tail)
tests_tail->next = new_test;
else
tests_head = new_test;

tests_tail = new_test;
}

void run_tests (void) {
uint64_t successful = 0, failed = 0;

for (reg_test_t* test = tests_head; test != nullptr; test = test->next) {
if (test->handler) {
if (!test->handler ()) {
kserial_printf ("Test failed: %s\n", test->description);
failed++;
} else {
kserial_printf ("Test succeeded: %s\n", test->description);
successful++;
}
}
}

kprintf ("Ran all tests! %lld succeeded, %lld failed.\n", successful, failed);
}
Loading