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