From 4c72f5b7fa7014fb9ee3ac1d9852142d6540696f Mon Sep 17 00:00:00 2001 From: LI Qing Date: Mon, 30 Oct 2023 15:37:59 +0800 Subject: [PATCH] Add VmSegment and rewrite the vm allocation code with VmAllocOptions --- .../src/arch/x86/iommu/context_table.rs | 12 +- framework/jinux-frame/src/task/task.rs | 12 +- framework/jinux-frame/src/vm/frame.rs | 317 ++++++++++++------ .../jinux-frame/src/vm/frame_allocator.rs | 63 ++-- framework/jinux-frame/src/vm/memory_set.rs | 13 +- framework/jinux-frame/src/vm/mod.rs | 4 +- framework/jinux-frame/src/vm/options.rs | 159 +++++++++ framework/jinux-frame/src/vm/page_table.rs | 12 +- services/comps/virtio/src/queue.rs | 29 +- .../libs/jinux-std/src/fs/utils/page_cache.rs | 15 +- services/libs/jinux-std/src/vm/vmo/mod.rs | 13 +- services/libs/jinux-std/src/vm/vmo/options.rs | 8 +- 12 files changed, 447 insertions(+), 210 deletions(-) create mode 100644 framework/jinux-frame/src/vm/options.rs diff --git a/framework/jinux-frame/src/arch/x86/iommu/context_table.rs b/framework/jinux-frame/src/arch/x86/iommu/context_table.rs index 38e06ed44..12c713ec0 100644 --- a/framework/jinux-frame/src/arch/x86/iommu/context_table.rs +++ b/framework/jinux-frame/src/arch/x86/iommu/context_table.rs @@ -8,7 +8,7 @@ use crate::{ bus::pci::PciDeviceLocation, vm::{ page_table::{PageTableConfig, PageTableError}, - Paddr, PageTable, Vaddr, VmAllocOptions, VmFrame, VmFrameVec, VmIo, + Paddr, PageTable, Vaddr, VmAllocOptions, VmFrame, VmIo, }, }; @@ -48,10 +48,7 @@ pub enum ContextTableError { impl RootTable { pub fn new() -> Self { Self { - root_frame: VmFrameVec::allocate(VmAllocOptions::new(1).uninit(false)) - .unwrap() - .pop() - .unwrap(), + root_frame: VmAllocOptions::new(1).alloc_single().unwrap(), context_tables: BTreeMap::new(), } } @@ -236,10 +233,7 @@ pub struct ContextTable { impl ContextTable { fn new() -> Self { Self { - entries_frame: VmFrameVec::allocate(VmAllocOptions::new(1).uninit(false)) - .unwrap() - .pop() - .unwrap(), + entries_frame: VmAllocOptions::new(1).alloc_single().unwrap(), page_tables: BTreeMap::new(), } } diff --git a/framework/jinux-frame/src/task/task.rs b/framework/jinux-frame/src/task/task.rs index 0c468d265..7cbc09f98 100644 --- a/framework/jinux-frame/src/task/task.rs +++ b/framework/jinux-frame/src/task/task.rs @@ -2,7 +2,7 @@ use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE}; use crate::cpu::CpuSet; use crate::prelude::*; use crate::user::UserSpace; -use crate::vm::{VmAllocOptions, VmFrameVec}; +use crate::vm::{VmAllocOptions, VmSegment}; use spin::{Mutex, MutexGuard}; use intrusive_collections::intrusive_adapter; @@ -38,20 +38,20 @@ extern "C" { } pub struct KernelStack { - frame: VmFrameVec, + segment: VmSegment, } impl KernelStack { pub fn new() -> Result { Ok(Self { - frame: VmFrameVec::allocate( - VmAllocOptions::new(KERNEL_STACK_SIZE / PAGE_SIZE).is_contiguous(true), - )?, + segment: VmAllocOptions::new(KERNEL_STACK_SIZE / PAGE_SIZE) + .is_contiguous(true) + .alloc_contiguous()?, }) } pub fn end_paddr(&self) -> Paddr { - self.frame.get(self.frame.len() - 1).unwrap().end_paddr() + self.segment.end_paddr() } } diff --git a/framework/jinux-frame/src/vm/frame.rs b/framework/jinux-frame/src/vm/frame.rs index 50b1f6d7f..729ef279e 100644 --- a/framework/jinux-frame/src/vm/frame.rs +++ b/framework/jinux-frame/src/vm/frame.rs @@ -2,7 +2,7 @@ use alloc::vec; use core::{ iter::Iterator, marker::PhantomData, - ops::{BitAnd, BitOr, Not}, + ops::{BitAnd, BitOr, Not, Range}, }; use crate::{arch::iommu, config::PAGE_SIZE, prelude::*, Error}; @@ -18,56 +18,9 @@ use super::{Paddr, VmIo}; /// more often than not, one needs to operate on a batch of frames rather /// a single frame. #[derive(Debug, Clone)] -pub struct VmFrameVec(Vec); +pub struct VmFrameVec(pub(crate) Vec); impl VmFrameVec { - /// Allocate a collection of free frames according to the given options. - /// - /// All returned frames are safe to use in the sense that they are - /// not _typed memory_. We define typed memory as the memory that - /// may store Rust objects or affect Rust memory safety, e.g., - /// the code and data segments of the OS kernel, the stack and heap - /// allocated for the OS kernel. - /// - /// For more information, see `VmAllocOptions`. - pub fn allocate(options: &VmAllocOptions) -> Result { - let page_size = options.page_size; - let mut flags = VmFrameFlags::empty(); - if options.can_dma { - flags.insert(VmFrameFlags::CAN_DMA); - } - let mut frames = if options.is_contiguous { - frame_allocator::alloc_continuous(options.page_size, flags).ok_or(Error::NoMemory)? - } else { - let mut frame_list = Vec::new(); - for _ in 0..page_size { - frame_list.push(frame_allocator::alloc(flags).ok_or(Error::NoMemory)?); - } - frame_list - }; - if options.can_dma { - for frame in frames.iter_mut() { - // Safety: The address is controlled by frame allocator. - unsafe { - if let Err(err) = iommu::map(frame.start_paddr(), frame) { - match err { - // do nothing - iommu::IommuError::NoIommu => {} - iommu::IommuError::ModificationError(err) => { - panic!("iommu map error:{:?}", err) - } - } - } - } - } - } - let frame_vec = Self(frames); - if !options.uninit { - frame_vec.zero(); - } - Ok(frame_vec) - } - pub fn get(&self, index: usize) -> Option<&VmFrame> { self.0.get(index) } @@ -216,58 +169,6 @@ impl<'a> Iterator for VmFrameVecIter<'a> { } } -/// Options for allocating physical memory pages (or frames). -/// See `VmFrameVec::alloc`. -pub struct VmAllocOptions { - page_size: usize, - is_contiguous: bool, - uninit: bool, - can_dma: bool, -} - -impl VmAllocOptions { - /// Creates new options for allocating the specified number of frames. - pub fn new(len: usize) -> Self { - Self { - page_size: len, - is_contiguous: false, - uninit: false, - can_dma: false, - } - } - - /// Sets whether the allocated frames should be contiguous. - /// - /// If the physical address is set, then the frames must be contiguous. - /// - /// The default value is `false`. - pub fn is_contiguous(&mut self, is_contiguous: bool) -> &mut Self { - self.is_contiguous = is_contiguous; - self - } - - /// Sets whether the allocated frames should be uninitialized. - /// - /// If `uninit` is set as `false`, the frame will be zeroed once allocated. - /// If `uninit` is set as `true`, the frame will **NOT** be zeroed and should *NOT* be read before writing. - /// - /// The default value is false. - pub fn uninit(&mut self, uninit: bool) -> &mut Self { - self.uninit = uninit; - self - } - - /// Sets whether the pages can be accessed by devices through - /// Direct Memory Access (DMA). - /// - /// In a TEE environment, DMAable pages are untrusted pages shared with - /// the VMM. - pub fn can_dma(&mut self, can_dma: bool) -> &mut Self { - self.can_dma = can_dma; - self - } -} - bitflags::bitflags! { pub(crate) struct VmFrameFlags : usize{ const NEED_DEALLOC = 1 << 63; @@ -326,15 +227,10 @@ impl VmFrame { (self.frame_index() + 1) * PAGE_SIZE } - /// fill the frame with zero + /// Fills the frame with zero. pub fn zero(&self) { - unsafe { - core::ptr::write_bytes( - super::paddr_to_vaddr(self.start_paddr()) as *mut u8, - 0, - PAGE_SIZE, - ) - } + // Safety: The range of memory is valid for writes of one page data. + unsafe { core::ptr::write_bytes(self.as_mut_ptr(), 0, PAGE_SIZE) } } /// Returns whether the page frame is accessible by DMA. @@ -423,7 +319,208 @@ impl Drop for VmFrame { } // Safety: the frame index is valid. unsafe { - frame_allocator::dealloc(self.frame_index()); + frame_allocator::dealloc_single(self.frame_index()); + } + } + } +} + +/// A handle to a contiguous range of page frames (physical memory pages). +/// +/// The biggest difference between `VmSegment` and `VmFrameVec` is that +/// the page frames must be contiguous for `VmSegment`. +/// +/// A cloned `VmSegment` refers to the same page frames as the original. +/// As the original and cloned instances point to the same physical address, +/// they are treated as equal to each other. +/// +/// #Example +/// +/// ```rust +/// let vm_segment = VmAllocOptions::new(2) +/// .is_contiguous(true) +/// .alloc_contiguous()?; +/// vm_segment.write_bytes(0, buf)?; +/// ``` +#[derive(Debug, Clone)] +pub struct VmSegment { + inner: Arc, + range: Range, +} + +#[derive(Debug)] +struct Inner { + start_frame_index: Paddr, + nframes: usize, +} + +impl Inner { + /// Creates the inner part of 'VmSegment'. + /// + /// # Safety + /// + /// The constructor of 'VmSegment' ensures the safety. + unsafe fn new(paddr: Paddr, nframes: usize, flags: VmFrameFlags) -> Self { + assert_eq!(paddr % PAGE_SIZE, 0); + Self { + start_frame_index: (paddr / PAGE_SIZE).bitor(flags.bits), + nframes, + } + } + + fn start_frame_index(&self) -> usize { + self.start_frame_index + .bitand(VmFrameFlags::all().bits().not()) + } + + fn start_paddr(&self) -> Paddr { + self.start_frame_index() * PAGE_SIZE + } +} + +impl HasPaddr for VmSegment { + fn paddr(&self) -> Paddr { + self.start_paddr() + } +} + +impl VmSegment { + /// Creates a new `VmSegment`. + /// + /// # Safety + /// + /// The given range of page frames must be contiguous and valid for use. + /// The given range of page frames must not have been allocated before, + /// as part of either a `VmFrame` or `VmSegment`. + pub(crate) unsafe fn new(paddr: Paddr, nframes: usize, flags: VmFrameFlags) -> Self { + Self { + inner: Arc::new(Inner::new(paddr, nframes, flags)), + range: 0..nframes, + } + } + + /// Returns a part of the `VmSegment`. + /// + /// # Panic + /// + /// If `range` is not within the range of this `VmSegment`, + /// then the method panics. + pub fn range(&self, range: Range) -> Self { + let orig_range = &self.range; + let adj_range = (range.start + orig_range.start)..(range.end + orig_range.start); + assert!(!adj_range.is_empty() && adj_range.end <= orig_range.end); + + Self { + inner: self.inner.clone(), + range: adj_range, + } + } + + /// Returns the start physical address. + pub fn start_paddr(&self) -> Paddr { + self.start_frame_index() * PAGE_SIZE + } + + /// Returns the end physical address. + pub fn end_paddr(&self) -> Paddr { + (self.start_frame_index() + self.nframes()) * PAGE_SIZE + } + + /// Returns the number of page frames. + pub fn nframes(&self) -> usize { + self.range.len() + } + + /// Returns the number of bytes. + pub fn nbytes(&self) -> usize { + self.nframes() * PAGE_SIZE + } + + /// Fills the page frames with zero. + pub fn zero(&self) { + // Safety: The range of memory is valid for writes of `self.nbytes()` data. + unsafe { core::ptr::write_bytes(self.as_mut_ptr(), 0, self.nbytes()) } + } + + /// Returns whether the page frames is accessible by DMA. + /// + /// In a TEE environment, DMAable pages are untrusted pages shared with + /// the VMM. + pub fn can_dma(&self) -> bool { + (self.inner.start_frame_index & VmFrameFlags::CAN_DMA.bits()) != 0 + } + + fn need_dealloc(&self) -> bool { + (self.inner.start_frame_index & VmFrameFlags::NEED_DEALLOC.bits()) != 0 + } + + fn start_frame_index(&self) -> usize { + self.inner.start_frame_index() + self.range.start + } + + pub fn as_ptr(&self) -> *const u8 { + super::paddr_to_vaddr(self.start_paddr()) as *const u8 + } + + pub fn as_mut_ptr(&self) -> *mut u8 { + super::paddr_to_vaddr(self.start_paddr()) as *mut u8 + } +} + +impl<'a> VmSegment { + /// Returns a reader to read data from it. + pub fn reader(&'a self) -> VmReader<'a> { + // Safety: the memory of the page frames is contiguous and is valid during `'a`. + unsafe { VmReader::from_raw_parts(self.as_ptr(), self.nbytes()) } + } + + /// Returns a writer to write data into it. + pub fn writer(&'a self) -> VmWriter<'a> { + // Safety: the memory of the page frames is contiguous and is valid during `'a`. + unsafe { VmWriter::from_raw_parts_mut(self.as_mut_ptr(), self.nbytes()) } + } +} + +impl VmIo for VmSegment { + fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> { + if buf.len() + offset > self.nbytes() { + return Err(Error::InvalidArgs); + } + let len = self.reader().skip(offset).read(&mut buf.into()); + debug_assert!(len == buf.len()); + Ok(()) + } + + fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> { + if buf.len() + offset > self.nbytes() { + return Err(Error::InvalidArgs); + } + let len = self.writer().skip(offset).write(&mut buf.into()); + debug_assert!(len == buf.len()); + Ok(()) + } +} + +impl Drop for VmSegment { + fn drop(&mut self) { + if self.need_dealloc() && Arc::strong_count(&self.inner) == 1 { + if self.can_dma() { + if let Err(err) = iommu::unmap(self.inner.start_paddr()) { + match err { + // do nothing + iommu::IommuError::NoIommu => {} + iommu::IommuError::ModificationError(err) => { + panic!("iommu map error:{:?}", err) + } + } + } + } + // Safety: the range of contiguous page frames is valid. + unsafe { + frame_allocator::dealloc_contiguous( + self.inner.start_frame_index(), + self.inner.nframes, + ); } } } diff --git a/framework/jinux-frame/src/vm/frame_allocator.rs b/framework/jinux-frame/src/vm/frame_allocator.rs index 9c19f7fe7..0d15281f8 100644 --- a/framework/jinux-frame/src/vm/frame_allocator.rs +++ b/framework/jinux-frame/src/vm/frame_allocator.rs @@ -7,29 +7,21 @@ use spin::Once; use crate::boot::memory_region::{MemoryRegion, MemoryRegionType}; use crate::{config::PAGE_SIZE, sync::SpinLock}; -use super::{frame::VmFrameFlags, VmFrame}; +use super::{frame::VmFrameFlags, VmFrame, VmFrameVec, VmSegment}; pub(super) static FRAME_ALLOCATOR: Once> = Once::new(); -pub(crate) fn alloc(flags: VmFrameFlags) -> Option { +pub(crate) fn alloc(nframes: usize, flags: VmFrameFlags) -> Option { FRAME_ALLOCATOR .get() .unwrap() .lock() - .alloc(1) - .map(|pa| unsafe { VmFrame::new(pa * PAGE_SIZE, flags.union(VmFrameFlags::NEED_DEALLOC)) }) -} - -pub(crate) fn alloc_continuous(frame_count: usize, flags: VmFrameFlags) -> Option> { - FRAME_ALLOCATOR - .get() - .unwrap() - .lock() - .alloc(frame_count) + .alloc(nframes) .map(|start| { let mut vector = Vec::new(); + // Safety: The frame index is valid. unsafe { - for i in 0..frame_count { + for i in 0..nframes { let frame = VmFrame::new( (start + i) * PAGE_SIZE, flags.union(VmFrameFlags::NEED_DEALLOC), @@ -37,26 +29,57 @@ pub(crate) fn alloc_continuous(frame_count: usize, flags: VmFrameFlags) -> Optio vector.push(frame); } } - vector + VmFrameVec(vector) }) } -pub(crate) fn alloc_zero(flags: VmFrameFlags) -> Option { - let frame = alloc(flags)?; - frame.zero(); - Some(frame) +pub(crate) fn alloc_single(flags: VmFrameFlags) -> Option { + FRAME_ALLOCATOR.get().unwrap().lock().alloc(1).map(|idx| + // Safety: The frame index is valid. + unsafe { VmFrame::new(idx * PAGE_SIZE, flags.union(VmFrameFlags::NEED_DEALLOC)) }) } -/// Dealloc a frame. +pub(crate) fn alloc_contiguous(nframes: usize, flags: VmFrameFlags) -> Option { + FRAME_ALLOCATOR + .get() + .unwrap() + .lock() + .alloc(nframes) + .map(|start| + // Safety: The range of page frames is contiguous and valid. + unsafe { + VmSegment::new( + start * PAGE_SIZE, + nframes, + flags.union(VmFrameFlags::NEED_DEALLOC), + ) + }) +} + +/// Deallocate a frame. /// /// # Safety /// /// User should ensure the index is valid /// -pub(crate) unsafe fn dealloc(index: usize) { +pub(crate) unsafe fn dealloc_single(index: usize) { FRAME_ALLOCATOR.get().unwrap().lock().dealloc(index, 1); } +/// Deallocate a contiguous range of page frames. +/// +/// # Safety +/// +/// User should ensure the range of page frames is valid. +/// +pub(crate) unsafe fn dealloc_contiguous(start_index: usize, nframes: usize) { + FRAME_ALLOCATOR + .get() + .unwrap() + .lock() + .dealloc(start_index, nframes); +} + pub(crate) fn init(regions: &[MemoryRegion]) { let mut allocator = FrameAllocator::<32>::new(); for region in regions.iter() { diff --git a/framework/jinux-frame/src/vm/memory_set.rs b/framework/jinux-frame/src/vm/memory_set.rs index 509553b56..81d3e0dd9 100644 --- a/framework/jinux-frame/src/vm/memory_set.rs +++ b/framework/jinux-frame/src/vm/memory_set.rs @@ -1,19 +1,14 @@ -use super::{ - frame::VmFrameFlags, - page_table::{PageTable, PageTableConfig}, -}; +use super::page_table::{PageTable, PageTableConfig}; use crate::{ arch::mm::{PageTableEntry, PageTableFlags}, config::{PAGE_SIZE, PHYS_OFFSET}, vm::is_page_aligned, - vm::{VmFrame, VmFrameVec, VmReader, VmWriter}, + vm::{VmAllocOptions, VmFrame, VmFrameVec, VmReader, VmWriter}, }; use crate::{prelude::*, Error}; use alloc::collections::{btree_map::Entry, BTreeMap}; use core::fmt; -use super::frame_allocator; - #[derive(Debug)] pub struct MapArea { pub flags: PageTableFlags, @@ -32,7 +27,7 @@ impl Clone for MapArea { fn clone(&self) -> Self { let mut mapper = BTreeMap::new(); for (&va, old) in &self.mapper { - let new = frame_allocator::alloc(VmFrameFlags::empty()).unwrap(); + let new = VmAllocOptions::new(1).uninit(true).alloc_single().unwrap(); new.copy_from_frame(old); mapper.insert(va, new.clone()); } @@ -97,7 +92,7 @@ impl MapArea { match self.mapper.entry(va) { Entry::Occupied(e) => e.get().start_paddr(), Entry::Vacant(e) => e - .insert(frame_allocator::alloc_zero(VmFrameFlags::empty()).unwrap()) + .insert(VmAllocOptions::new(1).alloc_single().unwrap()) .start_paddr(), } } diff --git a/framework/jinux-frame/src/vm/mod.rs b/framework/jinux-frame/src/vm/mod.rs index eaaef55fa..669434764 100644 --- a/framework/jinux-frame/src/vm/mod.rs +++ b/framework/jinux-frame/src/vm/mod.rs @@ -12,13 +12,15 @@ pub(crate) mod heap_allocator; mod io; mod memory_set; mod offset; +mod options; pub(crate) mod page_table; mod space; use crate::config::{KERNEL_OFFSET, PAGE_SIZE, PHYS_OFFSET}; -pub use self::frame::{VmAllocOptions, VmFrame, VmFrameVec, VmFrameVecIter, VmReader, VmWriter}; +pub use self::frame::{VmFrame, VmFrameVec, VmFrameVecIter, VmReader, VmSegment, VmWriter}; pub use self::io::VmIo; +pub use self::options::VmAllocOptions; pub use self::space::{VmMapOptions, VmPerm, VmSpace}; pub use self::{ diff --git a/framework/jinux-frame/src/vm/options.rs b/framework/jinux-frame/src/vm/options.rs new file mode 100644 index 000000000..732cfa818 --- /dev/null +++ b/framework/jinux-frame/src/vm/options.rs @@ -0,0 +1,159 @@ +use crate::{arch::iommu, prelude::*, Error}; + +use super::{frame::VmFrameFlags, frame_allocator, VmFrame, VmFrameVec, VmSegment}; + +/// Options for allocating physical memory pages (or frames). +/// +/// All allocated frames are safe to use in the sense that they are +/// not _typed memory_. We define typed memory as the memory that +/// may store Rust objects or affect Rust memory safety, e.g., +/// the code and data segments of the OS kernel, the stack and heap +/// allocated for the OS kernel. +pub struct VmAllocOptions { + nframes: usize, + is_contiguous: bool, + uninit: bool, + can_dma: bool, +} + +impl VmAllocOptions { + /// Creates new options for allocating the specified number of frames. + pub fn new(nframes: usize) -> Self { + Self { + nframes, + is_contiguous: false, + uninit: false, + can_dma: false, + } + } + + /// Sets whether the allocated frames should be contiguous. + /// + /// The default value is `false`. + pub fn is_contiguous(&mut self, is_contiguous: bool) -> &mut Self { + self.is_contiguous = is_contiguous; + self + } + + /// Sets whether the allocated frames should be uninitialized. + /// + /// If `uninit` is set as `false`, the frame will be zeroed once allocated. + /// If `uninit` is set as `true`, the frame will **NOT** be zeroed and should *NOT* be read before writing. + /// + /// The default value is false. + pub fn uninit(&mut self, uninit: bool) -> &mut Self { + self.uninit = uninit; + self + } + + /// Sets whether the pages can be accessed by devices through + /// Direct Memory Access (DMA). + /// + /// In a TEE environment, DMAable pages are untrusted pages shared with + /// the VMM. + pub fn can_dma(&mut self, can_dma: bool) -> &mut Self { + self.can_dma = can_dma; + self + } + + /// Allocate a collection of page frames according to the given options. + pub fn alloc(&self) -> Result { + let flags = self.flags(); + let frames = if self.is_contiguous { + frame_allocator::alloc(self.nframes, flags).ok_or(Error::NoMemory)? + } else { + let mut frame_list = Vec::new(); + for _ in 0..self.nframes { + frame_list.push(frame_allocator::alloc_single(flags).ok_or(Error::NoMemory)?); + } + VmFrameVec(frame_list) + }; + if self.can_dma { + for frame in frames.0.iter() { + // Safety: the frame is controlled by frame allocator + unsafe { map_frame(frame) }; + } + } + if !self.uninit { + frames.zero(); + } + + Ok(frames) + } + + /// Allocate a single page frame according to the given options. + pub fn alloc_single(&self) -> Result { + if self.nframes != 1 { + return Err(Error::InvalidArgs); + } + + let frame = frame_allocator::alloc_single(self.flags()).ok_or(Error::NoMemory)?; + if self.can_dma { + // Safety: the frame is controlled by frame allocator + unsafe { map_frame(&frame) }; + } + if !self.uninit { + frame.zero(); + } + + Ok(frame) + } + + /// Allocate a contiguous range of page frames according to the given options. + /// + /// The returned `VmSegment` contains at least one page frame. + pub fn alloc_contiguous(&self) -> Result { + if !self.is_contiguous || self.nframes == 0 { + return Err(Error::InvalidArgs); + } + + let segment = + frame_allocator::alloc_contiguous(self.nframes, self.flags()).ok_or(Error::NoMemory)?; + if self.can_dma { + // Safety: the segment is controlled by frame allocator + unsafe { map_segment(&segment) }; + } + if !self.uninit { + segment.zero(); + } + + Ok(segment) + } + + fn flags(&self) -> VmFrameFlags { + let mut flags = VmFrameFlags::empty(); + if self.can_dma { + flags.insert(VmFrameFlags::CAN_DMA); + } + flags + } +} + +/// Iommu map for the `VmFrame`. +/// +/// # Safety +/// +/// The address should be controlled by frame allocator. +unsafe fn map_frame(frame: &VmFrame) { + let Err(err) = iommu::map(frame.start_paddr(), frame) else { + return; + }; + + match err { + // do nothing + iommu::IommuError::NoIommu => {} + iommu::IommuError::ModificationError(err) => { + panic!("iommu map error:{:?}", err) + } + } +} + +/// Iommu map for the `VmSegment`. +/// +/// # Safety +/// +/// The address should be controlled by frame allocator. +unsafe fn map_segment(segment: &VmSegment) { + // TODO: Support to map a VmSegment. + panic!("VmSegment do not support DMA"); +} diff --git a/framework/jinux-frame/src/vm/page_table.rs b/framework/jinux-frame/src/vm/page_table.rs index 7b0245d49..cb35086f2 100644 --- a/framework/jinux-frame/src/vm/page_table.rs +++ b/framework/jinux-frame/src/vm/page_table.rs @@ -1,7 +1,4 @@ -use super::{ - frame::VmFrameFlags, - frame_allocator, paddr_to_vaddr, VmAllocOptions, VmFrameVec, {Paddr, Vaddr}, -}; +use super::{paddr_to_vaddr, Paddr, Vaddr, VmAllocOptions}; use crate::{ arch::mm::{is_kernel_vaddr, is_user_vaddr, tlb_flush, PageTableEntry}, config::{ENTRY_COUNT, PAGE_SIZE}, @@ -127,7 +124,7 @@ pub struct PageTable { impl PageTable { pub fn new(config: PageTableConfig) -> Self { - let root_frame = frame_allocator::alloc_zero(VmFrameFlags::empty()).unwrap(); + let root_frame = VmAllocOptions::new(1).alloc_single().unwrap(); Self { root_paddr: root_frame.start_paddr(), tables: vec![root_frame], @@ -279,10 +276,7 @@ impl PageTable { return None; } // Create next table - let frame = VmFrameVec::allocate(VmAllocOptions::new(1).uninit(false)) - .unwrap() - .pop() - .unwrap(); + let frame = VmAllocOptions::new(1).alloc_single().unwrap(); // Default flags: read, write, user, present let flags = T::F::new() .set_present(true) diff --git a/services/comps/virtio/src/queue.rs b/services/comps/virtio/src/queue.rs index cf9a8c635..1186e5d90 100644 --- a/services/comps/virtio/src/queue.rs +++ b/services/comps/virtio/src/queue.rs @@ -11,7 +11,7 @@ use core::{ use jinux_frame::{ io_mem::IoMem, offset_of, - vm::{HasPaddr, VmAllocOptions, VmFrame, VmFrameVec}, + vm::{HasPaddr, VmAllocOptions, VmFrame}, }; use jinux_rights::{Dup, TRightSet, TRights, Write}; use jinux_util::{field_ptr, safe_ptr::SafePtr}; @@ -79,13 +79,11 @@ impl VirtQueue { let desc_size = size_of::() * size as usize; let (page1, page2) = { - let mut continue_pages = VmFrameVec::allocate( - VmAllocOptions::new(2) - .uninit(false) - .can_dma(true) - .is_contiguous(true), - ) - .unwrap(); + let mut continue_pages = VmAllocOptions::new(2) + .can_dma(true) + .is_contiguous(true) + .alloc() + .unwrap(); let page1 = continue_pages.pop().unwrap(); let page2 = continue_pages.pop().unwrap(); if page1.paddr() > page2.paddr() { @@ -105,24 +103,15 @@ impl VirtQueue { } ( SafePtr::new( - VmFrameVec::allocate(VmAllocOptions::new(1).uninit(false).can_dma(true)) - .unwrap() - .pop() - .unwrap(), + VmAllocOptions::new(1).can_dma(true).alloc_single().unwrap(), 0, ), SafePtr::new( - VmFrameVec::allocate(VmAllocOptions::new(1).uninit(false).can_dma(true)) - .unwrap() - .pop() - .unwrap(), + VmAllocOptions::new(1).can_dma(true).alloc_single().unwrap(), 0, ), SafePtr::new( - VmFrameVec::allocate(VmAllocOptions::new(1).uninit(false).can_dma(true)) - .unwrap() - .pop() - .unwrap(), + VmAllocOptions::new(1).can_dma(true).alloc_single().unwrap(), 0, ), ) diff --git a/services/libs/jinux-std/src/fs/utils/page_cache.rs b/services/libs/jinux-std/src/fs/utils/page_cache.rs index 1251c5bdc..0d62fc0f2 100644 --- a/services/libs/jinux-std/src/fs/utils/page_cache.rs +++ b/services/libs/jinux-std/src/fs/utils/page_cache.rs @@ -4,7 +4,7 @@ use crate::vm::vmo::{get_page_idx_range, Pager, Vmo, VmoFlags, VmoOptions}; use jinux_rights::Full; use core::ops::Range; -use jinux_frame::vm::{VmAllocOptions, VmFrame, VmFrameVec}; +use jinux_frame::vm::{VmAllocOptions, VmFrame}; use lru::LruCache; pub struct PageCache { @@ -160,12 +160,7 @@ struct Page { impl Page { pub fn alloc() -> Result { - let frame = { - let mut vm_alloc_option = VmAllocOptions::new(1); - vm_alloc_option.uninit(true); - let mut frames = VmFrameVec::allocate(&vm_alloc_option)?; - frames.pop().unwrap() - }; + let frame = VmAllocOptions::new(1).uninit(true).alloc_single()?; Ok(Self { frame, state: PageState::Uninit, @@ -173,11 +168,7 @@ impl Page { } pub fn alloc_zero() -> Result { - let frame = { - let vm_alloc_option = VmAllocOptions::new(1); - let mut frames = VmFrameVec::allocate(&vm_alloc_option)?; - frames.pop().unwrap() - }; + let frame = VmAllocOptions::new(1).alloc_single()?; Ok(Self { frame, state: PageState::Dirty, diff --git a/services/libs/jinux-std/src/vm/vmo/mod.rs b/services/libs/jinux-std/src/vm/vmo/mod.rs index 14aef0a29..74d41c701 100644 --- a/services/libs/jinux-std/src/vm/vmo/mod.rs +++ b/services/libs/jinux-std/src/vm/vmo/mod.rs @@ -153,10 +153,7 @@ impl VmoInner { return Ok(()); } let frame = match &self.pager { - None => { - let vm_alloc_option = VmAllocOptions::new(1); - VmFrameVec::allocate(&vm_alloc_option)?.pop().unwrap() - } + None => VmAllocOptions::new(1).alloc_single()?, Some(pager) => pager.commit_page(offset)?, }; self.insert_frame(page_idx, frame); @@ -205,8 +202,7 @@ impl VmoInner { if page_idx >= inherited_frames.len() { if self.is_cow { - let options = VmAllocOptions::new(1); - return Ok(VmFrameVec::allocate(&options)?.pop().unwrap()); + return Ok(VmAllocOptions::new(1).alloc_single()?); } return_errno_with_message!(Errno::EINVAL, "the page is not inherited from parent"); } @@ -217,10 +213,7 @@ impl VmoInner { return Ok(inherited_frame); } - let frame = { - let options = VmAllocOptions::new(1); - VmFrameVec::allocate(&options)?.pop().unwrap() - }; + let frame = VmAllocOptions::new(1).alloc_single()?; frame.copy_from_frame(&inherited_frame); Ok(frame) } diff --git a/services/libs/jinux-std/src/vm/vmo/options.rs b/services/libs/jinux-std/src/vm/vmo/options.rs index ef5adc87f..468d2f453 100644 --- a/services/libs/jinux-std/src/vm/vmo/options.rs +++ b/services/libs/jinux-std/src/vm/vmo/options.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use core::ops::Range; use align_ext::AlignExt; -use jinux_frame::vm::{VmAllocOptions, VmFrame, VmFrameVec}; +use jinux_frame::vm::{VmAllocOptions, VmFrame}; use jinux_rights_proc::require; use typeflags_util::{SetExtend, SetExtendOp}; @@ -143,9 +143,9 @@ fn committed_pages_if_continuous(flags: VmoFlags, size: usize) -> Result