/ src / memory.rs
memory.rs
  1  use x86_64::{
  2      structures::paging::PageTable,
  3      structures::paging::OffsetPageTable,
  4      structures::paging::{Page, PhysFrame, Mapper, Size4KiB, FrameAllocator},
  5      VirtAddr,
  6      PhysAddr,
  7  };
  8  use bootloader::bootinfo::MemoryMap;
  9  use bootloader::bootinfo::MemoryRegionType;
 10  
 11  pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> {
 12      unsafe {
 13          let level_4_table = active_level_4_table(physical_memory_offset);
 14          OffsetPageTable::new(level_4_table, physical_memory_offset)
 15      }
 16  }
 17  
 18  pub unsafe fn active_level_4_table(physical_memory_offset: VirtAddr)
 19      -> &'static mut PageTable
 20  {
 21      use x86_64::registers::control::Cr3;
 22  
 23      let (level_4_table_frame, _) = Cr3::read();
 24  
 25      let phys = level_4_table_frame.start_address();
 26      let virt = physical_memory_offset + phys.as_u64();
 27      let page_table_ptr: *mut PageTable = virt.as_mut_ptr();
 28  
 29      unsafe { &mut *page_table_ptr }
 30  }
 31  
 32  pub unsafe fn translate_addr(addr: VirtAddr, physical_memory_offset: VirtAddr)
 33      -> Option<PhysAddr>
 34  {
 35      translate_addr_inner(addr, physical_memory_offset)
 36  }
 37  
 38  pub fn create_example_mapping(
 39      page: Page,
 40      mapper: &mut OffsetPageTable,
 41      frame_allocator: &mut impl FrameAllocator<Size4KiB>,
 42  ) {
 43      use x86_64::structures::paging::PageTableFlags as Flags;
 44  
 45      let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
 46      let flags = Flags::PRESENT | Flags::WRITABLE;
 47  
 48      let map_to_result = unsafe {
 49          mapper.map_to(page, frame, flags, frame_allocator)
 50      };
 51      map_to_result.expect("map_to failed").flush();
 52  }
 53  
 54  pub struct EmptyFrameAllocator;
 55  
 56  unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
 57      fn allocate_frame(&mut self) -> Option<PhysFrame> {
 58          None
 59      }
 60  }
 61  
 62  pub struct BootInfoFrameAllocator {
 63      memory_map: &'static MemoryMap,
 64      next: usize,
 65  }
 66  
 67  impl BootInfoFrameAllocator {
 68      pub unsafe fn init(memory_map: &'static MemoryMap) -> Self {
 69          BootInfoFrameAllocator {
 70              memory_map,
 71              next: 0,
 72          }
 73      }
 74  
 75      fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
 76          let regions = self.memory_map.iter();
 77          let usable_regions = regions
 78              .filter(|r| r.region_type == MemoryRegionType::Usable);
 79          let addr_ranges = usable_regions
 80              .map(|r| r.range.start_addr()..r.range.end_addr());
 81          let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
 82          frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
 83      }
 84  }
 85  
 86  unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
 87      fn allocate_frame(&mut self) -> Option<PhysFrame> {
 88         let frame = self.usable_frames().nth(self.next);
 89         self.next += 1;
 90         frame
 91      }
 92  }
 93  
 94  fn translate_addr_inner(addr: VirtAddr, physical_memory_offset: VirtAddr)
 95      -> Option<PhysAddr>
 96  {
 97      use x86_64::structures::paging::page_table::FrameError;
 98      use x86_64::registers::control::Cr3;
 99  
100      let (level_4_table_frame, _) = Cr3::read();
101  
102      let table_indexes = [
103          addr.p4_index(), addr.p3_index(), addr.p2_index(), addr.p1_index()
104      ];
105      let mut frame = level_4_table_frame;
106  
107      for &index in &table_indexes {
108          let virt = physical_memory_offset + frame.start_address().as_u64();
109          let table_ptr: *const PageTable = virt.as_ptr();
110          let table = unsafe { &*table_ptr };
111  
112          let entry = &table[index];
113          frame = match entry.frame() {
114              Ok(frame) => frame,
115              Err(FrameError::FrameNotPresent) => return None,
116              Err(FrameError::HugeFrame) => panic!("huge pages not supported"),
117          };
118      }
119  
120      Some(frame.start_address() + u64::from(addr.page_offset()))
121  }