1use std::fmt;
2use std::fs::File;
3use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
4
5use crate::{
6 error::{PageMapError, Result},
7 kpage::KPageFlags,
8 maps::{MapsEntry, MemoryRegion},
9 page_size,
10};
11
12#[derive(Debug, Clone, Copy)]
24pub struct PageMapEntry {
25 pgmap: u64,
26 kpgcn: Option<u64>,
27 kpgfl: Option<KPageFlags>,
28}
29
30impl std::convert::From<u64> for PageMapEntry {
31 fn from(pgmap: u64) -> Self {
32 PageMapEntry {
33 pgmap,
34 kpgcn: None,
35 kpgfl: None,
36 }
37 }
38}
39
40impl std::convert::From<(u64, u64, u64)> for PageMapEntry {
42 fn from((pgmap, kpgcn, kpgfl): (u64, u64, u64)) -> Self {
43 PageMapEntry {
44 pgmap,
45 kpgcn: Some(kpgcn),
46 kpgfl: Some(kpgfl.into()),
47 }
48 }
49}
50
51impl PageMapEntry {
53 pub const PM_PFRAME_BITS: u64 = 55;
58 pub const PM_PFRAME_MASK: u64 = (1 << Self::PM_PFRAME_BITS) - 1;
59 pub const PM_SOFT_DIRTY: u64 = 55;
60 pub const PM_MMAP_EXCLUSIVE: u64 = 56;
61 pub const PM_FILE: u64 = 61;
62 pub const PM_SWAP: u64 = 62;
63 pub const PM_PRESENT: u64 = 63;
64
65 #[inline(always)]
73 pub fn raw_pagemap(&self) -> u64 {
74 self.pgmap
75 }
76
77 #[inline(always)]
79 pub fn present(&self) -> bool {
80 self.pgmap >> Self::PM_PRESENT & 1 == 1
81 }
82
83 #[inline(always)]
85 pub fn swapped(&self) -> bool {
86 self.pgmap >> Self::PM_SWAP & 1 == 1
87 }
88
89 #[inline(always)]
91 pub fn file_mapped(&self) -> bool {
92 self.pgmap >> Self::PM_FILE & 1 == 1
93 }
94
95 #[inline(always)]
97 pub fn shared_anonymous(&self) -> bool {
98 self.pgmap >> Self::PM_FILE & 1 == 0
99 }
100
101 #[inline(always)]
103 pub fn exclusively_mapped(&self) -> bool {
104 self.pgmap >> Self::PM_MMAP_EXCLUSIVE & 1 == 1
105 }
106
107 #[inline(always)]
109 pub fn soft_dirty(&self) -> bool {
110 self.pgmap >> Self::PM_SOFT_DIRTY & 1 == 1
111 }
112
113 pub fn pfn(&self) -> Result<u64> {
116 if !self.present() {
117 Err(PageMapError::PageNotPresent)
118 } else {
119 Ok(self.pgmap & Self::PM_PFRAME_MASK)
120 }
121 }
122
123 pub fn swap_type(&self) -> Result<u8> {
126 if !self.swapped() {
127 Err(PageMapError::PageNotSwapped)
128 } else {
129 Ok((self.pgmap & 0x1fu64) as u8)
130 }
131 }
132
133 pub fn swap_offset(&self) -> Result<u64> {
136 if !self.swapped() {
137 Err(PageMapError::PageNotSwapped)
138 } else {
139 Ok((self.pgmap & (0x_007f_ffff_ffff_ffe0_u64)) >> 5)
140 }
141 }
142
143 #[inline(always)]
152 pub fn kpagecount(&self) -> Option<u64> {
153 self.kpgcn
154 }
155
156 #[inline(always)]
163 pub fn kpageflags(&self) -> Option<KPageFlags> {
164 self.kpgfl
165 }
166
167 #[inline(always)]
172 pub fn raw_kpageflags(&self) -> Option<u64> {
173 self.kpgfl.map(|kpgfl| kpgfl.bits())
174 }
175
176 fn_get_bit!(locked, KPF_LOCKED);
177 fn_get_bit!(error, KPF_ERROR);
178 fn_get_bit!(referenced, KPF_REFERENCED);
179 fn_get_bit!(uptodate, KPF_UPTODATE);
180 fn_get_bit!(dirty, KPF_DIRTY);
181 fn_get_bit!(lru, KPF_LRU);
182 fn_get_bit!(active, KPF_ACTIVE);
183 fn_get_bit!(slab, KPF_SLAB);
184 fn_get_bit!(writeback, KPF_WRITEBACK);
185 fn_get_bit!(reclaim, KPF_RECLAIM);
186 fn_get_bit!(buddy, KPF_BUDDY);
187 fn_get_bit!(mmap, KPF_MMAP);
188 fn_get_bit!(anon, KPF_ANON);
189 fn_get_bit!(swapcache, KPF_SWAPCACHE);
190 fn_get_bit!(swapbacked, KPF_SWAPBACKED);
191 fn_get_bit!(compound_head, KPF_COMPOUND_HEAD);
192 fn_get_bit!(compound_tail, KPF_COMPOUND_TAIL);
193 fn_get_bit!(huge, KPF_HUGE);
194 fn_get_bit!(unevictable, KPF_UNEVICTABLE);
195 fn_get_bit!(hwpoison, KPF_HWPOISON);
196 fn_get_bit!(nopage, KPF_NOPAGE);
197 fn_get_bit!(ksm, KPF_KSM);
198 fn_get_bit!(thp, KPF_THP);
199 fn_get_bit!(offline, KPF_OFFLINE);
200 fn_get_bit!(zero_page, KPF_ZERO_PAGE);
201 fn_get_bit!(idle, KPF_IDLE);
202 fn_get_bit!(pgtable, KPF_PGTABLE);
203}
204
205impl fmt::Display for PageMapEntry {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 match (self.present(), self.swapped()) {
208 (true, true) => unreachable!("PAGE BOTH PRESENT AND SWAPPED!"),
209 (true, false) => {
210 write!(
211 f,
212 "PageMapEntry{{ present: {}; swapped: {}; file_mapped: {}; exclusively_mapped: {}; soft_dirty: {}; pfn: 0x{:x} }}",
213 self.present(), self.swapped(), self.file_mapped(), self.exclusively_mapped(),
214 self.soft_dirty(), self.pfn().unwrap(), )
216 }
217 (false, true) => {
218 write!(
219 f,
220 "PageMapEntry{{ present: {}; swapped: {}; file_mapped: {}; exclusively_mapped: {}; soft_dirty: {}; swap_type: {}; swap_offset: 0x{:x} }}",
221 self.present(), self.swapped(), self.file_mapped(), self.exclusively_mapped(),
222 self.soft_dirty(), self.swap_type().unwrap(), self.swap_offset().unwrap(),
223 )
225 }
226 (false, false) => {
227 write!(
228 f,
229 "PageMapEntry{{ present: {}; swapped: {}; file_mapped: {}; exclusively_mapped: {}; soft_dirty: {} }}",
230 self.present(), self.swapped(), self.file_mapped(), self.exclusively_mapped(),
231 self.soft_dirty(),
232 )
233 }
234 }
235 }
236}
237
238#[derive(Debug)]
253pub struct PageMap {
254 pid: u64,
255 mf: BufReader<File>,
256 pmf: File,
257 kcf: Option<File>,
258 kff: Option<File>,
259 page_size: u64,
260}
261
262impl PageMap {
263 const KPAGECOUNT: &'static str = "/proc/kpagecount";
264 const KPAGEFLAGS: &'static str = "/proc/kpageflags";
265
266 pub fn new(pid: u64) -> Result<Self> {
268 let (kcf, kff) = (
269 File::open(Self::KPAGECOUNT)
270 .map_err(|e| PageMapError::Open {
271 path: Self::KPAGECOUNT.into(),
272 source: e,
273 })
274 .ok(),
275 File::open(Self::KPAGEFLAGS)
276 .map_err(|e| PageMapError::Open {
277 path: Self::KPAGEFLAGS.into(),
278 source: e,
279 })
280 .ok(),
281 );
282 let (maps_path, pagemap_path) = (
283 format!("/proc/{}/maps", pid),
284 format!("/proc/{}/pagemap", pid),
285 );
286 Ok(PageMap {
287 pid,
288 mf: BufReader::with_capacity(
289 1 << 14,
290 File::open(&maps_path).map_err(|e| PageMapError::Open {
291 path: maps_path,
292 source: e,
293 })?,
294 ),
295 pmf: File::open(&pagemap_path).map_err(|e| PageMapError::Open {
296 path: pagemap_path,
297 source: e,
298 })?,
299 kcf,
300 kff,
301 page_size: page_size()?,
302 })
303 }
304
305 pub fn pid(&self) -> u64 {
307 self.pid
308 }
309
310 pub fn maps(&mut self) -> Result<Vec<MapsEntry>> {
313 let pid = self.pid;
314 self.mf
315 .by_ref()
316 .lines()
317 .map(|line| {
318 line.map_err(|e| PageMapError::Read {
319 path: format!("/proc/{}/maps", pid),
320 source: e,
321 })?
322 .parse()
323 })
324 .collect()
325 }
326
327 pub fn pagemap_region(&mut self, region: &MemoryRegion) -> Result<Vec<PageMapEntry>> {
330 let mut buf = [0; 8];
331 (region.start..region.end)
332 .step_by(self.page_size as usize)
333 .map(|addr: u64| -> Result<_> {
334 let vpn = addr / self.page_size;
335 self.pmf
336 .seek(SeekFrom::Start(vpn * 8))
337 .map_err(|e| PageMapError::Seek {
338 path: format!("/proc/{}/pagemap", self.pid),
339 source: e,
340 })?;
341 self.pmf
342 .read_exact(&mut buf)
343 .map_err(|e| PageMapError::Read {
344 path: format!("/proc/{}/pagemap", self.pid),
345 source: e,
346 })?;
347 Ok(u64::from_ne_bytes(buf).into())
348 })
349 .collect::<Result<_>>()
350 }
351
352 pub fn pagemap(&mut self) -> Result<Vec<(MapsEntry, Vec<PageMapEntry>)>> {
359 self.maps()?
360 .into_iter()
361 .map(|map_entry| {
362 let mut pmes = self.pagemap_region(&map_entry.region)?;
363 if self.kcf.is_some() && self.kff.is_some() {
364 for pme in &mut pmes {
365 if let Ok(pfn) = pme.pfn() {
366 pme.kpgcn = Some(self.kpagecount(pfn)?);
367 pme.kpgfl = Some(self.kpageflags(pfn)?);
368 }
369 }
370 }
371 Ok((map_entry, pmes))
372 })
373 .collect()
374 }
375
376 pub fn kpagecount(&self, pfn: u64) -> Result<u64> {
387 let mut buf = [0; 8];
388 let mut kcf = self
389 .kcf
390 .as_ref()
391 .ok_or_else(|| PageMapError::Access(Self::KPAGECOUNT.into()))?;
392 kcf.seek(SeekFrom::Start(pfn * 8))
393 .map_err(|e| PageMapError::Seek {
394 path: Self::KPAGECOUNT.into(),
395 source: e,
396 })?;
397 kcf.read_exact(&mut buf).map_err(|e| PageMapError::Read {
398 path: Self::KPAGECOUNT.into(),
399 source: e,
400 })?;
401 Ok(u64::from_ne_bytes(buf))
402 }
403
404 pub fn kpageflags(&self, pfn: u64) -> Result<KPageFlags> {
414 let mut buf = [0; 8];
415 let mut kff = self
416 .kff
417 .as_ref()
418 .ok_or_else(|| PageMapError::Access(Self::KPAGEFLAGS.into()))?;
419 kff.seek(SeekFrom::Start(pfn * 8))
420 .map_err(|e| PageMapError::Seek {
421 path: Self::KPAGEFLAGS.into(),
422 source: e,
423 })?;
424 kff.read_exact(&mut buf).map_err(|e| PageMapError::Read {
425 path: Self::KPAGEFLAGS.into(),
426 source: e,
427 })?;
428 Ok(KPageFlags::from_bits_truncate(u64::from_ne_bytes(buf)))
429 }
430}