1use std::os::fd::BorrowedFd;
4
5use thiserror::Error;
6
7use crate::VirtioMediaHostMemoryMapper;
8
9#[derive(Debug, PartialEq, Eq)]
10pub struct MmapBufferMapping {
11 num_mappings: usize,
13 guest_addr: u64,
15 rw: bool,
17}
18
19#[derive(Debug, PartialEq, Eq)]
21pub struct MmapBuffer {
22 offset: u32,
24 size: u32,
26 registered: bool,
31 mapping: Option<MmapBufferMapping>,
33}
34
35impl MmapBuffer {
36 fn new(offset: u32, size: u32) -> Self {
39 Self {
40 offset,
41 size,
42 registered: true,
43 mapping: None,
44 }
45 }
46}
47
48pub struct MmapMappingManager<M: VirtioMediaHostMemoryMapper> {
55 buffers: Vec<MmapBuffer>,
61 mapper: M,
63}
64
65impl<M: VirtioMediaHostMemoryMapper> From<M> for MmapMappingManager<M> {
66 fn from(mapper: M) -> Self {
67 Self {
68 buffers: Vec::new(),
69 mapper,
70 }
71 }
72}
73
74#[derive(Debug, Error, PartialEq, Eq)]
75pub enum RegisterBufferError {
76 #[error("insufficient free space in the MMAP range")]
77 NoFreeSpace,
78 #[error("requested offset is already occupied")]
79 OffsetOccupied,
80 #[error("buffers of size 0 cannot be registered")]
81 EmptyBuffer,
82 #[error("buffer offset must be a multiple of the memory page size")]
83 UnalignedOffset,
84}
85
86#[derive(Debug, Error, PartialEq, Eq)]
87pub enum CreateMappingError {
88 #[error("no buffer registered at the requested offset")]
89 InvalidOffset,
90 #[error("cannot create new mappings for unregistered buffers")]
91 UnregisteredBuffer,
92 #[error("requested mapping range goes outside the buffer")]
93 SizeOutOfBounds,
94 #[error("error while cloning the FD for the buffer")]
95 FdCloneFailure(std::io::ErrorKind),
96 #[error("error while mapping the buffer: {0}")]
97 MappingFailure(i32),
98 #[error("mapping requested with different permission from the old one")]
99 NonMatchingPermissions,
100}
101
102#[derive(Debug, Error, PartialEq, Eq)]
103pub enum RemoveMappingError {
104 #[error("no buffer registered at the requested offset")]
105 InvalidOffset,
106}
107
108const PAGE_SIZE: u32 = 0x1000;
109const PAGE_MASK: u32 = !(PAGE_SIZE - 1);
110
111impl<M: VirtioMediaHostMemoryMapper> MmapMappingManager<M> {
112 pub fn register_buffer(
127 &mut self,
128 offset: Option<u32>,
129 size: u32,
130 ) -> Result<u32, RegisterBufferError> {
131 let offset = offset.unwrap_or_else(|| {
132 self.buffers
133 .last()
134 .map(|b| (b.offset + 1).next_multiple_of(PAGE_SIZE))
137 .unwrap_or(0)
138 });
139
140 self.register_buffer_by_offset(offset, size)
141 .map(|()| offset)
142 }
143
144 pub fn unregister_buffer(&mut self, offset: u32) -> bool {
147 match self.buffers.binary_search_by_key(&offset, |b| b.offset) {
148 Err(_) => false,
149 Ok(index) => {
150 let buffer = &mut self.buffers[index];
151
152 buffer.registered = false;
153 if buffer.mapping.is_none() {
155 self.buffers.remove(index);
156 }
157
158 true
159 }
160 }
161 }
162
163 fn register_buffer_by_offset(
168 &mut self,
169 offset: u32,
170 size: u32,
171 ) -> Result<(), RegisterBufferError> {
172 if size == 0 {
173 return Err(RegisterBufferError::EmptyBuffer);
174 }
175 if offset & PAGE_MASK != offset {
176 return Err(RegisterBufferError::UnalignedOffset);
177 }
178
179 match self.buffers.binary_search_by_key(&offset, |b| b.offset) {
181 Ok(_) => Err(RegisterBufferError::OffsetOccupied),
183 Err(index) => {
184 self.buffers.insert(index, MmapBuffer::new(offset, size));
185 Ok(())
186 }
187 }
188 }
189
190 pub fn create_mapping(
201 &mut self,
202 offset: u32,
203 fd: BorrowedFd,
204 rw: bool,
205 ) -> Result<(u64, u64), CreateMappingError> {
206 let buffer = self
207 .buffers
208 .binary_search_by_key(&offset, |b| b.offset)
209 .map(|i| &mut self.buffers[i])
210 .map_err(|_| CreateMappingError::InvalidOffset)?;
211 let last_buffer_address = buffer
212 .offset
213 .checked_add(buffer.size - 1)
214 .ok_or(CreateMappingError::InvalidOffset)?;
215
216 if !buffer.registered {
218 return Err(CreateMappingError::UnregisteredBuffer);
219 }
220
221 if last_buffer_address > buffer.offset + (buffer.size - 1) {
223 return Err(CreateMappingError::SizeOutOfBounds);
224 }
225
226 let guest_addr = match &mut buffer.mapping {
227 None => {
228 let guest_addr = self
229 .mapper
230 .add_mapping(
231 fd,
232 buffer.size as u64,
235 buffer.offset as u64,
236 rw,
237 )
238 .map_err(CreateMappingError::MappingFailure)?;
239
240 buffer.mapping = Some(MmapBufferMapping {
241 num_mappings: 1,
242 rw,
243 guest_addr,
244 });
245
246 guest_addr
249 }
250 Some(mapping) => {
251 if mapping.rw != rw {
252 return Err(CreateMappingError::NonMatchingPermissions);
253 }
254 mapping.num_mappings += 1;
255 mapping.guest_addr
256 }
257 };
258
259 Ok((guest_addr, buffer.size as u64))
260 }
261
262 pub fn remove_mapping(&mut self, guest_addr: u64) -> Result<bool, RemoveMappingError> {
264 for (i, buffer) in self.buffers.iter_mut().enumerate() {
266 match &mut buffer.mapping {
267 Some(mapping) if mapping.guest_addr == guest_addr => {
268 mapping.num_mappings -= 1;
269 if mapping.num_mappings == 0 {
270 if let Err(e) = self.mapper.remove_mapping(guest_addr) {
271 log::error!("error while unmapping MMAP buffer: {:#}", e);
272 }
273 buffer.mapping = None;
274 if !buffer.registered {
277 self.buffers.remove(i);
278 }
279 return Ok(false);
280 } else {
281 return Ok(true);
282 }
283 }
284 _ => (),
285 }
286 }
287
288 Err(RemoveMappingError::InvalidOffset)
289 }
290 pub fn is_mapped(&self, offset: u64) -> bool {
292 let Ok(offset) = u32::try_from(offset) else {
293 return false;
294 };
295
296 match self.buffers.binary_search_by_key(&offset, |b| b.offset) {
297 Err(_) => false,
298 Ok(index) => self.buffers[index].mapping.is_some(),
299 }
300 }
301
302 pub fn into_mapper(self) -> M {
304 self.mapper
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use std::fs::File;
311 use std::os::fd::AsFd;
312 use std::os::fd::BorrowedFd;
313 use std::os::fd::FromRawFd;
314
315 use crate::VirtioMediaHostMemoryMapper;
316
317 use super::CreateMappingError;
318 use super::MmapBuffer;
319 use super::MmapBufferMapping;
320 use super::MmapMappingManager;
321 use super::RegisterBufferError;
322 use super::RemoveMappingError;
323
324 struct DummyHostMemoryMapper;
325
326 impl VirtioMediaHostMemoryMapper for DummyHostMemoryMapper {
327 fn add_mapping(
328 &mut self,
329 _buffer: BorrowedFd,
330 _length: u64,
331 offset: u64,
332 _rw: bool,
333 ) -> Result<u64, i32> {
334 Ok(offset | 0x8000_0000)
335 }
336
337 fn remove_mapping(&mut self, _guest_addr: u64) -> Result<(), i32> {
338 Ok(())
339 }
340 }
341
342 #[test]
343 fn mmap_manager_register_by_offset() {
344 let mut mm = MmapMappingManager::from(DummyHostMemoryMapper);
345 assert_eq!(mm.buffers, vec![]);
346
347 assert_eq!(mm.register_buffer_by_offset(0x0, 0x1000), Ok(()));
348 assert_eq!(mm.buffers, vec![MmapBuffer::new(0x0, 0x1000)]);
349
350 assert_eq!(mm.register_buffer_by_offset(0x1000, 0x5000), Ok(()));
351 assert_eq!(
352 mm.buffers,
353 vec![
354 MmapBuffer::new(0x0, 0x1000),
355 MmapBuffer::new(0x1000, 0x5000),
356 ]
357 );
358
359 assert_eq!(mm.register_buffer_by_offset(0xa000, 0x1000), Ok(()));
360 assert_eq!(
361 mm.buffers,
362 vec![
363 MmapBuffer::new(0x0, 0x1000),
364 MmapBuffer::new(0x1000, 0x5000),
365 MmapBuffer::new(0xa000, 0x1000),
366 ]
367 );
368
369 assert_eq!(mm.register_buffer_by_offset(0x6000, 0x2000), Ok(()));
370 assert_eq!(
371 mm.buffers,
372 vec![
373 MmapBuffer::new(0x0, 0x1000),
374 MmapBuffer::new(0x1000, 0x5000),
375 MmapBuffer::new(0x6000, 0x2000),
376 MmapBuffer::new(0xa000, 0x1000),
377 ]
378 );
379
380 assert_eq!(
381 mm.register_buffer_by_offset(0x8000, 0x0),
382 Err(RegisterBufferError::EmptyBuffer)
383 );
384
385 assert_eq!(
386 mm.register_buffer_by_offset(0x8100, 0x1000),
387 Err(RegisterBufferError::UnalignedOffset)
388 );
389
390 assert_eq!(
391 mm.register_buffer_by_offset(0x0, 0x1000),
392 Err(RegisterBufferError::OffsetOccupied)
393 );
394
395 assert_eq!(
396 mm.register_buffer_by_offset(0x1000, 0x1000),
397 Err(RegisterBufferError::OffsetOccupied)
398 );
399
400 assert_eq!(mm.register_buffer_by_offset(0x2000, 0x1000), Ok(()));
401 assert_eq!(
402 mm.buffers,
403 vec![
404 MmapBuffer::new(0x0, 0x1000),
405 MmapBuffer::new(0x1000, 0x5000),
406 MmapBuffer::new(0x2000, 0x1000),
407 MmapBuffer::new(0x6000, 0x2000),
408 MmapBuffer::new(0xa000, 0x1000),
409 ]
410 );
411
412 assert_eq!(mm.register_buffer_by_offset(0x7000, 0x2000), Ok(()));
413 assert_eq!(
414 mm.buffers,
415 vec![
416 MmapBuffer::new(0x0, 0x1000),
417 MmapBuffer::new(0x1000, 0x5000),
418 MmapBuffer::new(0x2000, 0x1000),
419 MmapBuffer::new(0x6000, 0x2000),
420 MmapBuffer::new(0x7000, 0x2000),
421 MmapBuffer::new(0xa000, 0x1000),
422 ]
423 );
424
425 assert_eq!(mm.register_buffer_by_offset(0x8000, 0x2000), Ok(()));
426 assert_eq!(
427 mm.buffers,
428 vec![
429 MmapBuffer::new(0x0, 0x1000),
430 MmapBuffer::new(0x1000, 0x5000),
431 MmapBuffer::new(0x2000, 0x1000),
432 MmapBuffer::new(0x6000, 0x2000),
433 MmapBuffer::new(0x7000, 0x2000),
434 MmapBuffer::new(0x8000, 0x2000),
435 MmapBuffer::new(0xa000, 0x1000),
436 ]
437 );
438
439 assert_eq!(mm.register_buffer_by_offset(0xffff_f000, 0x1000), Ok(()));
440 assert_eq!(
441 mm.buffers,
442 vec![
443 MmapBuffer::new(0x0, 0x1000),
444 MmapBuffer::new(0x1000, 0x5000),
445 MmapBuffer::new(0x2000, 0x1000),
446 MmapBuffer::new(0x6000, 0x2000),
447 MmapBuffer::new(0x7000, 0x2000),
448 MmapBuffer::new(0x8000, 0x2000),
449 MmapBuffer::new(0xa000, 0x1000),
450 MmapBuffer::new(0xffff_f000, 0x1000),
451 ]
452 );
453
454 assert!(mm.unregister_buffer(0xffff_f000));
455 assert_eq!(
456 mm.buffers,
457 vec![
458 MmapBuffer::new(0x0, 0x1000),
459 MmapBuffer::new(0x1000, 0x5000),
460 MmapBuffer::new(0x2000, 0x1000),
461 MmapBuffer::new(0x6000, 0x2000),
462 MmapBuffer::new(0x7000, 0x2000),
463 MmapBuffer::new(0x8000, 0x2000),
464 MmapBuffer::new(0xa000, 0x1000),
465 ]
466 );
467
468 assert!(mm.unregister_buffer(0x6000));
469 assert_eq!(
470 mm.buffers,
471 vec![
472 MmapBuffer::new(0x0, 0x1000),
473 MmapBuffer::new(0x1000, 0x5000),
474 MmapBuffer::new(0x2000, 0x1000),
475 MmapBuffer::new(0x7000, 0x2000),
476 MmapBuffer::new(0x8000, 0x2000),
477 MmapBuffer::new(0xa000, 0x1000),
478 ]
479 );
480
481 assert!(!mm.unregister_buffer(0x6000));
482 assert_eq!(
483 mm.buffers,
484 vec![
485 MmapBuffer::new(0x0, 0x1000),
486 MmapBuffer::new(0x1000, 0x5000),
487 MmapBuffer::new(0x2000, 0x1000),
488 MmapBuffer::new(0x7000, 0x2000),
489 MmapBuffer::new(0x8000, 0x2000),
490 MmapBuffer::new(0xa000, 0x1000),
491 ]
492 );
493
494 assert!(!mm.unregister_buffer(0x8100));
495 assert_eq!(
496 mm.buffers,
497 vec![
498 MmapBuffer::new(0x0, 0x1000),
499 MmapBuffer::new(0x1000, 0x5000),
500 MmapBuffer::new(0x2000, 0x1000),
501 MmapBuffer::new(0x7000, 0x2000),
502 MmapBuffer::new(0x8000, 0x2000),
503 MmapBuffer::new(0xa000, 0x1000),
504 ]
505 );
506
507 assert!(mm.unregister_buffer(0x0));
508 assert_eq!(
509 mm.buffers,
510 vec![
511 MmapBuffer::new(0x1000, 0x5000),
512 MmapBuffer::new(0x2000, 0x1000),
513 MmapBuffer::new(0x7000, 0x2000),
514 MmapBuffer::new(0x8000, 0x2000),
515 MmapBuffer::new(0xa000, 0x1000),
516 ]
517 );
518
519 assert!(mm.unregister_buffer(0xa000));
520 assert_eq!(
521 mm.buffers,
522 vec![
523 MmapBuffer::new(0x1000, 0x5000),
524 MmapBuffer::new(0x2000, 0x1000),
525 MmapBuffer::new(0x7000, 0x2000),
526 MmapBuffer::new(0x8000, 0x2000),
527 ]
528 );
529
530 assert!(mm.unregister_buffer(0x1000));
531 assert_eq!(
532 mm.buffers,
533 vec![
534 MmapBuffer::new(0x2000, 0x1000),
535 MmapBuffer::new(0x7000, 0x2000),
536 MmapBuffer::new(0x8000, 0x2000),
537 ]
538 );
539
540 assert!(mm.unregister_buffer(0x8000));
541 assert_eq!(
542 mm.buffers,
543 vec![
544 MmapBuffer::new(0x2000, 0x1000),
545 MmapBuffer::new(0x7000, 0x2000),
546 ]
547 );
548
549 assert_eq!(
550 mm.buffers,
551 vec![
552 MmapBuffer::new(0x2000, 0x1000),
553 MmapBuffer::new(0x7000, 0x2000),
554 ]
555 );
556 }
557
558 #[test]
559 fn mmap_manager_register() {
560 let mut mm = MmapMappingManager::from(DummyHostMemoryMapper);
561
562 assert_eq!(mm.register_buffer(None, 0x1000), Ok(0x0));
563 assert_eq!(mm.buffers, vec![MmapBuffer::new(0x0, 0x1000)]);
564
565 assert_eq!(mm.register_buffer(None, 0x5000), Ok(0x1000));
566 assert_eq!(
567 mm.buffers,
568 vec![
569 MmapBuffer::new(0x0, 0x1000),
570 MmapBuffer::new(0x1000, 0x5000),
571 ]
572 );
573
574 assert_eq!(mm.register_buffer(None, 0xffff_a000), Ok(0x2000));
575 assert_eq!(
576 mm.buffers,
577 vec![
578 MmapBuffer::new(0x0, 0x1000),
579 MmapBuffer::new(0x1000, 0x5000),
580 MmapBuffer::new(0x2000, 0xffff_a000),
581 ]
582 );
583
584 assert!(mm.unregister_buffer(0x2000));
585 assert_eq!(
586 mm.buffers,
587 vec![
588 MmapBuffer::new(0x0, 0x1000),
589 MmapBuffer::new(0x1000, 0x5000),
590 ]
591 );
592
593 assert_eq!(mm.register_buffer(None, 0xffff_b000), Ok(0x2000));
594 assert_eq!(
595 mm.buffers,
596 vec![
597 MmapBuffer::new(0x0, 0x1000),
598 MmapBuffer::new(0x1000, 0x5000),
599 MmapBuffer::new(0x2000, 0xffff_b000),
600 ]
601 );
602 }
603
604 #[test]
605 fn mmap_manager_mapping() {
606 let mut mm = MmapMappingManager::from(DummyHostMemoryMapper);
607
608 assert_eq!(mm.register_buffer(None, 0x1000), Ok(0x0));
609 assert_eq!(mm.register_buffer(None, 0x5000), Ok(0x1000));
610 assert_eq!(
611 mm.buffers,
612 vec![
613 MmapBuffer::new(0x0, 0x1000),
614 MmapBuffer::new(0x1000, 0x5000),
615 ]
616 );
617
618 let file = unsafe { File::from_raw_fd(0) };
619
620 assert_eq!(
622 mm.create_mapping(0x1000, file.as_fd(), false),
623 Ok((0x8000_1000, 0x5000))
624 );
625 assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
626 assert_eq!(
627 mm.remove_mapping(0x8000_1000),
628 Err(RemoveMappingError::InvalidOffset)
629 );
630
631 assert_eq!(
633 mm.create_mapping(0x1000, file.as_fd(), false),
634 Ok((0x8000_1000, 0x5000))
635 );
636 assert_eq!(
637 mm.create_mapping(0x1000, file.as_fd(), false),
638 Ok((0x8000_1000, 0x5000))
639 );
640 assert_eq!(mm.remove_mapping(0x8000_1000), Ok(true));
641 assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
642 assert_eq!(
643 mm.remove_mapping(0x8000_1000),
644 Err(RemoveMappingError::InvalidOffset)
645 );
646
647 assert_eq!(
649 mm.create_mapping(0x2000, file.as_fd(), false),
650 Err(CreateMappingError::InvalidOffset)
651 );
652
653 assert_eq!(
655 mm.create_mapping(0x1000, file.as_fd(), false),
656 Ok((0x8000_1000, 0x5000))
657 );
658 assert_eq!(
659 mm.create_mapping(0x1000, file.as_fd(), true),
660 Err(CreateMappingError::NonMatchingPermissions)
661 );
662 assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
663
664 assert_eq!(
666 mm.create_mapping(0x1000, file.as_fd(), false),
667 Ok((0x8000_1000, 0x5000))
668 );
669 assert!(mm.unregister_buffer(0x1000));
670 assert_eq!(
671 mm.buffers,
672 vec![
673 MmapBuffer::new(0x0, 0x1000),
674 MmapBuffer {
675 offset: 0x1000,
676 size: 0x5000,
677 registered: false,
678 mapping: Some(MmapBufferMapping {
679 num_mappings: 1,
680 guest_addr: 0x8000_1000,
681 rw: false
682 })
683 }
684 ]
685 );
686 assert_eq!(mm.remove_mapping(0x8000_1000), Ok(false));
688 assert_eq!(mm.buffers, vec![MmapBuffer::new(0x0, 0x1000),])
689 }
690}