* 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
This commit is contained in:
Ingo Weinhold
2006-01-10 03:00:33 +00:00
parent 1777154c14
commit 6c678c57c7
10 changed files with 424 additions and 42 deletions
@@ -13,6 +13,7 @@
#define PAGE_SIZE 4096
struct iframe {
uint32 vector;
uint32 srr0;
uint32 srr1;
uint32 dar;
+39 -3
View File
@@ -1,8 +1,44 @@
/*
** Copyright 2003, Axel Dörfler, [email protected]. 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 <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#ifndef _KERNEL_ARCH_PPC_THREAD_H
#define _KERNEL_ARCH_PPC_THREAD_H
#include <arch/cpu.h>
#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 */
@@ -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 */
+5
View File
@@ -10,6 +10,7 @@
#include <KernelExport.h>
#include <arch_platform.h>
#include <arch_thread.h>
#include <arch/cpu.h>
#include <boot/kernel_args.h>
@@ -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;
}
+255 -6
View File
@@ -1,15 +1,260 @@
/*
* Copyright 2003-2005, Axel Dörfler, [email protected]. All rights reserved.
* Copyright 2003-2005, Axel Dfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <kernel.h>
#include <debug.h>
#include <arch/debug.h>
#include <debug.h>
#include <elf.h>
#include <kernel.h>
#include <kimage.h>
#include <thread.h>
// 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 <image>: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;
}
+3 -1
View File
@@ -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
+23 -8
View File
@@ -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);
}
+40 -18
View File
@@ -1,11 +1,16 @@
/*
* Copyright 2003-2005, Axel Dörfler, [email protected].
* Copyright 2003-2006, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler <[email protected]>
* Ingo Weinhold <[email protected]>
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <arch_thread.h>
#include <kernel.h>
#include <thread.h>
@@ -16,6 +21,40 @@
#include <string.h>
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)
{
+37 -2
View File
@@ -6,6 +6,7 @@
* Distributed under the terms of the NewOS License.
*/
#include <KernelExport.h>
#include <kernel.h>
#include <boot/kernel_args.h>
@@ -14,6 +15,14 @@
#include <arch_mmu.h>
//#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;
}
@@ -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))