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 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 init_sse(); 111 112 return B_OK; 113 } 114 115 116 void 117 arch_smp_send_broadcast_ici(void) 118 { 119 uint32 config; 120 cpu_status state = disable_interrupts(); 121 122 config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; 123 apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED 124 | APIC_INTR_COMMAND_1_ASSERT 125 | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL 126 | APIC_INTR_COMMAND_1_DEST_ALL_BUT_SELF); 127 128 restore_interrupts(state); 129 } 130 131 132 void 133 arch_smp_send_ici(int32 target_cpu) 134 { 135 uint32 config; 136 uint32 timeout; 137 cpu_status state; 138 139 state = disable_interrupts(); 140 141 config = apic_read(APIC_INTR_COMMAND_2) & APIC_INTR_COMMAND_2_MASK; 142 apic_write(APIC_INTR_COMMAND_2, config | sCPUAPICIds[target_cpu] << 24); 143 144 config = apic_read(APIC_INTR_COMMAND_1) & APIC_INTR_COMMAND_1_MASK; 145 apic_write(APIC_INTR_COMMAND_1, config | 0xfd | APIC_DELIVERY_MODE_FIXED 146 | APIC_INTR_COMMAND_1_ASSERT 147 | APIC_INTR_COMMAND_1_DEST_MODE_PHYSICAL 148 | APIC_INTR_COMMAND_1_DEST_FIELD); 149 150 timeout = 100000000; 151 // wait for message to be sent 152 while ((apic_read(APIC_INTR_COMMAND_1) & APIC_DELIVERY_STATUS) != 0 && --timeout != 0) 153 asm volatile ("pause;"); 154 155 if (timeout == 0) 156 panic("arch_smp_send_ici: timeout, target_cpu %ld", target_cpu); 157 158 restore_interrupts(state); 159 } 160