1 /* 2 * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 6 * Distributed under the terms of the MIT License. 7 * 8 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 9 * Distributed under the terms of the NewOS License. 10 */ 11 12 #include <arch/x86/apic.h> 13 #include <arch/x86/msi.h> 14 15 #include <debug.h> 16 #include <safemode.h> 17 #include <vm/vm.h> 18 19 #include "timers/apic_timer.h" 20 21 22 static void *sLocalAPIC = NULL; 23 static bool sX2APIC = false; 24 25 26 bool 27 apic_available() 28 { 29 return sLocalAPIC != NULL || sX2APIC; 30 } 31 32 33 uint32 34 apic_read(uint32 offset) 35 { 36 return *(volatile uint32 *)((char *)sLocalAPIC + offset); 37 } 38 39 40 void 41 apic_write(uint32 offset, uint32 data) 42 { 43 *(volatile uint32 *)((char *)sLocalAPIC + offset) = data; 44 } 45 46 47 uint32 48 apic_local_id() 49 { 50 if (sX2APIC) 51 return x86_read_msr(IA32_MSR_APIC_ID); 52 else 53 return (apic_read(APIC_ID) & 0xffffffff) >> 24; 54 } 55 56 57 uint32 58 apic_version() 59 { 60 if (sX2APIC) 61 return x86_read_msr(IA32_MSR_APIC_VERSION); 62 else 63 return apic_read(APIC_VERSION); 64 } 65 66 67 uint32 68 apic_task_priority() 69 { 70 if (sX2APIC) 71 return x86_read_msr(IA32_MSR_APIC_TASK_PRIORITY); 72 else 73 return apic_read(APIC_TASK_PRIORITY); 74 } 75 76 77 void 78 apic_set_task_priority(uint32 config) 79 { 80 if (sX2APIC) 81 x86_write_msr(IA32_MSR_APIC_TASK_PRIORITY, config); 82 else 83 apic_write(APIC_TASK_PRIORITY, config); 84 } 85 86 87 void 88 apic_end_of_interrupt() 89 { 90 if (sX2APIC) 91 x86_write_msr(IA32_MSR_APIC_EOI, 0); 92 else 93 apic_write(APIC_EOI, 0); 94 } 95 96 97 void 98 apic_disable_local_ints() 99 { 100 // just clear them out completely 101 if (sX2APIC) { 102 x86_write_msr(IA32_MSR_APIC_LVT_LINT0, APIC_LVT_MASKED); 103 x86_write_msr(IA32_MSR_APIC_LVT_LINT1, APIC_LVT_MASKED); 104 } else { 105 apic_write(APIC_LVT_LINT0, APIC_LVT_MASKED); 106 apic_write(APIC_LVT_LINT1, APIC_LVT_MASKED); 107 } 108 } 109 110 111 uint32 112 apic_spurious_intr_vector() 113 { 114 if (sX2APIC) 115 return x86_read_msr(IA32_MSR_APIC_SPURIOUS_INTR_VECTOR); 116 else 117 return apic_read(APIC_SPURIOUS_INTR_VECTOR); 118 } 119 120 121 void 122 apic_set_spurious_intr_vector(uint32 config) 123 { 124 if (sX2APIC) 125 x86_write_msr(IA32_MSR_APIC_SPURIOUS_INTR_VECTOR, config); 126 else 127 apic_write(APIC_SPURIOUS_INTR_VECTOR, config); 128 } 129 130 131 uint32 132 apic_intr_command_1() 133 { 134 if (sX2APIC) 135 return x86_read_msr(IA32_MSR_APIC_INTR_COMMAND) & 0xffffffff; 136 else 137 return apic_read(APIC_INTR_COMMAND_1); 138 } 139 140 141 void 142 apic_set_intr_command_1(uint32 config) 143 { 144 if (sX2APIC) { 145 uint64 value = x86_read_msr(IA32_MSR_APIC_INTR_COMMAND); 146 arch_cpu_memory_read_write_barrier(); 147 x86_write_msr(IA32_MSR_APIC_INTR_COMMAND, 148 (value & 0xffffffff00000000LL) | config); 149 } else 150 apic_write(APIC_INTR_COMMAND_1, config); 151 } 152 153 154 uint32 155 apic_intr_command_2() 156 { 157 if (sX2APIC) 158 return x86_read_msr(IA32_MSR_APIC_INTR_COMMAND) >> 32; 159 else 160 return apic_read(APIC_INTR_COMMAND_2); 161 } 162 163 164 void 165 apic_set_intr_command_2(uint32 config) 166 { 167 if (sX2APIC) { 168 uint64 value = x86_read_msr(IA32_MSR_APIC_INTR_COMMAND); 169 arch_cpu_memory_read_write_barrier(); 170 x86_write_msr(IA32_MSR_APIC_INTR_COMMAND, 171 (value & 0xffffffff) | ((uint64)config << 32)); 172 } else 173 apic_write(APIC_INTR_COMMAND_2, config); 174 } 175 176 177 uint32 178 apic_lvt_timer() 179 { 180 if (sX2APIC) 181 return x86_read_msr(IA32_MSR_APIC_LVT_TIMER); 182 else 183 return apic_read(APIC_LVT_TIMER); 184 } 185 186 187 void 188 apic_set_lvt_timer(uint32 config) 189 { 190 if (sX2APIC) 191 x86_write_msr(IA32_MSR_APIC_LVT_TIMER, config); 192 else 193 apic_write(APIC_LVT_TIMER, config); 194 } 195 196 197 uint32 198 apic_lvt_error() 199 { 200 if (sX2APIC) 201 return x86_read_msr(IA32_MSR_APIC_LVT_ERROR); 202 else 203 return apic_read(APIC_LVT_ERROR); 204 } 205 206 207 void 208 apic_set_lvt_error(uint32 config) 209 { 210 if (sX2APIC) 211 x86_write_msr(IA32_MSR_APIC_LVT_ERROR, config); 212 else 213 apic_write(APIC_LVT_ERROR, config); 214 } 215 216 217 uint32 218 apic_lvt_initial_timer_count() 219 { 220 if (sX2APIC) 221 return x86_read_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT); 222 else 223 return apic_read(APIC_INITIAL_TIMER_COUNT); 224 } 225 226 227 void 228 apic_set_lvt_initial_timer_count(uint32 config) 229 { 230 if (sX2APIC) 231 x86_write_msr(IA32_MSR_APIC_INITIAL_TIMER_COUNT, config); 232 else 233 apic_write(APIC_INITIAL_TIMER_COUNT, config); 234 } 235 236 237 uint32 238 apic_lvt_timer_divide_config() 239 { 240 if (sX2APIC) 241 return x86_read_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG); 242 else 243 return apic_read(APIC_TIMER_DIVIDE_CONFIG); 244 } 245 246 247 void 248 apic_set_lvt_timer_divide_config(uint32 config) 249 { 250 if (sX2APIC) 251 x86_write_msr(IA32_MSR_APIC_TIMER_DIVIDE_CONFIG, config); 252 else 253 apic_write(APIC_TIMER_DIVIDE_CONFIG, config); 254 } 255 256 257 status_t 258 apic_init(kernel_args *args) 259 { 260 if (args->arch_args.apic == NULL) 261 return B_NO_INIT; 262 263 if (x86_check_feature(IA32_FEATURE_EXT_X2APIC, FEATURE_EXT)) { 264 dprintf("found x2apic\n"); 265 #if 0 266 if (!get_safemode_boolean(B_SAFEMODE_DISABLE_X2APIC, false)) { 267 uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE); 268 if ((apic_base & IA32_MSR_APIC_BASE_X2APIC) == 0) { 269 x86_write_msr(IA32_MSR_APIC_BASE, apic_base 270 | IA32_MSR_APIC_BASE_X2APIC); 271 } 272 sX2APIC = true; 273 return B_OK; 274 } 275 276 dprintf("x2apic disabled per safemode setting\n"); 277 #else 278 if (get_safemode_boolean(B_SAFEMODE_ENABLE_X2APIC, false)) { 279 uint64 apic_base = x86_read_msr(IA32_MSR_APIC_BASE); 280 if ((apic_base & IA32_MSR_APIC_BASE_X2APIC) == 0) { 281 x86_write_msr(IA32_MSR_APIC_BASE, apic_base 282 | IA32_MSR_APIC_BASE_X2APIC); 283 } 284 sX2APIC = true; 285 286 dprintf("x2apic enabled per safemode setting\n"); 287 return B_OK; 288 } 289 #endif 290 } 291 292 sLocalAPIC = args->arch_args.apic; 293 dprintf("mapping local apic at %p\n", sLocalAPIC); 294 if (vm_map_physical_memory(B_SYSTEM_TEAM, "local apic", &sLocalAPIC, 295 B_EXACT_ADDRESS, B_PAGE_SIZE, 296 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 297 args->arch_args.apic_phys, true) < 0) { 298 panic("mapping the local apic failed"); 299 return B_ERROR; 300 } 301 302 return B_OK; 303 } 304 305 306 status_t 307 apic_per_cpu_init(kernel_args *args, int32 cpu) 308 { 309 dprintf("setting up apic for CPU %" B_PRId32 ": apic id %" B_PRIu32 ", " 310 "version %" B_PRIu32 "\n", cpu, apic_local_id(), apic_version()); 311 312 /* set spurious interrupt vector to 0xff */ 313 uint32 config = apic_spurious_intr_vector() & 0xffffff00; 314 config |= APIC_ENABLE | 0xff; 315 apic_set_spurious_intr_vector(config); 316 317 // don't touch the LINT0/1 configuration in virtual wire mode 318 // ToDo: implement support for other modes... 319 #if 0 320 if (cpu == 0) { 321 /* setup LINT0 as ExtINT */ 322 config = (apic_read(APIC_LINT0) & 0xffff00ff); 323 config |= APIC_LVT_DM_ExtINT | APIC_LVT_IIPP | APIC_LVT_TM; 324 apic_write(APIC_LINT0, config); 325 326 /* setup LINT1 as NMI */ 327 config = (apic_read(APIC_LINT1) & 0xffff00ff); 328 config |= APIC_LVT_DM_NMI | APIC_LVT_IIPP; 329 apic_write(APIC_LINT1, config); 330 } 331 if (cpu > 0) { 332 dprintf("LINT0: %p\n", (void *)apic_read(APIC_LINT0)); 333 dprintf("LINT1: %p\n", (void *)apic_read(APIC_LINT1)); 334 335 /* disable LINT0/1 */ 336 config = apic_read(APIC_LINT0); 337 apic_write(APIC_LINT0, config | APIC_LVT_MASKED); 338 339 config = apic_read(APIC_LINT1); 340 apic_write(APIC_LINT1, config | APIC_LVT_MASKED); 341 } else { 342 dprintf("0: LINT0: %p\n", (void *)apic_read(APIC_LINT0)); 343 dprintf("0: LINT1: %p\n", (void *)apic_read(APIC_LINT1)); 344 } 345 #endif 346 347 apic_timer_per_cpu_init(args, cpu); 348 349 /* setup error vector to 0xfe */ 350 config = (apic_lvt_error() & 0xffffff00) | 0xfe; 351 apic_set_lvt_error(config); 352 353 /* accept all interrupts */ 354 config = apic_task_priority() & 0xffffff00; 355 apic_set_task_priority(config); 356 357 config = apic_spurious_intr_vector(); 358 apic_end_of_interrupt(); 359 360 return B_OK; 361 } 362