| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos/mm/paging/pml_types.hpp" |
| 4 | #include "mos/platform/platform_defs.hpp" |
| 5 | #include "mos/tasks/signal.hpp" |
| 6 | #include "mos/x86/devices/rtc.hpp" |
| 7 | |
| 8 | #include <mos/allocator.hpp> |
| 9 | #include <mos/lib/sync/spinlock.hpp> |
| 10 | #include <mos/mm/paging/paging.hpp> |
| 11 | #include <mos/mm/physical/pmm.hpp> |
| 12 | #include <mos/mos_global.h> |
| 13 | #include <mos/platform/platform.hpp> |
| 14 | #include <mos/platform_syscall.h> |
| 15 | #include <mos/syslog/printk.hpp> |
| 16 | #include <mos/tasks/process.hpp> |
| 17 | #include <mos/tasks/task_types.hpp> |
| 18 | #include <mos/x86/cpu/cpu.hpp> |
| 19 | #include <mos/x86/delays.hpp> |
| 20 | #include <mos/x86/devices/port.hpp> |
| 21 | #include <mos/x86/interrupt/apic.hpp> |
| 22 | #include <mos/x86/mm/paging_impl.hpp> |
| 23 | #include <mos/x86/tasks/context.hpp> |
| 24 | #include <mos/x86/x86_interrupt.hpp> |
| 25 | #include <mos/x86/x86_platform.hpp> |
| 26 | #include <mos_stdio.hpp> |
| 27 | #include <mos_stdlib.hpp> |
| 28 | #include <mos_string.hpp> |
| 29 | |
| 30 | [[noreturn]] void platform_shutdown(void) |
| 31 | { |
| 32 | platform_interrupt_disable(); |
| 33 | port_outw(port: 0x604, value: 0x2000); |
| 34 | x86_cpu_halt(); |
| 35 | while (1) |
| 36 | ; |
| 37 | } |
| 38 | |
| 39 | void platform_halt_cpu(void) |
| 40 | { |
| 41 | x86_cpu_halt(); |
| 42 | } |
| 43 | |
| 44 | void platform_invalidate_tlb(ptr_t vaddr) |
| 45 | { |
| 46 | if (!vaddr) |
| 47 | x86_cpu_invlpg_all(); |
| 48 | else |
| 49 | x86_cpu_invlpg(addr: vaddr); |
| 50 | } |
| 51 | |
| 52 | u32 platform_current_cpu_id(void) |
| 53 | { |
| 54 | return x86_cpuid(b, 1, 0) >> 24; |
| 55 | } |
| 56 | |
| 57 | void platform_cpu_idle(void) |
| 58 | { |
| 59 | __asm__ volatile("hlt" ); |
| 60 | } |
| 61 | |
| 62 | u64 platform_get_timestamp() |
| 63 | { |
| 64 | return rdtsc(); |
| 65 | } |
| 66 | |
| 67 | datetime_str_t *platform_get_datetime_str(void) |
| 68 | { |
| 69 | static PER_CPU_DECLARE(datetime_str_t, datetime_str); |
| 70 | |
| 71 | timeval_t time; |
| 72 | platform_get_time(val: &time); |
| 73 | snprintf(str: *per_cpu(datetime_str), size: sizeof(datetime_str_t), format: "%d-%02d-%02d %02d:%02d:%02d" , time.year, time.month, time.day, time.hour, time.minute, time.second); |
| 74 | return per_cpu(datetime_str); |
| 75 | } |
| 76 | |
| 77 | void platform_interrupt_enable(void) |
| 78 | { |
| 79 | __asm__ volatile("sti" ); |
| 80 | } |
| 81 | |
| 82 | void platform_interrupt_disable(void) |
| 83 | { |
| 84 | __asm__ volatile("cli" ); |
| 85 | } |
| 86 | |
| 87 | void platform_switch_mm(const MMContext *mm) |
| 88 | { |
| 89 | x86_cpu_set_cr3(pgd_pfn(mm->pgd) * MOS_PAGE_SIZE); |
| 90 | } |
| 91 | |
| 92 | platform_regs_t *platform_thread_regs(Thread *thread) |
| 93 | { |
| 94 | return (platform_regs_t *) (thread->k_stack.top - sizeof(platform_regs_t)); |
| 95 | } |
| 96 | |
| 97 | void platform_dump_thread_kernel_stack(const Thread *thread) |
| 98 | { |
| 99 | if (!thread) |
| 100 | { |
| 101 | pr_warn("thread is null, cannot dump its stack" ); |
| 102 | return; |
| 103 | } |
| 104 | |
| 105 | if (thread->state != THREAD_STATE_BLOCKED) |
| 106 | { |
| 107 | pr_emph("thread %pt is not blocked, cannot dump stack" , thread); |
| 108 | return; |
| 109 | } |
| 110 | |
| 111 | ptr_t *rbp_ptr = (ptr_t *) thread->k_stack.head; |
| 112 | rbp_ptr += 6; // 6 registers pushed by x86_context_switch_impl |
| 113 | x86_dump_stack_at(this_frame: *rbp_ptr, can_access_vmaps: false); |
| 114 | } |
| 115 | |
| 116 | u64 platform_arch_syscall(u64 syscall, u64 __maybe_unused arg1, u64 __maybe_unused arg2, u64 __maybe_unused arg3, u64 __maybe_unused arg4) |
| 117 | { |
| 118 | switch (syscall) |
| 119 | { |
| 120 | case X86_SYSCALL_IOPL_ENABLE: |
| 121 | { |
| 122 | pr_dinfo2(syscall, "enabling IOPL for thread %pt" , current_thread); |
| 123 | current_process->platform_options.iopl = true; |
| 124 | platform_thread_regs(current_thread)->eflags |= 0x3000; |
| 125 | return 0; |
| 126 | } |
| 127 | case X86_SYSCALL_IOPL_DISABLE: |
| 128 | { |
| 129 | pr_dinfo2(syscall, "disabling IOPL for thread %pt" , current_thread); |
| 130 | current_process->platform_options.iopl = false; |
| 131 | platform_thread_regs(current_thread)->eflags &= ~0x3000; |
| 132 | return 0; |
| 133 | } |
| 134 | case X86_SYSCALL_SET_FS_BASE: |
| 135 | { |
| 136 | current_thread->platform_options.fs_base = arg1; |
| 137 | x86_set_fsbase(current_thread); |
| 138 | return 0; |
| 139 | } |
| 140 | case X86_SYSCALL_SET_GS_BASE: |
| 141 | { |
| 142 | current_thread->platform_options.gs_base = arg1; |
| 143 | // x86_update_current_gsbase(); |
| 144 | MOS_UNIMPLEMENTED("set_gs_base" ); |
| 145 | return 0; |
| 146 | } |
| 147 | default: |
| 148 | { |
| 149 | pr_warn("unknown arch-specific syscall %llu" , syscall); |
| 150 | return -1; |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | void platform_ipi_send(u8 target, ipi_type_t type) |
| 156 | { |
| 157 | if (target == TARGET_CPU_ALL) |
| 158 | lapic_interrupt(IPI_BASE + type, dest: 0xff, delivery_mode: APIC_DELIVER_MODE_NORMAL, dest_mode: LAPIC_DEST_MODE_PHYSICAL, shorthand: LAPIC_SHORTHAND_ALL_EXCLUDING_SELF); |
| 159 | else |
| 160 | lapic_interrupt(IPI_BASE + type, dest: target, delivery_mode: APIC_DELIVER_MODE_NORMAL, dest_mode: LAPIC_DEST_MODE_PHYSICAL, shorthand: LAPIC_SHORTHAND_NONE); |
| 161 | } |
| 162 | |
| 163 | ptr<platform_regs_t> platform_setup_signal_handler_regs(const platform_regs_t *regs, const sigreturn_data_t *sigreturn_data, const sigaction_t *sa) |
| 164 | { |
| 165 | current_thread->u_stack.head = regs->sp - 128; |
| 166 | |
| 167 | // backup previous frame |
| 168 | stack_push_val(¤t_thread->u_stack, *regs); |
| 169 | stack_push_val(¤t_thread->u_stack, *sigreturn_data); |
| 170 | |
| 171 | // Set up the new context |
| 172 | auto new_regs = mos::make_shared<platform_regs_t>(args&: regs); |
| 173 | // *new_regs = *regs; |
| 174 | |
| 175 | new_regs->ip = (ptr_t) sa->handler; |
| 176 | stack_push_val(¤t_thread->u_stack, (ptr_t) sa->sa_restorer); // the return address |
| 177 | |
| 178 | new_regs->di = sigreturn_data->signal; // arg1 |
| 179 | new_regs->sp = current_thread->u_stack.head; |
| 180 | |
| 181 | return new_regs; |
| 182 | } |
| 183 | |
| 184 | void platform_restore_from_signal_handler(void *sp) |
| 185 | { |
| 186 | current_thread->u_stack.head = (ptr_t) sp; |
| 187 | sigreturn_data_t data; |
| 188 | platform_regs_t regs; |
| 189 | stack_pop_val(¤t_thread->u_stack, data); |
| 190 | stack_pop_val(¤t_thread->u_stack, regs); |
| 191 | |
| 192 | signal_on_returned(supplimentary_data: &data); |
| 193 | x86_interrupt_return_impl(regs: ®s); |
| 194 | } |
| 195 | |
| 196 | void platform_get_time(timeval_t *time) |
| 197 | { |
| 198 | rtc_read_time(time); |
| 199 | } |
| 200 | |
| 201 | void platform_get_unix_timestamp(u64 *timestamp) |
| 202 | { |
| 203 | *timestamp = 0; |
| 204 | } |
| 205 | |
| 206 | void platform_dump_regs(const platform_regs_t *frame) |
| 207 | { |
| 208 | pr_emph("General Purpose Registers:\n" |
| 209 | " RAX: " PTR_FMT " RBX: " PTR_FMT " RCX: " PTR_FMT " RDX: " PTR_FMT "\n" |
| 210 | " RSI: " PTR_FMT " RDI: " PTR_FMT " RBP: " PTR_FMT " RSP: " PTR_FMT "\n" |
| 211 | " R8: " PTR_FMT " R9: " PTR_FMT " R10: " PTR_FMT " R11: " PTR_FMT "\n" |
| 212 | " R12: " PTR_FMT " R13: " PTR_FMT " R14: " PTR_FMT " R15: " PTR_FMT "\n" |
| 213 | " IP: " PTR_FMT "\n" |
| 214 | "Context:\n" |
| 215 | " EFLAGS: " PTR_FMT "\n" |
| 216 | " Instruction: 0x%lx:" PTR_FMT "\n" |
| 217 | " Stack: 0x%lx:" PTR_FMT, |
| 218 | frame->ax, frame->bx, frame->cx, frame->dx, // |
| 219 | frame->si, frame->di, frame->bp, frame->sp, // |
| 220 | frame->r8, frame->r9, frame->r10, frame->r11, // |
| 221 | frame->r12, frame->r13, frame->r14, frame->r15, // |
| 222 | frame->ip, // |
| 223 | frame->eflags, // |
| 224 | frame->cs, frame->ip, // |
| 225 | frame->ss, frame->sp // |
| 226 | ); |
| 227 | } |
| 228 | |
| 229 | void platform_syscall_setup_restart_context(platform_regs_t *regs, reg_t syscall_nr) |
| 230 | { |
| 231 | regs->ax = syscall_nr; |
| 232 | regs->ip -= 2; // replay the 'syscall' or 'int 0x88' instruction |
| 233 | } |
| 234 | |
| 235 | void platform_syscall_store_retval(platform_regs_t *regs, reg_t result) |
| 236 | { |
| 237 | regs->ax = result; |
| 238 | } |
| 239 | |