Index: kernel/arch/sparc64/include/interrupt.h
===================================================================
--- kernel/arch/sparc64/include/interrupt.h	(revision a9ac978f3aba8259d5f8270ee59a08a41d15d94c)
+++ kernel/arch/sparc64/include/interrupt.h	(revision 00b38a3225eae97d519c8185a42202d226dd8513)
@@ -46,5 +46,6 @@
 #define IVT_FIRST	1
 
-#define VECTOR_TLB_SHOOTDOWN_IPI	0	/* TODO */
+#define VECTOR_TLB_SHOOTDOWN_IPI	0
+#define IPI_TLB_SHOOTDOWN		VECTOR_TLB_SHOOTDOWN_IPI
 
 struct istate {
Index: kernel/arch/sparc64/include/trap/interrupt.h
===================================================================
--- kernel/arch/sparc64/include/trap/interrupt.h	(revision a9ac978f3aba8259d5f8270ee59a08a41d15d94c)
+++ kernel/arch/sparc64/include/trap/interrupt.h	(revision 00b38a3225eae97d519c8185a42202d226dd8513)
@@ -51,4 +51,5 @@
 #define ASI_UDB_INTR_W_DATA_1	0x50
 #define ASI_UDB_INTR_W_DATA_2	0x60
+#define ASI_UDB_INTR_W_DISPATCH	0x70
 
 /* VA's used with ASI_UDB_INTR_R register. */
@@ -56,4 +57,11 @@
 #define ASI_UDB_INTR_R_DATA_1	0x50
 #define ASI_UDB_INTR_R_DATA_2	0x60
+
+/* Shifts in the Interrupt Vector Dispatch virtual address. */
+#define INTR_VEC_DISPATCH_MID_SHIFT	14
+
+/* Bits in the Interrupt Dispatch Status register. */
+#define INTR_DISPATCH_STATUS_NACK	0x2
+#define INTR_DISPATCH_STATUS_BUSY	0x1
 
 #define TT_INTERRUPT_LEVEL_1			0x41
Index: kernel/arch/sparc64/src/smp/ipi.c
===================================================================
--- kernel/arch/sparc64/src/smp/ipi.c	(revision a9ac978f3aba8259d5f8270ee59a08a41d15d94c)
+++ kernel/arch/sparc64/src/smp/ipi.c	(revision 00b38a3225eae97d519c8185a42202d226dd8513)
@@ -34,8 +34,109 @@
 
 #include <smp/ipi.h>
+#include <cpu.h>
+#include <arch/cpu.h>
+#include <arch/asm.h>
+#include <config.h>
+#include <mm/tlb.h>
+#include <arch/interrupt.h>
+#include <arch/trap/interrupt.h>
+#include <arch/barrier.h>
+#include <preemption.h>
+#include <time/delay.h>
+#include <panic.h>
 
+/** Invoke function on another processor.
+ *
+ * Currently, only functions without arguments are supported.
+ * Supporting more arguments in the future should be no big deal.
+ *
+ * Interrupts must be disabled prior to this call.
+ *
+ * @param mid MID of the target processor.
+ * @param func Function to be invoked.
+ */
+static void cross_call(int mid, void (* func)(void))
+{
+	uint64_t status;
+	bool done;
+
+	/*
+	 * This functin might enable interrupts for a while.
+	 * In order to prevent migration to another processor,
+	 * we explicitly disable preemption.
+	 */
+	
+	preemption_disable();
+	
+	status = asi_u64_read(ASI_INTR_DISPATCH_STATUS, 0);
+	if (status & INTR_DISPATCH_STATUS_BUSY)
+		panic("Interrupt Dispatch Status busy bit set\n");
+	
+	do {
+		asi_u64_write(ASI_UDB_INTR_W, ASI_UDB_INTR_W_DATA_0, (uintptr_t) func);
+		asi_u64_write(ASI_UDB_INTR_W, ASI_UDB_INTR_W_DATA_1, 0);
+		asi_u64_write(ASI_UDB_INTR_W, ASI_UDB_INTR_W_DATA_2, 0);
+		asi_u64_write(ASI_UDB_INTR_W, (mid << INTR_VEC_DISPATCH_MID_SHIFT) | ASI_UDB_INTR_W_DISPATCH, 0);
+	
+		membar();
+		
+		do {
+			status = asi_u64_read(ASI_INTR_DISPATCH_STATUS, 0);
+		} while (status & INTR_DISPATCH_STATUS_BUSY);
+		
+		done = !(status & INTR_DISPATCH_STATUS_NACK);
+		if (!done) {
+			/*
+			 * Prevent deadlock.
+			 */			
+			(void) interrupts_enable();
+			delay(20 + (tick_read() & 0xff));
+			(void) interrupts_disable();
+		}
+	} while (done);
+	
+	preemption_enable();
+}
+
+/*
+ * Deliver IPI to all processors except the current one.
+ *
+ * The sparc64 architecture does not support any group addressing
+ * which is found, for instance, on ia32 and amd64. Therefore we
+ * need to simulate the broadcast by sending the message to
+ * all target processors step by step.
+ *
+ * We assume that interrupts are disabled.
+ *
+ * @param ipi IPI number.
+ */
 void ipi_broadcast_arch(int ipi)
 {
-	/* TODO */
+	int i;
+	
+	void (* func)(void);
+	
+	switch (ipi) {
+	case IPI_TLB_SHOOTDOWN:
+		func = tlb_shootdown_ipi_recv;
+		break;
+	default:
+		panic("Unknown IPI (%d).\n", ipi);
+		break;
+	}
+	
+	/*
+	 * As long as we don't support hot-plugging
+	 * or hot-unplugging of CPUs, we can walk
+	 * the cpus array and read processor's MID
+	 * without locking.
+	 */
+	
+	for (i = 0; i < config.cpu_active; i++) {
+		if (&cpus[i] == CPU)
+			continue;		/* skip the current CPU */
+
+		cross_call(cpus[i].arch.mid, func);
+	}
 }
 
Index: kernel/arch/sparc64/src/trap/interrupt.c
===================================================================
--- kernel/arch/sparc64/src/trap/interrupt.c	(revision a9ac978f3aba8259d5f8270ee59a08a41d15d94c)
+++ kernel/arch/sparc64/src/trap/interrupt.c	(revision 00b38a3225eae97d519c8185a42202d226dd8513)
@@ -46,4 +46,7 @@
 #include <print.h>
 #include <genarch/kbd/z8530.h>
+#include <arch.h>
+#include <mm/tlb.h>
+#include <config.h>
 
 /** Register Interrupt Level Handler.
@@ -98,4 +101,22 @@
 
 #endif
+	default:
+		if (data0 > config.base) {
+			/*
+			 * This is a cross-call.
+			 * data0 contains address of kernel function.
+			 * We call the function only after we verify
+			 * it is on of the supported ones.
+			 */
+#ifdef CONFIG_SMP
+			if (data0 == (uintptr_t) tlb_shootdown_ipi_recv) {
+				tlb_shootdown_ipi_recv();
+				break;
+			}
+#endif
+		}
+			
+		printf("cpu%d: spurious interrupt (intrcv=%#llx, data0=%#llx)\n", CPU->id, intrcv, data0);
+		break;
 	}
 
Index: kernel/generic/src/mm/tlb.c
===================================================================
--- kernel/generic/src/mm/tlb.c	(revision a9ac978f3aba8259d5f8270ee59a08a41d15d94c)
+++ kernel/generic/src/mm/tlb.c	(revision 00b38a3225eae97d519c8185a42202d226dd8513)
@@ -162,15 +162,15 @@
 
 		switch (type) {
-		    case TLB_INVL_ALL:
+		case TLB_INVL_ALL:
 			tlb_invalidate_all();
 			break;
-		    case TLB_INVL_ASID:
+		case TLB_INVL_ASID:
 			tlb_invalidate_asid(asid);
 			break;
-		    case TLB_INVL_PAGES:
+		case TLB_INVL_PAGES:
 		    	ASSERT(count);
 			tlb_invalidate_pages(asid, page, count);
 			break;
-		    default:
+		default:
 			panic("unknown type (%d)\n", type);
 			break;
