diff options
author | Neal H. Walfield <neal@gnu.org> | 2009-02-16 02:07:30 +0100 |
---|---|---|
committer | Neal H. Walfield <neal@gnu.org> | 2009-02-16 02:07:30 +0100 |
commit | 47e21c7acbdd593dac1482a5238315355c853e38 (patch) | |
tree | f9d95280e89334c1107b25015ba154989cd1007c /viengoos | |
parent | 39e1f97996d9f5d7a1f11d0bcc53f98b37e7ffa7 (diff) |
Rewrite CPU scheduler. Fix a few bugs. Add a lot of debugging support.
Diffstat (limited to 'viengoos')
-rw-r--r-- | viengoos/object.c | 146 | ||||
-rw-r--r-- | viengoos/scheduler.c | 151 | ||||
-rw-r--r-- | viengoos/server.c | 8 | ||||
-rw-r--r-- | viengoos/sysdeps/x86_64/idt.c | 89 | ||||
-rw-r--r-- | viengoos/sysdeps/x86_64/sys.h | 63 | ||||
-rw-r--r-- | viengoos/sysdeps/x86_64/thread-arch.c | 216 | ||||
-rw-r--r-- | viengoos/thread.c | 8 | ||||
-rw-r--r-- | viengoos/thread.h | 18 | ||||
-rw-r--r-- | viengoos/zalloc.h | 4 |
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__ */ |