page_fault.rs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. use arch::InterruptContext;
  2. use bindings::{PA_A, PA_ANON, PA_COW, PA_MMAP, PA_P, PA_RW};
  3. use bitflags::bitflags;
  4. use crate::kernel::mem::paging::{Page, PageBuffer};
  5. use crate::kernel::mem::{Mapping, VRange};
  6. use crate::kernel::task::{ProcessList, Signal, Thread};
  7. use crate::prelude::*;
  8. use super::{MMList, VAddr};
  9. bitflags! {
  10. pub struct PageFaultError: u64 {
  11. const Present = 0x0001;
  12. const Write = 0x0002;
  13. const User = 0x0004;
  14. const ReservedSet = 0x0008;
  15. const InstructionFetch = 0x0010;
  16. const ProtectionKey = 0x0020;
  17. const SGX = 0x8000;
  18. }
  19. }
  20. #[repr(C)]
  21. struct FixEntry {
  22. start: u64,
  23. length: u64,
  24. jump_address: u64,
  25. op_type: u64,
  26. }
  27. impl MMList {
  28. fn handle_page_fault(
  29. &self,
  30. int_stack: &mut InterruptContext,
  31. addr: VAddr,
  32. error: PageFaultError,
  33. ) -> Result<(), Signal> {
  34. let inner = self.inner.borrow();
  35. let inner = inner.lock();
  36. let area = match inner.areas.get(&VRange::from(addr)) {
  37. Some(area) => area,
  38. None => {
  39. if error.contains(PageFaultError::User) {
  40. return Err(Signal::SIGBUS);
  41. } else {
  42. try_page_fault_fix(int_stack, addr);
  43. return Ok(());
  44. }
  45. }
  46. };
  47. // User access permission violation, check user access permission.
  48. if error.contains(PageFaultError::User | PageFaultError::Present) {
  49. if error.contains(PageFaultError::Write) && !area.permission.write {
  50. ProcessList::kill_current(Signal::SIGSEGV)
  51. }
  52. if error.contains(PageFaultError::InstructionFetch) && !area.permission.execute {
  53. ProcessList::kill_current(Signal::SIGSEGV)
  54. }
  55. }
  56. let pte = inner
  57. .page_table
  58. .iter_user(VRange::new(addr.floor(), addr.floor() + 0x1000))
  59. .unwrap()
  60. .next()
  61. .expect("If we can find the mapped area, we should be able to find the PTE");
  62. let is_mapped = matches!(&area.mapping, Mapping::File(_));
  63. if !is_mapped && !error.contains(PageFaultError::Present) {
  64. try_page_fault_fix(int_stack, addr);
  65. return Ok(());
  66. }
  67. area.handle(pte, addr.floor() - area.range().start())
  68. .map_err(|_| Signal::SIGBUS)
  69. }
  70. }
  71. extern "C" {
  72. static FIX_START: *const FixEntry;
  73. static FIX_END: *const FixEntry;
  74. }
  75. /// Try to fix the page fault by jumping to the `error` address.
  76. ///
  77. /// Panic if we can't find the `ip` in the fix list.
  78. fn try_page_fault_fix(int_stack: &mut InterruptContext, addr: VAddr) {
  79. let ip = int_stack.rip as u64;
  80. // TODO: Use `op_type` to fix.
  81. // SAFETY: `FIX_START` and `FIX_END` are defined in the linker script in `.rodata` section.
  82. let entries = unsafe {
  83. core::slice::from_raw_parts(
  84. FIX_START,
  85. (FIX_END as usize - FIX_START as usize) / size_of::<FixEntry>(),
  86. )
  87. };
  88. for entry in entries.iter() {
  89. if ip >= entry.start && ip < entry.start + entry.length {
  90. int_stack.rip = entry.jump_address as u64;
  91. return;
  92. }
  93. }
  94. kernel_page_fault_die(addr, ip as usize)
  95. }
  96. fn kernel_page_fault_die(vaddr: VAddr, ip: usize) -> ! {
  97. panic!(
  98. "Invalid kernel mode memory access to {:#8x} while executing the instruction at {:#8x}",
  99. vaddr.0, ip
  100. )
  101. }
  102. pub fn handle_page_fault(int_stack: &mut InterruptContext) {
  103. let error = PageFaultError::from_bits_truncate(int_stack.error_code);
  104. let vaddr = VAddr(arch::get_page_fault_address());
  105. let result = Thread::current()
  106. .process
  107. .mm_list
  108. .handle_page_fault(int_stack, vaddr, error);
  109. if let Err(signal) = result {
  110. println_debug!(
  111. "Page fault on {:#x} in user space at {:#x}",
  112. vaddr.0,
  113. int_stack.rip
  114. );
  115. ProcessList::kill_current(signal)
  116. }
  117. }