1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/x86/x86_platform.hpp" |
4 | |
5 | #include "mos/device/console.hpp" |
6 | #include "mos/device/serial.hpp" |
7 | #include "mos/device/serial_console.hpp" |
8 | #include "mos/interrupt/interrupt.hpp" |
9 | #include "mos/mm/mm.hpp" |
10 | #include "mos/mm/paging/paging.hpp" |
11 | #include "mos/syslog/printk.hpp" |
12 | #include "mos/tasks/schedule.hpp" |
13 | #include "mos/x86/acpi/acpi.hpp" |
14 | #include "mos/x86/acpi/acpi_types.hpp" |
15 | #include "mos/x86/acpi/madt.hpp" |
16 | #include "mos/x86/cpu/ap_entry.hpp" |
17 | #include "mos/x86/cpu/cpu.hpp" |
18 | #include "mos/x86/descriptors/descriptors.hpp" |
19 | #include "mos/x86/devices/port.hpp" |
20 | #include "mos/x86/devices/rtc.hpp" |
21 | #include "mos/x86/interrupt/apic.hpp" |
22 | #include "mos/x86/mm/paging_impl.hpp" |
23 | #include "mos/x86/x86_interrupt.hpp" |
24 | |
25 | #include <mos_stdlib.hpp> |
26 | #include <mos_string.hpp> |
27 | |
28 | typedef enum : u16 |
29 | { |
30 | COM1 = 0x3F8, |
31 | COM2 = 0x2F8, |
32 | COM3 = 0x3E8, |
33 | COM4 = 0x2E8, |
34 | COM5 = 0x5F8, |
35 | COM6 = 0x4F8, |
36 | COM7 = 0x5E8, |
37 | COM8 = 0x4E8 |
38 | } x86ComPort; |
39 | |
40 | class x86SerialDevice : public ISerialDevice |
41 | { |
42 | x86ComPort port; |
43 | |
44 | public: |
45 | explicit x86SerialDevice(x86ComPort port) : port(port) |
46 | { |
47 | baudrate_divisor = BAUD_RATE_115200; |
48 | char_length = CHAR_LENGTH_8; |
49 | stop_bits = STOP_BITS_15_OR_2; |
50 | parity = PARITY_EVEN; |
51 | } |
52 | |
53 | public: |
54 | u8 read_byte() override |
55 | { |
56 | return port_inb(port); |
57 | } |
58 | |
59 | int write_byte(u8 data) override |
60 | { |
61 | port_outb(port, value: data); |
62 | return 0; |
63 | } |
64 | |
65 | u8 read_register(serial_register_t reg) override |
66 | { |
67 | return port_inb(port: (u16) port + reg); |
68 | } |
69 | |
70 | void write_register(serial_register_t reg, u8 data) override |
71 | { |
72 | port_outb(port: (u16) port + reg, value: data); |
73 | } |
74 | |
75 | bool get_data_ready() override |
76 | { |
77 | return (port_inb(port: port + 5) & 1) != 0; |
78 | } |
79 | }; |
80 | |
81 | static Buffer<MOS_PAGE_SIZE> com1_buf; |
82 | static Buffer<MOS_PAGE_SIZE> com2_buf; |
83 | |
84 | x86SerialDevice com1_device{ COM1 }; |
85 | x86SerialDevice com2_device{ COM2 }; |
86 | |
87 | SerialConsole com1_console{ "com1_console" , CONSOLE_CAP_READ, &com1_buf, &com1_device, LightBlue, Black }; |
88 | SerialConsole com2_console{ "com2_console" , CONSOLE_CAP_READ, &com2_buf, &com2_device, LightBlue, Black }; |
89 | |
90 | mos_platform_info_t *const platform_info = &x86_platform; |
91 | mos_platform_info_t x86_platform = { .boot_console = &com1_console }; |
92 | const acpi_rsdp_t *acpi_rsdp = NULL; |
93 | |
94 | static bool x86_keyboard_handler(u32 irq, void *data) |
95 | { |
96 | MOS_UNUSED(data); |
97 | MOS_ASSERT(irq == IRQ_KEYBOARD); |
98 | int scancode = port_inb(port: 0x60); |
99 | |
100 | pr_info("Keyboard scancode: %x" , scancode); |
101 | return true; |
102 | } |
103 | |
104 | static bool x86_pit_timer_handler(u32 irq, void *data) |
105 | { |
106 | MOS_UNUSED(data); |
107 | MOS_ASSERT(irq == IRQ_PIT_TIMER); |
108 | spinlock_acquire(¤t_thread->state_lock); |
109 | reschedule(); |
110 | return true; |
111 | } |
112 | |
113 | void x86_setup_lapic_timer() |
114 | { |
115 | lapic_set_timer(initial_count: 1000000); |
116 | } |
117 | |
118 | typedef struct _frame |
119 | { |
120 | struct _frame *bp; |
121 | ptr_t ip; |
122 | } frame_t; |
123 | |
124 | void x86_dump_stack_at(ptr_t this_frame, bool can_access_vmaps) |
125 | { |
126 | frame_t *frame = (frame_t *) this_frame; |
127 | |
128 | const bool do_mapped_check = current_cpu->mm_context; |
129 | |
130 | if (unlikely(!do_mapped_check)) |
131 | pr_warn(" no mm context available, mapping checks are disabled (early-boot panic?)" ); |
132 | |
133 | const bool no_relock = do_mapped_check && spinlock_is_locked(lock: ¤t_cpu->mm_context->mm_lock); |
134 | if (no_relock) |
135 | pr_emerg(" mm lock is already held, stack trace may be corrupted" ); |
136 | |
137 | pr_info("-- stack trace:" ); |
138 | for (u32 i = 0; frame; i++) |
139 | { |
140 | #define TRACE_FMT " %-3d [" PTR_FMT "]: " |
141 | if (do_mapped_check) |
142 | { |
143 | const pfn_t pfn = mm_get_phys_addr(current_cpu->mm_context, vaddr: (ptr_t) frame) / MOS_PAGE_SIZE; |
144 | if (!pfn) |
145 | { |
146 | pr_emerg(TRACE_FMT "<corrupted>, aborting backtrace" , i, (ptr_t) frame); |
147 | break; |
148 | } |
149 | } |
150 | |
151 | if (frame->bp == 0) |
152 | { |
153 | // end of stack |
154 | pr_warn(TRACE_FMT "<end>" , i, (ptr_t) 0); |
155 | break; |
156 | } |
157 | else if (frame == frame->bp) |
158 | { |
159 | pr_emerg(TRACE_FMT "<corrupted>, aborting backtrace" , i, (ptr_t) frame); |
160 | break; |
161 | } |
162 | else if (frame->ip >= MOS_KERNEL_START_VADDR) |
163 | { |
164 | pr_warn(TRACE_FMT "%ps" , i, frame->ip, (void *) frame->ip); |
165 | } |
166 | else if (frame->ip == 0) |
167 | { |
168 | pr_warn(TRACE_FMT "<end>" , i, frame->ip); |
169 | break; |
170 | } |
171 | else if (frame->ip < 1 KB) |
172 | { |
173 | pr_emerg(TRACE_FMT "<corrupted?>" , i, frame->ip); |
174 | } |
175 | else if (can_access_vmaps) |
176 | { |
177 | if (!no_relock) |
178 | spinlock_acquire(¤t_cpu->mm_context->mm_lock); |
179 | vmap_t *const vmap = vmap_obtain(current_cpu->mm_context, vaddr: (ptr_t) frame->ip, NULL); |
180 | |
181 | if (vmap && vmap->io) |
182 | { |
183 | char filepath[MOS_PATH_MAX_LENGTH]; |
184 | io_get_name(io: vmap->io, buf: filepath, size: sizeof(filepath)); |
185 | pr_warn(TRACE_FMT "%s (+" PTR_VLFMT ")" , i, frame->ip, filepath, frame->ip - vmap->vaddr + vmap->io_offset); |
186 | } |
187 | else |
188 | { |
189 | pr_warn(TRACE_FMT "<userspace?, unknown>" , i, frame->ip); |
190 | } |
191 | |
192 | if (vmap) |
193 | spinlock_release(&vmap->lock); |
194 | |
195 | if (!no_relock) |
196 | spinlock_release(¤t_cpu->mm_context->mm_lock); |
197 | } |
198 | else |
199 | { |
200 | pr_warn(TRACE_FMT "<unknown>" , i, frame->ip); |
201 | } |
202 | frame = frame->bp; |
203 | } |
204 | #undef TRACE_FMT |
205 | pr_info("-- end of stack trace" ); |
206 | } |
207 | |
208 | void platform_dump_current_stack(void) |
209 | { |
210 | ptr_t frame; |
211 | __asm__("mov %%rbp, %0" : "=r" (frame)); |
212 | x86_dump_stack_at(this_frame: frame, can_access_vmaps: true); |
213 | } |
214 | |
215 | void platform_dump_stack(platform_regs_t *regs) |
216 | { |
217 | x86_dump_stack_at(this_frame: regs->bp, can_access_vmaps: true); |
218 | } |
219 | |
220 | void platform_startup_early() |
221 | { |
222 | console_register(con: &com2_console); |
223 | x86_idt_init(); |
224 | x86_init_percpu_gdt(); |
225 | x86_init_percpu_idt(); |
226 | x86_init_percpu_tss(); |
227 | |
228 | // happens before setting up the kernel MM |
229 | x86_cpu_initialise_caps(); |
230 | |
231 | #if MOS_DEBUG_FEATURE(x86_startup) |
232 | pr_info2("cpu features:" ); |
233 | |
234 | #define do_print_cpu_feature(feature) \ |
235 | if (cpu_has_feature(CPU_FEATURE_##feature)) \ |
236 | pr_cont(" " #feature); |
237 | FOR_ALL_CPU_FEATURES(do_print_cpu_feature) |
238 | |
239 | #undef do_print_cpu_feature |
240 | |
241 | #endif |
242 | |
243 | x86_cpu_setup_xsave_area(); |
244 | } |
245 | |
246 | void platform_startup_setup_kernel_mm() |
247 | { |
248 | x86_paging_setup(); |
249 | } |
250 | |
251 | void platform_startup_late() |
252 | { |
253 | pr_dinfo2(x86_startup, "Parsing ACPI tables..." ); |
254 | |
255 | if (platform_info->arch_info.rsdp_addr) |
256 | { |
257 | pr_dinfo2(x86_startup, "Using RSDP from bootloader: " PTR_FMT, platform_info->arch_info.rsdp_addr); |
258 | acpi_rsdp = (const acpi_rsdp_t *) platform_info->arch_info.rsdp_addr; |
259 | } |
260 | else |
261 | { |
262 | pr_dinfo2(x86_startup, "Searching for RSDP in EBDA..." ); |
263 | acpi_rsdp = acpi_find_rsdp(pa_va(X86_EBDA_MEMREGION_PADDR), EBDA_MEMREGION_SIZE); |
264 | if (!acpi_rsdp) |
265 | { |
266 | pr_dinfo2(x86_startup, "Searching for RSDP in BIOS memory region..." ); |
267 | acpi_rsdp = acpi_find_rsdp(pa_va(X86_BIOS_MEMREGION_PADDR), BIOS_MEMREGION_SIZE); |
268 | if (!acpi_rsdp) |
269 | mos_panic("RSDP not found" ); |
270 | } |
271 | } |
272 | |
273 | const pmm_region_t *acpi_region = pmm_find_reserved_region(needle: acpi_rsdp->v1.rsdt_addr); |
274 | MOS_ASSERT_X(acpi_region && acpi_region->reserved, "ACPI region not found or not reserved" ); |
275 | |
276 | acpi_parse_rsdt(rsdp: acpi_rsdp); |
277 | |
278 | pr_dinfo2(x86_startup, "Initializing APICs..." ); |
279 | madt_parse_table(); |
280 | lapic_enable(); // enable the local APIC |
281 | current_cpu->id = x86_platform.boot_cpu_id = lapic_get_id(); |
282 | |
283 | pic_remap_irq(); |
284 | ioapic_init(); |
285 | |
286 | rtc_init(); |
287 | |
288 | interrupt_handler_register(irq: IRQ_PIT_TIMER, handler: x86_pit_timer_handler, NULL); |
289 | interrupt_handler_register(irq: IRQ_CMOS_RTC, handler: rtc_irq_handler, NULL); |
290 | interrupt_handler_register(irq: IRQ_KEYBOARD, handler: x86_keyboard_handler, NULL); |
291 | interrupt_handler_register(irq: IRQ_COM1, handler: serial_console_irq_handler, data: &com1_console); |
292 | |
293 | ioapic_enable_interrupt(irq: IRQ_CMOS_RTC, lapic_id: x86_platform.boot_cpu_id); |
294 | ioapic_enable_interrupt(irq: IRQ_KEYBOARD, lapic_id: x86_platform.boot_cpu_id); |
295 | ioapic_enable_interrupt(irq: IRQ_COM1, lapic_id: x86_platform.boot_cpu_id); |
296 | |
297 | x86_setup_lapic_timer(); |
298 | |
299 | x86_unblock_aps(); |
300 | } |
301 | |