Timer re-worked!
[lunaix-os.git] / lunaix-os / hal / apic.c
1 /**
2  * @file apic.c
3  * @author Lunaixsky
4  * @brief Abstraction for Advanced Programmable Interrupts Controller (APIC)
5  * @version 0.1
6  * @date 2022-03-06
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11 #include <hal/apic.h>
12 #include <hal/cpu.h>
13 #include <hal/pic.h>
14 #include <hal/rtc.h>
15
16 #include <arch/x86/interrupts.h>
17
18 #include <lunaix/spike.h>
19 #include <lunaix/syslog.h>
20
21 LOG_MODULE("APIC")
22
23 void
24 apic_setup_timer();
25
26 void
27 apic_setup_lvts();
28
29 void
30 apic_init()
31 {
32     // ensure that external interrupt is disabled
33     cpu_disable_interrupt();
34
35     // Make sure the APIC is there
36     //  FUTURE: Use 8259 as fallback
37     assert_msg(cpu_has_apic(), "No APIC detected!");
38
39     // As we are going to use APIC, disable the old 8259 PIC
40     pic_disable();
41
42     // Hardware enable the APIC
43     // By setting bit 11 of IA32_APIC_BASE register
44     // Note: After this point, you can't disable then re-enable it until a reset (i.e., reboot)
45     asm volatile (
46         "movl %0, %%ecx\n"
47         "rdmsr\n"
48         "orl %1, %%eax\n"
49         "wrmsr\n"
50         ::"i"(IA32_APIC_BASE_MSR), "i"(IA32_APIC_ENABLE)
51         : "eax", "ecx", "edx"
52     );
53
54     // Print the basic information of our current local APIC
55     uint32_t apic_id = apic_read_reg(APIC_IDR) >> 24;
56     uint32_t apic_ver = apic_read_reg(APIC_VER);
57
58     kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u\n",
59            apic_id,
60            apic_ver & 0xff,
61            (apic_ver >> 16) & 0xff);
62
63     // initialize the local vector table (LVT)
64     apic_setup_lvts();
65
66     // initialize priority registers
67     
68     // set the task priority to the lowest possible, so all external interrupts are acceptable
69     //   Note, the lowest possible priority class is 2, not 0, 1, as they are reserved for
70     //   internal interrupts (vector 0-31, and each p-class resposible for 16 vectors). 
71     //   See Intel Manual Vol. 3A, 10-29
72     apic_write_reg(APIC_TPR, APIC_PRIORITY(2, 0));
73
74     // enable APIC
75     uint32_t spiv = apic_read_reg(APIC_SPIVR);
76
77     // install our handler for spurious interrupt.
78     spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE  | APIC_SPIV_IV;
79     apic_write_reg(APIC_SPIVR, spiv);
80 }
81
82 #define LVT_ENTRY_LINT0(vector)           (LVT_DELIVERY_FIXED | vector)
83
84 // Pin LINT#1 is configured for relaying NMI, but we masked it here as I think
85 //  it is too early for that
86 // LINT#1 *must* be edge trigged (Intel manual vol3. 10-14)
87 #define LVT_ENTRY_LINT1                   (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE)
88 #define LVT_ENTRY_ERROR(vector)           (LVT_DELIVERY_FIXED | vector)
89
90 void
91 apic_setup_lvts()
92 {
93     apic_write_reg(APIC_LVT_LINT0, LVT_ENTRY_LINT0(APIC_LINT0_IV));
94     apic_write_reg(APIC_LVT_LINT1, LVT_ENTRY_LINT1);
95     apic_write_reg(APIC_LVT_ERROR, LVT_ENTRY_ERROR(APIC_ERROR_IV));
96 }