xref: /haiku/src/system/kernel/arch/x86/arch_smp.cpp (revision 4466b89c65970de4c7236ac87faa2bee4589f413)
1 /*
2  * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <boot/kernel_args.h>
11 #include <vm/vm.h>
12 #include <int.h>
13 #include <smp.h>
14 #include <smp_priv.h>
15 
16 #include <arch/cpu.h>
17 #include <arch/vm.h>
18 #include <arch/smp.h>
19 
20 #include <arch/x86/apic.h>
21 #include <arch/x86/arch_smp.h>
22 #include <arch/x86/smp_priv.h>
23 #include <arch/x86/timer.h>
24 
25 #include <string.h>
26 #include <stdio.h>
27 
28 
29 //#define TRACE_ARCH_SMP
30 #ifdef TRACE_ARCH_SMP
31 #	define TRACE(x) dprintf x
32 #else
33 #	define TRACE(x) ;
34 #endif
35 
36 static uint32 sCPUAPICIds[B_MAX_CPU_COUNT];
37 static uint32 sAPICVersions[B_MAX_CPU_COUNT];
38 
39 extern "C" void init_sse(void);
40 
41 
42 static int32
43 i386_ici_interrupt(void *data)
44 {
45 	// genuine inter-cpu interrupt
46 	int cpu = smp_get_current_cpu();
47 	TRACE(("inter-cpu interrupt on cpu %d\n", cpu));
48 	return smp_intercpu_int_handler(cpu);
49 }
50 
51 
52 static int32
53 i386_spurious_interrupt(void *data)
54 {
55 	// spurious interrupt
56 	TRACE(("spurious interrupt on cpu %ld\n", smp_get_current_cpu()));
57 
58 	// spurious interrupts must not be acknowledged as it does not expect
59 	// a end of interrupt - if we still do it we would loose the next best
60 	// interrupt
61 	return B_HANDLED_INTERRUPT;
62 }
63 
64 
65 static int32
66 i386_smp_error_interrupt(void *data)
67 {
68 	// smp error interrupt
69 	TRACE(("smp error interrupt on cpu %ld\n", smp_get_current_cpu()));
70 	return B_HANDLED_INTERRUPT;
71 }
72 
73 
74 status_t
75 arch_smp_init(kernel_args *args)
76 {
77 	TRACE(("arch_smp_init: entry\n"));
78 
79 	if (!apic_available()) {
80 		// if we don't have an apic we can't do smp
81 		return B_OK;
82 	}
83 
84 	// setup some globals
85 	memcpy(sCPUAPICIds, args->arch_args.cpu_apic_id, sizeof(args->arch_args.cpu_apic_id));
86 	memcpy(sAPICVersions, args->arch_args.cpu_apic_version, sizeof(args->arch_args.cpu_apic_version));
87 
88 	// set up the local apic on the boot cpu
89 	arch_smp_per_cpu_init(args, 0);
90 
91 	if (args->num_cpus > 1) {
92 		// I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted
93 		reserve_io_interrupt_vectors(3, 0xfd - ARCH_INTERRUPT_BASE);
94 		install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &i386_ici_interrupt, NULL, B_NO_LOCK_VECTOR);
95 		install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &i386_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR);
96 		install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &i386_spurious_interrupt, NULL, B_NO_LOCK_VECTOR);
97 	}
98 
99 	return B_OK;
100 }
101 
102 
103 status_t
104 arch_smp_per_cpu_init(kernel_args *args, int32 cpu)
105 {
106 	// set up the local apic on the current cpu
107 	TRACE(("arch_smp_init_percpu: setting up the apic on cpu %ld\n", cpu));
108 	apic_per_cpu_init(args, cpu);
109 
110 	init_sse();
111 
112 	return B_OK;
113 }
114 
115 
116 void
117 arch_smp_send_broadcast_ici(void)
118 {
119 	uint32 config;
120 	cpu_status state = disable_interrupts();
121 
122 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
123 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
124 		| APIC_INTR_COMMAND_1_ASSERT
125 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
126 		| APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF);
127 
128 	restore_interrupts(state);
129 }
130 
131 
132 void
133 arch_smp_send_ici(int32 target_cpu)
134 {
135 	uint32 config;
136 	uint32 timeout;
137 	cpu_status state;
138 
139 	state = disable_interrupts();
140 
141 	config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK;
142 	apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24);
143 
144 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
145 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
146 		| APIC_INTR_COMMAND_1_ASSERT
147 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
148 		| APIC_INTR_COMMAND_1_DEST_FIELD);
149 
150 	timeout = 100000000;
151 	// wait for message to be sent
152 	while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0)
153 		asm volatile ("pause;");
154 
155 	if (timeout == 0)
156 		panic("arch_smp_send_ici: timeout, target_cpu %ld", target_cpu);
157 
158 	restore_interrupts(state);
159 }
160