1#![expect(unsafe_code)]
9
10use crate::mapping_manager::Mappable;
11use crate::mapping_manager::MappingBacking;
12use crate::mapping_manager::MappingManagerClient;
13use crate::mapping_manager::MappingParams;
14use crate::mapping_manager::MemoryPolicy;
15use crate::mapping_manager::VaMapper;
16use crate::partition_mapper::PartitionMapper;
17use anyhow::Context as _;
18use futures::StreamExt;
19use inspect::Inspect;
20use inspect::InspectMut;
21use memory_range::MemoryRange;
22use mesh::MeshPayload;
23use mesh::error::RemoteError;
24use mesh::rpc::FailableRpc;
25use mesh::rpc::Rpc;
26use mesh::rpc::RpcSend;
27use pal_async::task::Spawn;
28use std::cmp::Ordering;
29use std::sync::Arc;
30use thiserror::Error;
31use vmcore::local_only::LocalOnly;
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq, Inspect, MeshPayload)]
35pub enum MappingType {
36 Ram,
39 Device,
43}
44
45pub struct DmaMapRequest<'a> {
47 pub range: MemoryRange,
49 pub host_va: *const u8,
51 pub mappable: Option<&'a Mappable>,
55 pub file_offset: u64,
59 pub writable: bool,
62 pub mapping_type: MappingType,
64}
65
66pub trait DmaTarget: Send + Sync {
83 unsafe fn map_dma(&self, request: DmaMapRequest<'_>) -> anyhow::Result<()>;
93
94 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()>;
103}
104
105struct DmaMapper {
111 id: DmaMapperId,
112 target: Arc<dyn DmaTarget>,
113 va_mapper: Arc<VaMapper>,
114 needs_fd: bool,
119}
120
121#[derive(Debug, Copy, Clone, PartialEq, Eq)]
122struct DmaMapperId(u64);
123
124struct SubMapping<'a> {
128 range: MemoryRange,
129 backing: &'a MappingBacking,
130 writable: bool,
131 mapping_type: MappingType,
132}
133
134impl DmaMapper {
135 fn map_dma(&self, mapping: SubMapping<'_>) -> anyhow::Result<()> {
137 let host_va = unsafe {
144 self.va_mapper
145 .as_ptr()
146 .add(mapping.range.start() as usize)
147 .cast_const()
148 };
149 let request = DmaMapRequest {
150 range: mapping.range,
151 host_va,
152 mappable: mapping.backing.mappable(),
153 file_offset: mapping.backing.file_offset(),
154 writable: mapping.writable,
155 mapping_type: mapping.mapping_type,
156 };
157 unsafe { self.target.map_dma(request) }
162 }
163
164 fn unmap_dma(&self, range: MemoryRange) {
166 if let Err(e) = self.target.unmap_dma(range) {
167 tracing::warn!(
168 error = &*e as &dyn std::error::Error,
169 %range,
170 "DMA unmap failed"
171 );
172 }
173 }
174}
175
176#[derive(Debug, Inspect)]
178pub struct RegionManager {
179 #[inspect(
180 flatten,
181 with = "|x| inspect::send(&x.req_send, RegionRequest::Inspect)"
182 )]
183 client: RegionManagerClient,
184}
185
186#[derive(Debug, MeshPayload, Clone)]
188pub struct RegionManagerClient {
189 req_send: mesh::Sender<RegionRequest>,
190}
191
192struct Region {
193 id: RegionId,
194 map_params: Option<MapParams>,
195 is_active: bool,
196 params: RegionParams,
197 mappings: Vec<RegionMapping>,
198}
199
200#[derive(Debug, MeshPayload)]
201struct RegionParams {
202 name: String,
203 range: MemoryRange,
204 priority: u8,
205 mapping_type: MappingType,
207}
208
209#[derive(Copy, Clone, Debug, MeshPayload, PartialEq, Eq, Inspect)]
210pub struct MapParams {
211 pub writable: bool,
212 pub executable: bool,
213 pub prefetch: bool,
214}
215
216impl Region {
217 fn active_range(&self) -> Option<MemoryRange> {
218 if self.is_active {
219 Some(self.params.range)
220 } else {
221 None
222 }
223 }
224}
225
226#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, MeshPayload)]
228pub struct RegionId(u64);
229
230#[derive(InspectMut)]
231struct RegionManagerTask {
232 #[inspect(with = "inspect_regions")]
233 regions: Vec<Region>,
234 #[inspect(skip)]
235 next_region_id: u64,
236 #[inspect(skip)]
237 inner: RegionManagerTaskInner,
238}
239
240fn inspect_regions(regions: &Vec<Region>) -> impl '_ + Inspect {
241 inspect::adhoc(move |req| {
242 let mut resp = req.respond();
243 for region in regions {
244 resp.field(
245 &format!("{}:{}", region.params.range, region.params.name),
246 inspect::adhoc(|req| {
247 req.respond()
248 .field("map_params", region.map_params)
249 .field("is_active", region.is_active)
250 .field("priority", region.params.priority)
251 .field(
252 "mappings",
253 inspect::adhoc(|req| {
254 inspect_mappings(req, region.params.range.start(), ®ion.mappings)
255 }),
256 );
257 }),
258 );
259 }
260 })
261}
262
263fn inspect_mappings(req: inspect::Request<'_>, region_start: u64, mappings: &[RegionMapping]) {
264 let mut resp = req.respond();
265 for mapping in mappings {
266 let range = MemoryRange::new(
267 region_start + mapping.params.range_in_region.start()
268 ..region_start + mapping.params.range_in_region.end(),
269 )
270 .to_string();
271
272 resp.field(
273 &range,
274 inspect::adhoc(|req| {
275 req.respond()
276 .field("writable", mapping.params.writable)
277 .field("backed_by_fd", mapping.params.backing.mappable().is_some())
278 .hex("file_offset", mapping.params.backing.file_offset());
279 }),
280 );
281 }
282}
283
284struct RegionManagerTaskInner {
285 partitions: Vec<PartitionMapper>,
286 dma_mappers: Vec<DmaMapper>,
287 next_dma_mapper_id: u64,
288 mapping_manager: MappingManagerClient,
289}
290
291#[derive(MeshPayload)]
292enum RegionRequest {
293 AddRegion(Rpc<RegionParams, Result<RegionId, AddRegionError>>),
294 RemoveRegion(Rpc<RegionId, ()>),
295 MapRegion(FailableRpc<(RegionId, MapParams), ()>),
296 UnmapRegion(Rpc<RegionId, ()>),
297 AddMapping(FailableRpc<(RegionId, RegionMappingParams), ()>),
298 RemoveMappings(Rpc<(RegionId, MemoryRange), ()>),
299 AddPartition(
300 LocalOnly<Rpc<PartitionMapper, Result<(), crate::partition_mapper::PartitionMapperError>>>,
301 ),
302 AddDmaMapper(LocalOnly<Rpc<(Arc<dyn DmaTarget>, bool), anyhow::Result<DmaMapperId>>>),
303 RemoveDmaMapper(LocalOnly<DmaMapperId>),
304 Inspect(inspect::Deferred),
305}
306
307struct RegionMapping {
308 params: RegionMappingParams,
309}
310
311#[derive(MeshPayload)]
312struct RegionMappingParams {
313 range_in_region: MemoryRange,
314 backing: MappingBacking,
319 writable: bool,
320 policy: MemoryPolicy,
321}
322
323fn range_within(outer: MemoryRange, inner: MemoryRange) -> MemoryRange {
324 assert!(inner.end() <= outer.len());
325 MemoryRange::new(outer.start() + inner.start()..outer.start() + inner.end())
326}
327
328#[derive(Debug, Error, MeshPayload)]
329pub enum AddRegionError {
330 #[error("memory region {new} overlaps with existing region {existing}")]
331 OverlapError { existing: String, new: String },
332}
333
334impl RegionManagerTask {
335 fn new(mapping_manager: MappingManagerClient) -> Self {
336 Self {
337 regions: Vec::new(),
338 next_region_id: 1,
339 inner: RegionManagerTaskInner {
340 mapping_manager,
341 partitions: Vec::new(),
342 dma_mappers: Vec::new(),
343 next_dma_mapper_id: 0,
344 },
345 }
346 }
347
348 async fn run(&mut self, req_recv: &mut mesh::Receiver<RegionRequest>) {
349 while let Some(req) = req_recv.next().await {
350 match req {
351 RegionRequest::AddMapping(rpc) => {
352 rpc.handle_failable(async |(id, params)| self.add_mapping(id, params).await)
353 .await
354 }
355 RegionRequest::RemoveMappings(rpc) => {
356 rpc.handle(async |(id, range)| self.remove_mappings(id, range).await)
357 .await
358 }
359 RegionRequest::AddPartition(LocalOnly(rpc)) => {
360 rpc.handle(async |partition| self.add_partition(partition).await)
361 .await
362 }
363 RegionRequest::AddDmaMapper(LocalOnly(rpc)) => {
364 let ((target, needs_fd), rpc) = rpc.split();
365 let result = self.add_dma_mapper(target, needs_fd).await;
366 rpc.complete(result);
367 }
368 RegionRequest::RemoveDmaMapper(LocalOnly(id)) => {
369 self.remove_dma_mapper(id);
370 }
371 RegionRequest::AddRegion(rpc) => rpc.handle_sync(|params| self.add_region(params)),
372 RegionRequest::RemoveRegion(rpc) => {
373 rpc.handle(async |id| self.unmap_region(id, true).await)
374 .await
375 }
376 RegionRequest::MapRegion(rpc) => {
377 rpc.handle_failable(async |(id, params)| self.map_region(id, params).await)
378 .await
379 }
380 RegionRequest::UnmapRegion(rpc) => {
381 rpc.handle(async |id| self.unmap_region(id, false).await)
382 .await
383 }
384 RegionRequest::Inspect(deferred) => {
385 deferred.inspect(&mut *self);
386 }
387 }
388 }
389 }
390
391 async fn add_partition(
392 &mut self,
393 partition: PartitionMapper,
394 ) -> Result<(), crate::partition_mapper::PartitionMapperError> {
395 for region in &self.regions {
398 if region.is_active {
399 partition
400 .map_region(region.params.range, region.map_params.unwrap())
401 .await?;
402 }
403 }
404 self.inner.partitions.push(partition);
405 Ok(())
406 }
407
408 async fn add_dma_mapper(
409 &mut self,
410 target: Arc<dyn DmaTarget>,
411 needs_fd: bool,
412 ) -> anyhow::Result<DmaMapperId> {
413 let va_mapper = self.inner.mapping_manager.new_mapper(true).await?;
416 assert!(
417 va_mapper.is_eager(),
418 "DMA mapper requires an eager VaMapper"
419 );
420
421 if needs_fd {
425 if let Some(region) = self.regions.iter().find(|r| {
426 r.mappings
427 .iter()
428 .any(|m| m.params.backing.mappable().is_none())
429 }) {
430 anyhow::bail!(
431 "cannot register a DMA mapper that requires a backing fd: \
432 region {} contains a mapping with no backing fd (private RAM)",
433 region.params.range
434 );
435 }
436 }
437
438 let id = DmaMapperId(self.inner.next_dma_mapper_id);
439 self.inner.next_dma_mapper_id += 1;
440
441 let mapper = DmaMapper {
442 id,
443 target,
444 va_mapper,
445 needs_fd,
446 };
447
448 for region in &self.regions {
451 if region.is_active {
452 for mapping in ®ion.mappings {
453 let range = range_within(region.params.range, mapping.params.range_in_region);
454 let writable = mapping.params.writable && region.map_params.unwrap().writable;
455 mapper.map_dma(SubMapping {
456 range,
457 backing: &mapping.params.backing,
458 writable,
459 mapping_type: region.params.mapping_type,
460 })?;
461 }
462 }
463 }
464
465 self.inner.dma_mappers.push(mapper);
466 Ok(id)
467 }
468
469 fn remove_dma_mapper(&mut self, id: DmaMapperId) {
470 if let Some(pos) = self.inner.dma_mappers.iter().position(|m| m.id == id) {
471 let mapper = &self.inner.dma_mappers[pos];
472 for region in &self.regions {
474 if region.is_active {
475 for mapping in ®ion.mappings {
476 let range =
477 range_within(region.params.range, mapping.params.range_in_region);
478 mapper.unmap_dma(range);
479 }
480 }
481 }
482 self.inner.dma_mappers.swap_remove(pos);
483 }
484 }
485
486 fn region_index(&self, id: RegionId) -> usize {
487 self.regions.iter().position(|r| r.id == id).unwrap()
488 }
489
490 fn add_region(&mut self, params: RegionParams) -> Result<RegionId, AddRegionError> {
491 let range = params.range;
494 for other_region in &self.regions {
495 let other_range = other_region.params.range;
496 if !range.overlaps(&other_range) {
497 continue;
498 };
499 let ok = match params.priority.cmp(&other_region.params.priority) {
500 Ordering::Less => other_range.contains(&range),
501 Ordering::Equal => other_range == range,
502 Ordering::Greater => range.contains(&other_range),
503 };
504 if !ok {
505 return Err(AddRegionError::OverlapError {
506 existing: other_region.params.name.clone(),
507 new: params.name,
508 });
509 }
510 }
511
512 tracing::debug!(
513 range = %params.range,
514 name = params.name,
515 priority = params.priority,
516 "new region"
517 );
518
519 let id = RegionId(self.next_region_id);
520 self.next_region_id += 1;
521 self.regions.push(Region {
522 id,
523 map_params: None,
524 is_active: false,
525 params,
526 mappings: Vec::new(),
527 });
528 Ok(id)
529 }
530
531 async fn enable_best_region(&mut self, mut range: MemoryRange) -> anyhow::Result<()> {
534 while !range.is_empty() {
535 if let Some(region) = self
540 .regions
541 .iter_mut()
542 .filter_map(|region| {
543 region.map_params?;
544 if !range.contains(®ion.params.range) {
545 assert!(
546 !range.overlaps(®ion.params.range),
547 "no overlap invariant violated"
548 );
549 return None;
550 }
551 assert!(!region.is_active);
552 Some(region)
553 })
554 .min_by_key(|region| {
555 (
556 region.params.range.start(),
557 u8::MAX - region.params.priority,
558 )
559 })
560 {
561 self.inner.enable_region(region).await?;
562 range = MemoryRange::new(region.params.range.end()..range.end());
563 } else {
564 range = MemoryRange::EMPTY;
565 }
566 }
567 Ok(())
568 }
569
570 async fn map_region(&mut self, id: RegionId, map_params: MapParams) -> anyhow::Result<()> {
571 let index = self.region_index(id);
572 let region = &mut self.regions[index];
573 let range = region.params.range;
574 let priority = region.params.priority;
575 if region.map_params == Some(map_params) {
576 return Ok(());
577 }
578
579 tracing::debug!(
580 name = region.params.name,
581 range = %region.params.range,
582 writable = map_params.writable,
583 "mapping region"
584 );
585
586 let mut enable = true;
589 for (other_index, other_region) in self.regions.iter_mut().enumerate() {
590 if !other_region.is_active || !other_region.params.range.overlaps(&range) {
591 continue;
592 }
593 if other_region.params.priority > priority
594 || (other_region.params.priority == priority && other_index < index)
595 {
596 enable = false;
597 } else {
598 assert!(enable);
599 self.inner.disable_region(other_region, true).await;
603 }
604 }
605
606 self.regions[index].map_params = Some(map_params);
607 if enable {
608 self.enable_best_region(range).await?;
609 }
610 Ok(())
611 }
612
613 async fn unmap_region(&mut self, id: RegionId, remove: bool) {
614 let index = self.region_index(id);
615 let region = &mut self.regions[index];
616 tracing::debug!(
617 name = region.params.name,
618 range = %region.params.range,
619 remove,
620 "unmapping region"
621 );
622
623 let active_range = region.is_active.then_some(region.params.range);
624 if active_range.is_some() {
625 self.inner.disable_region(region, !remove).await;
628 }
629
630 if remove {
631 self.regions.remove(index);
632 } else {
633 region.map_params = None;
634 }
635 if let Some(range) = active_range {
636 self.enable_best_region(range).await.expect(
637 "failed to re-enable region after unmap; \
638 this should not fail because the region was previously active",
639 );
640 }
641 }
642
643 async fn add_mapping(
644 &mut self,
645 id: RegionId,
646 params: RegionMappingParams,
647 ) -> anyhow::Result<()> {
648 if params.backing.mappable().is_none() && self.inner.dma_mappers.iter().any(|m| m.needs_fd)
653 {
654 anyhow::bail!(
655 "cannot add a mapping with no backing fd: a registered DMA mapper requires one"
656 );
657 }
658
659 let index = self.region_index(id);
660 let region = &mut self.regions[index];
661
662 assert!(
665 !region
666 .mappings
667 .iter()
668 .any(|m| m.params.range_in_region.overlaps(¶ms.range_in_region))
669 );
670
671 if let Some(region_range) = region.active_range() {
672 let range = range_within(region_range, params.range_in_region);
673 let writable = params.writable && region.map_params.unwrap().writable;
674 self.inner
679 .mapping_manager
680 .add_mapping(MappingParams {
681 range,
682 backing: params.backing.clone(),
683 writable,
684 mapping_type: region.params.mapping_type,
685 policy: params.policy,
686 })
687 .await?;
688
689 for (dma_idx, dma_mapper) in self.inner.dma_mappers.iter().enumerate() {
690 if let Err(e) = dma_mapper.map_dma(SubMapping {
691 range,
692 backing: ¶ms.backing,
693 writable,
694 mapping_type: region.params.mapping_type,
695 }) {
696 for dm in &self.inner.dma_mappers[..dma_idx] {
699 dm.unmap_dma(range);
700 }
701 self.inner.mapping_manager.remove_mappings(range).await;
702 return Err(e);
703 }
704 }
705 }
706
707 region.mappings.push(RegionMapping { params });
708 Ok(())
709 }
710
711 async fn remove_mappings(&mut self, id: RegionId, range_in_region: MemoryRange) {
712 let index = self.region_index(id);
713 let region = &mut self.regions[index];
714 let active_range = region.active_range();
715
716 let removed_ranges: Vec<MemoryRange> = if active_range.is_some() {
719 let region_range = region.params.range;
720 region
721 .mappings
722 .iter()
723 .filter(|m| range_in_region.contains(&m.params.range_in_region))
724 .map(|m| range_within(region_range, m.params.range_in_region))
725 .collect()
726 } else {
727 Vec::new()
728 };
729
730 region.mappings.retain_mut(|mapping| {
731 if !range_in_region.contains(&mapping.params.range_in_region) {
732 assert!(
733 !range_in_region.overlaps(&mapping.params.range_in_region),
734 "no partial unmappings allowed"
735 );
736 return true;
737 }
738 false
739 });
740 if let Some(region_range) = active_range {
741 for &removed in &removed_ranges {
744 for dma_mapper in &self.inner.dma_mappers {
745 dma_mapper.unmap_dma(removed);
746 }
747 }
748
749 self.inner
750 .mapping_manager
751 .remove_mappings(range_within(region_range, range_in_region))
752 .await;
753
754 }
758 }
759}
760
761impl RegionManagerTaskInner {
762 async fn enable_region(&mut self, region: &mut Region) -> anyhow::Result<()> {
763 assert!(!region.is_active);
764 let map_params = region.map_params.unwrap();
765
766 tracing::debug!(
767 name = region.params.name,
768 range = %region.params.range,
769 writable = map_params.writable,
770 "enabling region"
771 );
772
773 for (mapped_count, mapping) in region.mappings.iter().enumerate() {
776 if let Err(e) = self
781 .mapping_manager
782 .add_mapping(MappingParams {
783 range: range_within(region.params.range, mapping.params.range_in_region),
784 backing: mapping.params.backing.clone(),
785 writable: mapping.params.writable && map_params.writable,
786 mapping_type: region.params.mapping_type,
787 policy: mapping.params.policy,
788 })
789 .await
790 {
791 for prev in ®ion.mappings[..mapped_count] {
793 let range = range_within(region.params.range, prev.params.range_in_region);
794 for dma_mapper in &self.dma_mappers {
795 dma_mapper.unmap_dma(range);
796 }
797 }
798 self.mapping_manager
799 .remove_mappings(region.params.range)
800 .await;
801 return Err(e).context(format!(
802 "failed to map {} during region enable",
803 range_within(region.params.range, mapping.params.range_in_region),
804 ));
805 }
806
807 let range = range_within(region.params.range, mapping.params.range_in_region);
809 let writable = mapping.params.writable && map_params.writable;
810 for (dma_idx, dma_mapper) in self.dma_mappers.iter().enumerate() {
811 if let Err(e) = dma_mapper.map_dma(SubMapping {
812 range,
813 backing: &mapping.params.backing,
814 writable,
815 mapping_type: region.params.mapping_type,
816 }) {
817 for dm in &self.dma_mappers[..dma_idx] {
820 dm.unmap_dma(range);
821 }
822 for prev in ®ion.mappings[..mapped_count] {
825 let prev_range =
826 range_within(region.params.range, prev.params.range_in_region);
827 for dm in &self.dma_mappers {
828 dm.unmap_dma(prev_range);
829 }
830 }
831 self.mapping_manager
832 .remove_mappings(region.params.range)
833 .await;
834 return Err(e).context(format!(
835 "DMA mapper failed to map {range} during region enable"
836 ));
837 }
838 }
839 }
840
841 for partition in &mut self.partitions {
843 partition
844 .map_region(region.params.range, map_params)
845 .await
846 .expect("cannot recover from failed mapping");
847 }
848
849 region.is_active = true;
850 Ok(())
851 }
852
853 async fn disable_region(&mut self, region: &mut Region, transient: bool) {
865 assert!(region.is_active);
866 assert!(
867 !transient
868 || region
869 .mappings
870 .iter()
871 .all(|m| m.params.backing.mappable().is_some()),
872 "transiently disabling region {} would decommit private RAM and lose its contents",
873 region.params.range,
874 );
875
876 tracing::debug!(
877 name = region.params.name,
878 range = %region.params.range,
879 "disabling region"
880 );
881
882 let region_range = region.params.range;
887 for dma_mapper in &mut self.dma_mappers {
888 dma_mapper.unmap_dma(region_range);
889 }
890
891 for partition in &mut self.partitions {
892 partition.unmap_region(region_range);
893 }
894 self.mapping_manager.remove_mappings(region_range).await;
895 region.is_active = false;
896 }
897}
898
899impl RegionManager {
900 pub fn new(spawn: impl Spawn, mapping_manager: MappingManagerClient) -> Self {
902 let (req_send, mut req_recv) = mesh::mpsc_channel();
903 spawn
904 .spawn("region_manager", {
905 let mut task = RegionManagerTask::new(mapping_manager);
906 async move {
907 task.run(&mut req_recv).await;
908 }
909 })
910 .detach();
911 Self {
912 client: RegionManagerClient { req_send },
913 }
914 }
915
916 pub fn client(&self) -> &RegionManagerClient {
918 &self.client
919 }
920}
921
922impl RegionManagerClient {
923 pub async fn add_partition(
927 &self,
928 partition: PartitionMapper,
929 ) -> Result<(), crate::partition_mapper::PartitionMapperError> {
930 self.req_send
931 .call(|x| RegionRequest::AddPartition(LocalOnly(x)), partition)
932 .await
933 .unwrap()
934 }
935
936 pub async fn new_region(
940 &self,
941 name: String,
942 range: MemoryRange,
943 priority: u8,
944 mapping_type: MappingType,
945 ) -> Result<RegionHandle, AddRegionError> {
946 let params = RegionParams {
947 name,
948 range,
949 priority,
950 mapping_type,
951 };
952
953 let id = self
954 .req_send
955 .call(RegionRequest::AddRegion, params)
956 .await
957 .unwrap()?;
958
959 Ok(RegionHandle {
960 id: Some(id),
961 req_send: self.req_send.clone(),
962 })
963 }
964}
965
966#[derive(Clone)]
972pub struct DmaMapperClient {
973 req_send: mesh::Sender<RegionRequest>,
974}
975
976impl DmaMapperClient {
977 pub(crate) fn new(region_manager: &RegionManagerClient) -> Self {
978 Self {
979 req_send: region_manager.req_send.clone(),
980 }
981 }
982
983 pub async fn add_dma_mapper(
1007 &self,
1008 target: Arc<dyn DmaTarget>,
1009 needs_fd: bool,
1010 ) -> anyhow::Result<DmaMapperHandle> {
1011 let id = self
1012 .req_send
1013 .call(
1014 |x| RegionRequest::AddDmaMapper(LocalOnly(x)),
1015 (target, needs_fd),
1016 )
1017 .await
1018 .unwrap()?;
1019 Ok(DmaMapperHandle {
1020 id: Some(id),
1021 req_send: self.req_send.clone(),
1022 })
1023 }
1024}
1025
1026pub struct DmaMapperHandle {
1031 id: Option<DmaMapperId>,
1032 req_send: mesh::Sender<RegionRequest>,
1033}
1034
1035impl Drop for DmaMapperHandle {
1036 fn drop(&mut self) {
1037 if let Some(id) = self.id {
1038 self.req_send
1039 .send(RegionRequest::RemoveDmaMapper(LocalOnly(id)));
1040 }
1041 }
1042}
1043
1044#[derive(Debug)]
1048#[must_use]
1049pub struct RegionHandle {
1050 id: Option<RegionId>,
1051 req_send: mesh::Sender<RegionRequest>,
1052}
1053
1054impl RegionHandle {
1055 pub async fn map(&self, params: MapParams) -> Result<(), RemoteError> {
1057 self.req_send
1058 .call(RegionRequest::MapRegion, (self.id.unwrap(), params))
1059 .await
1060 .map_err(RemoteError::new)?
1061 }
1062
1063 pub async fn unmap(&self) {
1065 let _ = self
1066 .req_send
1067 .call(RegionRequest::UnmapRegion, self.id.unwrap())
1068 .await;
1069 }
1070
1071 pub async fn add_mapping(
1083 &self,
1084 range_in_region: MemoryRange,
1085 backing: MappingBacking,
1086 writable: bool,
1087 policy: MemoryPolicy,
1088 ) -> Result<(), RemoteError> {
1089 self.req_send
1090 .call(
1091 RegionRequest::AddMapping,
1092 (
1093 self.id.unwrap(),
1094 RegionMappingParams {
1095 range_in_region,
1096 backing,
1097 writable,
1098 policy,
1099 },
1100 ),
1101 )
1102 .await
1103 .map_err(RemoteError::new)?
1104 }
1105
1106 pub async fn remove_mappings(&self, range: MemoryRange) {
1110 let _ = self
1111 .req_send
1112 .call(RegionRequest::RemoveMappings, (self.id.unwrap(), range))
1113 .await;
1114 }
1115
1116 pub async fn teardown(mut self) {
1118 let _ = self
1119 .req_send
1120 .call(RegionRequest::RemoveRegion, self.id.take().unwrap())
1121 .await;
1122 }
1123}
1124
1125impl Drop for RegionHandle {
1126 fn drop(&mut self) {
1127 if let Some(id) = self.id {
1128 let _recv = self.req_send.call(RegionRequest::RemoveRegion, id);
1129 }
1131 }
1132}
1133
1134#[cfg(test)]
1135mod tests {
1136 use super::MapParams;
1137 use super::RegionManagerTask;
1138 use crate::mapping_manager::Mappable;
1139 use crate::mapping_manager::MappingBacking;
1140 use crate::mapping_manager::MappingManager;
1141 use crate::mapping_manager::MemoryPolicy;
1142 use crate::region_manager::AddRegionError;
1143 use crate::region_manager::DmaMapRequest;
1144 use crate::region_manager::DmaTarget;
1145 use crate::region_manager::MappingType;
1146 use crate::region_manager::RegionId;
1147 use crate::region_manager::RegionMappingParams;
1148 use crate::region_manager::RegionParams;
1149 use memory_range::MemoryRange;
1150 use pal_async::async_test;
1151 use pal_async::task::Spawn;
1152 use parking_lot::Mutex;
1153 use std::ops::Range;
1154 use std::sync::Arc;
1155
1156 #[derive(Default)]
1158 struct RecordingDmaTarget {
1159 events: Mutex<Vec<DmaEvent>>,
1160 backingless_maps: Mutex<Vec<MemoryRange>>,
1163 }
1164
1165 #[derive(Debug, Clone, PartialEq, Eq)]
1166 enum DmaEvent {
1167 Map(MemoryRange),
1168 Unmap(MemoryRange),
1169 }
1170
1171 impl DmaTarget for RecordingDmaTarget {
1172 unsafe fn map_dma(&self, request: DmaMapRequest<'_>) -> anyhow::Result<()> {
1173 if request.mappable.is_none() {
1174 self.backingless_maps.lock().push(request.range);
1175 }
1176 self.events.lock().push(DmaEvent::Map(request.range));
1177 Ok(())
1178 }
1179
1180 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()> {
1181 self.events.lock().push(DmaEvent::Unmap(range));
1182 Ok(())
1183 }
1184 }
1185
1186 impl RecordingDmaTarget {
1187 fn take_events(&self) -> Vec<DmaEvent> {
1188 std::mem::take(&mut self.events.lock())
1189 }
1190
1191 fn take_backingless_maps(&self) -> Vec<MemoryRange> {
1192 std::mem::take(&mut self.backingless_maps.lock())
1193 }
1194 }
1195
1196 fn test_mappable() -> Mappable {
1198 sparse_mmap::alloc_shared_memory(0x10000, "test-dma")
1199 .unwrap()
1200 .into()
1201 }
1202
1203 #[async_test]
1204 async fn test_region_overlap(spawn: impl Spawn) {
1205 struct TestTask(RegionManagerTask);
1206 impl TestTask {
1207 async fn add(
1208 &mut self,
1209 priority: u8,
1210 range: Range<u64>,
1211 ) -> Result<RegionId, AddRegionError> {
1212 let id = self.0.add_region(RegionParams {
1213 priority,
1214 name: priority.to_string(),
1215 range: MemoryRange::new(range),
1216 mapping_type: MappingType::Device,
1217 })?;
1218 self.0
1219 .map_region(
1220 id,
1221 MapParams {
1222 executable: true,
1223 writable: true,
1224 prefetch: false,
1225 },
1226 )
1227 .await
1228 .unwrap();
1229 Ok(id)
1230 }
1231
1232 async fn remove(&mut self, id: RegionId) {
1233 self.0.unmap_region(id, true).await;
1234 }
1235 }
1236
1237 let mm = MappingManager::new_without_primary(spawn, 0x200000, None);
1238 let mut task = TestTask(RegionManagerTask::new(mm.client().clone()));
1239
1240 let high = task.add(1, 0x1000..0x3000).await.unwrap();
1241
1242 task.add(0, 0x2000..0x4000).await.unwrap_err();
1243
1244 let low = task.add(0, 0x1000..0x3000).await.unwrap();
1245
1246 task.remove(high).await;
1247
1248 task.add(1, 0x2000..0x4000).await.unwrap_err();
1249 task.add(1, 0x2000..0x3000).await.unwrap_err();
1250
1251 let _high = task.add(1, 0..0x10000).await.unwrap();
1252
1253 task.remove(low).await;
1254
1255 task.add(0, 0..0x20000).await.unwrap_err();
1256
1257 let _low = task.add(0, 0x1000..0x8000).await.unwrap();
1258 }
1259
1260 struct DmaTestTask {
1262 task: RegionManagerTask,
1263 mappable: Mappable,
1264 }
1265
1266 impl DmaTestTask {
1267 fn new(spawn: impl Spawn) -> Self {
1268 let mm = MappingManager::new_without_primary(spawn, 0x200000, None);
1269 Self {
1270 task: RegionManagerTask::new(mm.client().clone()),
1271 mappable: test_mappable(),
1272 }
1273 }
1274
1275 async fn add_region(&mut self, range: Range<u64>) -> RegionId {
1276 let id = self
1277 .task
1278 .add_region(RegionParams {
1279 priority: 0,
1280 name: format!("{range:x?}"),
1281 range: MemoryRange::new(range),
1282 mapping_type: MappingType::Device,
1283 })
1284 .unwrap();
1285 self.task
1286 .map_region(
1287 id,
1288 MapParams {
1289 executable: true,
1290 writable: true,
1291 prefetch: false,
1292 },
1293 )
1294 .await
1295 .unwrap();
1296 id
1297 }
1298
1299 async fn add_mapping(&mut self, id: RegionId, range_in_region: Range<u64>) {
1300 self.task
1301 .add_mapping(
1302 id,
1303 RegionMappingParams {
1304 range_in_region: MemoryRange::new(range_in_region),
1305 backing: MappingBacking::File {
1306 mappable: self.mappable.clone(),
1307 file_offset: 0,
1308 },
1309 writable: true,
1310 policy: MemoryPolicy::none(),
1311 },
1312 )
1313 .await
1314 .unwrap();
1315 }
1316
1317 async fn add_private_mapping(&mut self, id: RegionId, range_in_region: Range<u64>) {
1319 self.task
1320 .add_mapping(
1321 id,
1322 RegionMappingParams {
1323 range_in_region: MemoryRange::new(range_in_region),
1324 backing: MappingBacking::Private,
1325 writable: true,
1326 policy: MemoryPolicy::none(),
1327 },
1328 )
1329 .await
1330 .unwrap();
1331 }
1332 }
1333
1334 #[async_test]
1335 async fn test_dma_replay_on_registration(spawn: impl Spawn) {
1336 let mut t = DmaTestTask::new(&spawn);
1337 let r = t.add_region(0x0..0x10000).await;
1338 t.add_mapping(r, 0x0..0x4000).await;
1339 t.add_mapping(r, 0x8000..0xC000).await;
1340
1341 let target = Arc::new(RecordingDmaTarget::default());
1343 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1344
1345 assert_eq!(
1346 target.take_events(),
1347 vec![
1348 DmaEvent::Map(MemoryRange::new(0x0..0x4000)),
1349 DmaEvent::Map(MemoryRange::new(0x8000..0xC000)),
1350 ]
1351 );
1352
1353 t.task.remove_dma_mapper(id);
1355 }
1356
1357 #[async_test]
1358 async fn test_dma_live_map_unmap(spawn: impl Spawn) {
1359 let mut t = DmaTestTask::new(&spawn);
1360 let r = t.add_region(0x0..0x10000).await;
1361
1362 let target = Arc::new(RecordingDmaTarget::default());
1363 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1364 target.take_events(); t.add_mapping(r, 0x0..0x4000).await;
1368 assert_eq!(
1369 target.take_events(),
1370 vec![DmaEvent::Map(MemoryRange::new(0x0..0x4000))]
1371 );
1372
1373 t.task
1375 .remove_mappings(r, MemoryRange::new(0x0..0x4000))
1376 .await;
1377 assert_eq!(
1378 target.take_events(),
1379 vec![DmaEvent::Unmap(MemoryRange::new(0x0..0x4000))]
1380 );
1381 }
1382
1383 #[async_test]
1389 async fn test_dma_private_mapping_maps_by_va(spawn: impl Spawn) {
1390 let mut t = DmaTestTask::new(&spawn);
1391 let r = t.add_region(0x0..0x10000).await;
1392 t.add_private_mapping(r, 0x0..0x4000).await;
1394
1395 let target = Arc::new(RecordingDmaTarget::default());
1396 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1397
1398 assert_eq!(
1400 target.take_events(),
1401 vec![DmaEvent::Map(MemoryRange::new(0x0..0x4000))]
1402 );
1403 assert_eq!(
1404 target.take_backingless_maps(),
1405 vec![MemoryRange::new(0x0..0x4000)]
1406 );
1407
1408 t.add_private_mapping(r, 0x8000..0xC000).await;
1411 assert_eq!(
1412 target.take_events(),
1413 vec![DmaEvent::Map(MemoryRange::new(0x8000..0xC000))]
1414 );
1415 t.task.remove_dma_mapper(id);
1416 }
1417
1418 #[async_test]
1422 async fn test_needs_fd_rejects_existing_private_mapping(spawn: impl Spawn) {
1423 let mut t = DmaTestTask::new(&spawn);
1424 let r = t.add_region(0x0..0x10000).await;
1425 t.add_private_mapping(r, 0x0..0x4000).await;
1426
1427 let target = Arc::new(RecordingDmaTarget::default());
1428 let result = t.task.add_dma_mapper(target.clone(), true).await;
1429 assert!(
1430 result.is_err(),
1431 "needs_fd mapper must fail to register when private RAM exists"
1432 );
1433 }
1434
1435 #[async_test]
1438 async fn test_needs_fd_rejects_new_private_mapping(spawn: impl Spawn) {
1439 let mut t = DmaTestTask::new(&spawn);
1440 let r = t.add_region(0x0..0x10000).await;
1441 t.add_mapping(r, 0x0..0x4000).await;
1443 let target = Arc::new(RecordingDmaTarget::default());
1444 let _id = t.task.add_dma_mapper(target.clone(), true).await.unwrap();
1445
1446 t.add_mapping(r, 0x4000..0x8000).await;
1448
1449 let result = t
1451 .task
1452 .add_mapping(
1453 r,
1454 RegionMappingParams {
1455 range_in_region: MemoryRange::new(0x8000..0xC000),
1456 backing: MappingBacking::Private,
1457 writable: true,
1458 policy: MemoryPolicy::none(),
1459 },
1460 )
1461 .await;
1462 assert!(
1463 result.is_err(),
1464 "creating a private mapping must fail while a needs_fd mapper is registered"
1465 );
1466 }
1467
1468 #[async_test]
1469 async fn test_dma_disable_region_unmaps(spawn: impl Spawn) {
1470 let mut t = DmaTestTask::new(&spawn);
1471 let r = t.add_region(0x0..0x10000).await;
1472 t.add_mapping(r, 0x0..0x4000).await;
1473 t.add_mapping(r, 0x8000..0xC000).await;
1474
1475 let target = Arc::new(RecordingDmaTarget::default());
1476 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1477 target.take_events(); t.task.unmap_region(r, false).await;
1481 assert_eq!(
1482 target.take_events(),
1483 vec![DmaEvent::Unmap(MemoryRange::new(0x0..0x10000))]
1484 );
1485 }
1486
1487 #[async_test]
1488 async fn test_dma_remove_mapper_unmaps_all(spawn: impl Spawn) {
1489 let mut t = DmaTestTask::new(&spawn);
1490 let r = t.add_region(0x0..0x10000).await;
1491 t.add_mapping(r, 0x0..0x4000).await;
1492 t.add_mapping(r, 0x8000..0xC000).await;
1493
1494 let target = Arc::new(RecordingDmaTarget::default());
1495 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1496 target.take_events(); t.task.remove_dma_mapper(id);
1500 assert_eq!(
1501 target.take_events(),
1502 vec![
1503 DmaEvent::Unmap(MemoryRange::new(0x0..0x4000)),
1504 DmaEvent::Unmap(MemoryRange::new(0x8000..0xC000)),
1505 ]
1506 );
1507 }
1508
1509 #[async_test]
1510 async fn test_dma_inactive_region_no_notifications(spawn: impl Spawn) {
1511 let mut t = DmaTestTask::new(&spawn);
1512 let r = t.add_region(0x0..0x10000).await;
1513 t.add_mapping(r, 0x0..0x4000).await;
1514
1515 t.task.unmap_region(r, false).await;
1517
1518 let target = Arc::new(RecordingDmaTarget::default());
1519 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1520
1521 assert_eq!(target.take_events(), vec![]);
1523
1524 t.add_mapping(r, 0x8000..0xC000).await;
1526 assert_eq!(target.take_events(), vec![]);
1527 }
1528
1529 struct FailAfterDmaTarget {
1532 fail_after: usize,
1534 inner: RecordingDmaTarget,
1535 call_count: Mutex<usize>,
1536 }
1537
1538 impl FailAfterDmaTarget {
1539 fn new(fail_after: usize) -> Self {
1540 Self {
1541 fail_after,
1542 inner: RecordingDmaTarget::default(),
1543 call_count: Mutex::new(0),
1544 }
1545 }
1546
1547 fn take_events(&self) -> Vec<DmaEvent> {
1548 self.inner.take_events()
1549 }
1550 }
1551
1552 impl DmaTarget for FailAfterDmaTarget {
1553 unsafe fn map_dma(&self, request: DmaMapRequest<'_>) -> anyhow::Result<()> {
1554 let mut count = self.call_count.lock();
1555 if *count >= self.fail_after {
1556 anyhow::bail!("simulated DMA mapping failure at {}", request.range);
1557 }
1558 *count += 1;
1559 drop(count);
1560 unsafe { self.inner.map_dma(request) }
1562 }
1563
1564 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()> {
1565 self.inner.unmap_dma(range)
1566 }
1567 }
1568
1569 #[async_test]
1570 async fn test_add_mapping_dma_failure_propagates(spawn: impl Spawn) {
1571 let mut t = DmaTestTask::new(&spawn);
1572 let r = t.add_region(0x0..0x10000).await;
1573
1574 let target = Arc::new(FailAfterDmaTarget::new(0));
1576 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1577 target.take_events();
1578
1579 let result = t
1582 .task
1583 .add_mapping(
1584 r,
1585 RegionMappingParams {
1586 range_in_region: MemoryRange::new(0x0..0x4000),
1587 backing: MappingBacking::File {
1588 mappable: t.mappable.clone(),
1589 file_offset: 0,
1590 },
1591 writable: true,
1592 policy: MemoryPolicy::none(),
1593 },
1594 )
1595 .await;
1596
1597 assert!(result.is_err(), "add_mapping should propagate DMA failure");
1598 }
1599
1600 #[async_test]
1601 async fn test_enable_region_rollback_unmaps_current_sub_mapping_from_earlier_dma_mappers(
1602 spawn: impl Spawn,
1603 ) {
1604 let mut t = DmaTestTask::new(&spawn);
1614
1615 let r = t.add_region(0x0..0x10000).await;
1616 t.add_mapping(r, 0x0..0x4000).await;
1617
1618 t.task.unmap_region(r, false).await;
1620
1621 let good_target = Arc::new(RecordingDmaTarget::default());
1622 let _good_id = t
1623 .task
1624 .add_dma_mapper(good_target.clone(), false)
1625 .await
1626 .unwrap();
1627
1628 let bad_target = Arc::new(FailAfterDmaTarget::new(0)); let _bad_id = t
1630 .task
1631 .add_dma_mapper(bad_target.clone(), false)
1632 .await
1633 .unwrap();
1634
1635 good_target.take_events();
1637 bad_target.take_events();
1638
1639 let result = t
1640 .task
1641 .map_region(
1642 r,
1643 MapParams {
1644 writable: true,
1645 executable: true,
1646 prefetch: false,
1647 },
1648 )
1649 .await;
1650
1651 assert!(result.is_err(), "enable should fail");
1652
1653 assert_eq!(
1655 good_target.take_events(),
1656 vec![
1657 DmaEvent::Map(MemoryRange::new(0x0..0x4000)),
1658 DmaEvent::Unmap(MemoryRange::new(0x0..0x4000)),
1659 ],
1660 "the successful DMA mapper must have its mapping rolled back"
1661 );
1662 }
1663
1664 #[async_test]
1665 async fn test_enable_region_rollback_unmaps_dma_sub_mappings(spawn: impl Spawn) {
1666 let mut t = DmaTestTask::new(&spawn);
1667
1668 let r = t.add_region(0x0..0x18000).await;
1670 t.add_mapping(r, 0x0..0x4000).await;
1671 t.add_mapping(r, 0x4000..0x8000).await;
1672 t.add_mapping(r, 0x8000..0xC000).await;
1673
1674 t.task.unmap_region(r, false).await;
1676
1677 let target = Arc::new(FailAfterDmaTarget::new(2));
1680 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1681
1682 let result = t
1685 .task
1686 .map_region(
1687 r,
1688 MapParams {
1689 writable: true,
1690 executable: true,
1691 prefetch: false,
1692 },
1693 )
1694 .await;
1695
1696 assert!(
1697 result.is_err(),
1698 "enable should fail on third DMA sub-mapping"
1699 );
1700
1701 let region = t.task.regions.iter().find(|reg| reg.id == r).unwrap();
1702 assert!(
1703 !region.is_active,
1704 "region should not be active after failed enable"
1705 );
1706
1707 assert_eq!(
1708 target.take_events(),
1709 vec![
1710 DmaEvent::Map(MemoryRange::new(0x0..0x4000)),
1711 DmaEvent::Map(MemoryRange::new(0x4000..0x8000)),
1712 DmaEvent::Unmap(MemoryRange::new(0x0..0x4000)),
1713 DmaEvent::Unmap(MemoryRange::new(0x4000..0x8000)),
1714 ],
1715 "successful DMA sub-mappings should be rolled back exactly once"
1716 );
1717 }
1718}