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 | |
28 | static u8 com1_buf[MOS_PAGE_SIZE] __aligned(MOS_PAGE_SIZE) = { 0 }; |
29 | static u8 com2_buf[MOS_PAGE_SIZE] __aligned(MOS_PAGE_SIZE) = { 0 }; |
30 | |
31 | serial_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 | |
47 | serial_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 | |
63 | mos_platform_info_t *const platform_info = &x86_platform; |
64 | mos_platform_info_t x86_platform = { .boot_console = &com1_console.con }; |
65 | const acpi_rsdp_t *acpi_rsdp = NULL; |
66 | |
67 | static 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 | |
77 | static bool x86_pit_timer_handler(u32 irq, void *data) |
78 | { |
79 | MOS_UNUSED(data); |
80 | MOS_ASSERT(irq == IRQ_PIT_TIMER); |
81 | spinlock_acquire(¤t_thread->state_lock); |
82 | reschedule(); |
83 | return true; |
84 | } |
85 | |
86 | void x86_setup_lapic_timer() |
87 | { |
88 | lapic_set_timer(initial_count: 1000000); |
89 | } |
90 | |
91 | typedef struct _frame |
92 | { |
93 | struct _frame *bp; |
94 | ptr_t ip; |
95 | } frame_t; |
96 | |
97 | void 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: ¤t_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(¤t_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(¤t_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 | |
181 | void 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 | |
188 | void platform_dump_stack(platform_regs_t *regs) |
189 | { |
190 | x86_dump_stack_at(this_frame: regs->bp, can_access_vmaps: true); |
191 | } |
192 | |
193 | void 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 | |
219 | void platform_startup_setup_kernel_mm() |
220 | { |
221 | x86_paging_setup(); |
222 | } |
223 | |
224 | void 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 | |