xref: /haiku/src/system/kernel/arch/x86/arch_smp.cpp (revision 1294543de9ac0eff000eaea1b18368c36435d08e)
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 		install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &i386_ici_interrupt, NULL, B_NO_LOCK_VECTOR);
94 		install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &i386_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR);
95 		install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &i386_spurious_interrupt, NULL, B_NO_LOCK_VECTOR);
96 	}
97 
98 	return B_OK;
99 }
100 
101 
102 status_t
103 arch_smp_per_cpu_init(kernel_args *args, int32 cpu)
104 {
105 	// set up the local apic on the current cpu
106 	TRACE(("arch_smp_init_percpu: setting up the apic on cpu %ld\n", cpu));
107 	apic_per_cpu_init(args, cpu);
108 
109 	init_sse();
110 
111 	return B_OK;
112 }
113 
114 
115 void
116 arch_smp_send_broadcast_ici(void)
117 {
118 	uint32 config;
119 	cpu_status state = disable_interrupts();
120 
121 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
122 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
123 		| APIC_INTR_COMMAND_1_ASSERT
124 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
125 		| APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF);
126 
127 	restore_interrupts(state);
128 }
129 
130 
131 void
132 arch_smp_send_ici(int32 target_cpu)
133 {
134 	uint32 config;
135 	uint32 timeout;
136 	cpu_status state;
137 
138 	state = disable_interrupts();
139 
140 	config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK;
141 	apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24);
142 
143 	config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK;
144 	apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED
145 		| APIC_INTR_COMMAND_1_ASSERT
146 		| APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL
147 		| APIC_INTR_COMMAND_1_DEST_FIELD);
148 
149 	timeout = 100000000;
150 	// wait for message to be sent
151 	while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0)
152 		asm volatile ("pause;");
153 
154 	if (timeout == 0)
155 		panic("arch_smp_send_ici: timeout, target_cpu %ld", target_cpu);
156 
157 	restore_interrupts(state);
158 }
159