Processor.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/StdLibExtras.h>
  7. #include <Kernel/Arch/x86/Processor.h>
  8. #include <Kernel/Arch/x86/TrapFrame.h>
  9. #include <Kernel/Process.h>
  10. #include <Kernel/Random.h>
  11. #include <Kernel/Sections.h>
  12. #include <Kernel/Thread.h>
  13. namespace Kernel {
  14. #define ENTER_THREAD_CONTEXT_ARGS_SIZE (2 * 4) // to_thread, from_thread
  15. NAKED void thread_context_first_enter(void)
  16. {
  17. // clang-format off
  18. // enter_thread_context returns to here first time a thread is executing
  19. asm(
  20. // switch_context will have pushed from_thread and to_thread to our new
  21. // stack prior to thread_context_first_enter() being called, and the
  22. // pointer to TrapFrame was the top of the stack before that
  23. " movl 8(%esp), %ebx \n" // save pointer to TrapFrame
  24. " cld \n"
  25. " call context_first_init \n"
  26. " addl $" __STRINGIFY(ENTER_THREAD_CONTEXT_ARGS_SIZE) ", %esp \n"
  27. " movl %ebx, 0(%esp) \n" // push pointer to TrapFrame
  28. " jmp common_trap_exit \n"
  29. );
  30. // clang-format on
  31. }
  32. NAKED void do_assume_context(Thread*, u32)
  33. {
  34. // clang-format off
  35. // FIXME: I hope (Thread* thread, u32 flags) aren't compiled away
  36. asm(
  37. " movl 4(%esp), %ebx \n"
  38. " movl 8(%esp), %esi \n"
  39. // We're going to call Processor::init_context, so just make sure
  40. // we have enough stack space so we don't stomp over it
  41. " subl $(" __STRINGIFY(4 + REGISTER_STATE_SIZE + TRAP_FRAME_SIZE + 4) "), %esp \n"
  42. " pushl %esi \n"
  43. " pushl %ebx \n"
  44. " cld \n"
  45. " call do_init_context \n"
  46. " addl $8, %esp \n"
  47. " movl %eax, %esp \n" // move stack pointer to what Processor::init_context set up for us
  48. " pushl %ebx \n" // push to_thread
  49. " pushl %ebx \n" // push from_thread
  50. " pushl $thread_context_first_enter \n" // should be same as regs.eip
  51. " jmp enter_thread_context \n"
  52. );
  53. // clang-format on
  54. }
  55. String Processor::platform_string() const
  56. {
  57. return "i386";
  58. }
  59. FlatPtr Processor::init_context(Thread& thread, bool leave_crit)
  60. {
  61. VERIFY(is_kernel_mode());
  62. VERIFY(g_scheduler_lock.is_locked());
  63. if (leave_crit) {
  64. // Leave the critical section we set up in in Process::exec,
  65. // but because we still have the scheduler lock we should end up with 1
  66. VERIFY(in_critical() == 2);
  67. m_in_critical = 1; // leave it without triggering anything or restoring flags
  68. }
  69. u32 kernel_stack_top = thread.kernel_stack_top();
  70. // Add a random offset between 0-256 (16-byte aligned)
  71. kernel_stack_top -= round_up_to_power_of_two(get_fast_random<u8>(), 16);
  72. u32 stack_top = kernel_stack_top;
  73. // TODO: handle NT?
  74. VERIFY((cpu_flags() & 0x24000) == 0); // Assume !(NT | VM)
  75. auto& regs = thread.regs();
  76. bool return_to_user = (regs.cs & 3) != 0;
  77. // make room for an interrupt frame
  78. if (!return_to_user) {
  79. // userspace_esp and userspace_ss are not popped off by iret
  80. // unless we're switching back to user mode
  81. stack_top -= sizeof(RegisterState) - 2 * sizeof(u32);
  82. // For kernel threads we'll push the thread function argument
  83. // which should be in regs.esp and exit_kernel_thread as return
  84. // address.
  85. stack_top -= 2 * sizeof(u32);
  86. *reinterpret_cast<u32*>(kernel_stack_top - 2 * sizeof(u32)) = regs.esp;
  87. *reinterpret_cast<u32*>(kernel_stack_top - 3 * sizeof(u32)) = FlatPtr(&exit_kernel_thread);
  88. } else {
  89. stack_top -= sizeof(RegisterState);
  90. }
  91. // we want to end up 16-byte aligned, %esp + 4 should be aligned
  92. stack_top -= sizeof(u32);
  93. *reinterpret_cast<u32*>(kernel_stack_top - sizeof(u32)) = 0;
  94. // set up the stack so that after returning from thread_context_first_enter()
  95. // we will end up either in kernel mode or user mode, depending on how the thread is set up
  96. // However, the first step is to always start in kernel mode with thread_context_first_enter
  97. RegisterState& iretframe = *reinterpret_cast<RegisterState*>(stack_top);
  98. iretframe.ss = regs.ss;
  99. iretframe.gs = regs.gs;
  100. iretframe.fs = regs.fs;
  101. iretframe.es = regs.es;
  102. iretframe.ds = regs.ds;
  103. iretframe.edi = regs.edi;
  104. iretframe.esi = regs.esi;
  105. iretframe.ebp = regs.ebp;
  106. iretframe.esp = 0;
  107. iretframe.ebx = regs.ebx;
  108. iretframe.edx = regs.edx;
  109. iretframe.ecx = regs.ecx;
  110. iretframe.eax = regs.eax;
  111. iretframe.eflags = regs.eflags;
  112. iretframe.eip = regs.eip;
  113. iretframe.cs = regs.cs;
  114. if (return_to_user) {
  115. iretframe.userspace_esp = regs.esp;
  116. iretframe.userspace_ss = regs.ss;
  117. }
  118. // make space for a trap frame
  119. stack_top -= sizeof(TrapFrame);
  120. TrapFrame& trap = *reinterpret_cast<TrapFrame*>(stack_top);
  121. trap.regs = &iretframe;
  122. trap.prev_irq_level = 0;
  123. trap.next_trap = nullptr;
  124. stack_top -= sizeof(u32); // pointer to TrapFrame
  125. *reinterpret_cast<u32*>(stack_top) = stack_top + 4;
  126. if constexpr (CONTEXT_SWITCH_DEBUG) {
  127. if (return_to_user) {
  128. dbgln("init_context {} ({}) set up to execute at eip={}:{}, esp={}, stack_top={}, user_top={}:{}",
  129. thread,
  130. VirtualAddress(&thread),
  131. iretframe.cs, regs.eip,
  132. VirtualAddress(regs.esp),
  133. VirtualAddress(stack_top),
  134. iretframe.userspace_ss,
  135. iretframe.userspace_esp);
  136. } else {
  137. dbgln("init_context {} ({}) set up to execute at eip={}:{}, esp={}, stack_top={}",
  138. thread,
  139. VirtualAddress(&thread),
  140. iretframe.cs, regs.eip,
  141. VirtualAddress(regs.esp),
  142. VirtualAddress(stack_top));
  143. }
  144. }
  145. // make switch_context() always first return to thread_context_first_enter()
  146. // in kernel mode, so set up these values so that we end up popping iretframe
  147. // off the stack right after the context switch completed, at which point
  148. // control is transferred to what iretframe is pointing to.
  149. regs.eip = FlatPtr(&thread_context_first_enter);
  150. regs.esp0 = kernel_stack_top;
  151. regs.esp = stack_top;
  152. regs.cs = GDT_SELECTOR_CODE0;
  153. regs.ds = GDT_SELECTOR_DATA0;
  154. regs.es = GDT_SELECTOR_DATA0;
  155. regs.gs = GDT_SELECTOR_DATA0;
  156. regs.ss = GDT_SELECTOR_DATA0;
  157. regs.gs = GDT_SELECTOR_PROC;
  158. return stack_top;
  159. }
  160. void Processor::switch_context(Thread*& from_thread, Thread*& to_thread)
  161. {
  162. VERIFY(!m_in_irq);
  163. VERIFY(m_in_critical == 1);
  164. VERIFY(is_kernel_mode());
  165. dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context --> switching out of: {} {}", VirtualAddress(from_thread), *from_thread);
  166. // m_in_critical is restored in enter_thread_context
  167. from_thread->save_critical(m_in_critical);
  168. // clang-format off
  169. // Switch to new thread context, passing from_thread and to_thread
  170. // through to the new context using registers edx and eax
  171. asm volatile(
  172. // NOTE: changing how much we push to the stack affects thread_context_first_enter()!
  173. "pushfl \n"
  174. "pushl %%ebx \n"
  175. "pushl %%esi \n"
  176. "pushl %%edi \n"
  177. "pushl %%ebp \n"
  178. "movl %%esp, %[from_esp] \n"
  179. "movl $1f, %[from_eip] \n"
  180. "movl %[to_esp0], %%ebx \n"
  181. "movl %%ebx, %[tss_esp0] \n"
  182. "movl %[to_esp], %%esp \n"
  183. "pushl %[to_thread] \n"
  184. "pushl %[from_thread] \n"
  185. "pushl %[to_eip] \n"
  186. "cld \n"
  187. "jmp enter_thread_context \n"
  188. "1: \n"
  189. "popl %%edx \n"
  190. "popl %%eax \n"
  191. "popl %%ebp \n"
  192. "popl %%edi \n"
  193. "popl %%esi \n"
  194. "popl %%ebx \n"
  195. "popfl \n"
  196. : [from_esp] "=m" (from_thread->regs().esp),
  197. [from_eip] "=m" (from_thread->regs().eip),
  198. [tss_esp0] "=m" (m_tss.esp0),
  199. "=d" (from_thread), // needed so that from_thread retains the correct value
  200. "=a" (to_thread) // needed so that to_thread retains the correct value
  201. : [to_esp] "g" (to_thread->regs().esp),
  202. [to_esp0] "g" (to_thread->regs().esp0),
  203. [to_eip] "c" (to_thread->regs().eip),
  204. [from_thread] "d" (from_thread),
  205. [to_thread] "a" (to_thread)
  206. : "memory"
  207. );
  208. // clang-format on
  209. dbgln_if(CONTEXT_SWITCH_DEBUG, "switch_context <-- from {} {} to {} {}", VirtualAddress(from_thread), *from_thread, VirtualAddress(to_thread), *to_thread);
  210. }
  211. UNMAP_AFTER_INIT void Processor::initialize_context_switching(Thread& initial_thread)
  212. {
  213. VERIFY(initial_thread.process().is_kernel_process());
  214. auto& regs = initial_thread.regs();
  215. m_tss.iomapbase = sizeof(m_tss);
  216. m_tss.esp0 = regs.esp0;
  217. m_tss.ss0 = GDT_SELECTOR_DATA0;
  218. m_scheduler_initialized = true;
  219. // clang-format off
  220. asm volatile(
  221. "movl %[new_esp], %%esp \n" // switch to new stack
  222. "pushl %[from_to_thread] \n" // to_thread
  223. "pushl %[from_to_thread] \n" // from_thread
  224. "pushl $" __STRINGIFY(GDT_SELECTOR_CODE0) " \n"
  225. "pushl %[new_eip] \n" // save the entry eip to the stack
  226. "movl %%esp, %%ebx \n"
  227. "addl $20, %%ebx \n" // calculate pointer to TrapFrame
  228. "pushl %%ebx \n"
  229. "cld \n"
  230. "pushl %[cpu] \n" // push argument for init_finished before register is clobbered
  231. "call pre_init_finished \n"
  232. "call init_finished \n"
  233. "addl $4, %%esp \n"
  234. "call post_init_finished \n"
  235. "call enter_trap_no_irq \n"
  236. "addl $4, %%esp \n"
  237. "lret \n"
  238. :: [new_esp] "g" (regs.esp),
  239. [new_eip] "a" (regs.eip),
  240. [from_to_thread] "b" (&initial_thread),
  241. [cpu] "c" (Processor::current_id())
  242. );
  243. // clang-format on
  244. VERIFY_NOT_REACHED();
  245. }
  246. }