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 x86_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 x86_spurious_interrupt(void *data) 53 { 54 // spurious interrupt 55 TRACE(("spurious interrupt on cpu %" B_PRId32 "\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 x86_smp_error_interrupt(void *data) 66 { 67 // smp error interrupt 68 TRACE(("smp error interrupt on cpu %" B_PRId32 "\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, &x86_ici_interrupt, NULL, B_NO_LOCK_VECTOR); 95 install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &x86_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR); 96 install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &x86_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 %" B_PRId32 "\n", 108 cpu)); 109 apic_per_cpu_init(args, cpu); 110 111 // setup FPU and SSE if supported 112 x86_init_fpu(); 113 114 return B_OK; 115 } 116 117 118 void 119 arch_smp_send_broadcast_ici(void) 120 { 121 uint32 config; 122 cpu_status state = disable_interrupts(); 123 124 config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; 125 apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED 126 | APIC_INTR_COMMAND_1_ASSERT 127 | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL 128 | APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF); 129 130 restore_interrupts(state); 131 } 132 133 134 void 135 arch_smp_send_ici(int32 target_cpu) 136 { 137 uint32 config; 138 uint32 timeout; 139 cpu_status state; 140 141 state = disable_interrupts(); 142 143 config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK; 144 apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24); 145 146 config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; 147 apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED 148 | APIC_INTR_COMMAND_1_ASSERT 149 | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL 150 | APIC_INTR_COMMAND_1_DEST_FIELD); 151 152 timeout = 100000000; 153 // wait for message to be sent 154 while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0) 155 asm volatile ("pause;"); 156 157 if (timeout == 0) 158 panic("arch_smp_send_ici: timeout, target_cpu %" B_PRId32, target_cpu); 159 160 restore_interrupts(state); 161 } 162