summaryrefslogtreecommitdiff
path: root/viengoos
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@gnu.org>2009-02-16 02:07:30 +0100
committerNeal H. Walfield <neal@gnu.org>2009-02-16 02:07:30 +0100
commit47e21c7acbdd593dac1482a5238315355c853e38 (patch)
treef9d95280e89334c1107b25015ba154989cd1007c /viengoos
parent39e1f97996d9f5d7a1f11d0bcc53f98b37e7ffa7 (diff)
Rewrite CPU scheduler. Fix a few bugs. Add a lot of debugging support.
Diffstat (limited to 'viengoos')
-rw-r--r--viengoos/object.c146
-rw-r--r--viengoos/scheduler.c151
-rw-r--r--viengoos/server.c8
-rw-r--r--viengoos/sysdeps/x86_64/idt.c89
-rw-r--r--viengoos/sysdeps/x86_64/sys.h63
-rw-r--r--viengoos/sysdeps/x86_64/thread-arch.c216
-rw-r--r--viengoos/thread.c8
-rw-r--r--viengoos/thread.h18
-rw-r--r--viengoos/zalloc.h4
9 files changed, 615 insertions, 88 deletions
diff --git a/viengoos/object.c b/viengoos/object.c
index 83b31f6..df944f0 100644
--- a/viengoos/object.c
+++ b/viengoos/object.c
@@ -36,6 +36,7 @@
#include "zalloc.h"
#include "messenger.h"
#include "debug.h"
+#include "scheduler.h"
/* For lack of a better place. */
ss_mutex_t kernel_lock;
@@ -54,7 +55,7 @@ object_debug_command (int argc, char *argv[])
return;
}
- vg_oid_t oid = strtol (argv[1], NULL, 16);
+ vg_oid_t oid = strtoul (argv[1], NULL, 16);
struct vg_object *object
= object_find (root_activity, oid, VG_OBJECT_POLICY_VOID);
if (! object)
@@ -96,6 +97,42 @@ object_debug_command (int argc, char *argv[])
? object_to_object_desc ((struct vg_object *)
desc->activity)->oid
: -1));
+
+ switch (object_type (object))
+ {
+ case vg_cap_page:
+ case vg_cap_rpage:
+ {
+ int i;
+
+ printf ("\n");
+ int skip;
+ for (i = 0; i < PAGESIZE / sizeof (uint64_t); i += 4)
+ if (((uint64_t *) object)[i] == 0
+ && ((uint64_t *) object)[i + 1] == 0
+ && ((uint64_t *) object)[i + 2] == 0
+ && ((uint64_t *) object)[i + 3] == 0)
+ skip ++;
+ else
+ {
+ if (skip > 0)
+ {
+ printf ("Skipped %d zero bytes.\n",
+ 4 * skip * sizeof (uint64_t));
+ skip = 0;
+ }
+ printf ("%3x: %016"PRIxPTR" %016"PRIxPTR
+ " %016"PRIxPTR" %016"PRIxPTR"\n",
+ (int) i * sizeof (uintptr_t),
+ ((uint64_t *) object)[i],
+ ((uint64_t *) object)[i + 1],
+ ((uint64_t *) object)[i + 2],
+ ((uint64_t *) object)[i + 3]);
+ }
+ }
+
+ break;
+ }
}
static struct debug_command object_debug =
@@ -105,6 +142,109 @@ static struct debug_command object_debug =
.help = "Display an object's state",
.function = object_debug_command
};
+
+static void
+memory_debug_command (int argc, char *argv[])
+{
+ if (argc < 2)
+ {
+ printf ("Usage: address [count]\n");
+ return;
+ }
+
+ uintptr_t *addr = (uintptr_t *) strtoul (argv[1], NULL, 16);
+ int count = sizeof (uintptr_t) * 4;
+
+ if (argc > 2)
+ count = strtol (argv[2], NULL, 0);
+
+ if (count < 0)
+ {
+ count = -count;
+ addr -= count;
+ }
+
+ int cells = 0;
+ for (; count > 0; count -= sizeof (uintptr_t))
+ {
+ if (cells ++ % 4 == 0)
+ {
+ printf ("\n");
+ printf ("%016"PRIxPTR":", (uintptr_t) addr);
+ }
+ printf (" %016"PRIxPTR, *addr);
+ addr ++;
+ }
+ printf ("\n");
+}
+
+static struct debug_command memory_debug =
+ {
+ .name = "memory",
+ .short_name = 'm',
+ .help = "Dump mapped memory",
+ .function = memory_debug_command
+ };
+
+static void
+object_lookup_command (int argc, char *argv[])
+{
+ if (argc < 2 || argc > 3)
+ {
+ printf ("Usage: [thread] prefix[/depth]\n");
+ return;
+ }
+
+ struct thread *thread = current_thread;
+
+ if (argc == 3)
+ {
+ vg_oid_t oid = strtoul (argv[1], NULL, 16);
+ struct vg_object *object
+ = object_find (root_activity, oid, VG_OBJECT_POLICY_VOID);
+ if (! object)
+ {
+ printf ("No such object: "VG_OID_FMT"\n", oid);
+ return;
+ }
+
+ if (! object_type (object))
+ {
+ printf (VG_OID_FMT" is not a thread\n", oid);
+ return;
+ }
+
+ thread = (struct thread *) object;
+ }
+
+ char *tail = NULL;
+ uintptr_t prefix = strtoul (argv[argc - 1], &tail, 16);
+
+ int depth;
+ if (*tail == 0)
+ depth = VG_ADDR_BITS - PAGESIZE_LOG2;
+ else if (*tail == '/')
+ depth = strtoul (tail + 1, NULL, 0);
+ else
+ {
+ printf ("%s must be in prefix[/depth] format.\n", argv[argc - 1]);
+ return;
+ }
+
+ /* Chop off lower bits. */
+ prefix &= ~((UINT64_C(1) << (VG_ADDR_BITS - depth)) - 1);
+
+ printf ("Path to %"PRIxPTR"/%d is:\n", prefix, depth);
+ as_dump_path_rel (root_activity, &thread->aspace, VG_ADDR (prefix, depth));
+}
+
+static struct debug_command object_lookup_debug =
+ {
+ .name = "object-lookup",
+ .short_name = 'l',
+ .help = "Look up an object ([thread] prefix[/depth])",
+ .function = object_lookup_command
+ };
/* XXX: The number of in memory folios. (Recall: one folio => 512kb
storage.) */
@@ -160,7 +300,7 @@ object_init (void)
/* Round up. */
size = (size + PAGESIZE - 1) & ~(PAGESIZE - 1);
- debug (0, "%d frames", (last_frame - first_frame) / PAGESIZE);
+ debug (0, "%"PRIdPTR" frames", (last_frame - first_frame) / PAGESIZE);
object_descs = (void *) zalloc (size);
debug (0, "object_descs: %p - %p",
object_descs, (void *) object_descs + size - 1);
@@ -172,6 +312,8 @@ object_init (void)
assert (! object_descs[i].live);
debug_register (&object_debug);
+ debug_register (&memory_debug);
+ debug_register (&object_lookup_debug);
}
const char *
diff --git a/viengoos/scheduler.c b/viengoos/scheduler.c
index e95c50f..3331410 100644
--- a/viengoos/scheduler.c
+++ b/viengoos/scheduler.c
@@ -24,6 +24,9 @@
#include "debug.h"
#include "timer.h"
+/* Set to 0 to show the thread state transitions. */
+#define SHOW_THREAD_STATE_TRANSITIONS 5
+
/* 100 milliseconds. */
#define TS (100 * 1000 * 1000)
@@ -34,6 +37,31 @@ static uint64_t quantum_start;
static struct thread_queue_list run_queue;
static struct thread_queue_list no_time_queue;
+static void
+thread_remove_from_run_queue (struct thread *thread)
+{
+ assertx (thread->state == THREAD_RUNNABLE,
+ "%s", thread_state_string (thread->state));
+
+ if (thread->time > 0)
+ thread_queue_list_unlink (&run_queue, thread);
+ else
+ thread_queue_list_unlink (&no_time_queue, thread);
+}
+
+static void
+thread_add_to_run_queue (struct thread *thread)
+{
+ assert (thread->state != THREAD_RUNNABLE);
+
+ if (thread->time > 0)
+ thread_queue_list_enqueue (&run_queue, thread);
+ else
+ thread_queue_list_enqueue (&no_time_queue, thread);
+
+ thread->state = THREAD_RUNNABLE;
+}
+
/* The tick the last time allowances were distributed. */
static uint64_t last_payout;
@@ -41,21 +69,29 @@ static void
deschedule_current_thread (bool suspend)
{
assert (current_thread);
- assert (current_thread->state == THREAD_RUNNING);
+ assert (current_thread->state == THREAD_RUNNING
+ || current_thread->state == THREAD_SUSPENDED);
int delta = time_data.ns_since_boot - quantum_start;
current_thread->time -= delta;
current_thread->total_time += delta;
- if (suspend)
- current_thread->state = THREAD_SUSPENDED;
- else
+ if (suspend || current_thread->state == THREAD_SUSPENDED)
+ {
+ debug (SHOW_THREAD_STATE_TRANSITIONS, OBJECT_NAME_FMT ": %s -> %s",
+ OBJECT_NAME_PRINTF ((struct vg_object *) current_thread),
+ thread_state_string (current_thread->state),
+ thread_state_string (THREAD_SUSPENDED));
+
+ current_thread->state = THREAD_SUSPENDED;
+ }
+ else if (current_thread->state == THREAD_RUNNING)
{
- current_thread->state = THREAD_RUNNABLE;
+ debug (SHOW_THREAD_STATE_TRANSITIONS, OBJECT_NAME_FMT ": %s -> %s",
+ OBJECT_NAME_PRINTF ((struct vg_object *) current_thread),
+ thread_state_string (current_thread->state),
+ thread_state_string (THREAD_RUNNABLE));
- if (current_thread->time > 0)
- thread_queue_list_enqueue (&run_queue, current_thread);
- else
- thread_queue_list_enqueue (&no_time_queue, current_thread);
+ thread_add_to_run_queue (current_thread);
}
current_thread = NULL;
@@ -64,23 +100,40 @@ deschedule_current_thread (bool suspend)
void
schedule (struct thread *thread)
{
- if (current_thread)
+ if (likely (current_thread))
/* See if the current thread has exceeded its allowance. */
{
- if (time_data.ns_since_boot - quantum_start >= current_thread->time)
+ if (current_thread->state == THREAD_SUSPENDED)
+ deschedule_current_thread (true);
+ else if (unlikely (time_data.ns_since_boot - quantum_start
+ >= current_thread->time))
{
+ assert (current_thread->state == THREAD_RUNNING);
+
debug (5, OBJECT_NAME_FMT ": Quantum up.",
OBJECT_NAME_PRINTF ((struct vg_object *) current_thread));
deschedule_current_thread (false);
}
}
- if (! thread && current_thread)
+ if (likely (! thread && current_thread))
/* We don't have to switch and the current thread hasn't exceed
its allowance. Keep it on the CPU. */
return;
+ if (thread)
+ /* The caller specified a thread to run. Use it. */
+ {
+ assert (thread->state == THREAD_RUNNABLE
+ || thread->state == THREAD_SUSPENDED);
+
+ if (thread->state == THREAD_RUNNABLE)
+ thread_remove_from_run_queue (thread);
+ }
+
if (! thread)
+ /* The caller didn't specify a thread to run. Dequeue the next
+ thread thread from the run queue. */
{
thread = thread_queue_list_dequeue (&run_queue);
if (thread)
@@ -88,7 +141,8 @@ schedule (struct thread *thread)
}
if (! thread)
- /* Refill all ready threads' allowances and move them to the run
+ /* It seems that there are no threads on the run queue. Refill
+ all ready threads' allowances and move them to the run
queue. */
{
debug (5, "Allocating time at %ld.", time_data.ns_since_boot);
@@ -103,6 +157,8 @@ schedule (struct thread *thread)
t;
t = thread_queue_list_next (t))
{
+ assert (t->state == THREAD_RUNNABLE);
+
assert (t->time <= 0);
t->time += TS;
@@ -117,21 +173,29 @@ schedule (struct thread *thread)
thread = thread_queue_list_dequeue (&run_queue);
}
+ /* Remove the current thread from the CPU. */
if (current_thread)
- deschedule_current_thread (false);
+ {
+ assert (thread);
+
+ deschedule_current_thread (false);
+ }
if (! thread)
return;
- static struct thread *last_thread;
- if (last_thread != thread)
+ if (thread->state != THREAD_RUNNING)
{
+ debug (SHOW_THREAD_STATE_TRANSITIONS, OBJECT_NAME_FMT ": %s -> %s",
+ OBJECT_NAME_PRINTF ((struct vg_object *) thread),
+ thread_state_string (thread->state),
+ thread_state_string (THREAD_RUNNING));
+
debug (5, OBJECT_NAME_FMT ": Scheduling (ip: %p) (%d ready).",
OBJECT_NAME_PRINTF ((struct vg_object *) thread),
thread_ip (thread),
thread_queue_list_count (&run_queue)
+ thread_queue_list_count (&no_time_queue));
- last_thread = thread;
}
quantum_start = time_data.ns_since_boot;
@@ -146,50 +210,61 @@ schedule (struct thread *thread)
void
thread_make_runnable (struct thread *thread)
{
- if (thread->state == THREAD_SUSPENDED)
- {
- debug (5, "%p made "OBJECT_NAME_FMT" runnable.",
- __builtin_return_address (0),
- OBJECT_NAME_PRINTF (thread));
+ if (thread->state == THREAD_RUNNABLE
+ || thread->state == THREAD_RUNNING)
+ return;
+ assert (thread->state == THREAD_SUSPENDED);
+
+ enum thread_state new
+ = thread == current_thread ? THREAD_RUNNING : THREAD_RUNNABLE;
+ debug (SHOW_THREAD_STATE_TRANSITIONS, OBJECT_NAME_FMT ": %s -> %s",
+ OBJECT_NAME_PRINTF ((struct vg_object *) thread),
+ thread_state_string (thread->state),
+ thread_state_string (new));
+ if (thread == current_thread)
+ thread->state = THREAD_RUNNING;
+ else
+ {
if (thread->last_payout != last_payout)
{
thread->time = TS;
thread->last_payout = last_payout;
}
- if (thread->time > 0)
- thread_queue_list_enqueue (&run_queue, thread);
- else
- thread_queue_list_enqueue (&no_time_queue, thread);
-
- thread->state = THREAD_RUNNABLE;
+ thread_add_to_run_queue (thread);
}
- else
- assert (thread->state == THREAD_RUNNABLE
- || thread->state == THREAD_RUNNING);
}
void
thread_suspend (struct thread *thread)
{
+ if (thread->state == THREAD_SUSPENDED)
+ return;
+ assert (thread->state == THREAD_RUNNABLE
+ || thread->state == THREAD_RUNNING);
+
+ debug (SHOW_THREAD_STATE_TRANSITIONS, OBJECT_NAME_FMT ": %s -> %s",
+ OBJECT_NAME_PRINTF ((struct vg_object *) thread),
+ thread_state_string (thread->state),
+ thread_state_string (THREAD_SUSPENDED));
+
switch (thread->state)
{
- case THREAD_SUSPENDED:
- break;
-
case THREAD_RUNNING:
assert (current_thread == thread);
- deschedule_current_thread (true);
- schedule (NULL);
+ /* Mark it as suspended. At the next reschedule, it will be
+ really removed from the CPU (unless it is not marked as
+ runnable in the mean time!). */
break;
case THREAD_RUNNABLE:
assert (current_thread != thread);
- thread->state = THREAD_SUSPENDED;
- thread_queue_list_unlink (&run_queue, thread);
+ thread_remove_from_run_queue (thread);
break;
}
+
+ thread->state = THREAD_SUSPENDED;
}
static void
diff --git a/viengoos/server.c b/viengoos/server.c
index b2e83ac..decc0fe 100644
--- a/viengoos/server.c
+++ b/viengoos/server.c
@@ -1,5 +1,5 @@
/* server.c - Server loop implementation.
- Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
Written by Neal H. Walfield <neal@gnu.org>.
This file is part of the GNU Hurd.
@@ -349,8 +349,8 @@ dispatcher (uintptr_t flags,
DEBUG (1, DEBUG_BOLD ("Returning error %d to " \
OBJECT_NAME_FMT), \
err_, OBJECT_NAME_PRINTF ((struct vg_object *) \
- current_thread)); \
- thread_return_set (current_thread, err_); \
+ thread)); \
+ thread_return_set (thread, err_); \
return; \
} \
while (0)
@@ -848,7 +848,7 @@ dispatcher (uintptr_t flags,
immediately. As such, we decide whether we suspend the thread
first. */
if ((flags & VG_IPC_RETURN))
- thread_return_set (current_thread, 0);
+ thread_return_set (thread, 0);
else
thread_suspend (thread);
diff --git a/viengoos/sysdeps/x86_64/idt.c b/viengoos/sysdeps/x86_64/idt.c
index 33f8c60..4ceb4f1 100644
--- a/viengoos/sysdeps/x86_64/idt.c
+++ b/viengoos/sysdeps/x86_64/idt.c
@@ -329,6 +329,26 @@ void handler_notimp ();
#include "page-tables.h"
#include "sys.h"
+#ifndef NDEBUG
+int in_interrupt_handler;
+#endif
+
+/* We need to disable interrupts before triggering some asserts... */
+#ifdef NDEBUG
+# define ASSERTX(x, fmt, ...) \
+ do { \
+ if (!(x)) \
+ { \
+ asm ("cli"); \
+ status (0); \
+ panic ("Assert " #x " failed: " fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+# define ASSERTX(x, fmt, ...) do { } while (0)
+#endif
+#define ASSERT(x) ASSERTX(x, "")
+
/* The high-level interrupt handler. If FROM_KERNEL is true, this
function must return to resume the interrupted kernel context.
Otherwise, it is permissible to not return (e.g., by calling
@@ -339,10 +359,21 @@ handler (struct interrupt_context *t, bool from_kernel)
static uintptr_t interrupts;
interrupts ++;
+#ifndef NDEBUG
+ __sync_fetch_and_add (&in_interrupt_handler, 1);
+#endif
+
void status (int d)
{
uintptr_t cr2;
asm ("movq %%cr2, %0" : "=r" (cr2));
+ uintptr_t db6;
+ asm ("movq %%db6, %0" : "=r" (db6));
+
+ uintptr_t last_branch_from_ip = x86_64_rdmsr (0x1db);
+ uintptr_t last_branch_to_ip = x86_64_rdmsr (0x1dc);
+ uintptr_t last_exception_from_ip = x86_64_rdmsr (0x1dd);
+ uintptr_t last_exception_to_ip = x86_64_rdmsr (0x1de);
printf ("Interrupt %"PRIdPTR": #%ld (%s), from %s!\n"
" err: 0x%lx\n"
@@ -351,11 +382,21 @@ handler (struct interrupt_context *t, bool from_kernel)
" rfl: 0x%lx "RFLAGS_FMT"\n"
" rsp: 0x%lx\n"
" ss: 0x%lx\n"
- " cr2 (last fault): %lx\n",
+ " cr2 (last fault): %lx\n"
+ " db6: %lx\n"
+ " last_branch_from_ip: %lx\n"
+ " last_branch_to_ip: %lx\n"
+ " last_exception_from_ip: %lx\n"
+ " last_exception_to_ip: %lx\n",
interrupts, t->num, get_int_name (t->num),
from_kernel ? "kernel" : "user space",
t->err, t->rip, t->cs, t->rflags.raw, RFLAGS_PRINTF (t->rflags),
- t->rsp, t->ss, cr2);
+ t->rsp, t->ss, cr2,
+ last_branch_from_ip,
+ last_branch_to_ip,
+ last_exception_from_ip,
+ last_exception_to_ip);
+
if (current_thread)
printf ("Current thread: " OBJECT_NAME_FMT":\n"REGS_FMT"\n",
@@ -378,9 +419,15 @@ handler (struct interrupt_context *t, bool from_kernel)
if (! from_kernel)
{
- assert (current_thread);
- assert (t->rip == thread_ip (current_thread));
- assert (t->rsp == thread_sp (current_thread));
+ ASSERT (current_thread);
+ ASSERT (t->rip == thread_ip (current_thread));
+ ASSERT (t->rsp == thread_sp (current_thread));
+
+ ASSERT (((struct segment_selector) { { .raw = t->ss } } ).rpl
+ == SEG_PL_USER);
+ ASSERT (((struct segment_selector) { { .raw = t->cs } } ).rpl
+ == SEG_PL_USER);
+ ASSERTX (t->rip < KERNEL_OFFSET, "%lx", t->rip);
#ifndef NDEBUG
/* Assert that interrupts are not enabled. */
@@ -392,6 +439,14 @@ handler (struct interrupt_context *t, bool from_kernel)
RFLAGS_PRINTF (rflags), rflags.raw);
#endif
}
+ else
+ {
+ ASSERT (((struct segment_selector) { { .raw = t->ss } } ).rpl
+ == SEG_PL_KERNEL);
+ ASSERT (((struct segment_selector) { { .raw = t->cs } } ).rpl
+ == SEG_PL_KERNEL);
+ ASSERT (t->rip >= KERNEL_OFFSET);
+ }
/* XXX Currently, we root all interrupts in this handler, just to
demangle manually, to mangle them again calling pic_handle_irq,
@@ -410,12 +465,15 @@ handler (struct interrupt_context *t, bool from_kernel)
if (! from_kernel)
schedule (NULL);
- return;
+ goto out;
}
else
/* Reenable interrupts. */
asm ("sti");
+ //if (object_to_object_desc (current_thread)->oid == 0x44b)
+ //status (0);
+
if (t->num == 14)
/* It's a page fault. cr2 holds the fault address and cr3 the
pml4. */
@@ -532,7 +590,7 @@ handler (struct interrupt_context *t, bool from_kernel)
page_fault_handler (current_thread, cr2, access);
- return;
+ goto out;
}
else if (t->num == 48)
/* Debug output. %rax identifies the buffer and %rbx the number
@@ -557,17 +615,24 @@ handler (struct interrupt_context *t, bool from_kernel)
for (i = 0; i < len; i ++)
putchar (buffer.data[i]);
- return;
+ goto out;
}
- else if (t->num == 3)
+ else if (t->num == 3 || t->num == 1)
{
status (0);
debugger ();
- return;
+ goto out;
}
status (0);
panic ("Unhandled exception %"PRIxPTR, t->num);
+
+ out:;
+#ifndef NDEBUG
+ int i = __sync_fetch_and_add (&in_interrupt_handler, -1);
+ ASSERTX (i > 0, "%d", in_interrupt_handler);
+#endif
+ return;
}
@@ -579,13 +644,15 @@ idt_init ()
extern void handler##n(void); \
igd_init (&idt[n], (uintptr_t) handler##n, 0, 0);
#define R REGISTER
- R (0) R (1) R (2) /* R (3) */ R (4) R (4) R (5) R (6) R (7) R (8) R (9) R (10)
+ R (0) /* R (1) */ R (2) /* R (3) */ R (4) R (4) R (5) R (6) R (7) R (8) R (9) R (10)
R (11) R (12) R (13) R (14) R (15) R (16) R (17) R (18) R (19) R (20)
R (21) R (22) R (23) R (24) R (25) R (26) R (27) R (28) R (29) R (30)
R (31) R (32) R (33) R (34) R (35) R (36) R (37) R (38) R (39) R (40)
R (41) R (42) R (43) R (44) R (45) R (46) R (47)
/* The debugging interrupt may be invoked from user space. */
+ extern void handler1 (void);
+ igd_init (&idt[1], (uintptr_t) handler1, 0, 3);
extern void handler3 (void);
igd_init (&idt[3], (uintptr_t) handler3, 0, 3);
extern void handler48 (void);
diff --git a/viengoos/sysdeps/x86_64/sys.h b/viengoos/sysdeps/x86_64/sys.h
index eaea502..5afaa6e 100644
--- a/viengoos/sysdeps/x86_64/sys.h
+++ b/viengoos/sysdeps/x86_64/sys.h
@@ -112,29 +112,36 @@ build_assert (PHYS_MEM_OFFSET == -(UINT64_C(1) << 36));
" rbp: %016lx r8: %016lx r9: %016lx\n" \
" r10: %016lx r11: %016lx r12: %016lx\n" \
" r13: %016lx r14: %016lx r15: %016lx\n" \
- " cr3: %016lx"
-#define REGS_PRINTF(t) \
- (t)->regs[RSP], \
- (t)->regs[RGS], \
- (t)->regs[RIP], \
- (t)->regs[RFLAGS], \
- RFLAGS_PRINTF (rflags ((t)->regs[RFLAGS])), \
- (t)->regs[RAX], \
- (t)->regs[RBX], \
- (t)->regs[RCX], \
- (t)->regs[RDX], \
- (t)->regs[RSI], \
- (t)->regs[RDI], \
- (t)->regs[RBP], \
- (t)->regs[R8], \
- (t)->regs[R9], \
- (t)->regs[R10], \
- (t)->regs[R11], \
- (t)->regs[R12], \
- (t)->regs[R13], \
- (t)->regs[R14], \
- (t)->regs[R15], \
- (unsigned long) (t)->pml4
+ " pml4: %016lx\n" \
+ " Breakpoints %s: dr0: %016lx dr1: %016lx\n" \
+ " dr2: %016lx dr3: %016lx"
+#define REGS_PRINTF(t) \
+ (t)->regs[RSP], \
+ (t)->regs[RGS], \
+ (t)->regs[RIP], \
+ (t)->regs[RFLAGS], \
+ RFLAGS_PRINTF (rflags ((t)->regs[RFLAGS])), \
+ (t)->regs[RAX], \
+ (t)->regs[RBX], \
+ (t)->regs[RCX], \
+ (t)->regs[RDX], \
+ (t)->regs[RSI], \
+ (t)->regs[RDI], \
+ (t)->regs[RBP], \
+ (t)->regs[R8], \
+ (t)->regs[R9], \
+ (t)->regs[R10], \
+ (t)->regs[R11], \
+ (t)->regs[R12], \
+ (t)->regs[R13], \
+ (t)->regs[R14], \
+ (t)->regs[R15], \
+ (unsigned long) (t)->pml4, \
+ (t)->debugging_enabled ? "enabled" : "disabled", \
+ (t)->breakpoints[0], \
+ (t)->breakpoints[1], \
+ (t)->breakpoints[2], \
+ (t)->breakpoints[3]
#define THREAD_SYSDEP_MEMBERS \
/* The root of the address space. */ \
@@ -143,7 +150,15 @@ build_assert (PHYS_MEM_OFFSET == -(UINT64_C(1) << 36));
uintptr_t regs[REGS_COUNT]; \
/* Whether to restore all registers or just %rsp, %rip, %rflags and \
%rax. */ \
- uintptr_t regs_restore_all;
+ uintptr_t regs_restore_all; \
+ \
+ /* Debugging state. */ \
+ /* If false, then no debugging facilities are assumed to be in use. */ \
+ bool debugging_enabled; \
+ /* x86-64 has 4 hardware breakpoints. */ \
+ uintptr_t breakpoints[4]; \
+ /* Whether to single step. */ \
+ bool single_step;
#define thread_sp(thread) ((thread)->regs[RSP])
#define thread_sp_set(thread, sp) \
diff --git a/viengoos/sysdeps/x86_64/thread-arch.c b/viengoos/sysdeps/x86_64/thread-arch.c
index 14f97ef..25139bc 100644
--- a/viengoos/sysdeps/x86_64/thread-arch.c
+++ b/viengoos/sysdeps/x86_64/thread-arch.c
@@ -3,6 +3,110 @@
#include "scheduler.h"
#include "gdt.h"
#include "x86-64.h"
+#include "debug.h"
+#include "activity.h"
+
+static void
+breakpoint (int argc, char *argv[])
+{
+ struct thread *thread = current_thread;
+
+ if (argc < 2)
+ {
+ printf ("Usage: breakpoint [thread] addr [register]\n"
+ "Usage: breakpoint [thread] --list\n"
+ "(Setting a breakpoint to zero disables it.)\n");
+ return;
+ }
+
+ int addr_index = 1;
+ if (argc > 2)
+ {
+ vg_oid_t oid = strtol (argv[1], NULL, 16);
+ struct vg_object *object
+ = object_find (root_activity, oid, VG_OBJECT_POLICY_VOID);
+ if (! object)
+ {
+ printf ("No such object: "VG_OID_FMT"\n", oid);
+ return;
+ }
+
+ if (object_type (object) != vg_cap_thread)
+ {
+ printf (VG_OID_FMT" is a %s, not a thread\n",
+ oid, vg_cap_type_string (object_type (object)));
+ return;
+ }
+
+ thread = (struct thread *) object;
+
+ addr_index = 2;
+ }
+
+
+ if (! thread)
+ {
+ printf ("No thread specified\n");
+ return;
+ }
+
+ if (strcmp (argv[addr_index], "--list") == 0)
+ {
+ int i;
+ for (i = 0; i < 4; i ++)
+ printf ("%d: %"PRIxPTR"\n", i, thread->breakpoints[i]);
+ return;
+ }
+
+ uintptr_t addr = strtoul (argv[addr_index], NULL, 16);
+
+ int reg = -1;
+ if (argc > addr_index + 1)
+ {
+ reg = strtol (argv[addr_index + 1], NULL, 0);
+ if (reg < 0 || reg >= 4)
+ {
+ printf ("Reg must be between 0 and 3.\n");
+ return;
+ }
+ }
+
+ thread->debugging_enabled = true;
+
+ if (reg == -1)
+ for (reg = 0; reg < 4; reg ++)
+ if (! thread->breakpoints[reg])
+ break;
+
+ if (reg == 4)
+ {
+ printf ("No free hardware breakpoints available\n");
+ return;
+ }
+
+ printf ("Setting thread "OBJECT_NAME_FMT "'s breakpoint register #%d "
+ "to %"PRIxPTR"\n",
+ OBJECT_NAME_PRINTF ((struct vg_object *) thread), reg, addr);
+
+ thread->breakpoints[reg] = addr;
+
+ /* Enable branch tracking. */
+ x86_64_wrmsr (0x1d9, 1 /* last branch record. */);
+}
+
+static struct debug_command breakpoint_command =
+ {
+ "breakpoint",
+ 'b',
+ "Set a hardware breakpoint at an address for a thread ([thread] addr).",
+ (void (*) (int, char *[])) breakpoint,
+ };
+
+void
+thread_arch_bootstrap (void)
+{
+ debug_register (&breakpoint_command);
+}
void
thread_arch_init (struct thread *thread)
@@ -29,6 +133,11 @@ thread_arch_deinit (struct thread *thread)
void
thread_resume (void)
{
+#ifndef NDEBUG
+ extern int in_interrupt_handler;
+ assertx (in_interrupt_handler == 0, "%d", in_interrupt_handler);
+#endif
+
if (! current_thread)
panic ("Aiiie! Nothing to run...");
@@ -77,10 +186,98 @@ thread_resume (void)
[gs] "i" (SEGSEL_GS)
: "rbx");
- if (current_thread->regs_restore_all)
+ if (current_thread->debugging_enabled)
{
+ struct dr7
+ {
+ union
+ {
+ struct
+ {
+ uint64_t l0: 1;
+ uint64_t g0: 1;
+ uint64_t l1: 1;
+ uint64_t g1: 1;
+ uint64_t l2: 1;
+ uint64_t g2: 1;
+ uint64_t l3: 1;
+ uint64_t g3: 1;
+
+ uint64_t le: 1;
+ uint64_t ge: 1;
+
+ uint64_t rao: 1;
+ uint64_t raz1: 2;
+
+
+ uint64_t general_detect: 1;
+
+ uint64_t raz2: 2;
+
+ uint64_t rw0 : 2;
+ uint64_t len0 : 2;
+ uint64_t rw1 : 2;
+ uint64_t len1 : 2;
+ uint64_t rw2 : 2;
+ uint64_t len2 : 2;
+ uint64_t rw3 : 2;
+ uint64_t len3 : 2;
+
+ uint64_t mbz : 32;
+ };
+ uint64_t raw;
+ };
+ };
+ build_assert (sizeof (struct dr7) == sizeof (uint64_t));
+
+ struct dr7 dr7 = { { .raw = 0 } };
+
#define S_(x) #x
#define S(x) S_(x)
+#define CONCAT(a, b) a##b
+#define C(n) \
+ if (current_thread->breakpoints[n]) \
+ { \
+ dr7.CONCAT(g, n) = 1; \
+ dr7.CONCAT(len, n) = 0; \
+ dr7.CONCAT(rw, n) = 0; \
+ asm ("mov %0, %%dr"S(n) \
+ : : "r"(current_thread->breakpoints[n])); \
+ }
+
+ C(0);
+ C(1);
+ C(2);
+ C(3);
+
+ if (dr7.raw)
+ {
+ asm ("mov %0, %%dr7" : : "r" (dr7.raw));
+
+ uintptr_t dr0, dr1, dr2, dr3, dr7;
+ asm ("mov %%dr0, %[dr0]\n\t"
+ "mov %%dr1, %[dr1]\n\t"
+ "mov %%dr2, %[dr2]\n\t"
+ "mov %%dr3, %[dr3]\n\t"
+ "mov %%dr7, %[dr7]\n\t"
+ : [dr0] "=r" (dr0), [dr1] "=r" (dr1),
+ [dr2] "=r" (dr2), [dr3] "=r" (dr3),
+ [dr7] "=r" (dr7));
+ }
+ else
+ current_thread->debugging_enabled = false;
+ }
+
+ if (! current_thread->debugging_enabled)
+ {
+ uintptr_t disable = 0;
+ asm ("mov %0, %%dr7" : : "r" (disable));
+ }
+
+ if (current_thread->regs_restore_all)
+ {
+ debug (5, "Return using iret");
+
asm volatile (/* Set up a interrupt context frame:
SS <- high
@@ -96,13 +293,10 @@ thread_resume (void)
/* SS. */
"pushq $"S(SEGSEL_STACK_USER)"\n\t"
/* RIP. */
- "mov ("S(RSP)"*8)(%%rdi), %%rbx\n\t"
- "pushq %%rbx\n\t"
- "mov ("S(RFLAGS)"*8)(%%rdi), %%rbx\n\t"
- "pushq %%rbx\n\t"
+ "pushq ("S(RSP)"*8)(%%rdi)\n\t"
+ "pushq ("S(RFLAGS)"*8)(%%rdi)\n\t"
"pushq $"S(SEGSEL_CODE_USER)"\n\t"
- "mov ("S(RIP)"*8)(%%rdi), %%rbx\n\t"
- "pushq %%rbx\n\t"
+ "pushq ("S(RIP)"*8)(%%rdi)\n\t"
/* Restore the rest of the registers. */
"mov ("S(RAX)"*8)(%%rdi), %%rax\n\t"
@@ -129,7 +323,15 @@ thread_resume (void)
}
else
{
+ debug (5, "Return using sysret");
+
asm volatile ("mov %[rflags], %%r11\n\t"
+ /* We don't want to take an interrupt while on the
+ user's stack. If we take an interrupt after
+ restoring the user's sp but before executing
+ sysretq, the interrupt handler won't switch
+ back to the kernel stack. Damn! */
+ "cli\n\t"
"mov %[rsp], %%rsp\n\t"
"sysretq\n\t"
:
diff --git a/viengoos/thread.c b/viengoos/thread.c
index 80e7d7a..04f190f 100644
--- a/viengoos/thread.c
+++ b/viengoos/thread.c
@@ -106,6 +106,8 @@ void
thread_bootstrap (void)
{
debug_register (&debug_command);
+
+ thread_arch_bootstrap ();
}
void
@@ -273,8 +275,10 @@ thread_activate (struct activity *activity,
return true;
}
- debug (5, "Activating "OBJECT_NAME_FMT" (ip: %"PRIxPTR"; sp: %"PRIxPTR")",
- OBJECT_NAME_PRINTF ((struct vg_object *) thread), ip, sp);
+ debug (5, "Activating "OBJECT_NAME_FMT" (ip: %"PRIxPTR"; sp: %"PRIxPTR")"
+ REGS_FMT,
+ OBJECT_NAME_PRINTF ((struct vg_object *) thread), ip, sp,
+ REGS_PRINTF (thread));
utcb->protected_payload = messenger->protected_payload;
utcb->messenger_id = messenger->id;
diff --git a/viengoos/thread.h b/viengoos/thread.h
index ca73798..ea6b49e 100644
--- a/viengoos/thread.h
+++ b/viengoos/thread.h
@@ -40,6 +40,22 @@ enum thread_state
THREAD_RUNNING = 2,
};
+static inline const char *
+thread_state_string (enum thread_state state)
+{
+ switch (state)
+ {
+ case THREAD_SUSPENDED:
+ return "suspended";
+ case THREAD_RUNNABLE:
+ return "runnable";
+ case THREAD_RUNNING:
+ return "running";
+ default:
+ panic ("Unknown thread state!");
+ }
+}
+
struct thread
{
/* User accessible fields. */
@@ -150,6 +166,8 @@ extern void thread_deliver_pending (struct activity *activity,
/* Given the thread id THREADID, find the associated thread. */
extern struct thread *thread_lookup (vg_thread_id_t threadid);
+extern void thread_arch_bootstrap (void);
+
/* Initialize the architecture dependent parts of THREAD, a new thread
object. */
extern void thread_arch_init (struct thread *thread);
diff --git a/viengoos/zalloc.h b/viengoos/zalloc.h
index 697428e..be52bcc 100644
--- a/viengoos/zalloc.h
+++ b/viengoos/zalloc.h
@@ -48,6 +48,10 @@ zalloc (uintptr_t size)
/* Dump some internal data structures. Only defined if zalloc was
compiled without NDEBUG defined. */
+#ifndef NDEBUG
void zalloc_dump_zones (const char *prefix);
+#else
+#define zalloc_dump_zones(prefix) do {} while (0)
+#endif
#endif /* __ZALLOC_H__ */