percpu.rs 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. use super::wrmsr;
  2. use core::{
  3. alloc::Layout,
  4. arch::asm,
  5. cell::UnsafeCell,
  6. ptr::{null_mut, NonNull},
  7. sync::atomic::{AtomicPtr, Ordering},
  8. };
  9. use eonix_mm::paging::PAGE_SIZE;
  10. pub const MAX_CPUS: usize = 256;
  11. #[repr(align(4096))]
  12. struct PercpuData(UnsafeCell<()>); // Not `Sync`.
  13. pub struct PercpuArea {
  14. data: NonNull<PercpuData>,
  15. }
  16. static PERCPU_POINTERS: [AtomicPtr<PercpuData>; MAX_CPUS] =
  17. [const { AtomicPtr::new(null_mut()) }; MAX_CPUS];
  18. impl PercpuArea {
  19. fn len() -> usize {
  20. extern "C" {
  21. fn PERCPU_LENGTH();
  22. }
  23. let len = PERCPU_LENGTH as usize;
  24. assert_ne!(len, 0, "Percpu length should not be zero.");
  25. len
  26. }
  27. fn page_count() -> usize {
  28. Self::len().div_ceil(PAGE_SIZE)
  29. }
  30. fn data_start() -> NonNull<u8> {
  31. extern "C" {
  32. fn PERCPU_DATA_START();
  33. }
  34. let addr = PERCPU_DATA_START as usize;
  35. NonNull::new(addr as *mut _).expect("Percpu data should not be null.")
  36. }
  37. fn layout() -> Layout {
  38. Layout::from_size_align(Self::page_count() * PAGE_SIZE, PAGE_SIZE).expect("Invalid layout.")
  39. }
  40. pub fn new<F>(allocate: F) -> Self
  41. where
  42. F: FnOnce(Layout) -> NonNull<u8>,
  43. {
  44. let data_pointer = allocate(Self::layout());
  45. unsafe {
  46. // SAFETY: The `data_pointer` is of valid length and properly aligned.
  47. data_pointer.copy_from_nonoverlapping(Self::data_start(), Self::len());
  48. }
  49. Self {
  50. data: data_pointer.cast(),
  51. }
  52. }
  53. /// Set up the percpu area for the current CPU.
  54. pub fn setup(&self) {
  55. wrmsr(0xC0000101, self.data.as_ptr() as u64);
  56. unsafe {
  57. // SAFETY: %gs:0 points to the start of the percpu area.
  58. asm!(
  59. "movq {}, %gs:0",
  60. in(reg) self.data.as_ptr(),
  61. options(nostack, preserves_flags, att_syntax)
  62. );
  63. }
  64. }
  65. pub fn register(self: Self, cpuid: usize) {
  66. PERCPU_POINTERS[cpuid].store(self.data.as_ptr(), Ordering::Release);
  67. }
  68. pub fn get_for(cpuid: usize) -> Option<NonNull<()>> {
  69. let pointer = PERCPU_POINTERS[cpuid].load(Ordering::Acquire);
  70. NonNull::new(pointer.cast())
  71. }
  72. }