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