xref: /haiku/src/system/kernel/arch/x86/arch_smp.cpp (revision 1026b0a1a76dc88927bb8175c470f638dc5464ee)
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 
37 static uint32 sCPUAPICIds[B_MAX_CPU_COUNT];
38 static uint32 sAPICVersions[B_MAX_CPU_COUNT];
39 
40 
41 static int32
42 i386_ici_interrupt(void *data)
43 {
44 	// genuine inter-cpu interrupt
45 	int cpu = smp_get_current_cpu();
46 	TRACE(("inter-cpu interrupt on cpu %d\n", cpu));
47 	return smp_intercpu_int_handler(cpu);
48 }
49 
50 
51 static int32
52 i386_spurious_interrupt(void *data)
53 {
54 	// spurious interrupt
55 	TRACE(("spurious interrupt on cpu %ld\n", smp_get_current_cpu()));
56 
57 	// spurious interrupts must not be acknowledged as it does not expect
58 	// a end of interrupt - if we still do it we would loose the next best
59 	// interrupt
60 	return B_HANDLED_INTERRUPT;
61 }
62 
63 
64 static int32
65 i386_smp_error_interrupt(void *data)
66 {
67 	// smp error interrupt
68 	TRACE(("smp error interrupt on cpu %ld\n", smp_get_current_cpu()));
69 	return B_HANDLED_INTERRUPT;
70 }
71 
72 
73 status_t
74 arch_smp_init(kernel_args *args)
75 {
76 	TRACE(("%s: entry\n", __func__));
77 
78 	if (!apic_available()) {
79 		// if we don't have an apic we can't do smp
80 		TRACE(("%s: apic not available for smp\n", __func__));
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 	// setup FPU and SSE if supported
111 	x86_init_fpu();
112 
113 	return B_OK;
114 }
115 
116 
117 void
118 arch_smp_send_broadcast_ici(void)
119 {
120 	uint32 config;
121 	cpu_status state = disable_interrupts();
122 
123 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
124 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
125 		| APIC_INTR_COMMAND_1_ASSERT
126 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
127 		| APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF);
128 
129 	restore_interrupts(state);
130 }
131 
132 
133 void
134 arch_smp_send_ici(int32 target_cpu)
135 {
136 	uint32 config;
137 	uint32 timeout;
138 	cpu_status state;
139 
140 	state = disable_interrupts();
141 
142 	config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK;
143 	apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24);
144 
145 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
146 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
147 		| APIC_INTR_COMMAND_1_ASSERT
148 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
149 		| APIC_INTR_COMMAND_1_DEST_FIELD);
150 
151 	timeout = 100000000;
152 	// wait for message to be sent
153 	while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0)
154 		asm volatile ("pause;");
155 
156 	if (timeout == 0)
157 		panic("arch_smp_send_ici: timeout, target_cpu %ld", target_cpu);
158 
159 	restore_interrupts(state);
160 }
161