1#![cfg(target_os = "linux")]
5
6use crate::memory::MappedDmaTarget;
7use anyhow::Context;
8use inspect::Inspect;
9use std::ffi::c_void;
10use std::fs::File;
11use std::io::Read;
12use std::io::Seek;
13use std::io::SeekFrom;
14use zerocopy::IntoBytes;
15
16const PAGE_SIZE: usize = 4096;
17
18pub struct LockedMemory {
19 mapping: Mapping,
20 pfns: Vec<u64>,
21}
22
23unsafe impl Send for Mapping {}
25unsafe impl Sync for Mapping {}
27
28struct Mapping {
29 addr: *mut c_void,
30 len: usize,
31}
32
33impl Mapping {
34 fn new(len: usize) -> std::io::Result<Self> {
35 let addr = unsafe {
38 libc::mmap(
39 std::ptr::null_mut(),
40 len,
41 libc::PROT_READ | libc::PROT_WRITE,
42 libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_LOCKED,
43 -1,
44 0,
45 )
46 };
47 if addr == libc::MAP_FAILED {
48 return Err(std::io::Error::last_os_error());
49 }
50
51 Ok(Self { addr, len })
52 }
53
54 fn lock(&self) -> std::io::Result<()> {
55 if unsafe { libc::mlock(self.addr, self.len) } < 0 {
57 return Err(std::io::Error::last_os_error());
58 }
59 Ok(())
60 }
61
62 fn pages(&self) -> anyhow::Result<Vec<u64>> {
63 let mut pagemap = File::open("/proc/self/pagemap").context("failed to open pagemap")?;
64 pagemap
65 .seek(SeekFrom::Start((8 * self.addr as usize / PAGE_SIZE) as u64))
66 .context("failed to seek")?;
67 let n = self.len / PAGE_SIZE;
68 let mut pfns = vec![0u64; n];
69 pagemap
70 .read(pfns.as_mut_bytes())
71 .context("failed to read from pagemap")?;
72 for pfn in &mut pfns {
73 if *pfn & (1 << 63) == 0 {
74 anyhow::bail!("page not present in RAM");
75 }
76 *pfn &= 0x3f_ffff_ffff_ffff;
77 }
78 Ok(pfns)
79 }
80}
81
82impl Drop for Mapping {
83 fn drop(&mut self) {
84 if unsafe { libc::munmap(self.addr, self.len) } < 0 {
86 panic!("{:?}", std::io::Error::last_os_error());
87 }
88 }
89}
90
91impl LockedMemory {
92 pub fn new(len: usize) -> anyhow::Result<Self> {
93 if len % PAGE_SIZE != 0 {
94 anyhow::bail!("not a page-size multiple");
95 }
96 let mapping = Mapping::new(len).context("failed to create mapping")?;
97 mapping.lock().context("failed to lock mapping")?;
98 let pages = mapping.pages()?;
99 Ok(Self {
100 mapping,
101 pfns: pages,
102 })
103 }
104}
105
106unsafe impl MappedDmaTarget for LockedMemory {
109 fn base(&self) -> *const u8 {
110 self.mapping.addr.cast()
111 }
112
113 fn len(&self) -> usize {
114 self.mapping.len
115 }
116
117 fn pfns(&self) -> &[u64] {
118 &self.pfns
119 }
120
121 fn pfn_bias(&self) -> u64 {
122 0
123 }
124}
125
126#[derive(Clone, Inspect)]
127pub struct LockedMemorySpawner;
128
129impl crate::DmaClient for LockedMemorySpawner {
130 fn allocate_dma_buffer(&self, len: usize) -> anyhow::Result<crate::memory::MemoryBlock> {
131 Ok(crate::memory::MemoryBlock::new(LockedMemory::new(len)?))
132 }
133
134 fn attach_pending_buffers(&self) -> anyhow::Result<Vec<crate::memory::MemoryBlock>> {
135 anyhow::bail!("restore not supported for lockmem")
136 }
137}