init.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. use core::{
  2. alloc::Layout,
  3. pin::Pin,
  4. ptr::{addr_of, NonNull},
  5. };
  6. use super::{enable_sse, percpu::init_percpu_area_thiscpu, GDTEntry, InterruptControl, GDT};
  7. #[repr(C)]
  8. #[derive(Debug, Clone, Copy)]
  9. #[allow(non_camel_case_types)]
  10. struct TSS_SP {
  11. low: u32,
  12. high: u32,
  13. }
  14. #[repr(C)]
  15. pub struct TSS {
  16. _reserved1: u32,
  17. rsp: [TSS_SP; 3],
  18. _reserved2: u32,
  19. _reserved3: u32,
  20. ist: [TSS_SP; 7],
  21. _reserved4: u32,
  22. _reserved5: u32,
  23. _reserved6: u16,
  24. iomap_base: u16,
  25. }
  26. impl TSS {
  27. pub fn new() -> Self {
  28. Self {
  29. _reserved1: 0,
  30. rsp: [TSS_SP { low: 0, high: 0 }; 3],
  31. _reserved2: 0,
  32. _reserved3: 0,
  33. ist: [TSS_SP { low: 0, high: 0 }; 7],
  34. _reserved4: 0,
  35. _reserved5: 0,
  36. _reserved6: 0,
  37. iomap_base: 0,
  38. }
  39. }
  40. pub fn set_rsp0(&mut self, rsp: u64) {
  41. self.rsp[0].low = rsp as u32;
  42. self.rsp[0].high = (rsp >> 32) as u32;
  43. }
  44. }
  45. /// Architecture-specific per-cpu status.
  46. #[allow(dead_code)]
  47. pub struct CPUStatus {
  48. id: usize,
  49. gdt: GDT,
  50. tss: TSS,
  51. percpu_area: NonNull<u8>,
  52. pub interrupt: InterruptControl,
  53. }
  54. impl CPUStatus {
  55. pub unsafe fn new_thiscpu<F>(allocate: F) -> Self
  56. where
  57. F: FnOnce(Layout) -> NonNull<u8>,
  58. {
  59. const PAGE_SIZE: usize = 0x1000;
  60. extern "C" {
  61. static PERCPU_PAGES: usize;
  62. fn _PERCPU_DATA_START();
  63. }
  64. let percpu_area = allocate(Layout::from_size_align_unchecked(
  65. PERCPU_PAGES * PAGE_SIZE,
  66. PAGE_SIZE,
  67. ));
  68. percpu_area.copy_from_nonoverlapping(
  69. NonNull::new(_PERCPU_DATA_START as *mut u8).unwrap(),
  70. PERCPU_PAGES * PAGE_SIZE,
  71. );
  72. let (interrupt_control, cpuid) = InterruptControl::new();
  73. init_percpu_area_thiscpu(percpu_area);
  74. Self {
  75. id: cpuid,
  76. gdt: GDT::new(),
  77. tss: TSS::new(),
  78. percpu_area,
  79. interrupt: interrupt_control,
  80. }
  81. }
  82. /// Load GDT and TSS in place.
  83. ///
  84. /// # Safety
  85. /// Make sure preemption and interrupt are disabled before calling this function.
  86. pub unsafe fn init(self: Pin<&mut Self>) {
  87. enable_sse();
  88. // SAFETY: We don't move the object.
  89. let self_mut = self.get_unchecked_mut();
  90. let tss_addr = addr_of!(self_mut.tss);
  91. self_mut.gdt.set_tss(tss_addr as u64);
  92. self_mut.gdt.load();
  93. // SAFETY: `self` is pinned, so are its fields.
  94. Pin::new_unchecked(&mut self_mut.interrupt).setup_idt();
  95. self_mut.interrupt.setup_timer();
  96. }
  97. /// Bootstrap all CPUs.
  98. /// This should only be called on the BSP.
  99. pub unsafe fn bootstrap_cpus(&self) {
  100. self.interrupt.send_sipi();
  101. }
  102. pub unsafe fn set_rsp0(&mut self, rsp: u64) {
  103. self.tss.set_rsp0(rsp);
  104. }
  105. pub unsafe fn set_tls32(&mut self, desc: GDTEntry) {
  106. self.gdt.set_tls32(desc);
  107. }
  108. pub fn cpuid(&self) -> usize {
  109. self.id
  110. }
  111. }
  112. #[macro_export]
  113. macro_rules! define_smp_bootstrap {
  114. ($cpu_count:literal, $ap_entry:ident, $alloc_kstack:tt) => {
  115. #[no_mangle]
  116. static BOOT_SEMAPHORE: core::sync::atomic::AtomicU64 =
  117. core::sync::atomic::AtomicU64::new(0);
  118. #[no_mangle]
  119. static BOOT_STACK: core::sync::atomic::AtomicU64 =
  120. core::sync::atomic::AtomicU64::new(0);
  121. #[no_mangle]
  122. static CPU_COUNT: core::sync::atomic::AtomicU64 =
  123. core::sync::atomic::AtomicU64::new(0);
  124. core::arch::global_asm!(
  125. r#"
  126. .pushsection .stage1.smp
  127. .code16
  128. .globl ap_bootstrap
  129. .type ap_bootstrap, @function
  130. ap_bootstrap:
  131. ljmp $0x0, $.Lap1
  132. .Lap1:
  133. # we use the shared gdt for cpu bootstrapping
  134. lgdt .Lshared_gdt_desc
  135. # set msr
  136. mov $0xc0000080, %ecx
  137. rdmsr
  138. or $0x901, %eax # set LME, NXE, SCE
  139. wrmsr
  140. # set cr4
  141. mov %cr4, %eax
  142. or $0xa0, %eax # set PAE, PGE
  143. mov %eax, %cr4
  144. # load new page table
  145. mov ${KERNEL_PML4}, %eax
  146. mov %eax, %cr3
  147. mov %cr0, %eax
  148. // SET PE, WP, PG
  149. or $0x80010001, %eax
  150. mov %eax, %cr0
  151. ljmp $0x08, $.Lap_bootstrap_end
  152. .align 16
  153. .Lshared_gdt_desc:
  154. .8byte 0x0000000000005f
  155. .code64
  156. .Lap_bootstrap_end:
  157. mov $0x10, %ax
  158. mov %ax, %ds
  159. mov %ax, %es
  160. mov %ax, %ss
  161. xor %rsp, %rsp
  162. xor %rax, %rax
  163. inc %rax
  164. 1:
  165. xchg %rax, {BOOT_SEMAPHORE}
  166. cmp $0, %rax
  167. je 1f
  168. pause
  169. jmp 1b
  170. 1:
  171. mov {BOOT_STACK}, %rsp # Acquire
  172. cmp $0, %rsp
  173. jne 1f
  174. pause
  175. jmp 1b
  176. 1:
  177. xor %rax, %rax
  178. mov %rax, {BOOT_STACK} # Release
  179. xchg %rax, {BOOT_SEMAPHORE}
  180. lock incq {CPU_COUNT}
  181. xor %rbp, %rbp
  182. push %rbp # NULL return address
  183. jmp {AP_ENTRY}
  184. .popsection
  185. "#,
  186. KERNEL_PML4 = const 0x2000,
  187. BOOT_SEMAPHORE = sym BOOT_SEMAPHORE,
  188. BOOT_STACK = sym BOOT_STACK,
  189. CPU_COUNT = sym CPU_COUNT,
  190. AP_ENTRY = sym $ap_entry,
  191. options(att_syntax),
  192. );
  193. pub unsafe fn wait_cpus_online() {
  194. use core::sync::atomic::Ordering;
  195. while CPU_COUNT.load(Ordering::Acquire) != $cpu_count - 1 {
  196. if BOOT_STACK.load(Ordering::Acquire) == 0 {
  197. let stack_bottom = $alloc_kstack as u64;
  198. BOOT_STACK.store(stack_bottom, Ordering::Release);
  199. }
  200. $crate::pause();
  201. }
  202. }
  203. };
  204. }