1#![expect(unsafe_code)]
9
10use crate::mapping_manager::Mappable;
11use crate::mapping_manager::MappingManagerClient;
12use crate::mapping_manager::MappingParams;
13use crate::mapping_manager::VaMapper;
14use crate::partition_mapper::PartitionMapper;
15use anyhow::Context as _;
16use futures::StreamExt;
17use inspect::Inspect;
18use inspect::InspectMut;
19use memory_range::MemoryRange;
20use mesh::MeshPayload;
21use mesh::rpc::Rpc;
22use mesh::rpc::RpcSend;
23use pal_async::task::Spawn;
24use std::cmp::Ordering;
25use std::sync::Arc;
26use thiserror::Error;
27use vmcore::local_only::LocalOnly;
28
29pub trait DmaTarget: Send + Sync {
45 unsafe fn map_dma(
59 &self,
60 range: MemoryRange,
61 host_va: Option<*const u8>,
62 mappable: &Mappable,
63 file_offset: u64,
64 ) -> anyhow::Result<()>;
65
66 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()>;
75}
76
77struct DmaMapper {
82 id: DmaMapperId,
83 target: Arc<dyn DmaTarget>,
84 va_mapper: Option<Arc<VaMapper>>,
85}
86
87#[derive(Debug, Copy, Clone, PartialEq, Eq)]
88struct DmaMapperId(u64);
89
90impl DmaMapper {
91 async fn map_dma(
93 &self,
94 range: MemoryRange,
95 mappable: &Mappable,
96 file_offset: u64,
97 ) -> anyhow::Result<()> {
98 let host_va = if let Some(va_mapper) = &self.va_mapper {
101 va_mapper
102 .ensure_mapped(range)
103 .await
104 .context("VA range has no backing mapping")?;
105 Some(unsafe { va_mapper.as_ptr().add(range.start() as usize).cast_const() })
109 } else {
110 None
111 };
112 unsafe { self.target.map_dma(range, host_va, mappable, file_offset) }
119 }
120
121 fn unmap_dma(&self, range: MemoryRange) {
123 if let Err(e) = self.target.unmap_dma(range) {
124 tracing::warn!(
125 error = &*e as &dyn std::error::Error,
126 %range,
127 "DMA unmap failed"
128 );
129 }
130 }
131}
132
133#[derive(Debug, Inspect)]
135pub struct RegionManager {
136 #[inspect(
137 flatten,
138 with = "|x| inspect::send(&x.req_send, RegionRequest::Inspect)"
139 )]
140 client: RegionManagerClient,
141}
142
143#[derive(Debug, MeshPayload, Clone)]
145pub struct RegionManagerClient {
146 req_send: mesh::Sender<RegionRequest>,
147}
148
149struct Region {
150 id: RegionId,
151 map_params: Option<MapParams>,
152 is_active: bool,
153 params: RegionParams,
154 mappings: Vec<RegionMapping>,
155}
156
157#[derive(Debug, MeshPayload)]
158struct RegionParams {
159 name: String,
160 range: MemoryRange,
161 priority: u8,
162 dma_target: bool,
165}
166
167#[derive(Copy, Clone, Debug, MeshPayload, PartialEq, Eq, Inspect)]
168pub struct MapParams {
169 pub writable: bool,
170 pub executable: bool,
171 pub prefetch: bool,
172}
173
174impl Region {
175 fn active_range(&self) -> Option<MemoryRange> {
176 if self.is_active {
177 Some(self.params.range)
178 } else {
179 None
180 }
181 }
182}
183
184#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, MeshPayload)]
186pub struct RegionId(u64);
187
188#[derive(InspectMut)]
189struct RegionManagerTask {
190 #[inspect(with = "inspect_regions")]
191 regions: Vec<Region>,
192 #[inspect(skip)]
193 next_region_id: u64,
194 #[inspect(skip)]
195 inner: RegionManagerTaskInner,
196}
197
198fn inspect_regions(regions: &Vec<Region>) -> impl '_ + Inspect {
199 inspect::adhoc(move |req| {
200 let mut resp = req.respond();
201 for region in regions {
202 resp.field(
203 &format!("{}:{}", region.params.range, ®ion.params.name),
204 inspect::adhoc(|req| {
205 req.respond()
206 .field("map_params", region.map_params)
207 .field("is_active", region.is_active)
208 .field("priority", region.params.priority)
209 .field(
210 "mappings",
211 inspect::adhoc(|req| {
212 inspect_mappings(req, region.params.range.start(), ®ion.mappings)
213 }),
214 );
215 }),
216 );
217 }
218 })
219}
220
221fn inspect_mappings(req: inspect::Request<'_>, region_start: u64, mappings: &[RegionMapping]) {
222 let mut resp = req.respond();
223 for mapping in mappings {
224 let range = MemoryRange::new(
225 region_start + mapping.params.range_in_region.start()
226 ..region_start + mapping.params.range_in_region.end(),
227 )
228 .to_string();
229
230 resp.field(
231 &range,
232 inspect::adhoc(|req| {
233 req.respond()
234 .field("writable", mapping.params.writable)
235 .hex("file_offset", mapping.params.file_offset);
236 }),
237 );
238 }
239}
240
241struct RegionManagerTaskInner {
242 partitions: Vec<PartitionMapper>,
243 dma_mappers: Vec<DmaMapper>,
244 next_dma_mapper_id: u64,
245 mapping_manager: MappingManagerClient,
246}
247
248#[derive(MeshPayload)]
249enum RegionRequest {
250 AddRegion(Rpc<RegionParams, Result<RegionId, AddRegionError>>),
251 RemoveRegion(Rpc<RegionId, ()>),
252 MapRegion(Rpc<(RegionId, MapParams), ()>),
253 UnmapRegion(Rpc<RegionId, ()>),
254 AddMapping(Rpc<(RegionId, RegionMappingParams), ()>),
255 RemoveMappings(Rpc<(RegionId, MemoryRange), ()>),
256 AddPartition(
257 LocalOnly<Rpc<PartitionMapper, Result<(), crate::partition_mapper::PartitionMapperError>>>,
258 ),
259 AddDmaMapper(LocalOnly<Rpc<(Arc<dyn DmaTarget>, bool), anyhow::Result<DmaMapperId>>>),
260 RemoveDmaMapper(LocalOnly<DmaMapperId>),
261 Inspect(inspect::Deferred),
262}
263
264struct RegionMapping {
265 params: RegionMappingParams,
266}
267
268#[derive(MeshPayload)]
269struct RegionMappingParams {
270 range_in_region: MemoryRange,
271 mappable: Mappable,
272 file_offset: u64,
273 writable: bool,
274}
275
276fn range_within(outer: MemoryRange, inner: MemoryRange) -> MemoryRange {
277 assert!(inner.end() <= outer.len());
278 MemoryRange::new(outer.start() + inner.start()..outer.start() + inner.end())
279}
280
281#[derive(Debug, Error, MeshPayload)]
282pub enum AddRegionError {
283 #[error("memory region {new} overlaps with existing region {existing}")]
284 OverlapError { existing: String, new: String },
285}
286
287impl RegionManagerTask {
288 fn new(mapping_manager: MappingManagerClient) -> Self {
289 Self {
290 regions: Vec::new(),
291 next_region_id: 1,
292 inner: RegionManagerTaskInner {
293 mapping_manager,
294 partitions: Vec::new(),
295 dma_mappers: Vec::new(),
296 next_dma_mapper_id: 0,
297 },
298 }
299 }
300
301 async fn run(&mut self, req_recv: &mut mesh::Receiver<RegionRequest>) {
302 while let Some(req) = req_recv.next().await {
303 match req {
304 RegionRequest::AddMapping(rpc) => {
305 rpc.handle(async |(id, params)| self.add_mapping(id, params).await)
306 .await
307 }
308 RegionRequest::RemoveMappings(rpc) => {
309 rpc.handle(async |(id, range)| self.remove_mappings(id, range).await)
310 .await
311 }
312 RegionRequest::AddPartition(LocalOnly(rpc)) => {
313 rpc.handle(async |partition| self.add_partition(partition).await)
314 .await
315 }
316 RegionRequest::AddDmaMapper(LocalOnly(rpc)) => {
317 let ((target, needs_va), rpc) = rpc.split();
318 let result = self.add_dma_mapper(target, needs_va).await;
319 rpc.complete(result);
320 }
321 RegionRequest::RemoveDmaMapper(LocalOnly(id)) => {
322 self.remove_dma_mapper(id);
323 }
324 RegionRequest::AddRegion(rpc) => rpc.handle_sync(|params| self.add_region(params)),
325 RegionRequest::RemoveRegion(rpc) => {
326 rpc.handle(async |id| self.unmap_region(id, true).await)
327 .await
328 }
329 RegionRequest::MapRegion(rpc) => {
330 rpc.handle(async |(id, params)| self.map_region(id, params).await)
331 .await
332 }
333 RegionRequest::UnmapRegion(rpc) => {
334 rpc.handle(async |id| self.unmap_region(id, false).await)
335 .await
336 }
337 RegionRequest::Inspect(deferred) => {
338 deferred.inspect(&mut *self);
339 }
340 }
341 }
342 }
343
344 async fn add_partition(
345 &mut self,
346 partition: PartitionMapper,
347 ) -> Result<(), crate::partition_mapper::PartitionMapperError> {
348 for region in &self.regions {
351 if region.is_active {
352 partition
353 .map_region(region.params.range, region.map_params.unwrap())
354 .await?;
355 }
356 }
357 self.inner.partitions.push(partition);
358 Ok(())
359 }
360
361 async fn add_dma_mapper(
362 &mut self,
363 target: Arc<dyn DmaTarget>,
364 needs_va: bool,
365 ) -> anyhow::Result<DmaMapperId> {
366 let va_mapper = if needs_va {
368 Some(
369 self.inner
370 .mapping_manager
371 .new_mapper()
372 .await
373 .map_err(|e| anyhow::anyhow!(e))?,
374 )
375 } else {
376 None
377 };
378
379 let id = DmaMapperId(self.inner.next_dma_mapper_id);
380 self.inner.next_dma_mapper_id += 1;
381
382 let mapper = DmaMapper {
383 id,
384 target,
385 va_mapper,
386 };
387
388 for region in &self.regions {
391 if region.is_active {
392 for mapping in ®ion.mappings {
393 let range = range_within(region.params.range, mapping.params.range_in_region);
394 mapper
395 .map_dma(range, &mapping.params.mappable, mapping.params.file_offset)
396 .await?;
397 }
398 }
399 }
400
401 self.inner.dma_mappers.push(mapper);
402 Ok(id)
403 }
404
405 fn remove_dma_mapper(&mut self, id: DmaMapperId) {
406 if let Some(pos) = self.inner.dma_mappers.iter().position(|m| m.id == id) {
407 let mapper = &self.inner.dma_mappers[pos];
408 for region in &self.regions {
410 if region.is_active {
411 for mapping in ®ion.mappings {
412 let range =
413 range_within(region.params.range, mapping.params.range_in_region);
414 mapper.unmap_dma(range);
415 }
416 }
417 }
418 self.inner.dma_mappers.swap_remove(pos);
419 }
420 }
421
422 fn region_index(&self, id: RegionId) -> usize {
423 self.regions.iter().position(|r| r.id == id).unwrap()
424 }
425
426 fn add_region(&mut self, params: RegionParams) -> Result<RegionId, AddRegionError> {
427 let range = params.range;
430 for other_region in &self.regions {
431 let other_range = other_region.params.range;
432 if !range.overlaps(&other_range) {
433 continue;
434 };
435 let ok = match params.priority.cmp(&other_region.params.priority) {
436 Ordering::Less => other_range.contains(&range),
437 Ordering::Equal => other_range == range,
438 Ordering::Greater => range.contains(&other_range),
439 };
440 if !ok {
441 return Err(AddRegionError::OverlapError {
442 existing: other_region.params.name.clone(),
443 new: params.name,
444 });
445 }
446 }
447
448 tracing::debug!(
449 range = %params.range,
450 name = params.name,
451 priority = params.priority,
452 "new region"
453 );
454
455 let id = RegionId(self.next_region_id);
456 self.next_region_id += 1;
457 self.regions.push(Region {
458 id,
459 map_params: None,
460 is_active: false,
461 params,
462 mappings: Vec::new(),
463 });
464 Ok(id)
465 }
466
467 async fn enable_best_region(&mut self, mut range: MemoryRange) {
470 while !range.is_empty() {
471 if let Some(region) = self
476 .regions
477 .iter_mut()
478 .filter_map(|region| {
479 region.map_params?;
480 if !range.contains(®ion.params.range) {
481 assert!(
482 !range.overlaps(®ion.params.range),
483 "no overlap invariant violated"
484 );
485 return None;
486 }
487 assert!(!region.is_active);
488 Some(region)
489 })
490 .min_by_key(|region| {
491 (
492 region.params.range.start(),
493 u8::MAX - region.params.priority,
494 )
495 })
496 {
497 self.inner.enable_region(region).await;
498 range = MemoryRange::new(region.params.range.end()..range.end());
499 } else {
500 range = MemoryRange::EMPTY;
501 }
502 }
503 }
504
505 async fn map_region(&mut self, id: RegionId, map_params: MapParams) {
506 let index = self.region_index(id);
507 let region = &mut self.regions[index];
508 let range = region.params.range;
509 let priority = region.params.priority;
510 if region.map_params == Some(map_params) {
511 return;
512 }
513
514 tracing::debug!(
515 name = region.params.name,
516 range = %region.params.range,
517 writable = map_params.writable,
518 "mapping region"
519 );
520
521 let mut enable = true;
524 for (other_index, other_region) in self.regions.iter_mut().enumerate() {
525 if !other_region.is_active || !other_region.params.range.overlaps(&range) {
526 continue;
527 }
528 if other_region.params.priority > priority
529 || (other_region.params.priority == priority && other_index < index)
530 {
531 enable = false;
532 } else {
533 assert!(enable);
534 self.inner.disable_region(other_region).await;
535 }
536 }
537
538 self.regions[index].map_params = Some(map_params);
539 if enable {
540 self.enable_best_region(range).await;
541 }
542 }
543
544 async fn unmap_region(&mut self, id: RegionId, remove: bool) {
545 let index = self.region_index(id);
546 let region = &mut self.regions[index];
547 tracing::debug!(
548 name = region.params.name,
549 range = %region.params.range,
550 remove,
551 "unmapping region"
552 );
553
554 let active_range = region.is_active.then_some(region.params.range);
555 if active_range.is_some() {
556 self.inner.disable_region(region).await;
557 }
558
559 if remove {
560 self.regions.remove(index);
561 } else {
562 region.map_params = None;
563 }
564 if let Some(range) = active_range {
565 self.enable_best_region(range).await;
566 }
567 }
568
569 async fn add_mapping(&mut self, id: RegionId, params: RegionMappingParams) {
570 let index = self.region_index(id);
571 let region = &mut self.regions[index];
572
573 assert!(
576 !region
577 .mappings
578 .iter()
579 .any(|m| m.params.range_in_region.overlaps(¶ms.range_in_region))
580 );
581
582 if let Some(region_range) = region.active_range() {
583 let range = range_within(region_range, params.range_in_region);
584 self.inner
585 .mapping_manager
586 .add_mapping(MappingParams {
587 range,
588 mappable: params.mappable.clone(),
589 file_offset: params.file_offset,
590 writable: params.writable,
591 dma_target: region.params.dma_target,
592 })
593 .await;
594
595 for partition in &mut self.inner.partitions {
596 partition.notify_new_mapping(range).await;
597 }
598
599 for dma_mapper in &self.inner.dma_mappers {
600 if let Err(e) = dma_mapper
601 .map_dma(range, ¶ms.mappable, params.file_offset)
602 .await
603 {
604 tracing::warn!(
605 error = &*e as &dyn std::error::Error,
606 %range,
607 "DMA mapper failed to map new sub-mapping"
608 );
609 }
610 }
611 }
612
613 region.mappings.push(RegionMapping { params });
614 }
615
616 async fn remove_mappings(&mut self, id: RegionId, range_in_region: MemoryRange) {
617 let index = self.region_index(id);
618 let region = &mut self.regions[index];
619 let active_range = region.active_range();
620
621 let removed_ranges: Vec<MemoryRange> = if active_range.is_some() {
624 let region_range = region.params.range;
625 region
626 .mappings
627 .iter()
628 .filter(|m| range_in_region.contains(&m.params.range_in_region))
629 .map(|m| range_within(region_range, m.params.range_in_region))
630 .collect()
631 } else {
632 Vec::new()
633 };
634
635 region.mappings.retain_mut(|mapping| {
636 if !range_in_region.contains(&mapping.params.range_in_region) {
637 assert!(
638 !range_in_region.overlaps(&mapping.params.range_in_region),
639 "no partial unmappings allowed"
640 );
641 return true;
642 }
643 false
644 });
645 if let Some(region_range) = active_range {
646 for &removed in &removed_ranges {
649 for dma_mapper in &self.inner.dma_mappers {
650 dma_mapper.unmap_dma(removed);
651 }
652 }
653
654 self.inner
655 .mapping_manager
656 .remove_mappings(range_within(region_range, range_in_region))
657 .await;
658
659 }
663 }
664}
665
666impl RegionManagerTaskInner {
667 async fn enable_region(&mut self, region: &mut Region) {
668 assert!(!region.is_active);
669 let map_params = region.map_params.unwrap();
670
671 tracing::debug!(
672 name = region.params.name,
673 range = %region.params.range,
674 writable = map_params.writable,
675 "enabling region"
676 );
677
678 for mapping in ®ion.mappings {
680 self.mapping_manager
681 .add_mapping(MappingParams {
682 range: range_within(region.params.range, mapping.params.range_in_region),
683 mappable: mapping.params.mappable.clone(),
684 file_offset: mapping.params.file_offset,
685 writable: mapping.params.writable && map_params.writable,
686 dma_target: region.params.dma_target,
687 })
688 .await;
689 }
690
691 for mapping in ®ion.mappings {
693 let range = range_within(region.params.range, mapping.params.range_in_region);
694 for dma_mapper in &self.dma_mappers {
695 if let Err(e) = dma_mapper
696 .map_dma(range, &mapping.params.mappable, mapping.params.file_offset)
697 .await
698 {
699 tracing::warn!(
700 error = &*e as &dyn std::error::Error,
701 %range,
702 "DMA mapper failed to map sub-mapping during region enable"
703 );
704 }
705 }
706 }
707
708 for partition in &mut self.partitions {
710 partition
711 .map_region(region.params.range, map_params)
712 .await
713 .expect("cannot recover from failed mapping");
714 }
715
716 region.is_active = true;
717 }
718
719 async fn disable_region(&mut self, region: &mut Region) {
720 assert!(region.is_active);
721
722 tracing::debug!(
723 name = region.params.name,
724 range = %region.params.range,
725 "disabling region"
726 );
727
728 let region_range = region.params.range;
733 for dma_mapper in &mut self.dma_mappers {
734 dma_mapper.unmap_dma(region_range);
735 }
736
737 for partition in &mut self.partitions {
738 partition.unmap_region(region_range);
739 }
740 self.mapping_manager.remove_mappings(region_range).await;
741 region.is_active = false;
742 }
743}
744
745impl RegionManager {
746 pub fn new(spawn: impl Spawn, mapping_manager: MappingManagerClient) -> Self {
748 let (req_send, mut req_recv) = mesh::mpsc_channel();
749 spawn
750 .spawn("region_manager", {
751 let mut task = RegionManagerTask::new(mapping_manager);
752 async move {
753 task.run(&mut req_recv).await;
754 }
755 })
756 .detach();
757 Self {
758 client: RegionManagerClient { req_send },
759 }
760 }
761
762 pub fn client(&self) -> &RegionManagerClient {
764 &self.client
765 }
766}
767
768impl RegionManagerClient {
769 pub async fn add_partition(
773 &self,
774 partition: PartitionMapper,
775 ) -> Result<(), crate::partition_mapper::PartitionMapperError> {
776 self.req_send
777 .call(|x| RegionRequest::AddPartition(LocalOnly(x)), partition)
778 .await
779 .unwrap()
780 }
781
782 pub async fn new_region(
786 &self,
787 name: String,
788 range: MemoryRange,
789 priority: u8,
790 dma_target: bool,
791 ) -> Result<RegionHandle, AddRegionError> {
792 let params = RegionParams {
793 name,
794 range,
795 priority,
796 dma_target,
797 };
798
799 let id = self
800 .req_send
801 .call(RegionRequest::AddRegion, params)
802 .await
803 .unwrap()?;
804
805 Ok(RegionHandle {
806 id: Some(id),
807 req_send: self.req_send.clone(),
808 })
809 }
810}
811
812#[derive(Clone)]
818pub struct DmaMapperClient {
819 req_send: mesh::Sender<RegionRequest>,
820}
821
822impl DmaMapperClient {
823 pub(crate) fn new(region_manager: &RegionManagerClient) -> Self {
824 Self {
825 req_send: region_manager.req_send.clone(),
826 }
827 }
828
829 pub async fn add_dma_mapper(
847 &self,
848 target: Arc<dyn DmaTarget>,
849 needs_va: bool,
850 ) -> anyhow::Result<DmaMapperHandle> {
851 let id = self
852 .req_send
853 .call(
854 |x| RegionRequest::AddDmaMapper(LocalOnly(x)),
855 (target, needs_va),
856 )
857 .await
858 .unwrap()?;
859 Ok(DmaMapperHandle {
860 id: Some(id),
861 req_send: self.req_send.clone(),
862 })
863 }
864}
865
866pub struct DmaMapperHandle {
871 id: Option<DmaMapperId>,
872 req_send: mesh::Sender<RegionRequest>,
873}
874
875impl Drop for DmaMapperHandle {
876 fn drop(&mut self) {
877 if let Some(id) = self.id {
878 self.req_send
879 .send(RegionRequest::RemoveDmaMapper(LocalOnly(id)));
880 }
881 }
882}
883
884#[derive(Debug)]
888#[must_use]
889pub struct RegionHandle {
890 id: Option<RegionId>,
891 req_send: mesh::Sender<RegionRequest>,
892}
893
894impl RegionHandle {
895 pub async fn map(&self, params: MapParams) {
897 self.req_send
898 .call(RegionRequest::MapRegion, (self.id.unwrap(), params))
899 .await
900 .unwrap()
901 }
902
903 pub async fn unmap(&self) {
905 let _ = self
906 .req_send
907 .call(RegionRequest::UnmapRegion, self.id.unwrap())
908 .await;
909 }
910
911 pub async fn add_mapping(
915 &self,
916 range_in_region: MemoryRange,
917 mappable: Mappable,
918 file_offset: u64,
919 writable: bool,
920 ) {
921 let _ = self
922 .req_send
923 .call(
924 RegionRequest::AddMapping,
925 (
926 self.id.unwrap(),
927 RegionMappingParams {
928 range_in_region,
929 mappable,
930 file_offset,
931 writable,
932 },
933 ),
934 )
935 .await;
936 }
937
938 pub async fn remove_mappings(&self, range: MemoryRange) {
942 let _ = self
943 .req_send
944 .call(RegionRequest::RemoveMappings, (self.id.unwrap(), range))
945 .await;
946 }
947
948 pub async fn teardown(mut self) {
950 let _ = self
951 .req_send
952 .call(RegionRequest::RemoveRegion, self.id.take().unwrap())
953 .await;
954 }
955}
956
957impl Drop for RegionHandle {
958 fn drop(&mut self) {
959 if let Some(id) = self.id {
960 let _recv = self.req_send.call(RegionRequest::RemoveRegion, id);
961 }
963 }
964}
965
966#[cfg(test)]
967mod tests {
968 use super::MapParams;
969 use super::RegionManagerTask;
970 use crate::mapping_manager::Mappable;
971 use crate::mapping_manager::MappingManager;
972 use crate::region_manager::AddRegionError;
973 use crate::region_manager::DmaTarget;
974 use crate::region_manager::RegionId;
975 use crate::region_manager::RegionMappingParams;
976 use crate::region_manager::RegionParams;
977 use memory_range::MemoryRange;
978 use pal_async::async_test;
979 use pal_async::task::Spawn;
980 use parking_lot::Mutex;
981 use std::ops::Range;
982 use std::sync::Arc;
983
984 #[derive(Default)]
986 struct RecordingDmaTarget {
987 events: Mutex<Vec<DmaEvent>>,
988 }
989
990 #[derive(Debug, Clone, PartialEq, Eq)]
991 enum DmaEvent {
992 Map(MemoryRange),
993 Unmap(MemoryRange),
994 }
995
996 impl DmaTarget for RecordingDmaTarget {
997 unsafe fn map_dma(
998 &self,
999 range: MemoryRange,
1000 _host_va: Option<*const u8>,
1001 _mappable: &Mappable,
1002 _file_offset: u64,
1003 ) -> anyhow::Result<()> {
1004 self.events.lock().push(DmaEvent::Map(range));
1005 Ok(())
1006 }
1007
1008 fn unmap_dma(&self, range: MemoryRange) -> anyhow::Result<()> {
1009 self.events.lock().push(DmaEvent::Unmap(range));
1010 Ok(())
1011 }
1012 }
1013
1014 impl RecordingDmaTarget {
1015 fn take_events(&self) -> Vec<DmaEvent> {
1016 std::mem::take(&mut self.events.lock())
1017 }
1018 }
1019
1020 fn test_mappable() -> Mappable {
1022 sparse_mmap::alloc_shared_memory(0x10000, "test-dma")
1023 .unwrap()
1024 .into()
1025 }
1026
1027 #[async_test]
1028 async fn test_region_overlap(spawn: impl Spawn) {
1029 struct TestTask(RegionManagerTask);
1030 impl TestTask {
1031 async fn add(
1032 &mut self,
1033 priority: u8,
1034 range: Range<u64>,
1035 ) -> Result<RegionId, AddRegionError> {
1036 let id = self.0.add_region(RegionParams {
1037 priority,
1038 name: priority.to_string(),
1039 range: MemoryRange::new(range),
1040 dma_target: false,
1041 })?;
1042 self.0
1043 .map_region(
1044 id,
1045 MapParams {
1046 executable: true,
1047 writable: true,
1048 prefetch: false,
1049 },
1050 )
1051 .await;
1052 Ok(id)
1053 }
1054
1055 async fn remove(&mut self, id: RegionId) {
1056 self.0.unmap_region(id, true).await;
1057 }
1058 }
1059
1060 let mm = MappingManager::new(spawn, 0x200000, false, None);
1061 let mut task = TestTask(RegionManagerTask::new(mm.client().clone()));
1062
1063 let high = task.add(1, 0x1000..0x3000).await.unwrap();
1064
1065 task.add(0, 0x2000..0x4000).await.unwrap_err();
1066
1067 let low = task.add(0, 0x1000..0x3000).await.unwrap();
1068
1069 task.remove(high).await;
1070
1071 task.add(1, 0x2000..0x4000).await.unwrap_err();
1072 task.add(1, 0x2000..0x3000).await.unwrap_err();
1073
1074 let _high = task.add(1, 0..0x10000).await.unwrap();
1075
1076 task.remove(low).await;
1077
1078 task.add(0, 0..0x20000).await.unwrap_err();
1079
1080 let _low = task.add(0, 0x1000..0x8000).await.unwrap();
1081 }
1082
1083 struct DmaTestTask {
1085 task: RegionManagerTask,
1086 mappable: Mappable,
1087 }
1088
1089 impl DmaTestTask {
1090 fn new(spawn: impl Spawn) -> Self {
1091 let mm = MappingManager::new(spawn, 0x200000, false, None);
1092 Self {
1093 task: RegionManagerTask::new(mm.client().clone()),
1094 mappable: test_mappable(),
1095 }
1096 }
1097
1098 async fn add_region(&mut self, range: Range<u64>) -> RegionId {
1099 let id = self
1100 .task
1101 .add_region(RegionParams {
1102 priority: 0,
1103 name: format!("{range:x?}"),
1104 range: MemoryRange::new(range),
1105 dma_target: false,
1106 })
1107 .unwrap();
1108 self.task
1109 .map_region(
1110 id,
1111 MapParams {
1112 executable: true,
1113 writable: true,
1114 prefetch: false,
1115 },
1116 )
1117 .await;
1118 id
1119 }
1120
1121 async fn add_mapping(&mut self, id: RegionId, range_in_region: Range<u64>) {
1122 self.task
1123 .add_mapping(
1124 id,
1125 RegionMappingParams {
1126 range_in_region: MemoryRange::new(range_in_region),
1127 mappable: self.mappable.clone(),
1128 file_offset: 0,
1129 writable: true,
1130 },
1131 )
1132 .await;
1133 }
1134 }
1135
1136 #[async_test]
1137 async fn test_dma_replay_on_registration(spawn: impl Spawn) {
1138 let mut t = DmaTestTask::new(&spawn);
1139 let r = t.add_region(0x0..0x10000).await;
1140 t.add_mapping(r, 0x0..0x4000).await;
1141 t.add_mapping(r, 0x8000..0xC000).await;
1142
1143 let target = Arc::new(RecordingDmaTarget::default());
1145 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1146
1147 assert_eq!(
1148 target.take_events(),
1149 vec![
1150 DmaEvent::Map(MemoryRange::new(0x0..0x4000)),
1151 DmaEvent::Map(MemoryRange::new(0x8000..0xC000)),
1152 ]
1153 );
1154
1155 t.task.remove_dma_mapper(id);
1157 }
1158
1159 #[async_test]
1160 async fn test_dma_live_map_unmap(spawn: impl Spawn) {
1161 let mut t = DmaTestTask::new(&spawn);
1162 let r = t.add_region(0x0..0x10000).await;
1163
1164 let target = Arc::new(RecordingDmaTarget::default());
1165 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1166 target.take_events(); t.add_mapping(r, 0x0..0x4000).await;
1170 assert_eq!(
1171 target.take_events(),
1172 vec![DmaEvent::Map(MemoryRange::new(0x0..0x4000))]
1173 );
1174
1175 t.task
1177 .remove_mappings(r, MemoryRange::new(0x0..0x4000))
1178 .await;
1179 assert_eq!(
1180 target.take_events(),
1181 vec![DmaEvent::Unmap(MemoryRange::new(0x0..0x4000))]
1182 );
1183 }
1184
1185 #[async_test]
1186 async fn test_dma_disable_region_unmaps(spawn: impl Spawn) {
1187 let mut t = DmaTestTask::new(&spawn);
1188 let r = t.add_region(0x0..0x10000).await;
1189 t.add_mapping(r, 0x0..0x4000).await;
1190 t.add_mapping(r, 0x8000..0xC000).await;
1191
1192 let target = Arc::new(RecordingDmaTarget::default());
1193 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1194 target.take_events(); t.task.unmap_region(r, false).await;
1198 assert_eq!(
1199 target.take_events(),
1200 vec![DmaEvent::Unmap(MemoryRange::new(0x0..0x10000))]
1201 );
1202 }
1203
1204 #[async_test]
1205 async fn test_dma_remove_mapper_unmaps_all(spawn: impl Spawn) {
1206 let mut t = DmaTestTask::new(&spawn);
1207 let r = t.add_region(0x0..0x10000).await;
1208 t.add_mapping(r, 0x0..0x4000).await;
1209 t.add_mapping(r, 0x8000..0xC000).await;
1210
1211 let target = Arc::new(RecordingDmaTarget::default());
1212 let id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1213 target.take_events(); t.task.remove_dma_mapper(id);
1217 assert_eq!(
1218 target.take_events(),
1219 vec![
1220 DmaEvent::Unmap(MemoryRange::new(0x0..0x4000)),
1221 DmaEvent::Unmap(MemoryRange::new(0x8000..0xC000)),
1222 ]
1223 );
1224 }
1225
1226 #[async_test]
1227 async fn test_dma_inactive_region_no_notifications(spawn: impl Spawn) {
1228 let mut t = DmaTestTask::new(&spawn);
1229 let r = t.add_region(0x0..0x10000).await;
1230 t.add_mapping(r, 0x0..0x4000).await;
1231
1232 t.task.unmap_region(r, false).await;
1234
1235 let target = Arc::new(RecordingDmaTarget::default());
1236 let _id = t.task.add_dma_mapper(target.clone(), false).await.unwrap();
1237
1238 assert_eq!(target.take_events(), vec![]);
1240
1241 t.add_mapping(r, 0x8000..0xC000).await;
1243 assert_eq!(target.take_events(), vec![]);
1244 }
1245}