diff --git a/Kernel/arch/x86/desctab.asm b/Kernel/arch/x86/desctab.asm
index fa7b84b239f31f778e5002706ad622e01be1e580..0c19fc0a013a740bb5326ca25334b4b9f3682f6c 100644
--- a/Kernel/arch/x86/desctab.asm
+++ b/Kernel/arch/x86/desctab.asm
@@ -89,6 +89,7 @@ Desctab_Install:
 	
 	; MP ISRs
 	%if USE_MP
+	SETISR	0xED	; 0xED Inter-processor HALT
 	SETISR	0xEE	; 0xEE Timer
 	SETISR	0xEF	; 0xEF Spurious Interrupt
 	%endif
@@ -206,12 +207,21 @@ ISR_NOERR	31; 31: Reserved
 DEF_SYSCALL	0xAC	; Acess System Call
 
 %if USE_MP
+[global Isr0xED]
+; 0xED - Interprocessor HALT
+Isr0xED:
+	cli
+.jmp:	hlt
+	jmp .jmp
+
 [global Isr0xEE]
 [extern SchedulerBase]
 ; AP's Timer Interrupt
 Isr0xEE:
-	push 0	; Line up with interrupt number
-	xchg bx, bx	; MAGIC BREAK
+	push eax	; Line up with interrupt number
+	mov eax, dr1	; CPU Number
+	push eax
+	mov eax, [esp-4]	; Load EAX back
 	jmp SchedulerBase
 ; Spurious Interrupt
 [global Isr0xEF]
diff --git a/Kernel/arch/x86/errors.c b/Kernel/arch/x86/errors.c
index c66e49d193c268f3a2a26d0862ba336864c60a26..d73fcea4a26f90f2cc8db9ebf8fea24d53b0d59b 100644
--- a/Kernel/arch/x86/errors.c
+++ b/Kernel/arch/x86/errors.c
@@ -5,6 +5,7 @@
  */
 #include <acess.h>
 #include <proc.h>
+#include <mm_virt.h>
 
 // === CONSTANTS ===
 #define	MAX_BACKTRACE	8	//!< Maximum distance to trace the stack backwards
@@ -233,6 +234,7 @@ void Error_Backtrace(Uint eip, Uint ebp)
 	
 	while( MM_GetPhysAddr(ebp) && i < MAX_BACKTRACE )
 	{
+		if( ebp >= MM_KERNEL_STACKS_END )	break;
 		//str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta);
 		if(str == NULL)
 			LogF(" >> 0x%x", *(Uint*)(ebp+4));
diff --git a/Kernel/arch/x86/include/mm_virt.h b/Kernel/arch/x86/include/mm_virt.h
index 4a11e7167f5370d3c10127a3db86146c517d0b67..31642c64b211afed37bdc66f9973bbcf09f4f29d 100644
--- a/Kernel/arch/x86/include/mm_virt.h
+++ b/Kernel/arch/x86/include/mm_virt.h
@@ -39,6 +39,10 @@
 // 1024 items per page
 #define	MM_REFCOUNT_BASE	0xE4000000
 
+#define MM_KERNEL_STACKS	0xF0000000
+#define	MM_KERNEL_STACK_SIZE	0x00008000
+#define MM_KERNEL_STACKS_END	0xFC000000
+
 // === FUNCTIONS ===
 extern void	MM_FinishVirtualInit(void);
 extern void	MM_SetCR3(Uint CR3);
diff --git a/Kernel/arch/x86/kpanic.c b/Kernel/arch/x86/kpanic.c
index ccb107370d469d2240faafd67a28994de5d6cc66..6a7b9b0fac943877d52db6e1650ba72785d8e390 100644
--- a/Kernel/arch/x86/kpanic.c
+++ b/Kernel/arch/x86/kpanic.c
@@ -1,23 +1,34 @@
 /*
  * Acess 2 Kernel
- * By John Hodge (thePowersGang)
+ * - By John Hodge (thePowersGang)
+ * 
+ * kpanic.c
  * - x86 Kernel Panic Handler
  */
 
 #include <acess.h>
+#include <proc.h>
 
-
-
+// === CONSTANTS ===
 #define	FB	((Uint16 *)(KERNEL_BASE|0xB8000))
 #define BGC	0x4F00	// White on Red
 //#define BGC	0xC000	// Black on Bright Red
 //#define BGC	0x1F00	// White on Blue (BSOD!)
 
+// === IMPORTS ===
 extern Uint32	GetEIP(void);
 extern void	Error_Backtrace(Uint32 eip, Uint32 ebp);
+#if USE_MP
+extern void	MP_SendIPIVector(int CPU, Uint8 Vector);
+extern int	giNumCPUs;
+extern int	GetCPUNum(void);
+#endif
 
- int	giKP_Pos = 0;
+// === PROTOTYPES ===
+void	KernelPanic_SetMode(void);
+void	KernelPanic_PutChar(char Ch);
 
+// === CONSTANTS ===
 const struct {
 	Uint16	IdxPort;
 	Uint16	DatPort;
@@ -57,9 +68,8 @@ const struct {
 };
 #define	NUM_REGVALUES	(sizeof(caRegValues)/sizeof(caRegValues[0]))
 
-// === PROTOTYPES ===
-void	KernelPanic_SetMode(void);
-void	KernelPanic_PutChar(char Ch);
+// === GLOBALS ===
+ int	giKP_Pos = 0;
 
 // === CODE ===
 /**
@@ -89,6 +99,16 @@ void KernelPanic_SetMode(void)
 	inb(0x3DA);
 	outb(0x3C0, 0x20);
 	#endif
+
+	#if USE_MP
+	// Send halt to all processors
+	for( i = 0; i < giNumCPUs; i ++ )
+	{
+		if(i == GetCPUNum())	continue ;
+		FB[i] = BGC|'A'+i;
+		MP_SendIPIVector(i, 0xED);
+	}
+	#endif
 	
 	// Clear Screen
 	for( i = 0; i < 80*25; i++ )
diff --git a/Kernel/arch/x86/mm_virt.c b/Kernel/arch/x86/mm_virt.c
index af3d4bf0098a04eb61192f83ccba1ca69447d4b7..73e19d3bbe835e9e4a7b843e0b9b8e2e43234015 100644
--- a/Kernel/arch/x86/mm_virt.c
+++ b/Kernel/arch/x86/mm_virt.c
@@ -19,11 +19,8 @@
 
 #define TAB	22
 
-#define KERNEL_STACKS		0xF0000000
-#define	KERNEL_STACK_SIZE	0x00008000
-#define KERNEL_STACKS_END	0xFC000000
 #define WORKER_STACKS		0x00100000	// Thread0 Only!
-#define	WORKER_STACK_SIZE	KERNEL_STACK_SIZE
+#define	WORKER_STACK_SIZE	MM_KERNEL_STACK_SIZE
 #define WORKER_STACKS_END	0xB0000000
 #define	NUM_WORKER_STACKS	((WORKER_STACKS_END-WORKER_STACKS)/WORKER_STACK_SIZE)
 
@@ -125,7 +122,7 @@ void MM_InstallVirtual(void)
 	{
 		if( gaPageDir[ i ] )	continue;
 		// Skip stack tables, they are process unique
-		if( i > KERNEL_STACKS >> 22 && i < KERNEL_STACKS_END >> 22) {
+		if( i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) {
 			gaPageDir[ i ] = 0;
 			continue;
 		}
@@ -515,7 +512,7 @@ tPAddr MM_Clone(void)
 	Uint	i, j;
 	tVAddr	ret;
 	Uint	page = 0;
-	tVAddr	kStackBase = Proc_GetCurThread()->KernelStack - KERNEL_STACK_SIZE;
+	tVAddr	kStackBase = Proc_GetCurThread()->KernelStack - MM_KERNEL_STACK_SIZE;
 	void	*tmp;
 	
 	Mutex_Acquire( &glTempFractal );
@@ -587,9 +584,7 @@ tPAddr MM_Clone(void)
 	}
 	
 	// Allocate kernel stack
-	for(i = KERNEL_STACKS >> 22;
-		i < KERNEL_STACKS_END >> 22;
-		i ++ )
+	for(i = MM_KERNEL_STACKS >> 22; i < MM_KERNEL_STACKS_END >> 22; i ++ )
 	{
 		// Check if directory is allocated
 		if( (gaPageDir[i] & 1) == 0 ) {
@@ -616,7 +611,7 @@ tPAddr MM_Clone(void)
 			}
 			
 			// We don't care about other kernel stacks
-			if( ((i*1024+j)*4096 & ~(KERNEL_STACK_SIZE-1)) != kStackBase ) {
+			if( ((i*1024+j)*4096 & ~(MM_KERNEL_STACK_SIZE-1)) != kStackBase ) {
 				gaTmpTable[i*1024+j] = 0;
 				continue;
 			}
@@ -647,27 +642,27 @@ tVAddr MM_NewKStack(void)
 {
 	tVAddr	base;
 	Uint	i;
-	for(base = KERNEL_STACKS; base < KERNEL_STACKS_END; base += KERNEL_STACK_SIZE)
+	for(base = MM_KERNEL_STACKS; base < MM_KERNEL_STACKS_END; base += MM_KERNEL_STACK_SIZE)
 	{
 		// Check if space is free
 		if(MM_GetPhysAddr(base) != 0)	continue;
 		// Allocate
-		//for(i = KERNEL_STACK_SIZE; i -= 0x1000 ; )
-		for(i = 0; i < KERNEL_STACK_SIZE; i += 0x1000 )
+		//for(i = MM_KERNEL_STACK_SIZE; i -= 0x1000 ; )
+		for(i = 0; i < MM_KERNEL_STACK_SIZE; i += 0x1000 )
 		{
 			if( MM_Allocate(base+i) == 0 )
 			{
 				// On error, print a warning and return error
 				Warning("MM_NewKStack - Out of memory");
 				// - Clean up
-				//for( i += 0x1000 ; i < KERNEL_STACK_SIZE; i += 0x1000 )
+				//for( i += 0x1000 ; i < MM_KERNEL_STACK_SIZE; i += 0x1000 )
 				//	MM_Deallocate(base+i);
 				return 0;
 			}
 		}
 		// Success
-//		Log("MM_NewKStack - Allocated %p", base + KERNEL_STACK_SIZE);
-		return base+KERNEL_STACK_SIZE;
+//		Log("MM_NewKStack - Allocated %p", base + MM_KERNEL_STACK_SIZE);
+		return base+MM_KERNEL_STACK_SIZE;
 	}
 	// No stacks left
 	Log_Warning("MMVirt", "MM_NewKStack - No address space left");
diff --git a/Kernel/arch/x86/proc.asm b/Kernel/arch/x86/proc.asm
index 4e7d1604ac218217df778d2213a080c5bf7685e9..74a25d46b18618c93a903a5d67c913d85fcef8b7 100644
--- a/Kernel/arch/x86/proc.asm
+++ b/Kernel/arch/x86/proc.asm
@@ -162,8 +162,7 @@ SetAPICTimerCount:
 	pop es
 	pop ds
 	popa
-	add esp, 4	; CPU ID
-	; No Error code / int num
+	add esp, 8	; CPU ID / Error Code
 	iret
 %endif
 ; --------------
@@ -376,3 +375,5 @@ User_Syscall_Exit:
 	xor eax, eax
 	mov ebx, [esp+4]
 	int 0xAC
+
+; vim: ft=nasm, ts=8
diff --git a/Kernel/arch/x86/proc.c b/Kernel/arch/x86/proc.c
index dfde359176f126b5d9d21ddb7fe3d221f1644522..9e8aa385da825d43af8121412290f5749db79974 100644
--- a/Kernel/arch/x86/proc.c
+++ b/Kernel/arch/x86/proc.c
@@ -14,7 +14,7 @@
 #include <hal_proc.h>
 
 // === FLAGS ===
-#define DEBUG_TRACE_SWITCH	0
+#define DEBUG_TRACE_SWITCH	1
 #define DEBUG_DISABLE_DOUBLEFAULT	1
 #define DEBUG_VERY_SLOW_SWITCH	0
 
@@ -34,7 +34,6 @@ typedef struct sCPU
 	Uint8	State;	// 0: Unavaliable, 1: Idle, 2: Active
 	Uint16	Resvd;
 	tThread	*Current;
-	tThread	*IdleThread;
 }	tCPU;
 
 // === IMPORTS ===
@@ -66,6 +65,7 @@ extern void	Proc_DisableSSE(void);
 //void	ArchThreads_Init(void);
 #if USE_MP
 void	MP_StartAP(int CPU);
+void	MP_SendIPIVector(int CPU, Uint8 Vector);
 void	MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
 #endif
 void	Proc_IdleThread(void *Ptr);
@@ -331,8 +331,8 @@ void ArchThreads_Init(void)
 	Log("APIC Count %i", giMP_TimerCount);
 	{
 		Uint64	freq = giMP_TimerCount;
-		freq /= TIMER_DIVISOR;
 		freq *= TIMER_BASE;
+		freq /= TIMER_DIVISOR;
 		if( (freq /= 1000) < 2*1000)
 			Log("Bus Frequency %i KHz", freq);
 		else if( (freq /= 1000) < 2*1000)
@@ -383,9 +383,12 @@ void ArchThreads_Init(void)
 }
 
 #if USE_MP
+/**
+ * \brief Start an AP
+ */
 void MP_StartAP(int CPU)
 {
-	Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
+	Log_Log("Proc", "Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
 	
 	// Set location of AP startup code and mark for a warm restart
 	*(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0);
@@ -409,11 +412,17 @@ void MP_StartAP(int CPU)
 	giNumInitingCPUs ++;
 }
 
+void MP_SendIPIVector(int CPU, Uint8 Vector)
+{
+	MP_SendIPI(gaCPUs[CPU].APICID, Vector, 0);
+}
+
 /**
  * \brief Send an Inter-Processor Interrupt
  * \param APICID	Processor's Local APIC ID
  * \param Vector	Argument of some kind
- * \param DeliveryMode	Type of signal?
+ * \param DeliveryMode	Type of signal
+ * \note 3A 10.5 "APIC/Handling Local Interrupts"
  */
 void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
 {
@@ -421,23 +430,24 @@ void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
 	
 	// Hi
 	val = (Uint)APICID << 24;
-	Log("*%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val);
+//	Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val);
 	gpMP_LocalAPIC->ICR[1].Val = val;
 	// Low (and send)
 	val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
-	Log("*%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val);
+//	Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val);
 	gpMP_LocalAPIC->ICR[0].Val = val;
 }
 #endif
 
 void Proc_IdleThread(void *Ptr)
 {
-	tCPU	*cpu = Ptr;
-	cpu->IdleThread->ThreadName = strdup("Idle Thread");
-	Threads_SetPriority( cpu->IdleThread, -1 );	// Never called randomly
-	cpu->IdleThread->Quantum = 1;	// 1 slice quantum
+	tCPU	*cpu = &gaCPUs[GetCPUNum()];
+	cpu->Current->ThreadName = strdup("Idle Thread");
+	Threads_SetPriority( cpu->Current, -1 );	// Never called randomly
+	cpu->Current->Quantum = 1;	// 1 slice quantum
 	for(;;) {
-		HALT();
+		__asm__ __volatile__ ("sti");	// Make sure interrupts are enabled
+		__asm__ __volatile__ ("hlt");	// Make sure interrupts are enabled
 		Proc_Reschedule();
 	}
 }
@@ -461,7 +471,6 @@ void Proc_Start(void)
 		
 		// Create Idle Task
 		tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[i]);
-		gaCPUs[i].IdleThread = Threads_GetThread(tid);
 		
 		// Start the AP
 		if( i != giProc_BootProcessorID ) {
@@ -473,7 +482,7 @@ void Proc_Start(void)
 	gaCPUs[0].Current = &gThreadZero;
 	
 	// Start interrupts and wait for APs to come up
-	Log("Waiting for APs to come up\n");
+	Log_Debug("Proc", "Waiting for APs to come up");
 	__asm__ __volatile__ ("sti");
 	while( giNumInitingCPUs )	__asm__ __volatile__ ("hlt");
 	#else
@@ -484,9 +493,6 @@ void Proc_Start(void)
 	// Set current task
 	gaCPUs[0].Current = &gThreadZero;
 
-//	while( gaCPUs[0].IdleThread == NULL )
-//		Threads_Yield();
-	
 	// Start Interrupts (and hence scheduler)
 	__asm__ __volatile__("sti");
 	#endif
@@ -906,18 +912,21 @@ void Proc_Reschedule(void)
 
 	nextthread = Threads_GetNextToRun(cpu, curthread);
 
-	if(!nextthread)
-		nextthread = gaCPUs[cpu].IdleThread;
 	if(!nextthread || nextthread == curthread)
 		return ;
 
 	#if DEBUG_TRACE_SWITCH
-	LogF("\nSwitching to task %i, CR3 = 0x%x, EIP = %p, ESP = %p\n",
-		nextthread->TID,
-		nextthread->MemState.CR3,
-		nextthread->SavedState.EIP,
-		nextthread->SavedState.ESP
-		);
+	// HACK: Ignores switches to the idle threads
+	if( nextthread->TID == 0 || nextthread->TID > giNumCPUs )
+	{
+		LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n",
+			GetCPUNum(),
+			nextthread, nextthread->TID, nextthread->ThreadName,
+			nextthread->MemState.CR3,
+			nextthread->SavedState.EIP,
+			nextthread->SavedState.ESP
+			);
+	}
 	#endif
 
 	// Update CPU state
@@ -926,7 +935,7 @@ void Proc_Reschedule(void)
 	__asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) );
 
 	// Save FPU/MMX/XMM/SSE state
-	if( curthread->SavedState.SSE )
+	if( curthread && curthread->SavedState.SSE )
 	{
 		Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
 		curthread->SavedState.bSSEModified = 0;
diff --git a/Kernel/arch/x86/start.asm b/Kernel/arch/x86/start.asm
index d504a5df5f5df718196e11011eab2c01a122e4ee..68bdc55d74979fbb3e445452cd82efc50ea1c7b3 100644
--- a/Kernel/arch/x86/start.asm
+++ b/Kernel/arch/x86/start.asm
@@ -120,6 +120,7 @@ APWait:
 .hlt:
 	;hlt
 	jmp .hlt
+[extern Proc_Reschedule]
 [global APStartup]
 APStartup:
 	;xchg bx, bx	; MAGIC BREAK!
@@ -160,11 +161,13 @@ APStartup:
 	mov ebx, [ebp+0x20]	; Read ID
 	shr ebx, 24
 	;xchg bx, bx	; MAGIC BREAK
-	; CL is now local APIC ID
+	; BL is now local APIC ID
 	mov cl, BYTE [gaAPIC_to_CPU+ebx]
 	xor ebx, ebx
 	mov bl, cl
 	; BL is now the CPU ID
+	mov dr1, ebx	; Save the CPU number to a debug register
+	; Mark the CPU as up
 	mov BYTE [gaCPUs+ebx*8+1], 1
 	; Decrement the remaining CPU count
 	dec DWORD [giNumInitingCPUs]
@@ -179,8 +182,6 @@ APStartup:
 	; Set TSS
 	lea ecx, [ebx*8+0x30]
 	ltr cx
-	; Save the CPU number to a debug register
-	mov dr1, ebx
 	
 	;xchg bx, bx	; MAGIC_BREAK
 	; Enable Local APIC
@@ -196,9 +197,10 @@ APStartup:
 	mov DWORD [ebp+0x0B0], 0	; Send an EOI (just in case)
 	
 	; CPU is now marked as initialised
-	sti
-	;xchg bx, bx	; MAGIC BREAK
+
 .hlt:
+	sti
+	call Proc_Reschedule	
 	hlt
 	jmp .hlt
 %endif
@@ -252,4 +254,5 @@ ALIGN 4096
 Kernel_Stack_Top:
 gInitAPStacks:
 	times 32*MAX_CPUS	dd	0
-	
+
+; vim: ft=nasm ts=8	
diff --git a/Modules/USB/Core/usb_lowlevel.c b/Modules/USB/Core/usb_lowlevel.c
index 65365d3be22a46e8adf49c9528ffefaf794f2386..0e53bad940bee2f6bc512b338c316f80b514f6f2 100644
--- a/Modules/USB/Core/usb_lowlevel.c
+++ b/Modules/USB/Core/usb_lowlevel.c
@@ -122,8 +122,15 @@ char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index)
 	struct sDescriptor_String	str;
 	 int	src_len, new_len;
 	char	*ret;
+
+	if(Index == 0)	return strdup("");
 	
 	USB_int_ReadDescriptor(Dev, Endpoint, 3, Index, sizeof(str), &str);
+	if(str.Length < 2) {
+		Log_Error("USB", "String %p:%i:%i:%i descriptor is undersized (%i)",
+			Dev->Host, Dev->Address, Endpoint, Index, str.Length);
+		return NULL;
+	}
 //	if(str.Length > sizeof(str)) {
 //		// IMPOSSIBLE!
 //		Log_Error("USB", "String is %i bytes, which is over prealloc size (%i)",
diff --git a/Modules/USB/UHCI/uhci.c b/Modules/USB/UHCI/uhci.c
index 5ea356618c468ce1d69cdaa5024e3ee9645664a3..9ef8b0bc43681177b1dee70cc4947d77bb9f84ab 100644
--- a/Modules/USB/UHCI/uhci.c
+++ b/Modules/USB/UHCI/uhci.c
@@ -97,8 +97,6 @@ int UHCI_Initialise(char **Arguments)
 		cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2);
 		LOG("cinfo->RootHub = %p", cinfo->RootHub);
 
-		UHCI_CheckPortUpdate(cinfo);
-
 		i ++;
 	}
 
@@ -271,7 +269,7 @@ int UHCI_IsTransferComplete(void *Ptr, void *Handle)
 	ret = !(td->Control & (1 << 23));
 	if(ret) {
 		td->_info.Callback = NULL;
-		td->Link = 1;
+		td->Link = 0;
 	}
 	return ret;
 }
@@ -399,7 +397,7 @@ void UHCI_InterruptHandler(int IRQ, void *Ptr)
 				}
 				link = td->Link;
 				if( td->_info.Callback != INVLPTR )
-					td->Link = 1;
+					td->Link = 0;
 			}
 			
 			if(frame == 0)