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