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 <timer.h> 21 22 #include <arch/x86/smp_priv.h> 23 #include <arch/x86/arch_apic.h> 24 #include <arch/x86/timer.h> 25 26 #include <string.h> 27 #include <stdio.h> 28 29 #include "timers/apic.h" 30 31 32 //#define TRACE_ARCH_SMP 33 #ifdef TRACE_ARCH_SMP 34 # define TRACE(x) dprintf x 35 #else 36 # define TRACE(x) ; 37 #endif 38 39 static void *sLocalAPIC = NULL; 40 static uint32 sCPUAPICIds[B_MAX_CPU_COUNT]; 41 static uint32 sCPUOSIds[B_MAX_CPU_COUNT]; 42 static uint32 sAPICVersions[B_MAX_CPU_COUNT]; 43 44 extern bool gUsingIOAPIC; 45 extern "C" void init_sse(void); 46 47 static uint32 48 apic_read(uint32 offset) 49 { 50 return *(volatile uint32 *)((char *)sLocalAPIC + offset); 51 } 52 53 54 static void 55 apic_write(uint32 offset, uint32 data) 56 { 57 *(volatile uint32 *)((char *)sLocalAPIC + offset) = data; 58 } 59 60 61 static status_t 62 setup_apic(kernel_args *args, int32 cpu) 63 { 64 uint32 config; 65 66 TRACE(("setting up the APIC for CPU %ld...\n", cpu)); 67 TRACE((" apic id %ld, version %ld\n", apic_read(APIC_ID), apic_read(APIC_VERSION))); 68 69 /* set spurious interrupt vector to 0xff */ 70 config = apic_read(APIC_SPURIOUS_INTR_VECTOR) & 0xffffff00; 71 config |= APIC_ENABLE | 0xff; 72 apic_write(APIC_SPURIOUS_INTR_VECTOR, config); 73 74 // don't touch the LINT0/1 configuration in virtual wire mode 75 // ToDo: implement support for other modes... 76 #if 0 77 if (cpu == 0) { 78 /* setup LINT0 as ExtINT */ 79 config = (apic_read(APIC_LINT0) & 0xffff00ff); 80 config |= APIC_LVT_DM_ExtINT | APIC_LVT_IIPP | APIC_LVT_TM; 81 apic_write(APIC_LINT0, config); 82 83 /* setup LINT1 as NMI */ 84 config = (apic_read(APIC_LINT1) & 0xffff00ff); 85 config |= APIC_LVT_DM_NMI | APIC_LVT_IIPP; 86 apic_write(APIC_LINT1, config); 87 } 88 if (cpu > 0) { 89 dprintf("LINT0: %p\n", (void *)apic_read(APIC_LINT0)); 90 dprintf("LINT1: %p\n", (void *)apic_read(APIC_LINT1)); 91 92 /* disable LINT0/1 */ 93 config = apic_read(APIC_LINT0); 94 apic_write(APIC_LINT0, config | APIC_LVT_MASKED); 95 96 config = apic_read(APIC_LINT1); 97 apic_write(APIC_LINT1, config | APIC_LVT_MASKED); 98 } else { 99 dprintf("0: LINT0: %p\n", (void *)apic_read(APIC_LINT0)); 100 dprintf("0: LINT1: %p\n", (void *)apic_read(APIC_LINT1)); 101 } 102 #endif 103 104 apic_init_timer(args, cpu); 105 106 /* setup error vector to 0xfe */ 107 config = (apic_read(APIC_LVT_ERROR) & 0xffffff00) | 0xfe; 108 apic_write(APIC_LVT_ERROR, config); 109 110 /* accept all interrupts */ 111 config = apic_read(APIC_TASK_PRIORITY) & 0xffffff00; 112 apic_write(APIC_TASK_PRIORITY, config); 113 114 config = apic_read(APIC_SPURIOUS_INTR_VECTOR); 115 apic_write(APIC_EOI, 0); 116 117 return B_OK; 118 } 119 120 121 static int32 122 i386_ici_interrupt(void *data) 123 { 124 // genuine inter-cpu interrupt 125 int cpu = smp_get_current_cpu(); 126 TRACE(("inter-cpu interrupt on cpu %d\n", cpu)); 127 128 // if we are not using the IO APIC we need to acknowledge the 129 // interrupt ourselfs 130 if (!gUsingIOAPIC) 131 apic_write(APIC_EOI, 0); 132 133 return smp_intercpu_int_handler(cpu); 134 } 135 136 137 static int32 138 i386_spurious_interrupt(void *data) 139 { 140 // spurious interrupt 141 TRACE(("spurious interrupt on cpu %ld\n", smp_get_current_cpu())); 142 143 // spurious interrupts must not be acknowledged as it does not expect 144 // a end of interrupt - if we still do it we would loose the next best 145 // interrupt 146 return B_HANDLED_INTERRUPT; 147 } 148 149 150 static int32 151 i386_smp_error_interrupt(void *data) 152 { 153 // smp error interrupt 154 TRACE(("smp error interrupt on cpu %ld\n", smp_get_current_cpu())); 155 156 // if we are not using the IO APIC we need to acknowledge the 157 // interrupt ourselfs 158 if (!gUsingIOAPIC) 159 apic_write(APIC_EOI, 0); 160 161 return B_HANDLED_INTERRUPT; 162 } 163 164 165 status_t 166 arch_smp_init(kernel_args *args) 167 { 168 TRACE(("arch_smp_init: entry\n")); 169 170 if (args->arch_args.apic == NULL) 171 return B_OK; 172 173 sLocalAPIC = args->arch_args.apic; 174 175 // setup some globals 176 memcpy(sCPUAPICIds, args->arch_args.cpu_apic_id, sizeof(args->arch_args.cpu_apic_id)); 177 memcpy(sCPUOSIds, args->arch_args.cpu_os_id, sizeof(args->arch_args.cpu_os_id)); 178 memcpy(sAPICVersions, args->arch_args.cpu_apic_version, sizeof(args->arch_args.cpu_apic_version)); 179 180 // set up the local apic on the boot cpu 181 arch_smp_per_cpu_init(args, 0); 182 183 if (args->num_cpus > 1) { 184 // I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted 185 install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &i386_ici_interrupt, NULL, B_NO_LOCK_VECTOR); 186 install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &i386_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR); 187 install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &i386_spurious_interrupt, NULL, B_NO_LOCK_VECTOR); 188 } 189 190 return B_OK; 191 } 192 193 194 status_t 195 arch_smp_per_cpu_init(kernel_args *args, int32 cpu) 196 { 197 // set up the local apic on the current cpu 198 TRACE(("arch_smp_init_percpu: setting up the apic on cpu %ld\n", cpu)); 199 setup_apic(args, cpu); 200 201 init_sse(); 202 203 return B_OK; 204 } 205 206 207 void 208 arch_smp_send_broadcast_ici(void) 209 { 210 uint32 config; 211 cpu_status state = disable_interrupts(); 212 213 config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; 214 apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED 215 | APIC_INTR_COMMAND_1_ASSERT 216 | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL 217 | APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF); 218 219 restore_interrupts(state); 220 } 221 222 223 void 224 arch_smp_send_ici(int32 target_cpu) 225 { 226 uint32 config; 227 uint32 timeout; 228 cpu_status state; 229 230 state = disable_interrupts(); 231 232 config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK; 233 apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24); 234 235 config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; 236 apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED 237 | APIC_INTR_COMMAND_1_ASSERT 238 | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL 239 | APIC_INTR_COMMAND_1_DEST_FIELD); 240 241 timeout = 100000000; 242 // wait for message to be sent 243 while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0) 244 asm volatile ("pause;"); 245 246 if (timeout == 0) 247 panic("arch_smp_send_ici: timeout, target_cpu %ld", target_cpu); 248 249 restore_interrupts(state); 250 } 251