SMP Booting, APIC, and IRQs
[akaros.git] / kern / apic.h
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * See LICENSE for details.
4  */
5
6 #ifndef ROS_KERN_APIC_H
7 #define ROS_KERN_APIC_H
8
9 /* 
10  * Functions and definitions for dealing with the APIC and PIC, specific to
11  * Intel.  Does not handle an x2APIC.
12  */
13
14 #include <inc/mmu.h>
15 #include <inc/x86.h>
16
17 // PIC
18 #define PIC1_CMD                                        0x20
19 #define PIC1_DATA                                       0x21
20 #define PIC2_CMD                                        0xA0
21 #define PIC2_DATA                                       0xA1
22 // These are also hardcoded into the IRQ_HANDLERs of kern/trapentry.S
23 #define PIC1_OFFSET                                     0x20
24 #define PIC2_OFFSET                                     0x28
25 #define PIC_EOI                                         0x20
26
27 // Local APIC
28 #define LAPIC_BASE                                      0xfee00000 // this is the default, can be changed
29 #define LAPIC_EOI                                       (LAPIC_BASE + 0x0b0)
30 #define LAPIC_SPURIOUS                          (LAPIC_BASE + 0x0f0)
31 #define LAPIC_VERSION                           (LAPIC_BASE + 0x030)
32 #define LAPIC_ERROR                                     (LAPIC_BASE + 0x280)
33 #define LAPIC_ID                                        (LAPIC_BASE + 0x020)
34 // LAPIC Local Vector Table
35 #define LAPIC_LVT_TIMER                         (LAPIC_BASE + 0x320)
36 #define LAPIC_LVT_LINT0                         (LAPIC_BASE + 0x350)
37 #define LAPIC_LVT_LINT1                         (LAPIC_BASE + 0x360)
38 #define LAPIC_LVT_ERROR                         (LAPIC_BASE + 0x370)
39 #define LAPIC_LVT_PERFMON                       (LAPIC_BASE + 0x340)
40 #define LAPIC_LVT_THERMAL                       (LAPIC_BASE + 0x330)
41 #define LAPIC_LVT_MASK                          0x00010000
42 // LAPIC Timer
43 #define LAPIC_TIMER_INIT                        (LAPIC_BASE + 0x380)
44 #define LAPIC_TIMER_CURRENT                     (LAPIC_BASE + 0x390)
45 #define LAPIC_TIMER_DIVIDE                      (LAPIC_BASE + 0x3e0)
46 // IPI Interrupt Command Register
47 #define LAPIC_IPI_ICR_LOWER                     (LAPIC_BASE + 0x300)
48 #define LAPIC_IPI_ICR_UPPER                     (LAPIC_BASE + 0x310)
49
50 // IOAPIC
51 #define IOAPIC_BASE                                     0xfec00000 // this is the default, can be changed
52
53 void remap_pic(void);
54 void lapic_set_timer(uint32_t ticks, uint8_t vector, bool periodic);
55 uint32_t lapic_get_default_id(void);
56
57 static inline void pic_send_eoi(uint32_t irq);
58 static inline void lapic_send_eoi(void);
59 static inline uint32_t lapic_get_version(void);
60 static inline uint32_t lapic_get_error(void);
61 static inline uint32_t lapic_get_id(void);
62 static inline void lapic_disable(void);
63 static inline void lapic_enable(void);
64 static inline void lapic_wait_to_send(void);
65 static inline void send_init_ipi(void);
66 static inline void send_startup_ipi(uint8_t vector);
67 static inline void send_self_ipi(uint8_t vector);
68 static inline void send_broadcast_ipi(uint8_t vector);
69 static inline void send_all_others_ipi(uint8_t vector);
70 static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector);
71
72 #define mask_lapic_lvt(entry) \
73         write_mmreg32(entry, read_mmreg32(entry) | LAPIC_LVT_MASK)
74 #define unmask_lapic_lvt(entry) \
75         write_mmreg32(entry, read_mmreg32(entry) & ~LAPIC_LVT_MASK)
76
77 static inline void pic_send_eoi(uint32_t irq)
78 {
79         // all irqs beyond the first seven need to be chained to the slave
80         if (irq > 7)
81                 outb(PIC2_CMD, PIC_EOI);
82         outb(PIC1_CMD, PIC_EOI);
83 }
84
85 static inline void lapic_send_eoi(void)
86 {
87         write_mmreg32(LAPIC_EOI, 0);
88 }
89
90 static inline uint32_t lapic_get_version(void)
91 {
92         return read_mmreg32(LAPIC_VERSION);     
93 }
94
95 static inline uint32_t lapic_get_error(void)
96 {
97         write_mmreg32(LAPIC_ERROR, 0xdeadbeef);
98         return read_mmreg32(LAPIC_ERROR);
99 }
100
101 static inline uint32_t lapic_get_id(void)
102 {
103         return read_mmreg32(LAPIC_ID) >> 24;
104 }
105
106 /* There are a couple ways to do it.  The MSR route doesn't seem to work
107  * in KVM.  It's also a somewhat permanent thing
108  */
109 static inline void lapic_disable(void)
110 {
111         write_mmreg32(LAPIC_SPURIOUS, read_mmreg32(LAPIC_SPURIOUS) & 0xffffefff);
112         //write_msr(IA32_APIC_BASE, read_msr(IA32_APIC_BASE) & ~MSR_APIC_ENABLE);
113 }
114
115 /* Spins until previous IPIs are delivered.  Not sure if we want it inlined
116  * Also not sure when we really need to do this. 
117  */
118 static inline void lapic_wait_to_send(void)
119 {
120         while(read_mmreg32(LAPIC_IPI_ICR_LOWER) & 0x1000)
121                 asm volatile("pause");
122 }
123
124 static inline void lapic_enable(void)
125 {
126         write_mmreg32(LAPIC_SPURIOUS, read_mmreg32(LAPIC_SPURIOUS) | 0x00001000);
127 }
128
129 static inline void send_init_ipi(void)
130 {
131         write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4500);
132 }
133
134 static inline void send_startup_ipi(uint8_t vector)
135 {
136         write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4600 | vector);
137 }
138
139 static inline void send_self_ipi(uint8_t vector)
140 {
141         write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00044000 | vector);
142 }
143
144 static inline void send_broadcast_ipi(uint8_t vector)
145 {
146         write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00084000 | vector);
147 }
148
149 static inline void send_all_others_ipi(uint8_t vector)
150 {
151         write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4000 | vector);
152 }
153
154 static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector)
155 {
156         write_mmreg32(LAPIC_IPI_ICR_UPPER, dest << 24);
157         write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00004000 | (logical_mode << 11) | vector);
158 }
159
160 /* To change the LAPIC Base (not recommended):
161         msr_val = read_msr(IA32_APIC_BASE);
162         msr_val = msr_val & ~MSR_APIC_BASE_ADDRESS | 0xfaa00000;
163         write_msr(IA32_APIC_BASE, msr_val);
164 */
165 #endif /* ROS_KERN_APIC_H */