Skip to main content

axdriver_block/
ramdisk.rs

1//! A RAM disk driver backed by heap memory or static slice.
2
3extern crate alloc;
4
5use alloc::alloc::{alloc_zeroed, dealloc};
6use core::{
7    alloc::Layout,
8    ops::{Deref, DerefMut},
9    ptr::NonNull,
10};
11
12use axdriver_base::{BaseDriverOps, DevError, DevResult, DeviceType};
13
14use crate::BlockDriverOps;
15
16const BLOCK_SIZE: usize = 512;
17
18/// A RAM disk driver.
19pub enum RamDisk {
20    /// A RAM disk backed by heap memory.
21    Heap(NonNull<[u8]>),
22    /// A RAM disk backed by a static slice.
23    Static(&'static mut [u8]),
24}
25
26unsafe impl Send for RamDisk {}
27unsafe impl Sync for RamDisk {}
28
29impl Default for RamDisk {
30    /// Creates a default RAM disk with zero size.
31    fn default() -> Self {
32        Self::Heap(NonNull::<[u8; 0]>::dangling())
33    }
34}
35
36impl RamDisk {
37    /// Creates a new RAM disk with the given size hint, allocated on the heap.
38    ///
39    /// The actual size of the RAM disk will be aligned upwards to the block
40    /// size (512 bytes).
41    pub fn new(size_hint: usize) -> Self {
42        let size = align_up(size_hint);
43        if size == 0 {
44            return Self::default();
45        }
46        // SAFETY: size > 0
47        let ptr = unsafe {
48            NonNull::new(alloc_zeroed(
49                Layout::from_size_align(size, BLOCK_SIZE).unwrap(),
50            ))
51            .unwrap()
52        };
53        Self::Heap(NonNull::slice_from_raw_parts(ptr, size))
54    }
55
56    /// Creates a new RAM disk from the given static buffer. This will not
57    /// allocate any memory.
58    ///
59    /// # Panics
60    /// Panics if the buffer is not aligned to block size or its size is not
61    /// a multiple of block size.
62    pub fn from_static(buf: &'static mut [u8]) -> Self {
63        assert_eq!(buf.as_ptr().addr() & (BLOCK_SIZE - 1), 0);
64        assert!(buf.len().is_multiple_of(BLOCK_SIZE));
65        Self::Static(buf)
66    }
67
68    /// Creates a new RAM disk from the given slice, by copying it.
69    pub fn copy_from_slice(data: &[u8]) -> Self {
70        let mut this = RamDisk::new(data.len());
71        this[..data.len()].copy_from_slice(data);
72        this
73    }
74}
75
76impl Drop for RamDisk {
77    fn drop(&mut self) {
78        if let RamDisk::Heap(ptr) = self
79            && !ptr.is_empty()
80        {
81            unsafe {
82                dealloc(
83                    ptr.cast::<u8>().as_ptr(),
84                    Layout::from_size_align(ptr.len(), BLOCK_SIZE).unwrap(),
85                )
86            }
87        }
88    }
89}
90
91impl Deref for RamDisk {
92    type Target = [u8];
93
94    fn deref(&self) -> &Self::Target {
95        match self {
96            RamDisk::Heap(ptr) => unsafe { ptr.as_ref() },
97            RamDisk::Static(slice) => slice,
98        }
99    }
100}
101
102impl DerefMut for RamDisk {
103    fn deref_mut(&mut self) -> &mut Self::Target {
104        match self {
105            RamDisk::Heap(ptr) => unsafe { ptr.as_mut() },
106            RamDisk::Static(slice) => slice,
107        }
108    }
109}
110
111impl From<&'static mut [u8]> for RamDisk {
112    /// Creates a RAM disk from a static mutable slice without copying.
113    fn from(data: &'static mut [u8]) -> Self {
114        RamDisk::from_static(data)
115    }
116}
117
118impl BaseDriverOps for RamDisk {
119    fn device_name(&self) -> &str {
120        "ramdisk"
121    }
122
123    fn device_type(&self) -> DeviceType {
124        DeviceType::Block
125    }
126}
127
128impl BlockDriverOps for RamDisk {
129    #[inline]
130    fn num_blocks(&self) -> u64 {
131        (self.len() / BLOCK_SIZE) as u64
132    }
133
134    #[inline]
135    fn block_size(&self) -> usize {
136        BLOCK_SIZE
137    }
138
139    fn read_block(&mut self, block_id: u64, buf: &mut [u8]) -> DevResult {
140        if !buf.len().is_multiple_of(BLOCK_SIZE) {
141            return Err(DevError::InvalidParam);
142        }
143        let block_id: usize = block_id.try_into().map_err(|_| DevError::InvalidParam)?;
144        let offset = block_id
145            .checked_mul(BLOCK_SIZE)
146            .ok_or(DevError::InvalidParam)?;
147        if offset.saturating_add(buf.len()) > self.len() {
148            return Err(DevError::InvalidParam);
149        }
150        buf.copy_from_slice(&self[offset..offset + buf.len()]);
151        Ok(())
152    }
153
154    fn write_block(&mut self, block_id: u64, buf: &[u8]) -> DevResult {
155        if !buf.len().is_multiple_of(BLOCK_SIZE) {
156            return Err(DevError::InvalidParam);
157        }
158        let block_id: usize = block_id.try_into().map_err(|_| DevError::InvalidParam)?;
159        let offset = block_id
160            .checked_mul(BLOCK_SIZE)
161            .ok_or(DevError::InvalidParam)?;
162        if offset.saturating_add(buf.len()) > self.len() {
163            return Err(DevError::InvalidParam);
164        }
165        self[offset..offset + buf.len()].copy_from_slice(buf);
166        Ok(())
167    }
168
169    fn flush(&mut self) -> DevResult {
170        Ok(())
171    }
172}
173
174const fn align_up(val: usize) -> usize {
175    (val + BLOCK_SIZE - 1) & !(BLOCK_SIZE - 1)
176}