1use rvm_types::{GuestPhysAddr, OwnedRegionId, PartitionId, PhysAddr, RvmError, RvmResult};
17
18use crate::tier::Tier;
19use crate::{MemoryPermissions, PAGE_SIZE};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct OwnedRegion {
24 pub id: OwnedRegionId,
26 pub owner: PartitionId,
28 pub guest_base: GuestPhysAddr,
30 pub host_base: PhysAddr,
32 pub page_count: u32,
34 pub tier: Tier,
36 pub permissions: MemoryPermissions,
38 occupied: bool,
40}
41
42impl OwnedRegion {
43 const EMPTY: Self = Self {
45 id: OwnedRegionId::new(0),
46 owner: PartitionId::new(0),
47 guest_base: GuestPhysAddr::new(0),
48 host_base: PhysAddr::new(0),
49 page_count: 0,
50 tier: Tier::Warm,
51 permissions: MemoryPermissions::READ_ONLY,
52 occupied: false,
53 };
54
55 #[must_use]
57 pub const fn size_bytes(&self) -> u64 {
58 self.page_count as u64 * PAGE_SIZE as u64
59 }
60
61 #[must_use]
63 pub const fn guest_end(&self) -> u64 {
64 self.guest_base.as_u64() + self.size_bytes()
65 }
66
67 #[must_use]
69 pub const fn host_end(&self) -> u64 {
70 self.host_base.as_u64() + self.size_bytes()
71 }
72
73 #[must_use]
75 pub const fn contains_guest(&self, addr: GuestPhysAddr) -> bool {
76 addr.as_u64() >= self.guest_base.as_u64() && addr.as_u64() < self.guest_end()
77 }
78}
79
80#[derive(Debug, Clone, Copy)]
82pub struct RegionConfig {
83 pub id: OwnedRegionId,
85 pub owner: PartitionId,
87 pub guest_base: GuestPhysAddr,
89 pub host_base: PhysAddr,
91 pub page_count: u32,
93 pub tier: Tier,
95 pub permissions: MemoryPermissions,
97}
98
99#[derive(Debug, Clone, Copy)]
101pub struct AddressMapping {
102 pub guest: GuestPhysAddr,
104 pub host: PhysAddr,
106 pub permissions: MemoryPermissions,
108}
109
110pub struct RegionManager<const MAX: usize> {
114 regions: [OwnedRegion; MAX],
116 count: usize,
118 next_id: u64,
120}
121
122impl<const MAX: usize> Default for RegionManager<MAX> {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128impl<const MAX: usize> RegionManager<MAX> {
129 #[must_use]
131 pub const fn new() -> Self {
132 Self {
133 regions: [OwnedRegion::EMPTY; MAX],
134 count: 0,
135 next_id: 1,
136 }
137 }
138
139 #[must_use]
141 pub const fn count(&self) -> usize {
142 self.count
143 }
144
145 #[must_use]
147 pub const fn capacity(&self) -> usize {
148 MAX
149 }
150
151 pub fn create(&mut self, config: RegionConfig) -> RvmResult<OwnedRegionId> {
164 if !config.guest_base.is_page_aligned() {
166 return Err(RvmError::AlignmentError);
167 }
168 if !config.host_base.is_page_aligned() {
169 return Err(RvmError::AlignmentError);
170 }
171 if config.page_count == 0 {
172 return Err(RvmError::ResourceLimitExceeded);
173 }
174
175 if self.count >= MAX {
177 return Err(RvmError::ResourceLimitExceeded);
178 }
179
180 let new_start = config.guest_base.as_u64();
182 let new_end = new_start + u64::from(config.page_count) * PAGE_SIZE as u64;
183 let new_host_start = config.host_base.as_u64();
184 let new_host_end = new_host_start + u64::from(config.page_count) * PAGE_SIZE as u64;
185 let mut first_free_slot: Option<usize> = None;
186
187 for (i, region) in self.regions.iter().enumerate() {
188 if !region.occupied {
189 if first_free_slot.is_none() {
190 first_free_slot = Some(i);
191 }
192 continue;
193 }
194 if region.owner == config.owner {
196 let existing_start = region.guest_base.as_u64();
197 let existing_end = region.guest_end();
198 if new_start < existing_end && existing_start < new_end {
199 return Err(RvmError::MemoryOverlap);
200 }
201 }
202 let existing_host_start = region.host_base.as_u64();
206 let existing_host_end = region.host_end();
207 if new_host_start < existing_host_end && existing_host_start < new_host_end {
208 return Err(RvmError::MemoryOverlap);
209 }
210 }
211
212 match first_free_slot {
214 Some(idx) => {
215 self.regions[idx] = OwnedRegion {
216 id: config.id,
217 owner: config.owner,
218 guest_base: config.guest_base,
219 host_base: config.host_base,
220 page_count: config.page_count,
221 tier: config.tier,
222 permissions: config.permissions,
223 occupied: true,
224 };
225 self.count += 1;
226 Ok(config.id)
227 }
228 None => Err(RvmError::ResourceLimitExceeded),
229 }
230 }
231
232 pub fn create_auto_id(
238 &mut self,
239 owner: PartitionId,
240 guest_base: GuestPhysAddr,
241 host_base: PhysAddr,
242 page_count: u32,
243 tier: Tier,
244 permissions: MemoryPermissions,
245 ) -> RvmResult<OwnedRegionId> {
246 let id = OwnedRegionId::new(self.next_id);
247 self.next_id += 1;
248 self.create(RegionConfig {
249 id,
250 owner,
251 guest_base,
252 host_base,
253 page_count,
254 tier,
255 permissions,
256 })
257 }
258
259 pub fn destroy(&mut self, region_id: OwnedRegionId) -> RvmResult<OwnedRegion> {
265 match self.find_slot(region_id) {
266 Some(idx) => {
267 let region = self.regions[idx];
268 self.regions[idx] = OwnedRegion::EMPTY;
269 self.count -= 1;
270 Ok(region)
271 }
272 None => Err(RvmError::PartitionNotFound),
273 }
274 }
275
276 pub fn transfer(&mut self, region_id: OwnedRegionId, new_owner: PartitionId) -> RvmResult<()> {
288 let idx = self
289 .find_slot(region_id)
290 .ok_or(RvmError::PartitionNotFound)?;
291
292 let r = &self.regions[idx];
294 let xfer_start = r.guest_base.as_u64();
295 let xfer_end = r.guest_end();
296 for (i, region) in self.regions.iter().enumerate() {
297 if i == idx || !region.occupied || region.owner != new_owner {
298 continue;
299 }
300 let existing_start = region.guest_base.as_u64();
301 let existing_end = region.guest_end();
302 if xfer_start < existing_end && existing_start < xfer_end {
303 return Err(RvmError::MemoryOverlap);
304 }
305 }
306
307 self.regions[idx].owner = new_owner;
308 Ok(())
309 }
310
311 #[must_use]
313 pub fn get(&self, region_id: OwnedRegionId) -> Option<&OwnedRegion> {
314 self.find_slot(region_id).map(|idx| &self.regions[idx])
315 }
316
317 pub fn get_mut(&mut self, region_id: OwnedRegionId) -> Option<&mut OwnedRegion> {
319 self.find_slot(region_id).map(|idx| &mut self.regions[idx])
320 }
321
322 #[must_use]
325 pub fn translate(&self, owner: PartitionId, guest: GuestPhysAddr) -> Option<AddressMapping> {
326 for region in &self.regions {
327 if !region.occupied || region.owner != owner {
328 continue;
329 }
330 if region.contains_guest(guest) {
331 let offset = guest.as_u64() - region.guest_base.as_u64();
332 return Some(AddressMapping {
333 guest,
334 host: PhysAddr::new(region.host_base.as_u64() + offset),
335 permissions: region.permissions,
336 });
337 }
338 }
339 None
340 }
341
342 #[must_use]
344 pub fn count_for_partition(&self, owner: PartitionId) -> usize {
345 self.regions
346 .iter()
347 .filter(|r| r.occupied && r.owner == owner)
348 .count()
349 }
350
351 pub fn regions_for_partition(&self, owner: PartitionId, out: &mut [OwnedRegionId]) -> usize {
354 let mut written = 0;
355 for region in &self.regions {
356 if written >= out.len() {
357 break;
358 }
359 if region.occupied && region.owner == owner {
360 out[written] = region.id;
361 written += 1;
362 }
363 }
364 written
365 }
366
367 fn find_slot(&self, region_id: OwnedRegionId) -> Option<usize> {
371 self.regions
372 .iter()
373 .position(|r| r.occupied && r.id == region_id)
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 fn pid(id: u32) -> PartitionId {
382 PartitionId::new(id)
383 }
384
385 fn rid(id: u64) -> OwnedRegionId {
386 OwnedRegionId::new(id)
387 }
388
389 fn gpa(addr: u64) -> GuestPhysAddr {
390 GuestPhysAddr::new(addr)
391 }
392
393 fn pa(addr: u64) -> PhysAddr {
394 PhysAddr::new(addr)
395 }
396
397 fn default_config(id: u64, owner: u32, guest: u64, host: u64) -> RegionConfig {
398 RegionConfig {
399 id: rid(id),
400 owner: pid(owner),
401 guest_base: gpa(guest),
402 host_base: pa(host),
403 page_count: 4,
404 tier: Tier::Warm,
405 permissions: MemoryPermissions::READ_WRITE,
406 }
407 }
408
409 #[test]
410 fn create_and_get() {
411 let mut mgr = RegionManager::<8>::new();
412 let config = default_config(1, 1, 0x1000, 0x2000_0000);
413 let id = mgr.create(config).unwrap();
414 assert_eq!(id, rid(1));
415 assert_eq!(mgr.count(), 1);
416
417 let region = mgr.get(id).unwrap();
418 assert_eq!(region.owner, pid(1));
419 assert_eq!(region.guest_base, gpa(0x1000));
420 assert_eq!(region.host_base, pa(0x2000_0000));
421 assert_eq!(region.page_count, 4);
422 assert_eq!(region.tier, Tier::Warm);
423 }
424
425 #[test]
426 fn create_unaligned_guest_fails() {
427 let mut mgr = RegionManager::<8>::new();
428 let config = RegionConfig {
429 guest_base: gpa(0x1001), ..default_config(1, 1, 0x1000, 0x2000_0000)
431 };
432 assert_eq!(mgr.create(config), Err(RvmError::AlignmentError));
433 }
434
435 #[test]
436 fn create_unaligned_host_fails() {
437 let mut mgr = RegionManager::<8>::new();
438 let config = RegionConfig {
439 host_base: pa(0x2000_0001), ..default_config(1, 1, 0x1000, 0x2000_0000)
441 };
442 assert_eq!(mgr.create(config), Err(RvmError::AlignmentError));
443 }
444
445 #[test]
446 fn create_zero_pages_fails() {
447 let mut mgr = RegionManager::<8>::new();
448 let config = RegionConfig {
449 page_count: 0,
450 ..default_config(1, 1, 0x1000, 0x2000_0000)
451 };
452 assert_eq!(mgr.create(config), Err(RvmError::ResourceLimitExceeded));
453 }
454
455 #[test]
456 fn create_at_capacity_fails() {
457 let mut mgr = RegionManager::<2>::new();
458 mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
459 mgr.create(default_config(2, 2, 0x1000, 0x2_0000)).unwrap();
460 assert_eq!(
461 mgr.create(default_config(3, 3, 0x1000, 0x3_0000)),
462 Err(RvmError::ResourceLimitExceeded)
463 );
464 }
465
466 #[test]
467 fn overlap_same_partition_fails() {
468 let mut mgr = RegionManager::<8>::new();
469 mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
471 assert_eq!(
473 mgr.create(default_config(2, 1, 0x3000, 0x2_0000)),
474 Err(RvmError::MemoryOverlap)
475 );
476 }
477
478 #[test]
479 fn no_overlap_different_partitions() {
480 let mut mgr = RegionManager::<8>::new();
481 mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
483 mgr.create(default_config(2, 2, 0x1000, 0x2_0000)).unwrap();
484 assert_eq!(mgr.count(), 2);
485 }
486
487 #[test]
488 fn host_overlap_cross_partition_rejected() {
489 let mut mgr = RegionManager::<8>::new();
490 mgr.create(default_config(1, 1, 0x1000, 0x10_0000)).unwrap();
492 assert_eq!(
493 mgr.create(default_config(2, 2, 0x5000, 0x10_0000)),
494 Err(RvmError::MemoryOverlap)
495 );
496 }
497
498 #[test]
499 fn destroy_region() {
500 let mut mgr = RegionManager::<8>::new();
501 let id = mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
502 let destroyed = mgr.destroy(id).unwrap();
503 assert_eq!(destroyed.id, rid(1));
504 assert_eq!(mgr.count(), 0);
505 assert!(mgr.get(id).is_none());
506 }
507
508 #[test]
509 fn destroy_nonexistent_fails() {
510 let mut mgr = RegionManager::<8>::new();
511 assert_eq!(mgr.destroy(rid(99)), Err(RvmError::PartitionNotFound));
512 }
513
514 #[test]
515 fn transfer_ownership() {
516 let mut mgr = RegionManager::<8>::new();
517 let id = mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
518 assert_eq!(mgr.get(id).unwrap().owner, pid(1));
519
520 mgr.transfer(id, pid(2)).unwrap();
521 assert_eq!(mgr.get(id).unwrap().owner, pid(2));
522 }
523
524 #[test]
525 fn transfer_overlap_fails() {
526 let mut mgr = RegionManager::<8>::new();
527 let id = mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
528 mgr.create(default_config(2, 2, 0x1000, 0x2_0000)).unwrap();
530 assert_eq!(mgr.transfer(id, pid(2)), Err(RvmError::MemoryOverlap));
531 }
532
533 #[test]
534 fn translate_guest_to_host() {
535 let mut mgr = RegionManager::<8>::new();
536 mgr.create(default_config(1, 1, 0x1000, 0x2000_0000))
538 .unwrap();
539
540 let m = mgr.translate(pid(1), gpa(0x1000)).unwrap();
542 assert_eq!(m.host, pa(0x2000_0000));
543
544 let m = mgr.translate(pid(1), gpa(0x2000)).unwrap();
546 assert_eq!(m.host, pa(0x2000_1000));
547
548 assert!(mgr.translate(pid(1), gpa(0x5000)).is_none());
550
551 assert!(mgr.translate(pid(2), gpa(0x1000)).is_none());
553 }
554
555 #[test]
556 fn region_contains_guest() {
557 let region = OwnedRegion {
558 id: rid(1),
559 owner: pid(1),
560 guest_base: gpa(0x1000),
561 host_base: pa(0x2000_0000),
562 page_count: 4,
563 tier: Tier::Warm,
564 permissions: MemoryPermissions::READ_WRITE,
565 occupied: true,
566 };
567 assert!(region.contains_guest(gpa(0x1000)));
569 assert!(region.contains_guest(gpa(0x4FFF)));
570 assert!(!region.contains_guest(gpa(0x5000)));
571 assert!(!region.contains_guest(gpa(0x0FFF)));
572 }
573
574 #[test]
575 fn create_auto_id() {
576 let mut mgr = RegionManager::<8>::new();
577 let id1 = mgr
578 .create_auto_id(
579 pid(1),
580 gpa(0x1000),
581 pa(0x1_0000),
582 4,
583 Tier::Warm,
584 MemoryPermissions::READ_WRITE,
585 )
586 .unwrap();
587 let id2 = mgr
588 .create_auto_id(
589 pid(1),
590 gpa(0x5000),
591 pa(0x2_0000),
592 2,
593 Tier::Hot,
594 MemoryPermissions::READ_ONLY,
595 )
596 .unwrap();
597 assert_ne!(id1, id2);
598 assert_eq!(mgr.count(), 2);
599 }
600
601 #[test]
602 fn count_for_partition() {
603 let mut mgr = RegionManager::<8>::new();
604 mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
605 mgr.create(default_config(2, 1, 0x5000, 0x2_0000)).unwrap();
606 mgr.create(default_config(3, 2, 0x1000, 0x3_0000)).unwrap();
607
608 assert_eq!(mgr.count_for_partition(pid(1)), 2);
609 assert_eq!(mgr.count_for_partition(pid(2)), 1);
610 assert_eq!(mgr.count_for_partition(pid(3)), 0);
611 }
612
613 #[test]
614 fn regions_for_partition() {
615 let mut mgr = RegionManager::<8>::new();
616 mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
617 mgr.create(default_config(2, 1, 0x5000, 0x2_0000)).unwrap();
618 mgr.create(default_config(3, 2, 0x1000, 0x3_0000)).unwrap();
619
620 let mut buf = [OwnedRegionId::new(0); 4];
621 let n = mgr.regions_for_partition(pid(1), &mut buf);
622 assert_eq!(n, 2);
623 assert!(buf[..n].contains(&rid(1)));
624 assert!(buf[..n].contains(&rid(2)));
625 }
626
627 #[test]
628 fn destroy_then_create_reuses_slot() {
629 let mut mgr = RegionManager::<2>::new();
630 let id1 = mgr.create(default_config(1, 1, 0x1000, 0x1_0000)).unwrap();
631 mgr.create(default_config(2, 2, 0x1000, 0x2_0000)).unwrap();
632 assert!(mgr.create(default_config(3, 3, 0x1000, 0x3_0000)).is_err());
634
635 mgr.destroy(id1).unwrap();
637 mgr.create(default_config(3, 3, 0x1000, 0x3_0000)).unwrap();
638 assert_eq!(mgr.count(), 2);
639 }
640
641 #[test]
642 fn size_bytes_and_ends() {
643 let region = OwnedRegion {
644 id: rid(1),
645 owner: pid(1),
646 guest_base: gpa(0x1000),
647 host_base: pa(0x2000_0000),
648 page_count: 4,
649 tier: Tier::Warm,
650 permissions: MemoryPermissions::READ_WRITE,
651 occupied: true,
652 };
653 assert_eq!(region.size_bytes(), 4 * PAGE_SIZE as u64);
654 assert_eq!(region.guest_end(), 0x1000 + 4 * PAGE_SIZE as u64);
655 assert_eq!(region.host_end(), 0x2000_0000 + 4 * PAGE_SIZE as u64);
656 }
657}