diff --git a/kernel/comps/mlsdisk/Cargo.toml b/kernel/comps/mlsdisk/Cargo.toml index ae3f22aa..c74d48c4 100644 --- a/kernel/comps/mlsdisk/Cargo.toml +++ b/kernel/comps/mlsdisk/Cargo.toml @@ -10,6 +10,12 @@ inherit-methods-macro = {git = "https://github.com/asterinas/inherit-methods-mac ostd-pod = { git = "https://github.com/asterinas/ostd-pod", rev = "c4644be", version = "0.1.1" } aster-block = { path = "../block" } ostd = { path = "../../../ostd" } +# Enable `force-soft` feature to disable `AES-NI` and `CLMUL` intrinsics, ensuring that the implementation +# relies solely on software, and in the software implementation, unsafe code is rarely used. +# FIXME: to utilize `AES-NI` and `CLMUL` intrinsics, some specific flags must be added to `RUSTFLAGS`, +# i.e. `-Ctarget-cpu=sandybridge -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3`. +# This suggests that `Asterinas` should support saving and restoring the `FPU` state within the kernel context, +# a capability it currently lacks. aes-gcm = { version = "0.9.4", features = ["force-soft"] } bittle = "0.5.6" ctr = "0.8.0" diff --git a/kernel/comps/mlsdisk/src/lib.rs b/kernel/comps/mlsdisk/src/lib.rs index 2f8472c3..6f61770a 100644 --- a/kernel/comps/mlsdisk/src/lib.rs +++ b/kernel/comps/mlsdisk/src/lib.rs @@ -16,6 +16,16 @@ mod util; extern crate alloc; +use alloc::{sync::Arc, vec}; +use core::ops::Range; + +use aster_block::{ + bio::{Bio, BioDirection, BioSegment, BioStatus, BioType}, + id::Sid, + BlockDevice, SECTOR_SIZE, +}; +use ostd::{mm::VmIo, prelude::*}; + pub use self::{ error::{Errno, Error}, layers::{ @@ -25,3 +35,169 @@ pub use self::{ os::{Aead, AeadIv, AeadKey, AeadMac, Rng}, util::{Aead as _, RandomInit, Rng as _}, }; + +#[derive(Clone, Debug)] +struct RawDisk { + inner: Arc, + region: Range, +} + +impl RawDisk { + fn new(host_disk: Arc) -> Self { + let end = host_disk.metadata().nr_sectors * SECTOR_SIZE / BLOCK_SIZE; + Self { + inner: host_disk, + region: Range { start: 0, end }, + } + } +} + +impl BlockSet for RawDisk { + fn read(&self, pos: BlockId, mut buf: BufMut) -> core::result::Result<(), Error> { + if pos + buf.nblocks() > self.region.end { + return_errno_with_msg!(Errno::InvalidArgs, "read position is out of range"); + } + let sid = Sid::from_offset((self.region.start + pos) * BLOCK_SIZE); + let bio_segment = BioSegment::alloc(buf.nblocks(), BioDirection::FromDevice); + let bio = Bio::new(BioType::Read, sid, vec![bio_segment.clone()], None); + + let res = bio.submit_and_wait(&*self.inner); + match res { + Ok(BioStatus::Complete) => { + bio_segment.read_bytes(0, buf.as_mut_slice()).unwrap(); + Ok(()) + } + _ => return_errno_with_msg!(Errno::IoFailed, "read io failed"), + } + } + + fn write(&self, pos: BlockId, buf: BufRef) -> core::result::Result<(), Error> { + if pos + buf.nblocks() > self.region.end { + return_errno_with_msg!(Errno::InvalidArgs, "write position is out of range"); + } + let sid = Sid::from_offset((self.region.start + pos) * BLOCK_SIZE); + let bio_segment = BioSegment::alloc(buf.nblocks(), BioDirection::ToDevice); + bio_segment.write_bytes(0, buf.as_slice()).unwrap(); + let bio = Bio::new(BioType::Write, sid, vec![bio_segment], None); + + let res = bio.submit_and_wait(&*self.inner); + match res { + Ok(BioStatus::Complete) => Ok(()), + _ => return_errno_with_msg!(Errno::IoFailed, "write io failed"), + } + } + + fn subset(&self, range: Range) -> core::result::Result { + if self.region.start + range.end > self.region.end { + return_errno_with_msg!(Errno::InvalidArgs, "subset is out of range"); + } + + Ok(RawDisk { + inner: self.inner.clone(), + region: Range { + start: self.region.start + range.start, + end: self.region.start + range.end, + }, + }) + } + + fn flush(&self) -> core::result::Result<(), Error> { + Ok(()) + } + + fn nblocks(&self) -> usize { + self.region.len() + } +} + +#[cfg(ktest)] +mod test { + use aster_block::{ + bio::{BioEnqueueError, BioStatus, BioType, SubmittedBio}, + BlockDevice, BlockDeviceMeta, SECTOR_SIZE, + }; + use ostd::{ + mm::{FrameAllocOptions, Segment, VmIo}, + prelude::*, + }; + + use super::*; + + #[derive(Debug)] + struct MemoryDisk { + blocks: Segment<()>, + } + + impl MemoryDisk { + fn new(nblocks: usize) -> Self { + let blocks = FrameAllocOptions::new() + .zeroed(false) + .alloc_segment(nblocks) + .unwrap(); + Self { blocks } + } + } + + impl BlockDevice for MemoryDisk { + fn enqueue(&self, bio: SubmittedBio) -> core::result::Result<(), BioEnqueueError> { + let bio_type = bio.type_(); + if bio_type == BioType::Flush || bio_type == BioType::Discard { + bio.complete(BioStatus::Complete); + return Ok(()); + } + + let mut current_offset = bio.sid_range().start.to_offset(); + for segment in bio.segments() { + let size = match bio_type { + BioType::Read => segment + .inner_segment() + .writer() + .write(&mut self.blocks.reader().skip(current_offset)), + BioType::Write => self + .blocks + .writer() + .skip(current_offset) + .write(&mut segment.inner_segment().reader()), + _ => 0, + }; + current_offset += size; + } + bio.complete(BioStatus::Complete); + Ok(()) + } + + fn metadata(&self) -> BlockDeviceMeta { + BlockDeviceMeta { + max_nr_segments_per_bio: usize::MAX, + nr_sectors: self.blocks.size() / SECTOR_SIZE, + } + } + } + + fn create_rawdisk(nblocks: usize) -> RawDisk { + let memory_disk = MemoryDisk::new(nblocks); + RawDisk::new(Arc::new(memory_disk)) + } + + #[ktest] + fn write_sync_read() { + let nblocks = 64 * 1024; + let raw_disk = create_rawdisk(nblocks); + let root_key = AeadKey::random(); + let mlsdisk = MlsDisk::create(raw_disk.clone(), root_key, None).unwrap(); + + let num_rw = 128; + let mut rw_buf = Buf::alloc(1).unwrap(); + for i in 0..num_rw { + rw_buf.as_mut_slice().fill(i as u8); + mlsdisk.write(i, rw_buf.as_ref()).unwrap(); + } + + mlsdisk.sync().unwrap(); + + for i in 0..num_rw { + mlsdisk.read(i, rw_buf.as_mut()).unwrap(); + assert_eq!(rw_buf.as_slice()[0], i as u8); + } + } +}