More work towards making our double fault handler less triple fault prone:

* SMP:
  - Added smp_send_broadcast_ici_interrupts_disabled(), which is basically
    equivalent to smp_send_broadcast_ici(), but is only called with interrupts
    disabled and gets the CPU index, so it doesn't have to use
    smp_get_current_cpu() (which dereferences the current thread).
  - Added cpu index parameter to smp_intercpu_int_handler().
* x86:
  - arch_int.c -> arch_int.cpp
  - Set up an IDT per CPU. We were using a single IDT for all CPUs, but that
    can't work, since we need different tasks for the double fault interrupt
    vector.
  - Set the per CPU double fault task gates correctly.
  - Renamed set_intr_gate() to set_interrupt_gate and set_system_gate() to
    set_trap_gate() and documented them a bit.
  - Renamed double_fault_exception() x86_double_fault_exception() and fixed
    it not to use smp_get_current_cpu(). Instead we have the new
    x86_double_fault_get_cpu() that deducts the CPU index from the used stack.
  - Fixed the double_fault interrupt handler: It no longer calls int_bottom to
    avoid accessing the current thread.
* debug.cpp:
  - Introduced explicit debug_double_fault() to enter the kernel debugger from
    a double fault handler.
  - Avoid using smp_get_current_cpu().
  - Don't use kprintf() before sDebuggerOnCPU is set. Otherwise
    acquire_spinlock() is invoked by arch_debug_serial_puts().

Things look a bit better when the current thread pointer is broken -- we run
into kernel_debugger_loop() and successfully print the "Welcome to KDL"
message -- but we still dereference the thread pointer afterwards, so that we
don't get a usable kernel debugger yet.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32050 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-08-01 01:53:54 +00:00
parent c9d653c1de
commit 671a2442d9
10 changed files with 364 additions and 135 deletions
+4 -2
View File
@@ -275,13 +275,15 @@ uint32 x86_read_cr4();
void x86_write_cr4(uint32 value); void x86_write_cr4(uint32 value);
uint64 x86_read_msr(uint32 registerNumber); uint64 x86_read_msr(uint32 registerNumber);
void x86_write_msr(uint32 registerNumber, uint64 value); void x86_write_msr(uint32 registerNumber, uint64 value);
void x86_set_task_gate(int32 n, int32 segment); void x86_set_task_gate(int32 cpu, int32 n, int32 segment);
void* x86_get_idt(int32 cpu);
uint32 x86_count_mtrrs(void); uint32 x86_count_mtrrs(void);
void x86_set_mtrr(uint32 index, uint64 base, uint64 length, uint8 type); void x86_set_mtrr(uint32 index, uint64 base, uint64 length, uint8 type);
status_t x86_get_mtrr(uint32 index, uint64 *_base, uint64 *_length, uint8 *_type); status_t x86_get_mtrr(uint32 index, uint64 *_base, uint64 *_length, uint8 *_type);
bool x86_check_feature(uint32 feature, enum x86_feature_type type); bool x86_check_feature(uint32 feature, enum x86_feature_type type);
void* x86_get_double_fault_stack(int32 cpu, size_t* _size); void* x86_get_double_fault_stack(int32 cpu, size_t* _size);
int x86_double_fault_get_cpu(); int32 x86_double_fault_get_cpu(void);
void x86_double_fault_exception(struct iframe* frame);
#define read_cr3(value) \ #define read_cr3(value) \
+2 -1
View File
@@ -120,7 +120,8 @@ extern void debug_stop_screen_debug_output(void);
extern void debug_set_page_fault_info(addr_t faultAddress, addr_t pc, extern void debug_set_page_fault_info(addr_t faultAddress, addr_t pc,
uint32 flags); uint32 flags);
extern debug_page_fault_info* debug_get_page_fault_info(); extern debug_page_fault_info* debug_get_page_fault_info();
extern void debug_trap_cpu_in_kdl(bool returnIfHandedOver); extern void debug_trap_cpu_in_kdl(int32 cpu, bool returnIfHandedOver);
extern void debug_double_fault(int32 cpu);
extern bool debug_emergency_key_pressed(char key); extern bool debug_emergency_key_pressed(char key);
extern char kgetc(void); extern char kgetc(void);
+3 -1
View File
@@ -52,12 +52,14 @@ void smp_send_multicast_ici(cpu_mask_t cpuMask, int32 message, uint32 data,
uint32 data2, uint32 data3, void *data_ptr, uint32 flags); uint32 data2, uint32 data3, void *data_ptr, uint32 flags);
void smp_send_broadcast_ici(int32 message, uint32 data, uint32 data2, uint32 data3, void smp_send_broadcast_ici(int32 message, uint32 data, uint32 data2, uint32 data3,
void *data_ptr, uint32 flags); void *data_ptr, uint32 flags);
void smp_send_broadcast_ici_interrupts_disabled(int32 currentCPU, int32 message,
uint32 data, uint32 data2, uint32 data3, void *data_ptr, uint32 flags);
int32 smp_get_num_cpus(void); int32 smp_get_num_cpus(void);
void smp_set_num_cpus(int32 numCPUs); void smp_set_num_cpus(int32 numCPUs);
int32 smp_get_current_cpu(void); int32 smp_get_current_cpu(void);
int smp_intercpu_int_handler(void); int smp_intercpu_int_handler(int32 cpu);
#ifdef __cplusplus #ifdef __cplusplus
} }
+1 -1
View File
@@ -16,7 +16,7 @@ KernelMergeObject kernel_arch_x86.o :
arch_debug.cpp arch_debug.cpp
arch_debug_console.c arch_debug_console.c
arch_elf.c arch_elf.c
arch_int.c arch_int.cpp
arch_platform.c arch_platform.c
# arch_selector.c # arch_selector.c
arch_real_time_clock.c arch_real_time_clock.c
+19 -6
View File
@@ -269,8 +269,11 @@ init_double_fault(int cpuNum)
tss->io_map_base = sizeof(struct tss); tss->io_map_base = sizeof(struct tss);
// add TSS descriptor for this new TSS // add TSS descriptor for this new TSS
set_tss_descriptor(&gGDT[DOUBLE_FAULT_TSS_BASE_SEGMENT + cpuNum], uint16 tssSegmentDescriptorIndex = DOUBLE_FAULT_TSS_BASE_SEGMENT + cpuNum;
set_tss_descriptor(&gGDT[tssSegmentDescriptorIndex],
(addr_t)tss, sizeof(struct tss)); (addr_t)tss, sizeof(struct tss));
x86_set_task_gate(cpuNum, 8, tssSegmentDescriptorIndex << 3);
} }
@@ -511,8 +514,8 @@ x86_get_double_fault_stack(int32 cpu, size_t* _size)
/*! Returns the index of the current CPU. Can only be called from the double /*! Returns the index of the current CPU. Can only be called from the double
fault handler. fault handler.
*/ */
int int32
x86_double_fault_get_cpu() x86_double_fault_get_cpu(void)
{ {
uint32 stack = x86_read_ebp(); uint32 stack = x86_read_ebp();
return (stack - (uint32)sDoubleFaultStacks) / kDoubleFaultStackSize; return (stack - (uint32)sDoubleFaultStacks) / kDoubleFaultStackSize;
@@ -561,9 +564,21 @@ arch_cpu_init_percpu(kernel_args *args, int cpu)
// load the TSS for this cpu // load the TSS for this cpu
// note the main cpu gets initialized in arch_cpu_init_post_vm() // note the main cpu gets initialized in arch_cpu_init_post_vm()
if (cpu != 0) if (cpu != 0) {
load_tss(cpu); load_tss(cpu);
// set the IDT
struct {
uint16 limit;
void* address;
} _PACKED descriptor = {
256 * 8 - 1, // 256 descriptors, 8 bytes each (-1 for "limit")
x86_get_idt(cpu)
};
asm volatile("lidt %0" : : "m"(descriptor));
}
return 0; return 0;
} }
@@ -621,8 +636,6 @@ arch_cpu_init_post_vm(kernel_args *args)
// set the current hardware task on cpu 0 // set the current hardware task on cpu 0
load_tss(0); load_tss(0);
x86_set_task_gate(8, DOUBLE_FAULT_TSS_BASE_SEGMENT << 3);
// setup TLS descriptors (one for every CPU) // setup TLS descriptors (one for every CPU)
for (i = 0; i < args->num_cpus; i++) { for (i = 0; i < args->num_cpus; i++) {
@@ -1,4 +1,5 @@
/* /*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
@@ -174,7 +175,7 @@ static const int kInterruptNameCount = 20;
typedef struct { typedef struct {
uint32 a, b; uint32 a, b;
} desc_table; } desc_table;
static desc_table *sIDT = NULL; static desc_table* sIDTs[B_MAX_CPU_COUNT];
static uint32 sLevelTriggeredInterrupts = 0; static uint32 sLevelTriggeredInterrupts = 0;
// binary mask: 1 level, 0 edge // binary mask: 1 level, 0 edge
@@ -186,6 +187,8 @@ interrupt_handler_function* gInterruptHandlerTable[
INTERRUPT_HANDLER_TABLE_SIZE]; INTERRUPT_HANDLER_TABLE_SIZE];
/*! Initializes a descriptor in an IDT.
*/
static void static void
set_gate(desc_table *gate_addr, addr_t addr, int type, int dpl) set_gate(desc_table *gate_addr, addr_t addr, int type, int dpl)
{ {
@@ -200,25 +203,45 @@ set_gate(desc_table *gate_addr, addr_t addr, int type, int dpl)
} }
/*! Initializes the descriptor for interrupt vector \a n in the IDT of the
boot CPU to an interrupt-gate descriptor with the given procedure address.
*/
static void static void
set_intr_gate(int n, void *addr) set_interrupt_gate(int n, void (*addr)())
{ {
set_gate(&sIDT[n], (addr_t)addr, 14, DPL_KERNEL); set_gate(&sIDTs[0][n], (addr_t)addr, 14, DPL_KERNEL);
} }
/*! Initializes the descriptor for interrupt vector \a n in the IDT of the
boot CPU to an trap-gate descriptor with the given procedure address.
*/
static void static void
set_system_gate(int n, void *addr) set_trap_gate(int n, void (*addr)())
{ {
set_gate(&sIDT[n], (unsigned int)addr, 15, DPL_USER); set_gate(&sIDTs[0][n], (unsigned int)addr, 15, DPL_USER);
} }
/*! Initializes the descriptor for interrupt vector \a n in the IDT of CPU
\a cpu to a task-gate descripter referring to the TSS segment identified
by TSS segment selector \a segment.
For CPUs other than the boot CPU it must not be called before
arch_int_init_post_vm() (arch_cpu_init_post_vm() is fine).
*/
void void
x86_set_task_gate(int32 n, int32 segment) x86_set_task_gate(int32 cpu, int32 n, int32 segment)
{ {
sIDT[n].a = (segment << 16); sIDTs[cpu][n].a = (segment << 16);
sIDT[n].b = 0x8000 | (0 << 13) | (0x5 << 8); // present, dpl 0, type 5 sIDTs[cpu][n].b = 0x8000 | (0 << 13) | (0x5 << 8); // present, dpl 0, type 5
}
/*! Returns the virtual IDT address for CPU \a cpu. */
void*
x86_get_idt(int32 cpu)
{
return sIDTs[cpu];
} }
@@ -446,7 +469,7 @@ ioapic_enable_io_interrupt(int32 num)
{ {
uint64 entry; uint64 entry;
int32 pin = sIRQToIOAPICPin[num]; int32 pin = sIRQToIOAPICPin[num];
if (pin < 0 || pin > sIOAPICMaxRedirectionEntry) if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
return; return;
TRACE(("ioapic_enable_io_interrupt: IRQ %ld -> pin %ld\n", num, pin)); TRACE(("ioapic_enable_io_interrupt: IRQ %ld -> pin %ld\n", num, pin));
@@ -463,7 +486,7 @@ ioapic_disable_io_interrupt(int32 num)
{ {
uint64 entry; uint64 entry;
int32 pin = sIRQToIOAPICPin[num]; int32 pin = sIRQToIOAPICPin[num];
if (pin < 0 || pin > sIOAPICMaxRedirectionEntry) if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
return; return;
TRACE(("ioapic_disable_io_interrupt: IRQ %ld -> pin %ld\n", num, pin)); TRACE(("ioapic_disable_io_interrupt: IRQ %ld -> pin %ld\n", num, pin));
@@ -480,7 +503,7 @@ ioapic_configure_io_interrupt(int32 num, uint32 config)
{ {
uint64 entry; uint64 entry;
int32 pin = sIRQToIOAPICPin[num]; int32 pin = sIRQToIOAPICPin[num];
if (pin < 0 || pin > sIOAPICMaxRedirectionEntry) if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
return; return;
TRACE(("ioapic_configure_io_interrupt: IRQ %ld -> pin %ld; config 0x%08lx\n", TRACE(("ioapic_configure_io_interrupt: IRQ %ld -> pin %ld; config 0x%08lx\n",
@@ -799,16 +822,18 @@ unexpected_exception(struct iframe* frame)
} }
static void void
double_fault_exception(struct iframe* frame) x86_double_fault_exception(struct iframe* frame)
{ {
int cpu = x86_double_fault_get_cpu();
// The double fault iframe contains no useful information (as // The double fault iframe contains no useful information (as
// per Intel's architecture spec). Thus we simply save the // per Intel's architecture spec). Thus we simply save the
// information from the (unhandable) exception which caused the // information from the (unhandlable) exception which caused the
// double in our iframe. This will result even in useful stack // double in our iframe. This will result even in useful stack
// traces. Only problem is that we trust that at least the // traces. Only problem is that we trust that at least the
// TSS is still accessible. // TSS is still accessible.
struct tss *tss = &gCPU[smp_get_current_cpu()].arch.tss; struct tss *tss = &gCPU[cpu].arch.tss;
frame->cs = tss->cs; frame->cs = tss->cs;
frame->es = tss->es; frame->es = tss->es;
@@ -826,7 +851,7 @@ double_fault_exception(struct iframe* frame)
frame->edi = tss->edi; frame->edi = tss->edi;
frame->flags = tss->eflags; frame->flags = tss->eflags;
panic("double fault!\n"); debug_double_fault(cpu);
} }
@@ -941,65 +966,65 @@ arch_int_init(struct kernel_args *args)
interrupt_handler_function** table; interrupt_handler_function** table;
// set the global sIDT variable // set the global sIDT variable
sIDT = (desc_table *)args->arch_args.vir_idt; sIDTs[0] = (desc_table *)args->arch_args.vir_idt;
// setup the standard programmable interrupt controller // setup the standard programmable interrupt controller
pic_init(); pic_init();
set_intr_gate(0, &trap0); set_interrupt_gate(0, &trap0);
set_intr_gate(1, &trap1); set_interrupt_gate(1, &trap1);
set_intr_gate(2, &trap2); set_interrupt_gate(2, &trap2);
set_system_gate(3, &trap3); set_trap_gate(3, &trap3);
set_intr_gate(4, &trap4); set_interrupt_gate(4, &trap4);
set_intr_gate(5, &trap5); set_interrupt_gate(5, &trap5);
set_intr_gate(6, &trap6); set_interrupt_gate(6, &trap6);
set_intr_gate(7, &trap7); set_interrupt_gate(7, &trap7);
// trap8 (double fault) is set in arch_cpu.c // trap8 (double fault) is set in arch_cpu.c
set_intr_gate(9, &trap9); set_interrupt_gate(9, &trap9);
set_intr_gate(10, &trap10); set_interrupt_gate(10, &trap10);
set_intr_gate(11, &trap11); set_interrupt_gate(11, &trap11);
set_intr_gate(12, &trap12); set_interrupt_gate(12, &trap12);
set_intr_gate(13, &trap13); set_interrupt_gate(13, &trap13);
set_intr_gate(14, &trap14); set_interrupt_gate(14, &trap14);
// set_intr_gate(15, &trap15); // set_interrupt_gate(15, &trap15);
set_intr_gate(16, &trap16); set_interrupt_gate(16, &trap16);
set_intr_gate(17, &trap17); set_interrupt_gate(17, &trap17);
set_intr_gate(18, &trap18); set_interrupt_gate(18, &trap18);
set_intr_gate(19, &trap19); set_interrupt_gate(19, &trap19);
set_intr_gate(32, &trap32); set_interrupt_gate(32, &trap32);
set_intr_gate(33, &trap33); set_interrupt_gate(33, &trap33);
set_intr_gate(34, &trap34); set_interrupt_gate(34, &trap34);
set_intr_gate(35, &trap35); set_interrupt_gate(35, &trap35);
set_intr_gate(36, &trap36); set_interrupt_gate(36, &trap36);
set_intr_gate(37, &trap37); set_interrupt_gate(37, &trap37);
set_intr_gate(38, &trap38); set_interrupt_gate(38, &trap38);
set_intr_gate(39, &trap39); set_interrupt_gate(39, &trap39);
set_intr_gate(40, &trap40); set_interrupt_gate(40, &trap40);
set_intr_gate(41, &trap41); set_interrupt_gate(41, &trap41);
set_intr_gate(42, &trap42); set_interrupt_gate(42, &trap42);
set_intr_gate(43, &trap43); set_interrupt_gate(43, &trap43);
set_intr_gate(44, &trap44); set_interrupt_gate(44, &trap44);
set_intr_gate(45, &trap45); set_interrupt_gate(45, &trap45);
set_intr_gate(46, &trap46); set_interrupt_gate(46, &trap46);
set_intr_gate(47, &trap47); set_interrupt_gate(47, &trap47);
set_intr_gate(48, &trap48); set_interrupt_gate(48, &trap48);
set_intr_gate(49, &trap49); set_interrupt_gate(49, &trap49);
set_intr_gate(50, &trap50); set_interrupt_gate(50, &trap50);
set_intr_gate(51, &trap51); set_interrupt_gate(51, &trap51);
set_intr_gate(52, &trap52); set_interrupt_gate(52, &trap52);
set_intr_gate(53, &trap53); set_interrupt_gate(53, &trap53);
set_intr_gate(54, &trap54); set_interrupt_gate(54, &trap54);
set_intr_gate(55, &trap55); set_interrupt_gate(55, &trap55);
set_system_gate(98, &trap98); // for performance testing only set_trap_gate(98, &trap98); // for performance testing only
set_system_gate(99, &trap99); set_trap_gate(99, &trap99);
set_intr_gate(251, &trap251); set_interrupt_gate(251, &trap251);
set_intr_gate(252, &trap252); set_interrupt_gate(252, &trap252);
set_intr_gate(253, &trap253); set_interrupt_gate(253, &trap253);
set_intr_gate(254, &trap254); set_interrupt_gate(254, &trap254);
set_intr_gate(255, &trap255); set_interrupt_gate(255, &trap255);
// init interrupt handler table // init interrupt handler table
table = gInterruptHandlerTable; table = gInterruptHandlerTable;
@@ -1018,7 +1043,7 @@ arch_int_init(struct kernel_args *args)
table[5] = unexpected_exception; // BOUND Range Exceeded Exception (#BR) table[5] = unexpected_exception; // BOUND Range Exceeded Exception (#BR)
table[6] = unexpected_exception; // Invalid Opcode Exception (#UD) table[6] = unexpected_exception; // Invalid Opcode Exception (#UD)
table[7] = fatal_exception; // Device Not Available Exception (#NM) table[7] = fatal_exception; // Device Not Available Exception (#NM)
table[8] = double_fault_exception; // Double Fault Exception (#DF) table[8] = x86_double_fault_exception; // Double Fault Exception (#DF)
table[9] = fatal_exception; // Coprocessor Segment Overrun table[9] = fatal_exception; // Coprocessor Segment Overrun
table[10] = fatal_exception; // Invalid TSS Exception (#TS) table[10] = fatal_exception; // Invalid TSS Exception (#TS)
table[11] = fatal_exception; // Segment Not Present (#NP) table[11] = fatal_exception; // Segment Not Present (#NP)
@@ -1037,13 +1062,33 @@ arch_int_init(struct kernel_args *args)
status_t status_t
arch_int_init_post_vm(struct kernel_args *args) arch_int_init_post_vm(struct kernel_args *args)
{ {
area_id area;
ioapic_init(args); ioapic_init(args);
sIDT = (desc_table *)args->arch_args.vir_idt; // create IDT area for the boot CPU
area = create_area("idt", (void *)&sIDT, B_EXACT_ADDRESS, B_PAGE_SIZE, B_ALREADY_WIRED, area_id area = create_area("idt", (void**)&sIDTs[0], B_EXACT_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); B_PAGE_SIZE, B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < 0)
return area;
// create IDTs for the off-boot CPU
size_t idtSize = 256 * 8;
// 256 8 bytes-sized descriptors
int32 cpuCount = smp_get_num_cpus();
if (cpuCount > 0) {
size_t areaSize = ROUNDUP(cpuCount * idtSize, B_PAGE_SIZE);
desc_table* idt;
area = create_area("idt", (void**)&idt, B_ANY_KERNEL_ADDRESS,
areaSize, B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < 0)
return area;
for (int32 i = 1; i < cpuCount; i++) {
sIDTs[i] = idt;
memcpy(idt, sIDTs[0], idtSize);
idt += 256;
// The CPU's IDTR will be set in arch_cpu_init_percpu().
}
}
return area >= B_OK ? B_OK : area; return area >= B_OK ? B_OK : area;
} }
+11 -1
View File
@@ -200,7 +200,17 @@ FUNCTION(double_fault):
pushl $8; pushl $8;
pushl $-1; pushl $-1;
pushl $-1; pushl $-1;
jmp int_bottom
PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
movl %esp, %ebp // frame pointer is the iframe
pushl %ebp
call x86_double_fault_exception
// Well, there's no returning from a double fault, but maybe a real hacker
// can repair things in KDL.
POP_IFRAME_AND_RETURN()
FUNCTION_END(double_fault) FUNCTION_END(double_fault)
TRAP(trap9, 9) TRAP(trap9, 9)
+3 -2
View File
@@ -122,14 +122,15 @@ static int32
i386_ici_interrupt(void *data) i386_ici_interrupt(void *data)
{ {
// genuine inter-cpu interrupt // genuine inter-cpu interrupt
TRACE(("inter-cpu interrupt on cpu %ld\n", smp_get_current_cpu())); int cpu = smp_get_current_cpu();
TRACE(("inter-cpu interrupt on cpu %ld\n", cpu));
// if we are not using the IO APIC we need to acknowledge the // if we are not using the IO APIC we need to acknowledge the
// interrupt ourselfs // interrupt ourselfs
if (!gUsingIOAPIC) if (!gUsingIOAPIC)
apic_write(APIC_EOI, 0); apic_write(APIC_EOI, 0);
return smp_intercpu_int_handler(); return smp_intercpu_int_handler(cpu);
} }
+54 -41
View File
@@ -7,8 +7,10 @@
* Distributed under the terms of the NewOS License. * Distributed under the terms of the NewOS License.
*/ */
/*! This file contains the debugger and debug output facilities */ /*! This file contains the debugger and debug output facilities */
#include "blue_screen.h" #include "blue_screen.h"
#include <cpu.h> #include <cpu.h>
@@ -662,13 +664,18 @@ kgets(char* buffer, int length)
static void static void
kernel_debugger_loop(void) kernel_debugger_loop(const char* message, int32 cpu)
{ {
int32 previousCPU = sDebuggerOnCPU; int32 previousCPU = sDebuggerOnCPU;
sDebuggerOnCPU = smp_get_current_cpu(); sDebuggerOnCPU = cpu;
DebugAllocPool* allocPool = create_debug_alloc_pool(); DebugAllocPool* allocPool = create_debug_alloc_pool();
sCurrentKernelDebuggerMessage = message;
if (message)
kprintf("PANIC: %s\n", message);
kprintf("Welcome to Kernel Debugging Land...\n"); kprintf("Welcome to Kernel Debugging Land...\n");
if (struct thread* thread = thread_get_current_thread()) { if (struct thread* thread = thread_get_current_thread()) {
@@ -741,11 +748,11 @@ kernel_debugger_loop(void)
static void static void
enter_kernel_debugger(const char* message) enter_kernel_debugger(int32 cpu)
{ {
while (atomic_add(&sInDebugger, 1) > 0) { while (atomic_add(&sInDebugger, 1) > 0) {
// The debugger is already running, find out where... // The debugger is already running, find out where...
if (sDebuggerOnCPU == smp_get_current_cpu()) { if (sDebuggerOnCPU == cpu) {
// We are re-entering the debugger on the same CPU. // We are re-entering the debugger on the same CPU.
break; break;
} }
@@ -755,19 +762,18 @@ enter_kernel_debugger(const char* message)
// blocking there until everyone leaves the debugger and we can // blocking there until everyone leaves the debugger and we can
// try to enter it again. // try to enter it again.
atomic_add(&sInDebugger, -1); atomic_add(&sInDebugger, -1);
smp_intercpu_int_handler(); smp_intercpu_int_handler(cpu);
} }
arch_debug_save_registers(&dbg_register_file[smp_get_current_cpu()][0]); arch_debug_save_registers(&dbg_register_file[cpu][0]);
sPreviousDprintfState = set_dprintf_enabled(true); sPreviousDprintfState = set_dprintf_enabled(true);
if (!gKernelStartup && sDebuggerOnCPU != smp_get_current_cpu() if (!gKernelStartup && sDebuggerOnCPU != cpu && smp_get_num_cpus() > 1) {
&& smp_get_num_cpus() > 1) {
// First entry on a MP system, send a halt request to all of the other // First entry on a MP system, send a halt request to all of the other
// CPUs. Should they try to enter the debugger they will be cought in // CPUs. Should they try to enter the debugger they will be cought in
// the loop above. // the loop above.
smp_send_broadcast_ici(SMP_MSG_CPU_HALT, 0, 0, 0, NULL, smp_send_broadcast_ici_interrupts_disabled(cpu, SMP_MSG_CPU_HALT, 0, 0,
SMP_MSG_FLAG_SYNC); 0, NULL, SMP_MSG_FLAG_SYNC);
} }
if (sBlueScreenOutput) { if (sBlueScreenOutput) {
@@ -779,11 +785,6 @@ enter_kernel_debugger(const char* message)
sDebuggedThread = NULL; sDebuggedThread = NULL;
if (message)
kprintf("PANIC: %s\n", message);
sCurrentKernelDebuggerMessage = message;
// sort the commands // sort the commands
sort_debugger_commands(); sort_debugger_commands();
@@ -821,6 +822,33 @@ hand_over_kernel_debugger()
} }
static void
kernel_debugger_internal(const char* message, int32 cpu)
{
while (true) {
if (sHandOverKDLToCPU == cpu) {
sHandOverKDLToCPU = -1;
sHandOverKDL = false;
} else
enter_kernel_debugger(cpu);
kernel_debugger_loop(message, cpu);
if (sHandOverKDLToCPU < 0) {
exit_kernel_debugger();
break;
}
hand_over_kernel_debugger();
debug_trap_cpu_in_kdl(cpu, true);
if (sHandOverKDLToCPU != cpu)
break;
}
}
static int static int
cmd_dump_kdl_message(int argc, char** argv) cmd_dump_kdl_message(int argc, char** argv)
{ {
@@ -1326,12 +1354,10 @@ debug_get_page_fault_info()
void void
debug_trap_cpu_in_kdl(bool returnIfHandedOver) debug_trap_cpu_in_kdl(int32 cpu, bool returnIfHandedOver)
{ {
InterruptsLocker locker; InterruptsLocker locker;
int cpu = smp_get_current_cpu();
// return, if we've been called recursively (we call // return, if we've been called recursively (we call
// smp_intercpu_int_handler() below) // smp_intercpu_int_handler() below)
if (sCPUTrapped[cpu]) if (sCPUTrapped[cpu])
@@ -1344,15 +1370,22 @@ debug_trap_cpu_in_kdl(bool returnIfHandedOver)
if (returnIfHandedOver) if (returnIfHandedOver)
break; break;
kernel_debugger(NULL); kernel_debugger_internal(NULL, cpu);
} else } else
smp_intercpu_int_handler(); smp_intercpu_int_handler(cpu);
} }
sCPUTrapped[cpu] = false; sCPUTrapped[cpu] = false;
} }
void
debug_double_fault(int32 cpu)
{
kernel_debugger_internal("Double Fault!\n", cpu);
}
bool bool
debug_emergency_key_pressed(char key) debug_emergency_key_pressed(char key)
{ {
@@ -1407,27 +1440,7 @@ kernel_debugger(const char* message)
{ {
cpu_status state = disable_interrupts(); cpu_status state = disable_interrupts();
while (true) { kernel_debugger_internal(message, smp_get_current_cpu());
if (sHandOverKDLToCPU == smp_get_current_cpu()) {
sHandOverKDLToCPU = -1;
sHandOverKDL = false;
} else
enter_kernel_debugger(message);
kernel_debugger_loop();
if (sHandOverKDLToCPU < 0) {
exit_kernel_debugger();
break;
}
hand_over_kernel_debugger();
debug_trap_cpu_in_kdl(true);
if (sHandOverKDLToCPU != smp_get_current_cpu())
break;
}
restore_interrupts(state); restore_interrupts(state);
} }
+149 -7
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
@@ -315,6 +315,58 @@ acquire_spinlock_nocheck(spinlock *lock)
} }
/*! Equivalent to acquire_spinlock(), save for currentCPU parameter. */
static void
acquire_spinlock_cpu(int32 currentCPU, spinlock *lock)
{
#if DEBUG_SPINLOCKS
if (are_interrupts_enabled()) {
panic("acquire_spinlock_cpu: attempt to acquire lock %p with "
"interrupts enabled", lock);
}
#endif
if (sNumCPUs > 1) {
#if B_DEBUG_SPINLOCK_CONTENTION
while (atomic_add(&lock->lock, 1) != 0)
process_all_pending_ici(currentCPU);
#else
while (1) {
uint32 count = 0;
while (*lock != 0) {
if (++count == SPINLOCK_DEADLOCK_COUNT) {
panic("acquire_spinlock_cpu(): Failed to acquire spinlock "
"%p for a long time!", lock);
count = 0;
}
process_all_pending_ici(currentCPU);
PAUSE();
}
if (atomic_set((int32 *)lock, 1) == 0)
break;
}
#if DEBUG_SPINLOCKS
push_lock_caller(arch_debug_get_caller(), lock);
#endif
#endif
} else {
#if DEBUG_SPINLOCKS
int32 oldValue;
oldValue = atomic_set((int32 *)lock, 1);
if (oldValue != 0) {
panic("acquire_spinlock_cpu(): attempt to acquire lock %p twice on "
"non-SMP system (last caller: %p, value %ld)", lock,
find_lock_caller(lock), oldValue);
}
push_lock_caller(arch_debug_get_caller(), lock);
#endif
}
}
void void
release_spinlock(spinlock *lock) release_spinlock(spinlock *lock)
{ {
@@ -391,6 +443,34 @@ retry:
} }
/*! Similar to find_free_message(), but expects the interrupts to be disabled
already.
*/
static void
find_free_message_interrupts_disabled(int32 currentCPU,
struct smp_msg** _message)
{
TRACE(("find_free_message_interrupts_disabled: entry\n"));
acquire_spinlock_cpu(currentCPU, &sFreeMessageSpinlock);
while (sFreeMessageCount <= 0) {
release_spinlock(&sFreeMessageSpinlock);
process_all_pending_ici(currentCPU);
PAUSE();
acquire_spinlock_cpu(currentCPU, &sFreeMessageSpinlock);
}
*_message = sFreeMessages;
sFreeMessages = (*_message)->next;
sFreeMessageCount--;
release_spinlock(&sFreeMessageSpinlock);
TRACE(("find_free_message_interrupts_disabled: returning msg %p\n",
*_message));
}
static void static void
return_free_message(struct smp_msg *msg) return_free_message(struct smp_msg *msg)
{ {
@@ -553,6 +633,8 @@ process_pending_ici(int32 currentCPU)
} }
case SMP_MSG_RESCHEDULE_IF_IDLE: case SMP_MSG_RESCHEDULE_IF_IDLE:
{ {
// TODO: We must not dereference the thread when entering the kernel
// debugger from a double fault.
struct thread* thread = thread_get_current_thread(); struct thread* thread = thread_get_current_thread();
if (thread->priority == B_IDLE_PRIORITY) if (thread->priority == B_IDLE_PRIORITY)
thread->cpu->invoke_scheduler = true; thread->cpu->invoke_scheduler = true;
@@ -568,7 +650,7 @@ process_pending_ici(int32 currentCPU)
// special case for the halt message // special case for the halt message
if (haltCPU) if (haltCPU)
debug_trap_cpu_in_kdl(false); debug_trap_cpu_in_kdl(currentCPU, false);
return retval; return retval;
} }
@@ -620,13 +702,11 @@ spinlock_contention_syscall(const char* subsystem, uint32 function,
int int
smp_intercpu_int_handler(void) smp_intercpu_int_handler(int32 cpu)
{ {
int currentCPU = smp_get_current_cpu(); TRACE(("smp_intercpu_int_handler: entry on cpu %d\n", cpu));
TRACE(("smp_intercpu_int_handler: entry on cpu %d\n", currentCPU)); process_all_pending_ici(cpu);
process_all_pending_ici(currentCPU);
TRACE(("smp_intercpu_int_handler: done\n")); TRACE(("smp_intercpu_int_handler: done\n"));
@@ -822,6 +902,68 @@ smp_send_broadcast_ici(int32 message, uint32 data, uint32 data2, uint32 data3,
} }
void
smp_send_broadcast_ici_interrupts_disabled(int32 currentCPU, int32 message,
uint32 data, uint32 data2, uint32 data3, void *data_ptr, uint32 flags)
{
if (!sICIEnabled)
return;
TRACE(("smp_send_broadcast_ici_interrupts_disabled: cpu %ld mess 0x%lx, "
"data 0x%lx, data2 0x%lx, data3 0x%lx, ptr %p, flags 0x%lx\n",
currentCPU, message, data, data2, data3, data_ptr, flags));
struct smp_msg *msg;
find_free_message_interrupts_disabled(currentCPU, &msg);
msg->message = message;
msg->data = data;
msg->data2 = data2;
msg->data3 = data3;
msg->data_ptr = data_ptr;
msg->ref_count = sNumCPUs - 1;
msg->flags = flags;
msg->proc_bitmap = SET_BIT(0, currentCPU);
msg->done = false;
TRACE(("smp_send_broadcast_ici_interrupts_disabled %ld: inserting msg %p "
"into broadcast mbox\n", currentCPU, msg));
// stick it in the appropriate cpu's mailbox
acquire_spinlock_nocheck(&sBroadcastMessageSpinlock);
msg->next = sBroadcastMessages;
sBroadcastMessages = msg;
release_spinlock(&sBroadcastMessageSpinlock);
arch_smp_send_broadcast_ici();
TRACE(("smp_send_broadcast_ici_interrupts_disabled %ld: sent interrupt\n",
currentCPU));
if (flags & SMP_MSG_FLAG_SYNC) {
// wait for the other cpus to finish processing it
// the interrupt handler will ref count it to <0
// if the message is sync after it has removed it from the mailbox
TRACE(("smp_send_broadcast_ici_interrupts_disabled %ld: waiting for "
"ack\n", currentCPU));
while (msg->done == false) {
process_all_pending_ici(currentCPU);
PAUSE();
}
TRACE(("smp_send_broadcast_ici_interrupts_disabled %ld: returning "
"message to free list\n", currentCPU));
// for SYNC messages, it's our responsibility to put it
// back into the free list
return_free_message(msg);
}
TRACE(("smp_send_broadcast_ici_interrupts_disabled: done\n"));
}
bool bool
smp_trap_non_boot_cpus(int32 cpu) smp_trap_non_boot_cpus(int32 cpu)
{ {