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