furix
cute os
kernel/kernel.c
6236 bytes raw
1#include <kernel.h>
2
3#include <acpi.h>
4#include <ap_boot.h>
5#include <gdt.h>
6#include <idt.h>
7#include <io.h>
8#include <ioapic.h>
9#include <lapic.h>
10#include <pic.h>
11#include <percpu.h>
12#include <serial.h>
13
14/* addresses spread across different PML4 slots. OVMF maps the first PML4 entry
15 (0-512 GiB), so low candidates may be present; the high ones are not. we READ
16 rather than write so a mapped candidate cannot corrupt firmware. */
17static const uint64_t probe_addrs[] = {
18 0x0000700000000000ull, /* ~123 TiB (PML4 slot 224, canonical) */
19 0xFFFF800000000000ull, /* kernel half (PML4 slot 256, canonical) */
20 0x0000008000000000ull, /* 512 GiB (PML4 slot 1) */
21};
22
23/*
24 * self-IPI round trip: proves the LAPIC can deliver, the IDT dispatches, and
25 * iretq returns us cleanly. on real hardware this is also how we would verify
26 * a vector before trusting it for inter-CPU traffic.
27 */
28static volatile uint32_t ipi_count;
29static volatile uint64_t ipi_sender_apic_id;
30
31static void on_ipi(void) {
32 ipi_sender_apic_id = lapic_id();
33 ipi_count++;
34}
35
36static bool test_self_ipi(void) {
37 const uint32_t spins = 2000000;
38 uint32_t i;
39
40 ipi_count = 0;
41 idt_set_handler(VECTOR_IPI_TIMER, on_ipi);
42
43 /*
44 * interrupts must be enabled for the IPI to be delivered: our gates are
45 * interrupt gates, so IF clear means the request is latched, not taken.
46 */
47 cpu_sti();
48
49 lapic_ipi_send(LAPIC_IPI_FIXED, LAPIC_SHORTHAND_SELF, VECTOR_IPI_TIMER, 0);
50
51 for (i = 0; i < spins && ipi_count == 0; i++) {
52 /* wait for the interrupt to arrive */
53 }
54
55 cpu_cli();
56
57 serial_puts("apic: self-IPI on vector ");
58 serial_put_hex(VECTOR_IPI_TIMER);
59 serial_puts(" -> count = ");
60 serial_put_hex(ipi_count);
61 serial_puts("\n");
62
63 if (ipi_count == 0) {
64 serial_puts("apic: FAIL (no interrupt delivered)\n");
65 return false;
66 }
67
68 serial_puts("apic: ipi sender apic id = ");
69 serial_put_hex(ipi_sender_apic_id);
70 serial_puts(ipi_sender_apic_id == lapic_id() ? " (ok)\n" : " (WRONG)\n");
71
72 idt_set_handler(VECTOR_IPI_TIMER, NULL);
73 return true;
74}
75
76static bool report_madt(const EFI_SYSTEM_TABLE *st, struct acpi_madt_info *madt) {
77 uint32_t i;
78
79 if (!acpi_madt_parse(st, madt)) {
80 serial_puts("madt: not found\n");
81 return false;
82 }
83
84 serial_puts("madt: cpus = ");
85 serial_put_hex(madt->cpu_count);
86 serial_puts(", ioapics = ");
87 serial_put_hex(madt->ioapic_count);
88 serial_puts("\n apic ids:");
89
90 for (i = 0; i < madt->cpu_count; i++) {
91 serial_puts(" ");
92 serial_put_hex(madt->apic_ids[i]);
93 }
94 serial_puts("\n");
95
96 return true;
97}
98
99void kernel_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *st) {
100 uint64_t trampoline_base = 0;
101 struct acpi_madt_info madt;
102 uint32_t started = 0;
103 uint64_t gs_check;
104 uint32_t count;
105 uint32_t i;
106
107 (void)image;
108
109 /* firmware console first: once our own GDT/IDT are live we must not call UEFI
110 boot services again */
111 st->ConOut->OutputString(st->ConOut, (CHAR16 *)L"meow :3 hello world!\r\n");
112
113 cpu_cli();
114 serial_init();
115 pic_disarm();
116 ioapic_disarm();
117 serial_puts("furix: phase 0\n");
118
119 /*
120 * the LAPIC timer MUST be masked before the first UEFI boot service call
121 * below, not merely before we enable the LAPIC. boot services re-enable
122 * interrupts on return, so the firmware's timer is live across the window
123 * where our IDT is installed but carries no handler for vector 0x20; a tick
124 * there lands on the default gate and halts the kernel, at a point that
125 * wanders with boot timing. observed roughly 1 run in 10 on smp=8.
126 */
127 lapic_enable();
128 serial_puts("lapic: base = ");
129 serial_put_hex(lapic_base());
130 serial_puts(", id = ");
131 serial_put_hex(lapic_id());
132 serial_puts("\n");
133
134 /*
135 * the boot services below can leave IF set on the way out, so re-assert it
136 * here rather than trusting the state we handed them.
137 */
138 cpu_cli();
139
140 /*
141 * must happen before we install our own GDT/IDT: afterwards we must not call
142 * UEFI boot services again, and we need GetMemoryMap to find a low page.
143 */
144 if (!ap_boot_reserve(st, &trampoline_base)) {
145 serial_puts("ERROR: ap trampoline reservation failed\n");
146 for (;;) {
147 cpu_hlt();
148 }
149 }
150
151 gdt_init(); /* GDT + TSS + IST stacks */
152 idt_init(); /* IDT, IST1 on #DF and IST2 on NMI */
153 serial_puts("gdt + tss + ist + idt ok\n");
154
155 percpu_init();
156 serial_puts("percpu: gs load = ");
157 gs_check = percpu_gs_selftest(0xfeedfacecafebeefull);
158 serial_put_hex(gs_check);
159 serial_puts(gs_check == 0xfeedfacecafebeefull ? " (ok)\n" : " (BROKEN)\n");
160 serial_puts("percpu: c load = ");
161 gs_check = percpu_gs_selftest_via_c(0x1111222233334444ull);
162 serial_put_hex(gs_check);
163 serial_puts(gs_check == 0x1111222233334444ull ? " (ok)\n" : " (BROKEN)\n");
164
165 percpu_set_id(lapic_id());
166
167 test_self_ipi();
168
169 if (report_madt(st, &madt)) {
170 /*
171 * bring the APs up before the deliberate page fault below: that probe halts
172 * the kernel, so it has to be the last thing that happens.
173 *
174 * interrupts are OFF for the duration. AP startup is driven entirely by
175 * IPIs sent FROM this CPU, so nothing here needs an interrupt serviced,
176 * and the spin that waits for an AP to report in is long enough (tens of
177 * millions of iterations) for the timer to fire. nothing routes vector
178 * 0x20 yet, so it lands on the default gate, which reports and halts,
179 * which stopped the BSP mid-bringup, making an AP failure and a BSP death
180 * indistinguishable from the outside.
181 */
182 cpu_cli();
183
184 started = ap_boot_start(&madt);
185
186 serial_puts("ap: ");
187 serial_put_hex(started);
188 serial_puts(" of ");
189 serial_put_hex(acpi_madt_ap_count(&madt));
190 serial_puts(" aps started\n");
191 }
192
193 cpu_cli();
194
195 serial_puts("probing unmapped addresses...\n");
196
197 count = (uint32_t)(sizeof(probe_addrs) / sizeof(probe_addrs[0]));
198 for (i = 0; i < count; i++) {
199 volatile uint64_t sink;
200
201 serial_puts(" probing ");
202 serial_put_hex(probe_addrs[i]);
203 serial_puts(" ...\n");
204
205 sink = *(volatile uint64_t *)probe_addrs[i];
206 (void)sink;
207 }
208
209 serial_puts("ERROR: none of the probe addresses faulted\n");
210
211 /* do not return to firmware with our GDT/IDT installed */
212 for (;;) {
213 cpu_hlt();
214 }
215}