| 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 <ansi_colors.h> |
| 26 | #include <mos_stdlib.hpp> |
| 27 | #include <mos_string.hpp> |
| 28 | |
| 29 | typedef enum : u16 |
| 30 | { |
| 31 | COM1 = 0x3F8, |
| 32 | COM2 = 0x2F8, |
| 33 | COM3 = 0x3E8, |
| 34 | COM4 = 0x2E8, |
| 35 | COM5 = 0x5F8, |
| 36 | COM6 = 0x4F8, |
| 37 | COM7 = 0x5E8, |
| 38 | COM8 = 0x4E8 |
| 39 | } x86ComPort; |
| 40 | |
| 41 | class x86SerialDevice : public ISerialDevice |
| 42 | { |
| 43 | x86ComPort port; |
| 44 | |
| 45 | public: |
| 46 | explicit x86SerialDevice(x86ComPort port) : port(port) |
| 47 | { |
| 48 | baudrate_divisor = BAUD_RATE_115200; |
| 49 | char_length = CHAR_LENGTH_8; |
| 50 | stop_bits = STOP_BITS_15_OR_2; |
| 51 | parity = PARITY_EVEN; |
| 52 | } |
| 53 | |
| 54 | public: |
| 55 | u8 ReadByte() override |
| 56 | { |
| 57 | return port_inb(port); |
| 58 | } |
| 59 | |
| 60 | int WriteByte(u8 data) override |
| 61 | { |
| 62 | port_outb(port, value: data); |
| 63 | return 0; |
| 64 | } |
| 65 | |
| 66 | u8 read_register(serial_register_t reg) override |
| 67 | { |
| 68 | return port_inb(port: (u16) port + reg); |
| 69 | } |
| 70 | |
| 71 | void write_register(serial_register_t reg, u8 data) override |
| 72 | { |
| 73 | port_outb(port: (u16) port + reg, value: data); |
| 74 | } |
| 75 | |
| 76 | bool get_data_ready() override |
| 77 | { |
| 78 | return (port_inb(port: port + 5) & 1) != 0; |
| 79 | } |
| 80 | }; |
| 81 | |
| 82 | static Buffer<MOS_PAGE_SIZE> com1_buf; |
| 83 | static Buffer<MOS_PAGE_SIZE> com2_buf; |
| 84 | |
| 85 | x86SerialDevice com1_device{ COM1 }; |
| 86 | x86SerialDevice com2_device{ COM2 }; |
| 87 | |
| 88 | SerialConsole COM1Console{ "com1_console" , CONSOLE_CAP_READ, &com1_buf, &com1_device, LightBlue, Black }; |
| 89 | SerialConsole COM2Console{ "com2_console" , CONSOLE_CAP_READ, &com2_buf, &com2_device, LightBlue, Black }; |
| 90 | |
| 91 | mos_platform_info_t *const platform_info = &x86_platform; |
| 92 | mos_platform_info_t x86_platform = { .boot_console = &COM1Console }; |
| 93 | const acpi_rsdp_t *acpi_rsdp = NULL; |
| 94 | |
| 95 | static bool x86_keyboard_handler(u32 irq, void *data) |
| 96 | { |
| 97 | MOS_UNUSED(data); |
| 98 | MOS_ASSERT(irq == IRQ_KEYBOARD); |
| 99 | int scancode = port_inb(port: 0x60); |
| 100 | |
| 101 | pr_info("Keyboard scancode: %x" , scancode); |
| 102 | return true; |
| 103 | } |
| 104 | |
| 105 | static bool x86_pit_timer_handler(u32 irq, void *data) |
| 106 | { |
| 107 | MOS_UNUSED(data); |
| 108 | MOS_ASSERT(irq == IRQ_PIT_TIMER); |
| 109 | spinlock_acquire(¤t_thread->state_lock); |
| 110 | reschedule(); |
| 111 | return true; |
| 112 | } |
| 113 | |
| 114 | void x86_setup_lapic_timer() |
| 115 | { |
| 116 | lapic_set_timer(initial_count: 1000000); |
| 117 | } |
| 118 | |
| 119 | typedef struct _frame |
| 120 | { |
| 121 | struct _frame *bp; |
| 122 | ptr_t ip; |
| 123 | } frame_t; |
| 124 | |
| 125 | void x86_dump_stack_at(ptr_t this_frame, bool can_access_vmaps) |
| 126 | { |
| 127 | frame_t *frame = (frame_t *) this_frame; |
| 128 | |
| 129 | const bool do_mapped_check = current_cpu->mm_context; |
| 130 | |
| 131 | if (unlikely(!do_mapped_check)) |
| 132 | pr_warn(" no mm context available, mapping checks are disabled (early-boot panic?)" ); |
| 133 | |
| 134 | const bool no_relock = do_mapped_check && spinlock_is_locked(lock: ¤t_cpu->mm_context->mm_lock); |
| 135 | if (no_relock) |
| 136 | pr_emerg(" mm lock is already held, stack trace may be corrupted" ); |
| 137 | |
| 138 | pr_info("-- stack trace:" ); |
| 139 | for (u32 i = 0; frame; i++) |
| 140 | { |
| 141 | #define TRACE_FMT " %-3d [" PTR_FMT "]: " |
| 142 | if (do_mapped_check) |
| 143 | { |
| 144 | const pfn_t pfn = mm_get_phys_addr(current_cpu->mm_context, vaddr: (ptr_t) frame) / MOS_PAGE_SIZE; |
| 145 | if (!pfn) |
| 146 | { |
| 147 | pr_emerg(TRACE_FMT "<corrupted>, aborting backtrace" , i, (ptr_t) frame); |
| 148 | break; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | if (frame->bp == 0) |
| 153 | { |
| 154 | // end of stack |
| 155 | pr_warn(TRACE_FMT "<end>" , i, (ptr_t) 0); |
| 156 | break; |
| 157 | } |
| 158 | else if (frame == frame->bp) |
| 159 | { |
| 160 | pr_emerg(TRACE_FMT "<corrupted>, aborting backtrace" , i, (ptr_t) frame); |
| 161 | break; |
| 162 | } |
| 163 | else if (frame->ip >= MOS_KERNEL_START_VADDR) |
| 164 | { |
| 165 | pr_warn(TRACE_FMT "%ps" , i, frame->ip, (void *) frame->ip); |
| 166 | } |
| 167 | else if (frame->ip == 0) |
| 168 | { |
| 169 | pr_warn(TRACE_FMT "<end>" , i, frame->ip); |
| 170 | break; |
| 171 | } |
| 172 | else if (frame->ip < 1 KB) |
| 173 | { |
| 174 | pr_emerg(TRACE_FMT "<corrupted?>" , i, frame->ip); |
| 175 | } |
| 176 | else if (can_access_vmaps) |
| 177 | { |
| 178 | if (!no_relock) |
| 179 | spinlock_acquire(¤t_cpu->mm_context->mm_lock); |
| 180 | vmap_t *const vmap = vmap_obtain(current_cpu->mm_context, vaddr: (ptr_t) frame->ip); |
| 181 | |
| 182 | if (vmap && vmap->io) |
| 183 | { |
| 184 | const auto name = vmap->io->name(); |
| 185 | pr_warn(TRACE_FMT "%s (+" PTR_VLFMT ")" , i, frame->ip, name.c_str(), 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(const 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 | COM2Console.Register(); |
| 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: &COM1Console); |
| 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 | |