1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(feature = "std")]
5use std::{
6 ops::Range,
7 path::Path,
8 fs::{File, read_to_string},
9 io::{Result, BufReader, BufRead, Error, ErrorKind},
10};
11
12pub const BUFFER_SIZE: usize = 16384;
14pub const QUEUE_SIZE: u32 = 64;
15pub const MAX_QUEUE_SIZE: usize = QUEUE_SIZE as usize * BUFFER_SIZE;
16
17pub const PROC_IOMEM: &str = "/proc/iomem";
19pub const SEPARATOR_SYSTEM_RAM: &str = " : System RAM";
20pub const SEPARATOR_HYPHEN: char = '-';
21
22pub const PROC_KALLSYMS: &str = "/proc/kallsyms";
24pub const PAGE_OFFSET_BASE: &str = "page_offset_base";
25#[cfg(feature = "std")]
26const PROC_KPTR_RESTRICT: &str = "/proc/sys/kernel/kptr_restrict";
27#[cfg(feature = "std")]
28const PROC_OSRELEASE: &str = "/proc/sys/kernel/osrelease";
29#[cfg(feature = "std")]
30const SYSTEMMAP_PREFIX: &str = "/boot/System.map-";
31
32pub const PROC_SELF_EXE: &str = "/proc/self/exe";
34
35pub const READ_KERNEL_MEM: &str = "read_kernel_memory";
37
38pub const ERROR_DUMP_MEMORY_IOMEM_SEPARATE_KEY_VAL_MAP: &str = "There is no left side in key/value pair";
40pub const ERROR_DUMP_MEMORY_IOMEM_CAPSYSADM: &str = "Need CAP_SYS_ADMIN to read /proc/iomem";
41pub const ERROR_PATH_READ_SYS: &str = "An error occured while trying to read necessary data from /sys";
42
43#[derive(Debug)]
44pub struct LimeHeader {
45 pub magic_bytes: u32,
46 pub header_version: u32,
47 pub start_address: u64,
48 pub end_address: u64,
49 pub reserved_space: [u8; 8],
50}
51
52impl Default for LimeHeader {
53 fn default() -> Self {
54 Self {
55 magic_bytes: 0x4C694D45,
56 header_version: 1,
57 start_address: 0,
58 end_address: 0,
59 reserved_space: [0u8; 8],
60 }
61 }
62}
63
64impl LimeHeader {
65 pub fn new(start_address: u64, end_address: u64) -> Self {
66 Self {
67 start_address,
68 end_address,
69 ..Default::default()
70 }
71 }
72
73 pub fn as_bytes(&self) -> [u8; 32] {
74 let mut bytes = [0u8; 32];
75 bytes[0..4].copy_from_slice(&self.magic_bytes.to_le_bytes());
76 bytes[4..8].copy_from_slice(&self.header_version.to_le_bytes());
77 bytes[8..16].copy_from_slice(&self.start_address.to_le_bytes());
78 bytes[16..24].copy_from_slice(&self.end_address.to_le_bytes());
79 bytes[24..32].copy_from_slice(&self.reserved_space);
80 bytes
81 }
82}
83
84pub enum Header {
85 None,
86 Lime(LimeHeader),
87}
88
89pub fn calc_queue_elements(dump_size: usize) -> usize {
90 if dump_size % BUFFER_SIZE == 0 {
91 dump_size / BUFFER_SIZE
92 } else {
93 dump_size / BUFFER_SIZE + 1
94 }
95}
96
97#[cfg(feature = "std")]
128pub fn extract_mem_range<I: Into<String>>(identifier: I) -> Result<Vec<Range<u64>>> {
129 let identifier = identifier.into();
130 let path = Path::new(PROC_IOMEM);
131 let file = File::open(path)?;
132 let reader = BufReader::new(file);
133 let mut ranges = Vec::new();
134
135 for line in reader.lines() {
136 let line = line?;
137 if line.contains(&identifier) {
138 if let Some((start, end)) = parse_memory_range(&line) {
139 ranges.push(start..(end + 1));
140 }
141 }
142 }
143
144 Ok(ranges)
145}
146
147#[cfg(feature = "std")]
148pub fn get_page_offset_base_address_from_file() -> Result<u64>{
149 let file = match get_kptr_restrict()? {
150 KptrRestrict::Full => get_system_map_fd()?,
151 _ => get_kallsyms_fd()?,
152 };
153 let reader = BufReader::new(file);
154 for line in reader.lines() {
155 let line = line?;
156 if line.contains(PAGE_OFFSET_BASE) {
157 let parts: Vec<&str> = line.split_whitespace().collect();
158 if let Some(offset_str) = parts.first() {
159 return u64::from_str_radix(offset_str, 16)
160 .map_err(|e| Error::new(ErrorKind::InvalidData, e))
161 }
162 }
163 }
164 Ok(0)
166}
167
168#[cfg(feature = "std")]
169fn get_system_map_fd() -> Result<File> {
170 let os_release = read_to_string(PROC_OSRELEASE)?;
171 let path = Path::new(SYSTEMMAP_PREFIX).join(os_release);
172 File::open(path)
173}
174
175#[cfg(feature = "std")]
176fn get_kallsyms_fd() -> Result<File> {
177 let path = Path::new(PROC_KALLSYMS);
178 File::open(path)
179}
180
181#[cfg(feature = "std")]
182fn get_kptr_restrict() -> Result<KptrRestrict> {
183 let value_str = read_to_string(PROC_KPTR_RESTRICT)?;
184 let value = value_str.trim().parse::<u8>().unwrap();
185 KptrRestrict::try_from(value)
186}
187
188#[cfg(feature = "std")]
189fn parse_memory_range(line: &str) -> Option<(u64, u64)> {
190 let parts: Vec<&str> = line.split_whitespace().collect();
191 if let Some(range_part) = parts.first() {
192 let bounds: Vec<&str> = range_part.split(SEPARATOR_HYPHEN).collect();
193 if bounds.len() == 2 {
194 if let (Ok(start), Ok(end)) = (u64::from_str_radix(bounds[0], 16), u64::from_str_radix(bounds[1], 16)) {
195 return Some((start, end));
196 }
197 }
198 }
199 None
200}
201
202#[cfg(feature = "std")]
203enum KptrRestrict {
204 None,
205 Partial,
206 Full
207}
208
209#[cfg(feature = "std")]
210impl TryFrom<u8> for KptrRestrict {
211 type Error = std::io::Error;
212
213 fn try_from(value: u8) -> core::result::Result<Self, Self::Error> {
214 match value {
215 0 => Ok(KptrRestrict::None),
216 1 => Ok(KptrRestrict::Partial),
217 2 => Ok(KptrRestrict::Full),
218 _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{value}"))),
219 }
220 }
221}