swage_core/memory/
virt_to_phys.rs

1use std::fmt::{Debug, Formatter};
2use std::ops::{Add, Sub};
3
4use crate::util::PAGE_SHIFT;
5use itertools::Itertools;
6use log::warn;
7use pagemap2::{MapsEntry, PageMapEntry, PageMapError, VirtualMemoryArea};
8use serde::Serialize;
9use thiserror::Error;
10
11#[repr(transparent)]
12#[derive(Clone, Copy, Default, Serialize, PartialEq, Eq)]
13/// Physical memory address.
14///
15/// A newtype wrapper around a physical address value.
16pub struct PhysAddr(usize);
17
18impl Debug for PhysAddr {
19    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
20        f.write_fmt(format_args!("PhysAddr(0x{:02x})", self.0))
21    }
22}
23
24impl PhysAddr {
25    /// Creates a new physical address.
26    pub fn new(addr: usize) -> Self {
27        PhysAddr(addr)
28    }
29
30    /// Returns the address as a usize.
31    pub fn as_usize(&self) -> usize {
32        self.0
33    }
34}
35
36/// Trait for resolving virtual addresses to physical addresses.
37///
38/// Implementors provide methods to translate virtual memory addresses
39/// to physical addresses using system interfaces like `/proc/{pid}/pagemap`.
40pub trait VirtToPhysResolver {
41    /// Errors that can occur during phsical address resolution
42    type Error;
43    /// Translates a virtual address to a physical address.
44    ///
45    /// # Errors
46    ///
47    /// Returns an error if address translation fails.
48    fn get_phys(&mut self, virt: u64) -> Result<PhysAddr, Self::Error>;
49
50    /// Translates a range of virtual addresses to physical addresses.
51    ///
52    /// # Errors
53    ///
54    /// Returns an error if address translation fails.
55    fn get_phys_range(&mut self, region: VirtualMemoryArea) -> Result<Vec<PhysAddr>, Self::Error>;
56}
57
58/// Errors that can happen during PageMap operations
59#[derive(Debug, Error)]
60#[error(transparent)]
61pub struct LinuxPageMapError(#[from] PageMapError);
62
63/// Virtual to physical address translator using Linux pagemap.
64///
65/// Uses `/proc/{pid}/pagemap` to translate virtual to physical addresses.
66/// Requires root privileges to access pagemap.
67pub struct LinuxPageMap {
68    pagemap_wrapper: pagemap2::PageMap,
69}
70
71impl LinuxPageMap {
72    /// Creates a new pagemap for the current process.
73    ///
74    /// # Errors
75    ///
76    /// Returns an error if opening `/proc/self/pagemap` fails.
77    pub fn new() -> Result<LinuxPageMap, LinuxPageMapError> {
78        Self::for_process(std::process::id())
79    }
80
81    /// Creates a new pagemap for a specific process.
82    ///
83    /// # Arguments
84    ///
85    /// * `pid` - Process ID to open pagemap for
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if opening the process pagemap fails.
90    pub fn for_process(pid: u32) -> Result<LinuxPageMap, LinuxPageMapError> {
91        let res = LinuxPageMap {
92            pagemap_wrapper: pagemap2::PageMap::new(pid as u64)?,
93        };
94        Ok(res)
95    }
96}
97
98pub struct PageMap(pub Vec<(MapsEntry, Vec<PageMapEntry>)>);
99
100impl LinuxPageMap {
101    /// Get pagemap
102    pub fn pagemap(&mut self) -> Result<PageMap, LinuxPageMapError> {
103        self.pagemap_wrapper
104            .pagemap()
105            .map(PageMap)
106            .map_err(|e| e.into())
107    }
108}
109
110impl VirtToPhysResolver for LinuxPageMap {
111    type Error = LinuxPageMapError;
112    fn get_phys(&mut self, virt: u64) -> Result<PhysAddr, Self::Error> {
113        //calc virtual address of page containing ptr_to_start
114        let vaddr_start_page = virt & !0xFFF;
115        let vaddr_end_page = vaddr_start_page + 4095;
116
117        //query pagemap
118        let memory_region = VirtualMemoryArea::from((vaddr_start_page, vaddr_end_page));
119        let entry = self.pagemap_wrapper.pagemap_vma(&memory_region)?;
120        assert_eq!(
121            entry.len(),
122            1,
123            "Got {} pagemap entries for virtual address 0x{:x}, expected exactly one",
124            entry.len(),
125            virt
126        );
127        let pfn = entry[0].pfn()?;
128        if pfn == 0 {
129            warn!(
130                "Got invalid PFN 0 for virtual address 0x{:x}. Are we root?",
131                virt
132            );
133        }
134
135        let phys_addr = ((pfn << PAGE_SHIFT) | (virt & 0xFFF)) as usize;
136
137        Ok(PhysAddr(phys_addr))
138    }
139    fn get_phys_range(
140        &mut self,
141        memory_region: VirtualMemoryArea,
142    ) -> Result<Vec<PhysAddr>, Self::Error> {
143        let entry = self.pagemap_wrapper.pagemap_vma(&memory_region)?;
144        Ok(entry
145            .into_iter()
146            .map(|e| e.pfn().map(|p| p << PAGE_SHIFT).map_err(|e| e.into()))
147            .collect::<Result<Vec<u64>, Self::Error>>()?
148            .iter()
149            .map(|p| PhysAddr(*p as usize))
150            .collect_vec())
151    }
152}
153
154impl From<PhysAddr> for usize {
155    fn from(addr: PhysAddr) -> usize {
156        addr.0
157    }
158}
159
160impl From<PhysAddr> for *const u8 {
161    fn from(addr: PhysAddr) -> *const u8 {
162        addr.0 as *const u8
163    }
164}
165
166impl std::fmt::Pointer for PhysAddr {
167    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
168        write!(f, "{:p}", self.0 as *const u8)
169    }
170}
171
172impl Add<PhysAddr> for PhysAddr {
173    type Output = PhysAddr;
174
175    fn add(self, rhs: PhysAddr) -> Self::Output {
176        PhysAddr(self.0 + rhs.0)
177    }
178}
179
180impl Sub<PhysAddr> for PhysAddr {
181    type Output = PhysAddr;
182
183    fn sub(self, rhs: PhysAddr) -> Self::Output {
184        assert!(self.0 >= rhs.0);
185        PhysAddr(self.0 - rhs.0)
186    }
187}
188
189impl Add<usize> for PhysAddr {
190    type Output = PhysAddr;
191
192    fn add(self, rhs: usize) -> Self::Output {
193        PhysAddr(self.0 + rhs)
194    }
195}
196
197impl Sub<usize> for PhysAddr {
198    type Output = PhysAddr;
199
200    fn sub(self, rhs: usize) -> Self::Output {
201        assert!(self.0 >= rhs);
202        PhysAddr(self.0 - rhs)
203    }
204}