From 6c678c57c736f4688c9bab047bed3fe00fd27a13 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 10 Jan 2006 03:00:33 +0000 Subject: [PATCH] * The exception vector offset is now also part of the iframe. * Cloned iframe stack management from x86. * Reimplemented arch_thread_{get,set}_current_thread(). The thread structure is stored in SPRG2. It is set to NULL in arch_cpu_preboot_init(), now. A non-null current thread causes all kinds of undesired behavior in early boot code. * We establish the address space mappings we know from the Open Firmware as areas. At least those in kernel address space. The ones in userland address space are tougher. Fortunately on my Mac mini there aren't any save the boot_loader stack, which is not needed any longer anyway. * Added stack trace support to the kernel debugger. Mostly cloned and adjusted the x86 code. Some bits are still missing, like stack traces for other threads. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15890 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/arch/ppc/arch_cpu.h | 1 + headers/private/kernel/arch/ppc/arch_thread.h | 42 ++- .../kernel/arch/ppc/arch_thread_types.h | 23 +- src/system/kernel/arch/ppc/arch_cpu.cpp | 5 + src/system/kernel/arch/ppc/arch_debug.cpp | 261 +++++++++++++++++- src/system/kernel/arch/ppc/arch_exceptions.S | 4 +- src/system/kernel/arch/ppc/arch_int.cpp | 31 ++- src/system/kernel/arch/ppc/arch_thread.c | 58 ++-- src/system/kernel/arch/ppc/arch_vm.cpp | 39 ++- .../arch/ppc/arch_vm_translation_map.cpp | 2 +- 10 files changed, 424 insertions(+), 42 deletions(-) diff --git a/headers/private/kernel/arch/ppc/arch_cpu.h b/headers/private/kernel/arch/ppc/arch_cpu.h index 17865443ed..15bbe330f7 100644 --- a/headers/private/kernel/arch/ppc/arch_cpu.h +++ b/headers/private/kernel/arch/ppc/arch_cpu.h @@ -13,6 +13,7 @@ #define PAGE_SIZE 4096 struct iframe { + uint32 vector; uint32 srr0; uint32 srr1; uint32 dar; diff --git a/headers/private/kernel/arch/ppc/arch_thread.h b/headers/private/kernel/arch/ppc/arch_thread.h index 96805569db..b9e1cbdacb 100644 --- a/headers/private/kernel/arch/ppc/arch_thread.h +++ b/headers/private/kernel/arch/ppc/arch_thread.h @@ -1,8 +1,44 @@ /* -** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the OpenBeOS License. -*/ + * Copyright 2003-2006, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler + * Ingo Weinhold + */ #ifndef _KERNEL_ARCH_PPC_THREAD_H #define _KERNEL_ARCH_PPC_THREAD_H +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void ppc_push_iframe(struct iframe_stack *stack, struct iframe *frame); +void ppc_pop_iframe(struct iframe_stack *stack); +struct iframe *ppc_get_user_iframe(void); + + +extern inline struct thread * +arch_thread_get_current_thread(void) +{ + struct thread *t; + asm volatile("mfsprg2 %0" : "=r"(t)); + return t; +} + + +extern inline void +arch_thread_set_current_thread(struct thread *t) +{ + asm volatile("mtsprg2 %0" : : "r"(t)); +} + + +#ifdef __cplusplus +} +#endif + + #endif /* _KERNEL_ARCH_PPC_THREAD_H */ diff --git a/headers/private/kernel/arch/ppc/arch_thread_types.h b/headers/private/kernel/arch/ppc/arch_thread_types.h index 96fb0bda74..17c3aa851b 100644 --- a/headers/private/kernel/arch/ppc/arch_thread_types.h +++ b/headers/private/kernel/arch/ppc/arch_thread_types.h @@ -5,17 +5,34 @@ #ifndef KERNEL_ARCH_PPC_THREAD_TYPES_H #define KERNEL_ARCH_PPC_THREAD_TYPES_H +#define IFRAME_TRACE_DEPTH 4 + +struct iframe_stack { + struct iframe *frames[IFRAME_TRACE_DEPTH]; + int32 index; +}; + // architecture specific thread info struct arch_thread { - void *sp; // stack pointer + void *sp; // stack pointer + void *interrupt_stack; + + // used to track interrupts on this thread + struct iframe_stack iframes; }; struct arch_team { - // nothing here + // gcc treats empty structures as zero-length in C, but as if they contain + // a char in C++. So we have to put a dummy in to be able to use the struct + // from both in a consistent way. + char dummy; }; struct arch_fork_arg { - // nothing here yet + // gcc treats empty structures as zero-length in C, but as if they contain + // a char in C++. So we have to put a dummy in to be able to use the struct + // from both in a consistent way. + char dummy; }; #endif /* KERNEL_ARCH_PPC_THREAD_TYPES_H */ diff --git a/src/system/kernel/arch/ppc/arch_cpu.cpp b/src/system/kernel/arch/ppc/arch_cpu.cpp index 2adf2fb165..29ea49a677 100644 --- a/src/system/kernel/arch/ppc/arch_cpu.cpp +++ b/src/system/kernel/arch/ppc/arch_cpu.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -17,6 +18,10 @@ status_t arch_cpu_preboot_init(kernel_args *args) { + // The current thread must be NULL for all CPUs till we have threads. + // Some boot code relies on this. + arch_thread_set_current_thread(NULL); + return B_OK; } diff --git a/src/system/kernel/arch/ppc/arch_debug.cpp b/src/system/kernel/arch/ppc/arch_debug.cpp index 8f81866b28..02e50193d2 100644 --- a/src/system/kernel/arch/ppc/arch_debug.cpp +++ b/src/system/kernel/arch/ppc/arch_debug.cpp @@ -1,15 +1,260 @@ /* - * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2003-2005, Axel D�fler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ -#include -#include #include +#include +#include +#include +#include +#include -// ToDo: put stack trace and disassembly routines here +struct stack_frame { + struct stack_frame *previous; + addr_t return_address; +}; + +#define NUM_PREVIOUS_LOCATIONS 32 + +extern struct iframe_stack gBootFrameStack; + + +static bool +already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 framePointer) +{ + int32 last = *_last; + int32 num = *_num; + int32 i; + + for (i = 0; i < num; i++) { + if (visited[(NUM_PREVIOUS_LOCATIONS + last - i) + % NUM_PREVIOUS_LOCATIONS] == framePointer) { + return true; + } + } + + *_last = last = (last + 1) % NUM_PREVIOUS_LOCATIONS; + visited[last] = framePointer; + + if (num < NUM_PREVIOUS_LOCATIONS) + *_num = num + 1; + + return false; +} + + +static inline stack_frame * +get_current_stack_frame() +{ + stack_frame *frame; + asm volatile("mr %0, %%r1" : "=r"(frame)); + return frame; +} + + +static status_t +get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip) +{ + struct thread *thread = thread_get_current_thread(); + + // set fault handler, so that we can safely access user stacks + if (thread) + thread->fault_handler = (addr_t)&&error; + + *ip = ((struct stack_frame *)framePointer)->return_address; + *next = (addr_t)((struct stack_frame *)framePointer)->previous; + + if (thread) + thread->fault_handler = NULL; + return B_OK; + +error: + thread->fault_handler = NULL; + return B_BAD_ADDRESS; +} + + +static void +print_stack_frame(struct thread *thread, addr_t ip, addr_t framePointer, + addr_t nextFramePointer) +{ + addr_t diff = nextFramePointer - framePointer; + + // kernel space/user space switch + if (diff & 0x80000000) + diff = 0; + + // lookup symbol + const char *symbol, *image; + addr_t baseAddress; + bool exactMatch; + status_t status = elf_debug_lookup_symbol_address(ip, &baseAddress, &symbol, + &image, &exactMatch); + if (status != B_OK && !IS_KERNEL_ADDRESS(ip) && thread) { + // try to locate the image in the images loaded into user space + status = image_debug_lookup_user_symbol_address(thread->team, ip, + &baseAddress, &symbol, &image, &exactMatch); + } + if (status == B_OK) { + if (symbol != NULL) { + kprintf("%08lx (+%4ld) %08lx <%s>:%s + 0x%04lx%s\n", framePointer, + diff, ip, image, symbol, ip - baseAddress, + (exactMatch ? "" : " (nearest)")); + } else { + kprintf("%08lx (+%4ld) %08lx <%s@%p>:unknown + 0x%04lx\n", + framePointer, diff, ip, image, (void *)baseAddress, + ip - baseAddress); + } + } else + kprintf("%08lx (+%4ld) %08lx\n", framePointer, diff, ip); +} + + +static int +stack_trace(int argc, char **argv) +{ + uint32 previousLocations[NUM_PREVIOUS_LOCATIONS]; + struct iframe_stack *frameStack; + struct thread *thread; + addr_t framePointer; + int32 i, num = 0, last = 0; + + if (argc < 2) { + thread = thread_get_current_thread(); + framePointer = (addr_t)get_current_stack_frame(); + } else { +// TODO: Add support for stack traces of other threads. +/* thread_id id = strtoul(argv[1], NULL, 0); + thread = thread_get_thread_struct_locked(id); + if (thread == NULL) { + kprintf("could not find thread %ld\n", id); + return 0; + } + + // read %ebp from the thread's stack stored by a pushad + ebp = thread->arch_info.current_stack.esp[2]; + + if (id != thread_get_current_thread_id()) { + // switch to the page directory of the new thread to be + // able to follow the stack trace into userland + addr_t newPageDirectory = (addr_t)x86_next_page_directory( + thread_get_current_thread(), thread); + + if (newPageDirectory != 0) { + read_cr3(oldPageDirectory); + write_cr3(newPageDirectory); + } + } +*/ +kprintf("Stack traces of other threads not supported yet!\n"); +return 0; + } + + // We don't have a thread pointer early in the boot process + if (thread != NULL) + frameStack = &thread->arch_info.iframes; + else + frameStack = &gBootFrameStack; + + for (i = 0; i < frameStack->index; i++) { + kprintf("iframe %p (end = %p)\n", + frameStack->frames[i], frameStack->frames[i] + 1); + } + + if (thread != NULL) { + kprintf("stack trace for thread 0x%lx \"%s\"\n", thread->id, + thread->name); + + kprintf(" kernel stack: %p to %p\n", + (void *)thread->kernel_stack_base, + (void *)(thread->kernel_stack_base + KERNEL_STACK_SIZE)); + if (thread->user_stack_base != 0) { + kprintf(" user stack: %p to %p\n", + (void *)thread->user_stack_base, + (void *)(thread->user_stack_base + thread->user_stack_size)); + } + } + + kprintf("frame caller :function + offset\n"); + + for (;;) { + // see if the frame pointer matches the iframe + struct iframe *frame = NULL; + for (i = 0; i < frameStack->index; i++) { + if (framePointer == (((addr_t)frameStack->frames[i] - 8) & ~0xf)) { + // it's an iframe + frame = frameStack->frames[i]; + break; + } + } + + if (frame) { + kprintf("iframe at %p\n", frame); + kprintf(" r0 0x%08lx r1 0x%08lx r2 0x%08lx r3 0x%08lx\n", + frame->r0, frame->r1, frame->r2, frame->r3); + kprintf(" r4 0x%08lx r5 0x%08lx r6 0x%08lx r7 0x%08lx\n", + frame->r4, frame->r5, frame->r6, frame->r7); + kprintf(" r8 0x%08lx r9 0x%08lx r10 0x%08lx r11 0x%08lx\n", + frame->r8, frame->r9, frame->r10, frame->r11); + kprintf(" r12 0x%08lx r13 0x%08lx r14 0x%08lx r15 0x%08lx\n", + frame->r12, frame->r13, frame->r14, frame->r15); + kprintf(" r16 0x%08lx r17 0x%08lx r18 0x%08lx r19 0x%08lx\n", + frame->r16, frame->r17, frame->r18, frame->r19); + kprintf(" r20 0x%08lx r21 0x%08lx r22 0x%08lx r23 0x%08lx\n", + frame->r20, frame->r21, frame->r22, frame->r23); + kprintf(" r24 0x%08lx r25 0x%08lx r26 0x%08lx r27 0x%08lx\n", + frame->r24, frame->r25, frame->r26, frame->r27); + kprintf(" r28 0x%08lx r29 0x%08lx r30 0x%08lx r31 0x%08lx\n", + frame->r28, frame->r29, frame->r30, frame->r31); + kprintf(" srr0 0x%08lx srr1 0x%08lx", frame->srr0, frame->srr1); +// TODO: Look up the bit in srr1! +// if ((frame->error_code & 0x4) != 0) { +// // from user space +// kprintf("user esp 0x%lx", frame->user_esp); +// } + kprintf("\n"); + kprintf(" vector: 0x%lx\n", frame->vector); + + print_stack_frame(thread, frame->srr0, framePointer, frame->r1); + framePointer = frame->r1; + } else { + addr_t ip, nextFramePointer; + + if (get_next_frame(framePointer, &nextFramePointer, &ip) != B_OK) { + kprintf("%08lx -- read fault\n", framePointer); + break; + } + + if (ip == 0 || framePointer == 0) + break; + + print_stack_frame(thread, ip, framePointer, nextFramePointer); + framePointer = nextFramePointer; + } + + if (already_visited(previousLocations, &last, &num, framePointer)) { + kprintf("circular stack frame: %p!\n", (void *)framePointer); + break; + } + if (framePointer == 0) + break; + } + +/* if (oldPageDirectory != 0) { + // switch back to the previous page directory to no cause any troubles + write_cr3(oldPageDirectory); + } +*/ + + return 0; +} + + + +// #pragma mark - void @@ -21,7 +266,7 @@ arch_debug_save_registers(int *regs) void * arch_debug_get_caller(void) { - // TODO: imeplement me + // TODO: implement me return (void *)&arch_debug_get_caller; } @@ -29,7 +274,11 @@ arch_debug_get_caller(void) status_t arch_debug_init(kernel_args *args) { - return B_OK; + add_debugger_command("where", &stack_trace, "Same as \"sc\""); + add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)"); + add_debugger_command("sc", &stack_trace, "Stack crawl for current thread"); + + return B_NO_ERROR; } diff --git a/src/system/kernel/arch/ppc/arch_exceptions.S b/src/system/kernel/arch/ppc/arch_exceptions.S index f4bf327469..c73770db2b 100644 --- a/src/system/kernel/arch/ppc/arch_exceptions.S +++ b/src/system/kernel/arch/ppc/arch_exceptions.S @@ -288,6 +288,8 @@ __save_regs: mfspr %r0, %srr0 stwu %r0, -4(%r1) /* push SRR0 */ + stwu %r3, -4(%r1) /* exception vector offset */ + blr @@ -295,7 +297,7 @@ __save_regs: * r1: iframe pointer */ __restore_regs_and_rfi: - lwz %r0, 0(%r1) /* SRR0 */ + lwzu %r0, 4(%r1) /* SRR0 (skip vector offset) */ mtspr %srr0, %r0 lwzu %r0, 4(%r1) /* SRR1 */ mtspr %srr1, %r0 diff --git a/src/system/kernel/arch/ppc/arch_int.cpp b/src/system/kernel/arch/ppc/arch_int.cpp index ad14367fc9..54ab9b23c6 100644 --- a/src/system/kernel/arch/ppc/arch_int.cpp +++ b/src/system/kernel/arch/ppc/arch_int.cpp @@ -36,6 +36,11 @@ extern"C" void ppc_exception_tail(void); static ppc_cpu_exception_context sCPUExceptionContexts[SMP_MAX_CPUS]; +// An iframe stack used in the early boot process when we don't have +// threads yet. +struct iframe_stack gBootFrameStack; + + void arch_int_enable_io_interrupt(int irq) { @@ -72,20 +77,24 @@ print_iframe(struct iframe *frame) } -extern "C" void exception_tail_test(); -void -exception_tail_test() -{ -} - extern "C" void ppc_exception_entry(int vector, struct iframe *iframe); void ppc_exception_entry(int vector, struct iframe *iframe) { int ret = B_HANDLED_INTERRUPT; - if (vector != 0x900) - dprintf("ppc_exception_entry: time %Ld vector 0x%x, iframe %p\n", system_time(), vector, iframe); + if (vector != 0x900) { + dprintf("ppc_exception_entry: time %lld vector 0x%x, iframe %p, " + "srr0: %p\n", system_time(), vector, iframe, (void*)iframe->srr0); + } + + struct thread *thread = thread_get_current_thread(); + + // push iframe + if (thread) + ppc_push_iframe(&thread->arch_info.iframes, iframe); + else + ppc_push_iframe(&gBootFrameStack, iframe); switch (vector) { case 0x100: // system reset @@ -170,6 +179,12 @@ ppc_exception_entry(int vector, struct iframe *iframe) RELEASE_THREAD_LOCK(); restore_interrupts(state); } + + // pop iframe + if (thread) + ppc_pop_iframe(&thread->arch_info.iframes); + else + ppc_pop_iframe(&gBootFrameStack); } diff --git a/src/system/kernel/arch/ppc/arch_thread.c b/src/system/kernel/arch/ppc/arch_thread.c index 4d62145c35..8354cf10bf 100644 --- a/src/system/kernel/arch/ppc/arch_thread.c +++ b/src/system/kernel/arch/ppc/arch_thread.c @@ -1,11 +1,16 @@ /* - * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2003-2006, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * + * Authors: + * Axel Dörfler + * Ingo Weinhold + * * Copyright 2001, Travis Geiselbrecht. All rights reserved. * Distributed under the terms of the NewOS License. */ +#include #include #include @@ -16,6 +21,40 @@ #include +void +ppc_push_iframe(struct iframe_stack *stack, struct iframe *frame) +{ + ASSERT(stack->index < IFRAME_TRACE_DEPTH); + stack->frames[stack->index++] = frame; +} + + +void +ppc_pop_iframe(struct iframe_stack *stack) +{ + ASSERT(stack->index > 0); + stack->index--; +} + + +/** Returns the current iframe structure of the running thread. + * This function must only be called in a context where it's actually + * sure that such iframe exists; ie. from syscalls, but usually not + * from standard kernel threads. + */ +static struct iframe * +ppc_get_current_iframe(void) +{ + struct thread *thread = thread_get_current_thread(); + + ASSERT(thread->arch_info.iframes.index >= 0); + return thread->arch_info.iframes.frames[thread->arch_info.iframes.index - 1]; +} + + +// #pragma mark - + + status_t arch_thread_init(struct kernel_args *args) { @@ -61,23 +100,6 @@ arch_thread_init_kthread_stack(struct thread *t, int (*start_func)(void), void ( } -// ToDo: we are single proc for now -static struct thread *current_thread = NULL; - -struct thread * -arch_thread_get_current_thread(void) -{ - return current_thread; -} - - -void -arch_thread_set_current_thread(struct thread *t) -{ - current_thread = t; -} - - void arch_thread_init_tls(struct thread *thread) { diff --git a/src/system/kernel/arch/ppc/arch_vm.cpp b/src/system/kernel/arch/ppc/arch_vm.cpp index e7664a45a7..14f48cc7ac 100644 --- a/src/system/kernel/arch/ppc/arch_vm.cpp +++ b/src/system/kernel/arch/ppc/arch_vm.cpp @@ -6,6 +6,7 @@ * Distributed under the terms of the NewOS License. */ +#include #include #include @@ -14,6 +15,14 @@ #include +//#define TRACE_ARCH_VM +#ifdef TRACE_ARCH_VM +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + + status_t arch_vm_init(kernel_args *args) { @@ -93,8 +102,34 @@ arch_vm_init_post_area(kernel_args *args) status_t arch_vm_init_end(kernel_args *args) { - // throw away anything in the kernel_args.pgtable[] that's not yet mapped - //vm_free_unused_boot_loader_range(KERNEL_BASE, 0x400000 * args->arch_args.num_pgtables); + TRACE(("arch_vm_init_end(): %lu virtual ranges to keep:\n", + args->arch_args.num_virtual_ranges_to_keep)); + + for (int i = 0; i < (int)args->arch_args.num_virtual_ranges_to_keep; i++) { + addr_range &range = args->arch_args.virtual_ranges_to_keep[i]; + + TRACE((" start: %p, size: 0x%lx\n", (void*)range.start, range.size)); + + // skip ranges outside the kernel address space + if (!IS_KERNEL_ADDRESS(range.start)) { + TRACE((" no kernel address, skipping...\n")); + continue; + } + + void *address = (void*)range.start; + area_id area = create_area("boot loader reserved area", &address, + B_EXACT_ADDRESS, range.size, B_ALREADY_WIRED, + B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); + if (area < 0) { + panic("arch_vm_init_end(): Failed to create area for boot loader " + "reserved area: %p - %p\n", (void*)range.start, + (void*)(range.start + range.size)); + } + } + + // Throw away any address space mappings we've inherited from the boot + // loader and have not yet turned into an area. + vm_free_unused_boot_loader_range(0, 0xffffffff - B_PAGE_SIZE + 1); return B_OK; } diff --git a/src/system/kernel/arch/ppc/arch_vm_translation_map.cpp b/src/system/kernel/arch/ppc/arch_vm_translation_map.cpp index 351305435b..729aef0484 100644 --- a/src/system/kernel/arch/ppc/arch_vm_translation_map.cpp +++ b/src/system/kernel/arch/ppc/arch_vm_translation_map.cpp @@ -323,7 +323,7 @@ unmap_tmap(vm_translation_map *map, addr_t start, addr_t end) start = ROUNDOWN(start, B_PAGE_SIZE); end = ROUNDUP(end, B_PAGE_SIZE); - dprintf("vm_translation_map.unmap_tmap: start 0x%lx, end 0x%lx\n", start, end); +// dprintf("vm_translation_map.unmap_tmap: start 0x%lx, end 0x%lx\n", start, end); while (start < end) { if (remove_page_table_entry(map, start))