page_fault.rs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. use super::{MMList, VAddr};
  2. use crate::kernel::task::Thread;
  3. use eonix_hal::mm::flush_tlb;
  4. use eonix_hal::traits::fault::PageFaultErrorCode;
  5. use eonix_mm::address::{Addr as _, AddrOps as _, VRange};
  6. use eonix_mm::paging::PAGE_SIZE;
  7. use eonix_runtime::task::Task;
  8. use posix_types::signal::Signal;
  9. #[repr(C)]
  10. struct FixEntry {
  11. start: u64,
  12. length: u64,
  13. jump_address: u64,
  14. op_type: u64,
  15. }
  16. impl FixEntry {
  17. fn start(&self) -> VAddr {
  18. VAddr::from(self.start as usize)
  19. }
  20. fn end(&self) -> VAddr {
  21. VAddr::from((self.start + self.length) as usize)
  22. }
  23. #[allow(dead_code)]
  24. fn range(&self) -> VRange {
  25. VRange::new(self.start(), self.end())
  26. }
  27. fn jump_address(&self) -> VAddr {
  28. VAddr::from(self.jump_address as usize)
  29. }
  30. fn entries() -> &'static [FixEntry] {
  31. extern "C" {
  32. fn FIX_START();
  33. fn FIX_END();
  34. }
  35. unsafe {
  36. // SAFETY: `FIX_START` and `FIX_END` are defined in the
  37. // linker script in `.rodata` section.
  38. core::slice::from_raw_parts(
  39. FIX_START as usize as *const FixEntry,
  40. (FIX_END as usize - FIX_START as usize) / size_of::<FixEntry>(),
  41. )
  42. }
  43. }
  44. }
  45. impl MMList {
  46. /// Handle a user page fault.
  47. pub async fn handle_user_page_fault(
  48. &self,
  49. addr: VAddr,
  50. error: PageFaultErrorCode,
  51. ) -> Result<(), Signal> {
  52. debug_assert!(
  53. error.contains(PageFaultErrorCode::UserAccess),
  54. "Kernel mode page fault happened in user space."
  55. );
  56. let inner = self.inner.borrow();
  57. let inner = inner.lock().await;
  58. let area = inner.areas.get(&VRange::from(addr)).ok_or(Signal::SIGBUS)?;
  59. // Check user access permission.
  60. if error.contains(PageFaultErrorCode::Read) && !area.permission.read {
  61. // Under x86_64, we don't have a way to distinguish
  62. // between a read fault and a non-present fault. But it should be OK
  63. // since non-readable pages are not allowed under x86 and if we read
  64. // both the two false.
  65. Err(Signal::SIGSEGV)?
  66. }
  67. if error.contains(PageFaultErrorCode::Write) && !area.permission.write {
  68. Err(Signal::SIGSEGV)?
  69. }
  70. if error.contains(PageFaultErrorCode::InstructionFetch) && !area.permission.execute {
  71. Err(Signal::SIGSEGV)?
  72. }
  73. let pte = inner
  74. .page_table
  75. .iter_user(VRange::from(addr.floor()).grow(PAGE_SIZE))
  76. .next()
  77. .expect("If we can find the mapped area, we should be able to find the PTE");
  78. area.handle(pte, addr.floor() - area.range().start())
  79. .map_err(|_| Signal::SIGBUS)?;
  80. flush_tlb(addr.floor().addr());
  81. Ok(())
  82. }
  83. }
  84. /// Try to fix the page fault by jumping to the `error` address.
  85. ///
  86. /// # Return
  87. /// Returns the new program counter after fixing.
  88. ///
  89. /// # Panic
  90. /// Panics if we can't find the instruction causing the fault in the fix list.
  91. fn try_page_fault_fix(pc: VAddr, addr: VAddr) -> VAddr {
  92. // TODO: Use `op_type` to fix.
  93. for entry in FixEntry::entries().iter() {
  94. if pc >= entry.start() && pc < entry.end() {
  95. return entry.jump_address();
  96. }
  97. }
  98. kernel_page_fault_die(addr, pc)
  99. }
  100. #[cold]
  101. fn kernel_page_fault_die(vaddr: VAddr, pc: VAddr) -> ! {
  102. panic!(
  103. "Invalid kernel mode memory access to {:?} while executing the instruction at {:?}",
  104. vaddr, pc
  105. )
  106. }
  107. pub fn handle_kernel_page_fault(
  108. fault_pc: VAddr,
  109. addr: VAddr,
  110. error: PageFaultErrorCode,
  111. ) -> Option<VAddr> {
  112. debug_assert!(
  113. !error.contains(PageFaultErrorCode::UserAccess),
  114. "User mode page fault happened in kernel space."
  115. );
  116. debug_assert!(
  117. !error.contains(PageFaultErrorCode::InstructionFetch),
  118. "Kernel mode instruction fetch fault."
  119. );
  120. // TODO: Move this to `UserBuffer` handler since we shouldn'e get any page fault
  121. // in the kernel except for the instructions in the fix list.
  122. let mms = &Thread::current().process.mm_list;
  123. let inner = mms.inner.borrow();
  124. let inner = Task::block_on(inner.lock());
  125. let area = match inner.areas.get(&VRange::from(addr)) {
  126. Some(area) => area,
  127. None => {
  128. return Some(try_page_fault_fix(fault_pc, addr));
  129. }
  130. };
  131. let pte = inner
  132. .page_table
  133. .iter_user(VRange::from(addr.floor()).grow(PAGE_SIZE))
  134. .next()
  135. .expect("If we can find the mapped area, we should be able to find the PTE");
  136. if let Err(_) = area.handle(pte, addr.floor() - area.range().start()) {
  137. return Some(try_page_fault_fix(fault_pc, addr));
  138. }
  139. None
  140. }