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