xref: /aosp_15_r20/external/virtio-media/device/src/memfd.rs (revision 1b4853f54772485c5dd4001ae33a7a958bcc97a1)
1*1b4853f5SAndroid Build Coastguard Worker // Copyright 2024 The ChromiumOS Authors
2*1b4853f5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*1b4853f5SAndroid Build Coastguard Worker // found in the LICENSE file.
4*1b4853f5SAndroid Build Coastguard Worker 
5*1b4853f5SAndroid Build Coastguard Worker //! Provides a simple memory allocator using `memfd`. The `MemFd` crate provides the same
6*1b4853f5SAndroid Build Coastguard Worker //! functionality, but also pulls some unwanted dependencies in, so we use this simple
7*1b4853f5SAndroid Build Coastguard Worker //! implementation instead.
8*1b4853f5SAndroid Build Coastguard Worker 
9*1b4853f5SAndroid Build Coastguard Worker use core::slice;
10*1b4853f5SAndroid Build Coastguard Worker use std::fs::File;
11*1b4853f5SAndroid Build Coastguard Worker use std::io;
12*1b4853f5SAndroid Build Coastguard Worker use std::num::NonZeroU64;
13*1b4853f5SAndroid Build Coastguard Worker use std::num::NonZeroUsize;
14*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::AsFd;
15*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::AsRawFd;
16*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::BorrowedFd;
17*1b4853f5SAndroid Build Coastguard Worker use std::os::fd::RawFd;
18*1b4853f5SAndroid Build Coastguard Worker use std::ptr::NonNull;
19*1b4853f5SAndroid Build Coastguard Worker 
20*1b4853f5SAndroid Build Coastguard Worker use nix::errno::Errno;
21*1b4853f5SAndroid Build Coastguard Worker use nix::sys::memfd::memfd_create;
22*1b4853f5SAndroid Build Coastguard Worker use nix::sys::memfd::MemFdCreateFlag;
23*1b4853f5SAndroid Build Coastguard Worker use nix::sys::mman;
24*1b4853f5SAndroid Build Coastguard Worker use thiserror::Error;
25*1b4853f5SAndroid Build Coastguard Worker 
26*1b4853f5SAndroid Build Coastguard Worker /// A chunk of memory allocated through `memfd`.
27*1b4853f5SAndroid Build Coastguard Worker ///
28*1b4853f5SAndroid Build Coastguard Worker /// Buffers allocated this way are of fixed size, and can be manipulated as files.
29*1b4853f5SAndroid Build Coastguard Worker pub struct MemFdBuffer {
30*1b4853f5SAndroid Build Coastguard Worker     file: File,
31*1b4853f5SAndroid Build Coastguard Worker     size: NonZeroU64,
32*1b4853f5SAndroid Build Coastguard Worker }
33*1b4853f5SAndroid Build Coastguard Worker 
34*1b4853f5SAndroid Build Coastguard Worker #[derive(Debug, Error)]
35*1b4853f5SAndroid Build Coastguard Worker pub enum NewMemFdBufferError {
36*1b4853f5SAndroid Build Coastguard Worker     #[error("MemFdBuffer size cannot be zero")]
37*1b4853f5SAndroid Build Coastguard Worker     ZeroSize,
38*1b4853f5SAndroid Build Coastguard Worker     #[error("call to memfd_create failed: {0}")]
39*1b4853f5SAndroid Build Coastguard Worker     FailedToCreate(#[from] Errno),
40*1b4853f5SAndroid Build Coastguard Worker     #[error("failed to set size of memfd: {0}")]
41*1b4853f5SAndroid Build Coastguard Worker     FailedToSetSize(io::Error),
42*1b4853f5SAndroid Build Coastguard Worker     #[error("failed to seal memfd: {0}")]
43*1b4853f5SAndroid Build Coastguard Worker     FailedToSeal(io::Error),
44*1b4853f5SAndroid Build Coastguard Worker }
45*1b4853f5SAndroid Build Coastguard Worker 
46*1b4853f5SAndroid Build Coastguard Worker #[derive(Debug, Error)]
47*1b4853f5SAndroid Build Coastguard Worker pub enum MemFdMmapError {
48*1b4853f5SAndroid Build Coastguard Worker     #[error("buffer size {0} larger than usize")]
49*1b4853f5SAndroid Build Coastguard Worker     BufferTooLarge(u64),
50*1b4853f5SAndroid Build Coastguard Worker     #[error("mmap call returned error: {0}")]
51*1b4853f5SAndroid Build Coastguard Worker     Mmap(#[from] Errno),
52*1b4853f5SAndroid Build Coastguard Worker }
53*1b4853f5SAndroid Build Coastguard Worker 
54*1b4853f5SAndroid Build Coastguard Worker impl MemFdBuffer {
new(size: u64) -> Result<Self, NewMemFdBufferError>55*1b4853f5SAndroid Build Coastguard Worker     pub fn new(size: u64) -> Result<Self, NewMemFdBufferError> {
56*1b4853f5SAndroid Build Coastguard Worker         let size = NonZeroU64::new(size).ok_or(NewMemFdBufferError::ZeroSize)?;
57*1b4853f5SAndroid Build Coastguard Worker 
58*1b4853f5SAndroid Build Coastguard Worker         // Dummy name, we may want to support names for debugging purposes.
59*1b4853f5SAndroid Build Coastguard Worker         let fd = memfd_create(c"", MemFdCreateFlag::MFD_ALLOW_SEALING)?;
60*1b4853f5SAndroid Build Coastguard Worker 
61*1b4853f5SAndroid Build Coastguard Worker         let file: File = fd.into();
62*1b4853f5SAndroid Build Coastguard Worker 
63*1b4853f5SAndroid Build Coastguard Worker         // Allocate requested size.
64*1b4853f5SAndroid Build Coastguard Worker         file.set_len(size.into())
65*1b4853f5SAndroid Build Coastguard Worker             .map_err(NewMemFdBufferError::FailedToSetSize)?;
66*1b4853f5SAndroid Build Coastguard Worker 
67*1b4853f5SAndroid Build Coastguard Worker         // Seal so the memory size cannot be changed.
68*1b4853f5SAndroid Build Coastguard Worker         //
69*1b4853f5SAndroid Build Coastguard Worker         // SAFETY: `file` is a valid file.
70*1b4853f5SAndroid Build Coastguard Worker         if unsafe {
71*1b4853f5SAndroid Build Coastguard Worker             libc::fcntl(
72*1b4853f5SAndroid Build Coastguard Worker                 file.as_raw_fd(),
73*1b4853f5SAndroid Build Coastguard Worker                 libc::F_ADD_SEALS,
74*1b4853f5SAndroid Build Coastguard Worker                 libc::F_SEAL_SHRINK | libc::F_SEAL_GROW | libc::F_SEAL_SEAL,
75*1b4853f5SAndroid Build Coastguard Worker             )
76*1b4853f5SAndroid Build Coastguard Worker         } < 0
77*1b4853f5SAndroid Build Coastguard Worker         {
78*1b4853f5SAndroid Build Coastguard Worker             return Err(NewMemFdBufferError::FailedToSeal(io::Error::last_os_error()));
79*1b4853f5SAndroid Build Coastguard Worker         }
80*1b4853f5SAndroid Build Coastguard Worker 
81*1b4853f5SAndroid Build Coastguard Worker         Ok(Self { file, size })
82*1b4853f5SAndroid Build Coastguard Worker     }
83*1b4853f5SAndroid Build Coastguard Worker 
as_file(&self) -> &File84*1b4853f5SAndroid Build Coastguard Worker     pub fn as_file(&self) -> &File {
85*1b4853f5SAndroid Build Coastguard Worker         &self.file
86*1b4853f5SAndroid Build Coastguard Worker     }
87*1b4853f5SAndroid Build Coastguard Worker 
mmap(&self) -> Result<MemFdMapping, MemFdMmapError>88*1b4853f5SAndroid Build Coastguard Worker     pub fn mmap(&self) -> Result<MemFdMapping, MemFdMmapError> {
89*1b4853f5SAndroid Build Coastguard Worker         let size = NonZeroUsize::try_from(self.size)
90*1b4853f5SAndroid Build Coastguard Worker             .map_err(|_| MemFdMmapError::BufferTooLarge(self.size.into()))?;
91*1b4853f5SAndroid Build Coastguard Worker 
92*1b4853f5SAndroid Build Coastguard Worker         // SAFETY: `self.file` is a valid file.
93*1b4853f5SAndroid Build Coastguard Worker         let data = unsafe {
94*1b4853f5SAndroid Build Coastguard Worker             mman::mmap(
95*1b4853f5SAndroid Build Coastguard Worker                 None,
96*1b4853f5SAndroid Build Coastguard Worker                 size,
97*1b4853f5SAndroid Build Coastguard Worker                 mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE,
98*1b4853f5SAndroid Build Coastguard Worker                 mman::MapFlags::MAP_SHARED,
99*1b4853f5SAndroid Build Coastguard Worker                 &self.file,
100*1b4853f5SAndroid Build Coastguard Worker                 0,
101*1b4853f5SAndroid Build Coastguard Worker             )?
102*1b4853f5SAndroid Build Coastguard Worker         };
103*1b4853f5SAndroid Build Coastguard Worker 
104*1b4853f5SAndroid Build Coastguard Worker         Ok(MemFdMapping {
105*1b4853f5SAndroid Build Coastguard Worker             // SAFETY: `data` is non-null and obtained through a `mmap` of size `self.size`.
106*1b4853f5SAndroid Build Coastguard Worker             data: unsafe { slice::from_raw_parts_mut(data.as_ptr().cast(), size.into()) },
107*1b4853f5SAndroid Build Coastguard Worker         })
108*1b4853f5SAndroid Build Coastguard Worker     }
109*1b4853f5SAndroid Build Coastguard Worker }
110*1b4853f5SAndroid Build Coastguard Worker 
111*1b4853f5SAndroid Build Coastguard Worker impl AsFd for MemFdBuffer {
as_fd(&self) -> BorrowedFd<'_>112*1b4853f5SAndroid Build Coastguard Worker     fn as_fd(&self) -> BorrowedFd<'_> {
113*1b4853f5SAndroid Build Coastguard Worker         self.file.as_fd()
114*1b4853f5SAndroid Build Coastguard Worker     }
115*1b4853f5SAndroid Build Coastguard Worker }
116*1b4853f5SAndroid Build Coastguard Worker 
117*1b4853f5SAndroid Build Coastguard Worker impl AsRawFd for MemFdBuffer {
as_raw_fd(&self) -> RawFd118*1b4853f5SAndroid Build Coastguard Worker     fn as_raw_fd(&self) -> RawFd {
119*1b4853f5SAndroid Build Coastguard Worker         self.file.as_raw_fd()
120*1b4853f5SAndroid Build Coastguard Worker     }
121*1b4853f5SAndroid Build Coastguard Worker }
122*1b4853f5SAndroid Build Coastguard Worker 
123*1b4853f5SAndroid Build Coastguard Worker impl From<MemFdBuffer> for File {
from(memfd: MemFdBuffer) -> Self124*1b4853f5SAndroid Build Coastguard Worker     fn from(memfd: MemFdBuffer) -> Self {
125*1b4853f5SAndroid Build Coastguard Worker         memfd.file
126*1b4853f5SAndroid Build Coastguard Worker     }
127*1b4853f5SAndroid Build Coastguard Worker }
128*1b4853f5SAndroid Build Coastguard Worker 
129*1b4853f5SAndroid Build Coastguard Worker /// A CPU mapping of a `MemFdBuffer`.
130*1b4853f5SAndroid Build Coastguard Worker pub struct MemFdMapping {
131*1b4853f5SAndroid Build Coastguard Worker     // A mapping remains valid until we munmap it, that is, until the
132*1b4853f5SAndroid Build Coastguard Worker     // PlaneMapping object is deleted. Hence the static lifetime.
133*1b4853f5SAndroid Build Coastguard Worker     data: &'static mut [u8],
134*1b4853f5SAndroid Build Coastguard Worker }
135*1b4853f5SAndroid Build Coastguard Worker 
136*1b4853f5SAndroid Build Coastguard Worker impl MemFdMapping {
size(&self) -> usize137*1b4853f5SAndroid Build Coastguard Worker     pub fn size(&self) -> usize {
138*1b4853f5SAndroid Build Coastguard Worker         self.data.len()
139*1b4853f5SAndroid Build Coastguard Worker     }
140*1b4853f5SAndroid Build Coastguard Worker }
141*1b4853f5SAndroid Build Coastguard Worker 
142*1b4853f5SAndroid Build Coastguard Worker impl Drop for MemFdMapping {
drop(&mut self)143*1b4853f5SAndroid Build Coastguard Worker     fn drop(&mut self) {
144*1b4853f5SAndroid Build Coastguard Worker         // Safe because the pointer and length were constructed in mmap() and
145*1b4853f5SAndroid Build Coastguard Worker         // are always valid.
146*1b4853f5SAndroid Build Coastguard Worker         unsafe {
147*1b4853f5SAndroid Build Coastguard Worker             mman::munmap(
148*1b4853f5SAndroid Build Coastguard Worker                 NonNull::new_unchecked(self.data.as_mut_ptr().cast()),
149*1b4853f5SAndroid Build Coastguard Worker                 self.data.len(),
150*1b4853f5SAndroid Build Coastguard Worker             )
151*1b4853f5SAndroid Build Coastguard Worker         }
152*1b4853f5SAndroid Build Coastguard Worker         .unwrap_or_else(|e| {
153*1b4853f5SAndroid Build Coastguard Worker             log::error!("error while unmapping MemFdBuffer: {:#}", e);
154*1b4853f5SAndroid Build Coastguard Worker         });
155*1b4853f5SAndroid Build Coastguard Worker     }
156*1b4853f5SAndroid Build Coastguard Worker }
157*1b4853f5SAndroid Build Coastguard Worker 
158*1b4853f5SAndroid Build Coastguard Worker impl AsRef<[u8]> for MemFdMapping {
as_ref(&self) -> &[u8]159*1b4853f5SAndroid Build Coastguard Worker     fn as_ref(&self) -> &[u8] {
160*1b4853f5SAndroid Build Coastguard Worker         self.data
161*1b4853f5SAndroid Build Coastguard Worker     }
162*1b4853f5SAndroid Build Coastguard Worker }
163*1b4853f5SAndroid Build Coastguard Worker 
164*1b4853f5SAndroid Build Coastguard Worker impl AsMut<[u8]> for MemFdMapping {
as_mut(&mut self) -> &mut [u8]165*1b4853f5SAndroid Build Coastguard Worker     fn as_mut(&mut self) -> &mut [u8] {
166*1b4853f5SAndroid Build Coastguard Worker         self.data
167*1b4853f5SAndroid Build Coastguard Worker     }
168*1b4853f5SAndroid Build Coastguard Worker }
169