1/** 2 * \file 3 * \brief Intel 64 local APIC driver. 4 */ 5 6/* 7 * Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <kernel.h> 16#include <systime.h> 17#include <arch/x86/apic.h> 18#include <arch/x86/start_aps.h> 19#include <paging_kernel_arch.h> 20#include <x86.h> 21 22#include <dev/ia32_dev.h> 23#include <dev/xapic_dev.h> 24 25#define APIC_BASE_ADDRESS_MASK 0x000ffffffffff000 26#define APIC_PAGE_SIZE 4096 27#define APIC_DEFAULT_PRIORITY 0x0000 28 29/// Ratio of APIC frequency to systime frequency * 2^32 30uint64_t apic_systime_frequency_ratio; 31 32/** 33 * The kernel's APIC ID. 34 */ 35uint8_t apic_id; 36 37/** 38 * True if we're on the BSP 39 */ 40bool apic_bsp = true; 41 42static xapic_t apic; 43 44/// TSC deadline functionality 45static bool use_tsc_deadline = false; 46 47/** 48 * \brief Initializes the local APIC timer. 49 * 50 * \param masked Mask interrupt 51 * \param periodic Periodic or one-shot 52 */ 53void apic_timer_init(bool masked, bool periodic) 54{ 55 xapic_lvt_timer_t t = xapic_lvt_timer_initial; 56 t = xapic_lvt_timer_vector_insert(t, APIC_TIMER_INTERRUPT_VECTOR); 57 t = xapic_lvt_timer_mask_insert(t, masked ? xapic_masked : xapic_not_masked); 58 t = xapic_lvt_timer_mode_insert(t, periodic ? xapic_periodic : 59 (use_tsc_deadline && !masked ? xapic_tsc_deadline : xapic_one_shot)); 60 xapic_lvt_timer_wr(&apic, t); 61} 62 63/** 64 * \brief Initializes performance counter overflow interrupt 65 * 66 */ 67void apic_perfcnt_init(void) 68{ 69 // Activate use of APIC performance counter interrupts 70 xapic_lvt_mon_t t = xapic_lvt_perf_cnt_initial; 71 t = xapic_lvt_mon_vector_insert(t, APIC_PERFORMANCE_INTERRUPT_VECTOR); 72 t = xapic_lvt_mon_msgType_insert(t, 0); 73 t = xapic_lvt_mon_mask_insert(t, 0); 74 xapic_lvt_perf_cnt_wr(&apic, t); 75} 76/** 77 * \brief Initializes performance counter overflow interrupt 78 * 79 */ 80void apic_perfcnt_stop(void) 81{ 82 // Deactivate use of APIC performance counter interrupts 83 // Activate use of APIC performance counter interrupts 84 xapic_lvt_mon_t t = xapic_lvt_perf_cnt_initial; 85 t = xapic_lvt_mon_vector_insert(t, APIC_PERFORMANCE_INTERRUPT_VECTOR); 86 t = xapic_lvt_mon_msgType_insert(t, 0); 87 t = xapic_lvt_mon_mask_insert(t, 1); 88 xapic_lvt_perf_cnt_wr(&apic, t); 89} 90 91void apic_timer_set_count(uint32_t count) 92{ 93 xapic_init_count_wr(&apic, count); 94} 95 96uint32_t apic_timer_get_count(void) 97{ 98 return xapic_cur_count_rd(&apic); 99} 100 101void apic_timer_set_divide(xapic_divide_t divide) 102{ 103 xapic_dcr_div_val_wrf(&apic, divide); 104} 105 106/** \brief This function initializes the local APIC. 107 This function initialzes the local APIC by writes to the memory mapped 108 registers and by enabling it in the MSR. 109*/ 110void apic_init(void) 111{ 112 ia32_apic_base_t apic_base_msr = ia32_apic_base_rd(NULL); 113 lpaddr_t apic_phys = ((lpaddr_t)apic_base_msr) & APIC_BASE_ADDRESS_MASK; 114 lvaddr_t apic_base = paging_map_device(apic_phys, APIC_PAGE_SIZE); 115 116 if(apic_base == 0) { 117 panic("apic_init(): could not map APIC registers"); 118 } 119 120 debug(SUBSYS_APIC, "Accessing APIC at 0x%"PRIxLPADDR" / 0x%"PRIxLVADDR"\n", 121 apic_phys, apic_base); 122 xapic_initialize(&apic, (void *)apic_base); 123 124 125 apic_id = apic_get_id(); 126 debug(SUBSYS_APIC, "APIC ID=%hhu\n", apic_id); 127 if (ia32_apic_base_bsp_extract(apic_base_msr)) { 128 debug(SUBSYS_APIC, "APIC: bootstrap processor\n"); 129 apic_bsp = true; 130 } else { 131 debug(SUBSYS_APIC, "APIC: application processor\n"); 132 apic_bsp = false; 133 } 134 135 // initialize spurious interrupt register 136 // no focus, software enabled 137 { 138 xapic_svr_t t = xapic_svr_initial; 139 t = xapic_svr_vector_insert(t, APIC_SPURIOUS_INTERRUPT_VECTOR); 140 t = xapic_svr_enable_insert(t, 1); 141 t = xapic_svr_focus_insert( t, 1); 142 xapic_svr_wr(&apic, t); 143 } 144 145 // Task priority 146 { 147 xapic_priority_t t = xapic_tpr_initial; 148 t = xapic_priority_sub_class_insert(t, APIC_DEFAULT_PRIORITY); 149 t = xapic_priority_priority_insert( t, APIC_DEFAULT_PRIORITY); 150 xapic_tpr_wr(&apic, t); 151 } 152 153 //LVT timer register 154 //set vector number and disable reception of this interrupt 155 { 156 xapic_lvt_timer_t t = xapic_lvt_timer_initial; 157 t = xapic_lvt_timer_vector_insert(t, APIC_TIMER_INTERRUPT_VECTOR); 158 t = xapic_lvt_timer_mask_insert( t, xapic_masked); 159 xapic_lvt_timer_wr(&apic, t); 160 } 161 162 //thermal sensor 163 { 164 xapic_lvt_mon_t t = xapic_lvt_thermal_initial; 165 t = xapic_lvt_timer_vector_insert(t, APIC_THERMAL_INTERRUPT_VECTOR); 166 t = xapic_lvt_timer_mask_insert( t, xapic_masked); 167 xapic_lvt_thermal_wr(&apic, t); 168 } 169 170 /* 171 * TODO: How are the local intercore interrups handled using the APIC? 172 */ 173 //LINT0: external interrupts, delivered by the 8259 PIC 174 //generate extInt as if INTR pin were activated 175 //disabled (we use IOAPICs exclusively) 176 { 177 xapic_lvt_lint_t t = xapic_lvt_lint0_initial; 178 t = xapic_lvt_lint_vector_insert( t, 0); 179 t = xapic_lvt_lint_dlv_mode_insert( t, xapic_extint); 180 t = xapic_lvt_lint_trig_mode_insert(t, xapic_edge); 181 t = xapic_lvt_lint_mask_insert( t, xapic_masked); 182 xapic_lvt_lint0_wr(&apic, t); 183 184 //LINT1: usually used to generate an NMI 185 //generate NMI 186 //disabled (FIXME?) 187 t = xapic_lvt_lint1_initial; 188 t = xapic_lvt_lint_vector_insert( t, 0); 189 t = xapic_lvt_lint_dlv_mode_insert( t, xapic_extint); //xapic_nmi, 190 t = xapic_lvt_lint_trig_mode_insert(t, xapic_edge); 191 t = xapic_lvt_lint_mask_insert( t, xapic_masked); 192 xapic_lvt_lint1_wr(&apic, t); 193 } 194 195 //error interrupt register 196 { 197 xapic_lvt_err_t t = xapic_lvt_err_initial; 198 t = xapic_lvt_err_vector_insert(t, APIC_ERROR_INTERRUPT_VECTOR); 199 t = xapic_lvt_err_mask_insert( t, xapic_not_masked); 200 xapic_lvt_err_wr(&apic, t); 201 } 202 203#if 0 // this doesn't seem necessary, and causes problems on non-AMD HW -AB 204 //we need to enable this bit in the AMD case, but not in the Intel case. 205 if (CPU_IS_M5_SIMULATOR) { 206 printf("Warning: not setting xapic_eacr_wr on M5\n"); 207 } else { 208 // if (strcmp(cpuid_res.cpu_vendor,"AuthenticAMD")==0) { 209 xapic_eacr_iern_wrf(&apic, 1); 210 // } 211 } 212#endif 213 214 // enable the thing, if it wasn't already! 215 if (!(ia32_apic_base_global_extract(apic_base_msr))) { 216 apic_base_msr = ia32_apic_base_global_insert(apic_base_msr, 1); 217 ia32_apic_base_wr(NULL,apic_base_msr); 218 } 219 220 uint32_t eax, ebx, ecx, edx; 221 222 cpuid(1, &eax, &ebx, &ecx, &edx); 223 if (ecx & (1 << 24)) { // check for TSC deadline functionality 224 use_tsc_deadline = true; 225 } 226 // cpuid(0x80000007, &eax, &ebx, &ecx, &edx); 227 // if (edx && (1 << 8)) { // check if TSC is invariant 228 // printf("TSC invariant\n"); 229 // } 230} 231 232/** \brief This function sends an IPI 233 Two INIT IPIs can be sent, assert or de-assert INIT IPIs. This function is 234 called by either apic_send_init_assert or apic_send_init_deassert. 235 apic_init() has to be called once before using this function, 236 because this function doesn't map the physical page of the APIC 237 registers another time. 238 \param int_cmd_1 The structure containing the bits for interrupt 239 command register 1 240 \param destination The destination for the INIT IPI 241 \param wait Should we wait for delivery? 242*/ 243 244static void apic_send_ipi( xapic_icr_lo_t cmd, uint8_t destination, bool wait) 245{ 246 //clear the previous error, if nobody was interested before. 247 //otherwise it isn't possible to send another IPI 248 xapic_esr_rawwr(&apic,0); 249 xapic_esr_rawwr(&apic,0); 250 251 //send the IPI 252 xapic_icr_hi_t t = xapic_icr_hi_initial; 253#if !defined(__k1om__) 254 t = xapic_icr_hi_dest_insert(t, destination); 255#else 256 t = xapic_icr_hi_xeon_phi_dest_insert(t, destination); 257#endif 258 xapic_icr_hi_rawwr(&apic, t); 259 xapic_icr_lo_rawwr(&apic, cmd); 260 261 // Wait for delivery 262 while( wait && xapic_icr_lo_dlv_stat_rdf(&apic) ); 263} 264 265/** \brief Send an INIT IPI (assert mode) 266 This function sends an INIT IPI to the destination processor 267 or the processors included in the shorthand in assert mode. 268 \param destination The destination, if shorthand = xapic_none 269 \param destination_shorthand THe shorthand of the destination which may be 270 xapic_none, xapic_self, xapic_all_inc or xapic_all_exc 271*/ 272 273void apic_send_init_assert(uint8_t destination, uint8_t destination_shorthand) 274{ 275 xapic_icr_lo_t t = xapic_icr_lo_initial; 276 t = xapic_icr_lo_vector_insert( t, 0); 277 t = xapic_icr_lo_dlv_mode_insert( t, xapic_init); 278 t = xapic_icr_lo_dst_mode_insert( t, xapic_dst_phys); 279 t = xapic_icr_lo_level_insert( t, xapic_lvl_set); 280 t = xapic_icr_lo_trig_mode_insert(t, xapic_level); 281 t = xapic_icr_lo_dst_short_insert(t, destination_shorthand); 282 apic_send_ipi( t, destination, true ); 283} 284 285/** \brief Send an INIT IPI (de-assert mode) 286 This function sends an INIT IPI to all processors. INIT IPIs in 287 de-assert mode are always sent to all processors, regardless of the 288 destination value and the destination shorthand value. 289*/ 290 291void apic_send_init_deassert(void) 292{ 293 xapic_icr_lo_t t = xapic_icr_lo_initial; 294 t = xapic_icr_lo_vector_insert( t, 0); 295 t = xapic_icr_lo_dlv_mode_insert( t, xapic_init); 296 t = xapic_icr_lo_dst_mode_insert( t, xapic_dst_phys); 297 t = xapic_icr_lo_level_insert( t, xapic_lvl_clr); 298 t = xapic_icr_lo_trig_mode_insert(t, xapic_level); 299 t = xapic_icr_lo_dst_short_insert(t, xapic_all_inc); 300 apic_send_ipi( t, 0, true ); 301} 302 303 304/** \brief Send a Start-Up IPI 305 306 This function sends a Start-Up IPI to the destination processor 307 or the processors included in the shorthand. 308 \param destination The destination, if shorthand = xapic_none 309 \param destination_shorthand THe shorthand of the destination which may be 310 xapic_none, xapic_self, xapic_all_inc or xapic_all_exc 311*/ 312 313void apic_send_start_up(uint8_t destination, 314 uint8_t destination_shorthand, 315 uint8_t realmode_startpage) 316{ 317 xapic_icr_lo_t t = xapic_icr_lo_initial; 318 t = xapic_icr_lo_vector_insert( t, realmode_startpage); 319 t = xapic_icr_lo_dlv_mode_insert( t, xapic_startup); 320 t = xapic_icr_lo_dst_mode_insert( t, xapic_dst_phys); 321 t = xapic_icr_lo_level_insert( t, xapic_lvl_set); 322 t = xapic_icr_lo_trig_mode_insert(t, xapic_edge); 323 t = xapic_icr_lo_dst_short_insert(t, destination_shorthand); 324 apic_send_ipi( t, destination, true ); 325} 326 327 328/** \brief Send an standard IPI to the definded target APIC 329 This function sends an standard IPI to the target APIC 330 \param destination The destination, if shorthand = xapic_none 331 \param destination_shorthand THe shorthand of the destination which may be 332 xapic_none, xapic_self, xapic_all_inc or xapic_all_exc 333*/ 334 335void apic_send_std_ipi(uint8_t destination, 336 uint8_t destination_shorthand, 337 uint8_t vector) 338{ 339 xapic_icr_lo_t t = xapic_icr_lo_initial; 340 t = xapic_icr_lo_vector_insert( t, vector); 341 t = xapic_icr_lo_dlv_mode_insert( t, xapic_fixed); 342 t = xapic_icr_lo_dst_mode_insert( t, xapic_dst_phys); 343 t = xapic_icr_lo_level_insert( t, xapic_lvl_clr); 344 t = xapic_icr_lo_trig_mode_insert(t, xapic_edge); 345 t = xapic_icr_lo_dst_short_insert(t, destination_shorthand); 346 apic_send_ipi( t, destination, false ); 347} 348 349/** \brief This function sends an EOI to the local APIC 350 An End-Of-Interrupt is sent to the local APIC. This function should be 351 called at the end of an interrupt handler. 352*/ 353void apic_eoi(void) 354{ 355 //has to be 0 for future compability 356 xapic_eoi_wr(&apic,0); 357} 358 359/** \brief This function sends a specific EOI to the local APIC 360 A specific End-Of-Interrupt is sent to the local APIC. This function should 361 be called at the end of an interrupt handler. 362*/ 363void apic_seoi(uint8_t int_nr) 364{ 365 xapic_seoi_vector_wrf(&apic, int_nr); 366} 367 368uint8_t apic_get_id(void) 369{ 370#if !defined(__k1om__) 371 return xapic_id_id_rdf(&apic); 372#else 373 return xapic_id_xeon_phi_id_rdf(&apic); 374#endif 375} 376 377/** 378 * Mask the timer interrupt. 379 */ 380void apic_mask_timer(void) 381{ 382 xapic_lvt_timer_mask_wrf(&apic, xapic_masked); 383} 384 385/** 386 * Unmask the timer interrupt. 387 */ 388void apic_unmask_timer(void) 389{ 390 xapic_lvt_timer_mask_wrf(&apic, xapic_not_masked); 391} 392 393bool arch_core_is_bsp(void) 394{ 395 return apic_is_bsp(); 396} 397 398xapic_esr_t apic_get_esr(void) 399{ 400 // 10.5.3: Have to write to ESR before reading it 401 xapic_esr_rawwr(&apic, 0); 402 return xapic_esr_rd(&apic); 403} 404 405void apic_disable(void) { 406 ia32_apic_base_t apic_base_msr = ia32_apic_base_rd(NULL); 407 apic_base_msr = ia32_apic_base_global_insert(apic_base_msr, 0); 408 ia32_apic_base_wr(NULL, apic_base_msr); 409} 410 411static uint32_t systime_to_apic_ticks(systime_t ticks) 412{ 413 uint64_t q, r; 414 415 q = ticks >> 32; 416 r = ticks & 0xffffffff; 417 uint64_t rt = q * apic_systime_frequency_ratio + 418 ((r * apic_systime_frequency_ratio) >> 32); 419 return MIN(rt, UINT32_MAX); 420} 421 422void systime_set_timeout(systime_t absolute_timeout) 423{ 424 if (use_tsc_deadline) { // we have TSC deadline 425 ia32_tsc_deadline_wr(NULL, absolute_timeout); 426 } else { // fallback to APIC timer 427 uint32_t apic_ticks = 1; 428 429 systime_t now = systime_now(); 430 if (absolute_timeout > now) { 431 apic_ticks = systime_to_apic_ticks(absolute_timeout - now); 432 if (apic_ticks == 0) 433 apic_ticks = 1; 434 } 435 apic_timer_set_count(apic_ticks); 436 } 437} 438 439void systime_set_timer(systime_t relative_timeout) 440{ 441 if (use_tsc_deadline) { // we have TSC deadline 442 systime_t now = systime_now(); 443 ia32_tsc_deadline_wr(NULL, now + relative_timeout); 444 } else { // fallback to APIC timer 445 uint32_t apic_ticks; 446 447 apic_ticks = systime_to_apic_ticks(relative_timeout); 448 if (apic_ticks == 0) 449 apic_ticks = 1; 450 apic_timer_set_count(apic_ticks); 451 } 452} 453