procfs_core/process/
pagemap.rs

1use bitflags::bitflags;
2use std::{fmt, mem::size_of};
3
4#[cfg(feature = "serde1")]
5use serde::{Deserialize, Serialize};
6
7const fn genmask(high: usize, low: usize) -> u64 {
8    let mask_bits = size_of::<u64>() * 8;
9    (!0 - (1 << low) + 1) & (!0 >> (mask_bits - 1 - high))
10}
11
12// source: include/linux/swap.h
13const MAX_SWAPFILES_SHIFT: usize = 5;
14
15// source: fs/proc/task_mmu.c
16bitflags! {
17    /// Represents the fields and flags in a page table entry for a swapped page.
18    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
19    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
20    pub struct SwapPageFlags: u64 {
21        /// Swap type if swapped
22        #[doc(hidden)]
23        const SWAP_TYPE = genmask(MAX_SWAPFILES_SHIFT - 1, 0);
24        /// Swap offset if swapped
25        #[doc(hidden)]
26        const SWAP_OFFSET = genmask(54, MAX_SWAPFILES_SHIFT);
27        /// PTE is soft-dirty
28        const SOFT_DIRTY = 1 << 55;
29        /// Page is exclusively mapped
30        const MMAP_EXCLUSIVE = 1 << 56;
31        /// Page is file-page or shared-anon
32        const FILE = 1 << 61;
33        /// Page is swapped
34        #[doc(hidden)]
35        const SWAP = 1 << 62;
36        /// Page is present
37        const PRESENT = 1 << 63;
38    }
39}
40
41impl SwapPageFlags {
42    /// Returns the swap type recorded in this entry.
43    pub fn get_swap_type(&self) -> u64 {
44        (*self & Self::SWAP_TYPE).bits()
45    }
46
47    /// Returns the swap offset recorded in this entry.
48    pub fn get_swap_offset(&self) -> u64 {
49        (*self & Self::SWAP_OFFSET).bits() >> MAX_SWAPFILES_SHIFT
50    }
51}
52
53bitflags! {
54    /// Represents the fields and flags in a page table entry for a memory page.
55    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
56    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
57    pub struct MemoryPageFlags: u64 {
58        /// Page frame number if present
59        #[doc(hidden)]
60        const PFN = genmask(54, 0);
61        /// PTE is soft-dirty
62        const SOFT_DIRTY = 1 << 55;
63        /// Page is exclusively mapped
64        const MMAP_EXCLUSIVE = 1 << 56;
65        /// Page is file-page or shared-anon
66        const FILE = 1 << 61;
67        /// Page is swapped
68        #[doc(hidden)]
69        const SWAP = 1 << 62;
70        /// Page is present
71        const PRESENT = 1 << 63;
72    }
73}
74
75impl MemoryPageFlags {
76    /// Returns the page frame number recorded in this entry.
77    pub fn get_page_frame_number(&self) -> Pfn {
78        Pfn((*self & Self::PFN).bits())
79    }
80}
81
82/// A Page Frame Number, representing a 4 kiB physical memory page
83#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
84#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
85pub struct Pfn(pub u64);
86
87impl fmt::UpperHex for Pfn {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        let val = self.0;
90
91        fmt::UpperHex::fmt(&val, f)
92    }
93}
94
95impl fmt::LowerHex for Pfn {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        let val = self.0;
98
99        fmt::LowerHex::fmt(&val, f)
100    }
101}
102
103/// Represents a page table entry in `/proc/<pid>/pagemap`.
104#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
105pub enum PageInfo {
106    /// Entry referring to a memory page
107    MemoryPage(MemoryPageFlags),
108    /// Entry referring to a swapped page
109    SwapPage(SwapPageFlags),
110}
111
112impl PageInfo {
113    pub fn parse_info(info: u64) -> Self {
114        let flags = MemoryPageFlags::from_bits_retain(info);
115
116        if flags.contains(MemoryPageFlags::SWAP) {
117            Self::SwapPage(SwapPageFlags::from_bits_retain(info))
118        } else {
119            Self::MemoryPage(flags)
120        }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_genmask() {
130        let mask = genmask(3, 1);
131        assert_eq!(mask, 0b1110);
132
133        let mask = genmask(3, 0);
134        assert_eq!(mask, 0b1111);
135
136        let mask = genmask(63, 62);
137        assert_eq!(mask, 0b11 << 62);
138    }
139
140    #[test]
141    fn test_page_info() {
142        let pagemap_entry: u64 = 0b1000000110000000000000000000000000000000000000000000000000000011;
143        let info = PageInfo::parse_info(pagemap_entry);
144        if let PageInfo::MemoryPage(memory_flags) = info {
145            assert!(memory_flags
146                .contains(MemoryPageFlags::PRESENT | MemoryPageFlags::MMAP_EXCLUSIVE | MemoryPageFlags::SOFT_DIRTY));
147            assert_eq!(memory_flags.get_page_frame_number(), Pfn(0b11));
148        } else {
149            panic!("Wrong SWAP decoding");
150        }
151
152        let pagemap_entry: u64 = 0b1100000110000000000000000000000000000000000000000000000001100010;
153        let info = PageInfo::parse_info(pagemap_entry);
154        if let PageInfo::SwapPage(swap_flags) = info {
155            assert!(
156                swap_flags.contains(SwapPageFlags::PRESENT | SwapPageFlags::MMAP_EXCLUSIVE | SwapPageFlags::SOFT_DIRTY)
157            );
158            assert_eq!(swap_flags.get_swap_type(), 0b10);
159            assert_eq!(swap_flags.get_swap_offset(), 0b11);
160        } else {
161            panic!("Wrong SWAP decoding");
162        }
163    }
164}