mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 01:43:22 +00:00
Fix releasing lock during the creation of VmMapping
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
b3f8d21c3d
commit
42b3541dcb
@ -4,7 +4,7 @@ use core::ops::Range;
|
||||
|
||||
use aster_rights::Rights;
|
||||
|
||||
use super::{vm_mapping::VmarMapOptions, VmPerms, Vmar, VmarRightsOp, Vmar_};
|
||||
use super::{VmPerms, Vmar, VmarMapOptions, VmarRightsOp, Vmar_};
|
||||
use crate::{
|
||||
prelude::*, thread::exception::PageFaultInfo, vm::page_fault_handler::PageFaultHandler,
|
||||
};
|
||||
|
@ -7,7 +7,7 @@ mod interval_set;
|
||||
mod static_cap;
|
||||
pub mod vm_mapping;
|
||||
|
||||
use core::ops::Range;
|
||||
use core::{num::NonZeroUsize, ops::Range};
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use aster_rights::Rights;
|
||||
@ -18,13 +18,16 @@ use ostd::{
|
||||
|
||||
use self::{
|
||||
interval_set::{Interval, IntervalSet},
|
||||
vm_mapping::VmMapping,
|
||||
vm_mapping::{MappedVmo, VmMapping},
|
||||
};
|
||||
use super::page_fault_handler::PageFaultHandler;
|
||||
use crate::{
|
||||
prelude::*,
|
||||
thread::exception::{handle_page_fault_from_vm_space, PageFaultInfo},
|
||||
vm::perms::VmPerms,
|
||||
vm::{
|
||||
perms::VmPerms,
|
||||
vmo::{Vmo, VmoRightsOp},
|
||||
},
|
||||
};
|
||||
|
||||
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
|
||||
@ -364,61 +367,11 @@ impl Vmar_ {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_overwrite(&self, mapping_range: Range<usize>, can_overwrite: bool) -> Result<()> {
|
||||
let inner = self.inner.read();
|
||||
|
||||
if !can_overwrite && inner.vm_mappings.find(&mapping_range).next().is_some() {
|
||||
return_errno_with_message!(
|
||||
Errno::EACCES,
|
||||
"mapping range overlapped with another mapping"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the attached `VmSpace`.
|
||||
fn vm_space(&self) -> &Arc<VmSpace> {
|
||||
&self.vm_space
|
||||
}
|
||||
|
||||
/// Maps a `VmMapping` to this VMAR.
|
||||
fn add_mapping(&self, mapping: VmMapping) {
|
||||
self.inner.write().vm_mappings.insert(mapping);
|
||||
}
|
||||
|
||||
fn allocate_free_region_for_mapping(
|
||||
&self,
|
||||
map_size: usize,
|
||||
offset: Option<usize>,
|
||||
align: usize,
|
||||
can_overwrite: bool,
|
||||
) -> Result<Vaddr> {
|
||||
trace!("allocate free region, map_size = 0x{:x}, offset = {:x?}, align = 0x{:x}, can_overwrite = {}", map_size, offset, align, can_overwrite);
|
||||
|
||||
if can_overwrite {
|
||||
// If can overwrite, the offset is ensured not to be `None`.
|
||||
let offset = offset.ok_or(Error::with_message(
|
||||
Errno::EINVAL,
|
||||
"offset cannot be None since can overwrite is set",
|
||||
))?;
|
||||
self.inner.write().alloc_free_region_exact_truncate(
|
||||
&self.vm_space,
|
||||
offset,
|
||||
map_size,
|
||||
)?;
|
||||
Ok(offset)
|
||||
} else if let Some(offset) = offset {
|
||||
self.inner
|
||||
.write()
|
||||
.alloc_free_region_exact(offset, map_size)?;
|
||||
Ok(offset)
|
||||
} else {
|
||||
let free_region = self.inner.write().alloc_free_region(map_size, align)?;
|
||||
Ok(free_region.start)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_fork_root(self: &Arc<Self>) -> Result<Arc<Self>> {
|
||||
let new_vmar_ = {
|
||||
let vmar_inner = VmarInner::new();
|
||||
@ -482,6 +435,231 @@ impl<R> Vmar<R> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for creating a new mapping. The mapping is not allowed to overlap
|
||||
/// with any child VMARs. And unless specified otherwise, it is not allowed
|
||||
/// to overlap with any existing mapping, either.
|
||||
pub struct VmarMapOptions<R1, R2> {
|
||||
parent: Vmar<R1>,
|
||||
vmo: Option<Vmo<R2>>,
|
||||
perms: VmPerms,
|
||||
vmo_offset: usize,
|
||||
vmo_limit: usize,
|
||||
size: usize,
|
||||
offset: Option<usize>,
|
||||
align: usize,
|
||||
can_overwrite: bool,
|
||||
// Whether the mapping is mapped with `MAP_SHARED`
|
||||
is_shared: bool,
|
||||
// Whether the mapping needs to handle surrounding pages when handling page fault.
|
||||
handle_page_faults_around: bool,
|
||||
}
|
||||
|
||||
impl<R1, R2> VmarMapOptions<R1, R2> {
|
||||
/// Creates a default set of options with the VMO and the memory access
|
||||
/// permissions.
|
||||
///
|
||||
/// The VMO must have access rights that correspond to the memory
|
||||
/// access permissions. For example, if `perms` contains `VmPerms::Write`,
|
||||
/// then `vmo.rights()` should contain `Rights::WRITE`.
|
||||
pub fn new(parent: Vmar<R1>, size: usize, perms: VmPerms) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
vmo: None,
|
||||
perms,
|
||||
vmo_offset: 0,
|
||||
vmo_limit: usize::MAX,
|
||||
size,
|
||||
offset: None,
|
||||
align: PAGE_SIZE,
|
||||
can_overwrite: false,
|
||||
is_shared: false,
|
||||
handle_page_faults_around: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a VMO to the mapping.
|
||||
///
|
||||
/// If the mapping is a private mapping, its size may not be equal to that of the VMO.
|
||||
/// For example, it is ok to create a mapping whose size is larger than
|
||||
/// that of the VMO, although one cannot read from or write to the
|
||||
/// part of the mapping that is not backed by the VMO.
|
||||
///
|
||||
/// So you may wonder: what is the point of supporting such _oversized_
|
||||
/// mappings? The reason is two-fold.
|
||||
/// 1. VMOs are resizable. So even if a mapping is backed by a VMO whose
|
||||
/// size is equal to that of the mapping initially, we cannot prevent
|
||||
/// the VMO from shrinking.
|
||||
/// 2. Mappings are not allowed to overlap by default. As a result,
|
||||
/// oversized mappings can serve as a placeholder to prevent future
|
||||
/// mappings from occupying some particular address ranges accidentally.
|
||||
pub fn vmo(mut self, vmo: Vmo<R2>) -> Self {
|
||||
self.vmo = Some(vmo);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the offset of the first memory page in the VMO that is to be
|
||||
/// mapped into the VMAR.
|
||||
///
|
||||
/// The offset must be page-aligned and within the VMO.
|
||||
///
|
||||
/// The default value is zero.
|
||||
pub fn vmo_offset(mut self, offset: usize) -> Self {
|
||||
self.vmo_offset = offset;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the access limit offset for the binding VMO.
|
||||
pub fn vmo_limit(mut self, limit: usize) -> Self {
|
||||
self.vmo_limit = limit;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mapping's alignment.
|
||||
///
|
||||
/// The default value is the page size.
|
||||
///
|
||||
/// The provided alignment must be a power of two and a multiple of the
|
||||
/// page size.
|
||||
pub fn align(mut self, align: usize) -> Self {
|
||||
self.align = align;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mapping's offset inside the VMAR.
|
||||
///
|
||||
/// The offset must satisfy the alignment requirement.
|
||||
/// Also, the mapping's range `[offset, offset + size)` must be within
|
||||
/// the VMAR.
|
||||
///
|
||||
/// If not set, the system will choose an offset automatically.
|
||||
pub fn offset(mut self, offset: usize) -> Self {
|
||||
self.offset = Some(offset);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the mapping can overwrite existing mappings.
|
||||
///
|
||||
/// The default value is false.
|
||||
///
|
||||
/// If this option is set to true, then the `offset` option must be
|
||||
/// set.
|
||||
pub fn can_overwrite(mut self, can_overwrite: bool) -> Self {
|
||||
self.can_overwrite = can_overwrite;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the mapping can be shared with other process.
|
||||
///
|
||||
/// The default value is false.
|
||||
///
|
||||
/// If this value is set to true, the mapping will be shared with child
|
||||
/// process when forking.
|
||||
pub fn is_shared(mut self, is_shared: bool) -> Self {
|
||||
self.is_shared = is_shared;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mapping to handle surrounding pages when handling page fault.
|
||||
pub fn handle_page_faults_around(mut self) -> Self {
|
||||
self.handle_page_faults_around = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates the mapping and adds it to the parent VMAR.
|
||||
///
|
||||
/// All options will be checked at this point.
|
||||
///
|
||||
/// On success, the virtual address of the new mapping is returned.
|
||||
pub fn build(self) -> Result<Vaddr> {
|
||||
self.check_options()?;
|
||||
let Self {
|
||||
parent,
|
||||
vmo,
|
||||
perms,
|
||||
vmo_offset,
|
||||
vmo_limit,
|
||||
size: map_size,
|
||||
offset,
|
||||
align,
|
||||
can_overwrite,
|
||||
is_shared,
|
||||
handle_page_faults_around,
|
||||
} = self;
|
||||
|
||||
// Allocates a free region.
|
||||
trace!("allocate free region, map_size = 0x{:x}, offset = {:x?}, align = 0x{:x}, can_overwrite = {}", map_size, offset, align, can_overwrite);
|
||||
let mut inner = parent.0.inner.write();
|
||||
let map_to_addr = if can_overwrite {
|
||||
// If can overwrite, the offset is ensured not to be `None`.
|
||||
let offset = offset.ok_or(Error::with_message(
|
||||
Errno::EINVAL,
|
||||
"offset cannot be None since can overwrite is set",
|
||||
))?;
|
||||
inner.alloc_free_region_exact_truncate(parent.vm_space(), offset, map_size)?;
|
||||
offset
|
||||
} else if let Some(offset) = offset {
|
||||
inner.alloc_free_region_exact(offset, map_size)?;
|
||||
offset
|
||||
} else {
|
||||
let free_region = inner.alloc_free_region(map_size, align)?;
|
||||
free_region.start
|
||||
};
|
||||
|
||||
// Build the mapping.
|
||||
let vmo = vmo.map(|vmo| MappedVmo::new(vmo.to_dyn(), vmo_offset..vmo_limit));
|
||||
let vm_mapping = VmMapping::new(
|
||||
NonZeroUsize::new(map_size).unwrap(),
|
||||
map_to_addr,
|
||||
vmo,
|
||||
is_shared,
|
||||
handle_page_faults_around,
|
||||
perms,
|
||||
);
|
||||
|
||||
// Add the mapping to the VMAR.
|
||||
inner.vm_mappings.insert(vm_mapping);
|
||||
|
||||
Ok(map_to_addr)
|
||||
}
|
||||
|
||||
/// Checks whether all options are valid.
|
||||
fn check_options(&self) -> Result<()> {
|
||||
// Check align.
|
||||
debug_assert!(self.align % PAGE_SIZE == 0);
|
||||
debug_assert!(self.align.is_power_of_two());
|
||||
if self.align % PAGE_SIZE != 0 || !self.align.is_power_of_two() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid align");
|
||||
}
|
||||
debug_assert!(self.size % self.align == 0);
|
||||
if self.size % self.align != 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid mapping size");
|
||||
}
|
||||
debug_assert!(self.vmo_offset % self.align == 0);
|
||||
if self.vmo_offset % self.align != 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid vmo offset");
|
||||
}
|
||||
if let Some(offset) = self.offset {
|
||||
debug_assert!(offset % self.align == 0);
|
||||
if offset % self.align != 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid offset");
|
||||
}
|
||||
}
|
||||
self.check_perms()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks whether the permissions of the mapping is subset of vmo rights.
|
||||
fn check_perms(&self) -> Result<()> {
|
||||
let Some(vmo) = &self.vmo else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let perm_rights = Rights::from(self.perms);
|
||||
vmo.check_rights(perm_rights)
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether two ranges are intersected.
|
||||
/// returns false if one of the ranges has a length of 0
|
||||
pub fn is_intersected(range1: &Range<usize>, range2: &Range<usize>) -> bool {
|
||||
|
@ -5,7 +5,7 @@ use core::ops::Range;
|
||||
use aster_rights::{Dup, Rights, TRightSet, TRights, Write};
|
||||
use aster_rights_proc::require;
|
||||
|
||||
use super::{vm_mapping::VmarMapOptions, VmPerms, Vmar, VmarRightsOp, Vmar_};
|
||||
use super::{VmPerms, Vmar, VmarMapOptions, VmarRightsOp, Vmar_};
|
||||
use crate::{
|
||||
prelude::*, thread::exception::PageFaultInfo, vm::page_fault_handler::PageFaultHandler,
|
||||
};
|
||||
|
@ -7,21 +7,16 @@ use core::{
|
||||
};
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use aster_rights::Rights;
|
||||
use ostd::mm::{
|
||||
tlb::TlbFlushOp, vm_space::VmItem, CachePolicy, Frame, FrameAllocOptions, PageFlags,
|
||||
PageProperty, VmSpace,
|
||||
};
|
||||
|
||||
use super::{interval_set::Interval, Vmar};
|
||||
use super::interval_set::Interval;
|
||||
use crate::{
|
||||
prelude::*,
|
||||
thread::exception::PageFaultInfo,
|
||||
vm::{
|
||||
perms::VmPerms,
|
||||
util::duplicate_frame,
|
||||
vmo::{Vmo, VmoRightsOp},
|
||||
},
|
||||
vm::{perms::VmPerms, util::duplicate_frame, vmo::Vmo},
|
||||
};
|
||||
|
||||
/// Mapping a range of physical pages into a `Vmar`.
|
||||
@ -78,39 +73,22 @@ impl Interval<Vaddr> for VmMapping {
|
||||
/***************************** Basic methods *********************************/
|
||||
|
||||
impl VmMapping {
|
||||
pub fn build_mapping<R1, R2>(option: VmarMapOptions<R1, R2>) -> Result<Self> {
|
||||
let VmarMapOptions {
|
||||
parent,
|
||||
vmo,
|
||||
perms,
|
||||
vmo_offset,
|
||||
vmo_limit,
|
||||
size,
|
||||
offset,
|
||||
align,
|
||||
can_overwrite,
|
||||
is_shared,
|
||||
handle_page_faults_around,
|
||||
} = option;
|
||||
let Vmar(parent_vmar, _) = parent;
|
||||
let map_to_addr =
|
||||
parent_vmar.allocate_free_region_for_mapping(size, offset, align, can_overwrite)?;
|
||||
trace!(
|
||||
"build mapping, map_range = 0x{:x}- 0x{:x}",
|
||||
pub(super) fn new(
|
||||
map_size: NonZeroUsize,
|
||||
map_to_addr: Vaddr,
|
||||
vmo: Option<MappedVmo>,
|
||||
is_shared: bool,
|
||||
handle_page_faults_around: bool,
|
||||
perms: VmPerms,
|
||||
) -> Self {
|
||||
Self {
|
||||
map_size,
|
||||
map_to_addr,
|
||||
map_to_addr + size
|
||||
);
|
||||
|
||||
let vmo = vmo.map(|vmo| MappedVmo::new(vmo.to_dyn(), vmo_offset..vmo_limit));
|
||||
|
||||
Ok(Self {
|
||||
vmo,
|
||||
is_shared,
|
||||
handle_page_faults_around,
|
||||
map_size: NonZeroUsize::new(size).unwrap(),
|
||||
map_to_addr,
|
||||
perms,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_fork(&self) -> Result<VmMapping> {
|
||||
@ -431,215 +409,6 @@ impl VmMapping {
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for creating a new mapping. The mapping is not allowed to overlap
|
||||
/// with any child VMARs. And unless specified otherwise, it is not allowed
|
||||
/// to overlap with any existing mapping, either.
|
||||
pub struct VmarMapOptions<R1, R2> {
|
||||
parent: Vmar<R1>,
|
||||
vmo: Option<Vmo<R2>>,
|
||||
perms: VmPerms,
|
||||
vmo_offset: usize,
|
||||
vmo_limit: usize,
|
||||
size: usize,
|
||||
offset: Option<usize>,
|
||||
align: usize,
|
||||
can_overwrite: bool,
|
||||
// Whether the mapping is mapped with `MAP_SHARED`
|
||||
is_shared: bool,
|
||||
// Whether the mapping needs to handle surrounding pages when handling page fault.
|
||||
handle_page_faults_around: bool,
|
||||
}
|
||||
|
||||
impl<R1, R2> VmarMapOptions<R1, R2> {
|
||||
/// Creates a default set of options with the VMO and the memory access
|
||||
/// permissions.
|
||||
///
|
||||
/// The VMO must have access rights that correspond to the memory
|
||||
/// access permissions. For example, if `perms` contains `VmPerms::Write`,
|
||||
/// then `vmo.rights()` should contain `Rights::WRITE`.
|
||||
pub fn new(parent: Vmar<R1>, size: usize, perms: VmPerms) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
vmo: None,
|
||||
perms,
|
||||
vmo_offset: 0,
|
||||
vmo_limit: usize::MAX,
|
||||
size,
|
||||
offset: None,
|
||||
align: PAGE_SIZE,
|
||||
can_overwrite: false,
|
||||
is_shared: false,
|
||||
handle_page_faults_around: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds a VMO to the mapping.
|
||||
///
|
||||
/// If the mapping is a private mapping, its size may not be equal to that of the VMO.
|
||||
/// For example, it is ok to create a mapping whose size is larger than
|
||||
/// that of the VMO, although one cannot read from or write to the
|
||||
/// part of the mapping that is not backed by the VMO.
|
||||
///
|
||||
/// So you may wonder: what is the point of supporting such _oversized_
|
||||
/// mappings? The reason is two-fold.
|
||||
/// 1. VMOs are resizable. So even if a mapping is backed by a VMO whose
|
||||
/// size is equal to that of the mapping initially, we cannot prevent
|
||||
/// the VMO from shrinking.
|
||||
/// 2. Mappings are not allowed to overlap by default. As a result,
|
||||
/// oversized mappings can serve as a placeholder to prevent future
|
||||
/// mappings from occupying some particular address ranges accidentally.
|
||||
pub fn vmo(mut self, vmo: Vmo<R2>) -> Self {
|
||||
self.vmo = Some(vmo);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the offset of the first memory page in the VMO that is to be
|
||||
/// mapped into the VMAR.
|
||||
///
|
||||
/// The offset must be page-aligned and within the VMO.
|
||||
///
|
||||
/// The default value is zero.
|
||||
pub fn vmo_offset(mut self, offset: usize) -> Self {
|
||||
self.vmo_offset = offset;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the access limit offset for the binding VMO.
|
||||
pub fn vmo_limit(mut self, limit: usize) -> Self {
|
||||
self.vmo_limit = limit;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mapping's alignment.
|
||||
///
|
||||
/// The default value is the page size.
|
||||
///
|
||||
/// The provided alignment must be a power of two and a multiple of the
|
||||
/// page size.
|
||||
pub fn align(mut self, align: usize) -> Self {
|
||||
self.align = align;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mapping's offset inside the VMAR.
|
||||
///
|
||||
/// The offset must satisfy the alignment requirement.
|
||||
/// Also, the mapping's range `[offset, offset + size)` must be within
|
||||
/// the VMAR.
|
||||
///
|
||||
/// If not set, the system will choose an offset automatically.
|
||||
pub fn offset(mut self, offset: usize) -> Self {
|
||||
self.offset = Some(offset);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the mapping can overwrite existing mappings.
|
||||
///
|
||||
/// The default value is false.
|
||||
///
|
||||
/// If this option is set to true, then the `offset` option must be
|
||||
/// set.
|
||||
pub fn can_overwrite(mut self, can_overwrite: bool) -> Self {
|
||||
self.can_overwrite = can_overwrite;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the mapping can be shared with other process.
|
||||
///
|
||||
/// The default value is false.
|
||||
///
|
||||
/// If this value is set to true, the mapping will be shared with child
|
||||
/// process when forking.
|
||||
pub fn is_shared(mut self, is_shared: bool) -> Self {
|
||||
self.is_shared = is_shared;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mapping to handle surrounding pages when handling page fault.
|
||||
pub fn handle_page_faults_around(mut self) -> Self {
|
||||
self.handle_page_faults_around = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates the mapping.
|
||||
///
|
||||
/// All options will be checked at this point.
|
||||
///
|
||||
/// On success, the virtual address of the new mapping is returned.
|
||||
pub fn build(self) -> Result<Vaddr> {
|
||||
self.check_options()?;
|
||||
let parent_vmar = self.parent.0.clone();
|
||||
let vm_mapping = VmMapping::build_mapping(self)?;
|
||||
let map_to_addr = vm_mapping.map_to_addr();
|
||||
parent_vmar.add_mapping(vm_mapping);
|
||||
Ok(map_to_addr)
|
||||
}
|
||||
|
||||
/// Checks whether all options are valid.
|
||||
fn check_options(&self) -> Result<()> {
|
||||
// Check align.
|
||||
debug_assert!(self.align % PAGE_SIZE == 0);
|
||||
debug_assert!(self.align.is_power_of_two());
|
||||
if self.align % PAGE_SIZE != 0 || !self.align.is_power_of_two() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid align");
|
||||
}
|
||||
debug_assert!(self.size % self.align == 0);
|
||||
if self.size % self.align != 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid mapping size");
|
||||
}
|
||||
debug_assert!(self.vmo_offset % self.align == 0);
|
||||
if self.vmo_offset % self.align != 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid vmo offset");
|
||||
}
|
||||
if let Some(offset) = self.offset {
|
||||
debug_assert!(offset % self.align == 0);
|
||||
if offset % self.align != 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid offset");
|
||||
}
|
||||
}
|
||||
self.check_perms()?;
|
||||
self.check_overwrite()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks whether the permissions of the mapping is subset of vmo rights.
|
||||
fn check_perms(&self) -> Result<()> {
|
||||
let Some(vmo) = &self.vmo else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let perm_rights = Rights::from(self.perms);
|
||||
vmo.check_rights(perm_rights)
|
||||
}
|
||||
|
||||
/// Checks whether the mapping will overwrite with any existing mapping or vmar.
|
||||
fn check_overwrite(&self) -> Result<()> {
|
||||
if self.can_overwrite {
|
||||
// If `can_overwrite` is set, the offset cannot be None.
|
||||
debug_assert!(self.offset.is_some());
|
||||
if self.offset.is_none() {
|
||||
return_errno_with_message!(
|
||||
Errno::EINVAL,
|
||||
"offset can not be none when can overwrite is true"
|
||||
);
|
||||
}
|
||||
}
|
||||
if self.offset.is_none() {
|
||||
// If does not specify the offset, we assume the map can always find suitable free region.
|
||||
// FIXME: is this always true?
|
||||
return Ok(());
|
||||
}
|
||||
let offset = self.offset.unwrap();
|
||||
// We should spare enough space at least for the whole mapping.
|
||||
let size = self.size;
|
||||
let mapping_range = offset..(offset + size);
|
||||
self.parent
|
||||
.0
|
||||
.check_overwrite(mapping_range, self.can_overwrite)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper that represents a mapped [`Vmo`] and provide required functionalities
|
||||
/// that need to be provided to mappings from the VMO.
|
||||
#[derive(Debug)]
|
||||
@ -651,7 +420,7 @@ pub(super) struct MappedVmo {
|
||||
|
||||
impl MappedVmo {
|
||||
/// Creates a `MappedVmo` used for mapping.
|
||||
fn new(vmo: Vmo, range: Range<usize>) -> Self {
|
||||
pub(super) fn new(vmo: Vmo, range: Range<usize>) -> Self {
|
||||
Self { vmo, range }
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user