1mod allocator;
6pub(crate) mod util;
7
8use core::{
9 marker::PhantomData,
10 ops::{Deref, Range},
11};
12
13use align_ext::AlignExt;
14use inherit_methods_macro::inherit_methods;
15
16pub(crate) use self::allocator::IoMemAllocatorBuilder;
17pub(super) use self::allocator::init;
18#[cfg(all(target_arch = "x86_64", feature = "cvm_guest"))]
19use crate::arch::{if_tdx_enabled, tdx_guest::unprotect_gpa_tdvm_call};
20use crate::{
21 Error,
22 arch::io::io_mem::{read_once, write_once},
23 cpu::{AtomicCpuSet, CpuSet},
24 mm::{
25 Fallible, Infallible, PAGE_SIZE, PodOnce, VmIo, VmIoFill, VmIoOnce, VmReader, VmWriter,
26 io::{
27 Io,
28 copy::{memcpy, memset},
29 },
30 kspace::kvirt_area::KVirtArea,
31 page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags},
32 tlb::{TlbFlushOp, TlbFlusher},
33 },
34 prelude::*,
35 task::disable_preempt,
36};
37
38#[derive(Clone, Debug)]
41pub(crate) enum Sensitive {}
42
43#[derive(Clone, Debug)]
46pub enum Insensitive {}
47
48#[derive(Clone, Debug)]
50pub struct IoMem<SecuritySensitivity = Insensitive> {
51 kvirt_area: Arc<KVirtArea>,
52 offset: usize,
54 limit: usize,
55 pa: Paddr,
56 cache_policy: CachePolicy,
57 phantom: PhantomData<SecuritySensitivity>,
58}
59
60impl<SecuritySensitivity> IoMem<SecuritySensitivity> {
61 pub fn slice(&self, range: Range<usize>) -> Self {
67 assert!(!range.is_empty() && range.end <= self.limit);
69
70 Self {
72 kvirt_area: self.kvirt_area.clone(),
73 offset: self.offset + range.start,
74 limit: range.len(),
75 pa: self.pa + range.start,
76 cache_policy: self.cache_policy,
77 phantom: PhantomData,
78 }
79 }
80
81 pub(crate) unsafe fn new(range: Range<Paddr>, flags: PageFlags, cache: CachePolicy) -> Self {
92 let first_page_start = range.start.align_down(PAGE_SIZE);
93 let last_page_end = range.end.align_up(PAGE_SIZE);
94
95 let frames_range = first_page_start..last_page_end;
96 let area_size = frames_range.len();
97
98 #[cfg(target_arch = "x86_64")]
99 let priv_flags = if_tdx_enabled!({
100 assert!(
101 first_page_start == range.start && last_page_end == range.end,
102 "I/O memory is not page aligned, which cannot be unprotected in TDX: {:#x?}..{:#x?}",
103 range.start,
104 range.end,
105 );
106
107 unsafe { unprotect_gpa_tdvm_call(first_page_start, area_size).unwrap() };
115
116 PrivilegedPageFlags::SHARED
117 } else {
118 PrivilegedPageFlags::empty()
119 });
120 #[cfg(not(target_arch = "x86_64"))]
121 let priv_flags = PrivilegedPageFlags::empty();
122
123 let prop = PageProperty {
124 flags,
125 cache,
126 priv_flags,
127 };
128
129 let kva = {
130 let kva = unsafe { KVirtArea::map_untracked_frames(area_size, 0, frames_range, prop) };
133
134 let target_cpus = AtomicCpuSet::new(CpuSet::new_full());
135 let mut flusher = TlbFlusher::new(&target_cpus, disable_preempt());
136 flusher.issue_tlb_flush(TlbFlushOp::for_range(kva.range()));
137 flusher.dispatch_tlb_flush();
138 flusher.sync_tlb_flush();
139
140 kva
141 };
142
143 Self {
144 kvirt_area: Arc::new(kva),
145 offset: range.start - first_page_start,
146 limit: range.len(),
147 pa: range.start,
148 cache_policy: cache,
149 phantom: PhantomData,
150 }
151 }
152
153 pub fn cache_policy(&self) -> CachePolicy {
155 self.cache_policy
156 }
157
158 fn base(&self) -> usize {
160 self.kvirt_area.deref().start() + self.offset
161 }
162
163 fn check_range(&self, offset: usize, len: usize) -> Result<()> {
165 if offset.checked_add(len).is_none_or(|end| end > self.limit) {
166 return Err(Error::InvalidArgs);
167 }
168 Ok(())
169 }
170}
171
172#[cfg_attr(
173 any(target_arch = "loongarch64", target_arch = "aarch64"),
174 expect(unused)
175)]
176impl IoMem<Sensitive> {
177 pub(crate) unsafe fn read_once<T: PodOnce>(&self, offset: usize) -> T {
190 debug_assert!(offset + size_of::<T>() <= self.limit);
191 let ptr = (self.kvirt_area.deref().start() + self.offset + offset) as *const T;
192 unsafe { read_once(ptr) }
194 }
195
196 pub(crate) unsafe fn write_once<T: PodOnce>(&self, offset: usize, value: &T) {
209 debug_assert!(offset + size_of::<T>() <= self.limit);
210 let ptr = (self.kvirt_area.deref().start() + self.offset + offset) as *mut T;
211 unsafe { write_once(ptr, *value) };
213 }
214}
215
216impl IoMem<Insensitive> {
217 pub fn acquire(range: Range<Paddr>) -> Result<IoMem<Insensitive>> {
221 Self::acquire_with_cache_policy(range, CachePolicy::Uncacheable)
222 }
223
224 pub fn acquire_with_cache_policy(
226 range: Range<Paddr>,
227 cache_policy: CachePolicy,
228 ) -> Result<IoMem<Insensitive>> {
229 allocator::IO_MEM_ALLOCATOR
230 .get()
231 .unwrap()
232 .acquire(range, cache_policy)
233 .ok_or(Error::AccessDenied)
234 }
235
236 pub fn read_fallible(
241 &self,
242 offset: usize,
243 writer: &mut VmWriter,
244 ) -> Result<usize, (Error, usize)> {
245 let len = writer.avail();
246 self.check_range(offset, len).map_err(|err| (err, 0))?;
247
248 let src = (self.base() + offset) as *const u8;
249 let copied = unsafe { memcpy::<Fallible, Io>(writer.cursor(), src, len) };
252 writer.skip(copied);
253
254 if copied < len {
255 Err((Error::PageFault, copied))
256 } else {
257 Ok(copied)
258 }
259 }
260
261 pub fn write_fallible(
266 &self,
267 offset: usize,
268 reader: &mut VmReader,
269 ) -> Result<usize, (Error, usize)> {
270 let len = reader.remain();
271 self.check_range(offset, len).map_err(|err| (err, 0))?;
272
273 let dst = (self.base() + offset) as *mut u8;
274 let copied = unsafe { memcpy::<Io, Fallible>(dst, reader.cursor(), len) };
277 reader.skip(copied);
278
279 if copied < len {
280 Err((Error::PageFault, copied))
281 } else {
282 Ok(copied)
283 }
284 }
285}
286
287impl VmIoOnce for IoMem<Insensitive> {
288 fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T> {
289 self.check_range(offset, size_of::<T>())?;
290 let ptr = (self.base() + offset) as *const T;
291 if !ptr.is_aligned() {
292 return Err(Error::InvalidArgs);
293 }
294
295 let val = unsafe { read_once(ptr) };
297 Ok(val)
298 }
299
300 fn write_once<T: PodOnce>(&self, offset: usize, value: &T) -> Result<()> {
301 self.check_range(offset, size_of::<T>())?;
302 let ptr = (self.base() + offset) as *mut T;
303 if !ptr.is_aligned() {
304 return Err(Error::InvalidArgs);
305 }
306
307 unsafe { write_once(ptr, *value) };
309 Ok(())
310 }
311}
312
313impl VmIo for IoMem<Insensitive> {
314 fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
315 let len = writer.avail();
316 self.check_range(offset, len)?;
317
318 let src = (self.base() + offset) as *const u8;
319 let copied = unsafe { memcpy::<Fallible, Io>(writer.cursor(), src, len) };
322 if copied < len {
323 return Err(Error::PageFault);
324 }
325
326 writer.skip(copied);
327 Ok(())
328 }
329
330 fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
331 let len = buf.len();
332 self.check_range(offset, len)?;
333 let src = (self.base() + offset) as *const u8;
334 let dst = buf.as_mut_ptr();
335
336 unsafe { memcpy::<Infallible, Io>(dst, src, len) };
338 Ok(())
339 }
340
341 fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
342 let len = reader.remain();
343 self.check_range(offset, len)?;
344
345 let dst = (self.base() + offset) as *mut u8;
346 let copied = unsafe { memcpy::<Io, Fallible>(dst, reader.cursor(), len) };
349 if copied < len {
350 return Err(Error::PageFault);
351 }
352
353 reader.skip(copied);
354 Ok(())
355 }
356
357 fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
358 let len = buf.len();
359 self.check_range(offset, len)?;
360 let src = buf.as_ptr();
361 let dst = (self.base() + offset) as *mut u8;
362
363 unsafe { memcpy::<Io, Infallible>(dst, src, len) };
365 Ok(())
366 }
367}
368
369impl VmIoFill for IoMem<Insensitive> {
370 fn fill_zeros(&self, offset: usize, len: usize) -> Result<(), (Error, usize)> {
371 if len == 0 {
372 return Ok(());
373 }
374
375 if offset > self.limit {
376 return Err((Error::InvalidArgs, 0));
377 }
378
379 let available = self.limit - offset;
380 let write_len = core::cmp::min(len, available);
381 if write_len == 0 {
382 return Err((Error::InvalidArgs, 0));
383 }
384
385 let dst = (self.base() + offset) as *mut u8;
386 unsafe { memset::<Io>(dst, 0u8, write_len) };
388
389 if write_len == len {
390 Ok(())
391 } else {
392 Err((Error::InvalidArgs, write_len))
393 }
394 }
395}
396
397macro_rules! impl_vm_io_pointer {
398 ($ty:ty, $from:tt) => {
399 #[inherit_methods(from = $from)]
400 impl VmIo for $ty {
401 fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()>;
402 fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()>;
403 }
404
405 #[inherit_methods(from = $from)]
406 impl VmIoOnce for $ty {
407 fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T>;
408 fn write_once<T: PodOnce>(&self, offset: usize, value: &T) -> Result<()>;
409 }
410
411 #[inherit_methods(from = $from)]
412 impl VmIoFill for $ty {
413 fn fill_zeros(&self, offset: usize, len: usize) -> Result<(), (Error, usize)>;
414 }
415 };
416}
417
418impl_vm_io_pointer!(&IoMem<Insensitive>, "(**self)");
419impl_vm_io_pointer!(&mut IoMem<Insensitive>, "(**self)");
420
421impl<SecuritySensitivity> HasPaddr for IoMem<SecuritySensitivity> {
422 fn paddr(&self) -> Paddr {
423 self.pa
424 }
425}
426
427impl<SecuritySensitivity> HasSize for IoMem<SecuritySensitivity> {
428 fn size(&self) -> usize {
429 self.limit
430 }
431}
432
433impl<SecuritySensitivity> Drop for IoMem<SecuritySensitivity> {
434 fn drop(&mut self) {
435 }
439}
440
441#[cfg(ktest)]
442mod test {
443 use crate::{
444 arch::io::io_mem::{copy_from_mmio, copy_to_mmio, read_once, write_once},
445 prelude::ktest,
446 };
447
448 #[ktest]
449 fn read_write_u8() {
450 let mut data: u8 = 0;
451 unsafe {
453 write_once(&mut data, 42u8);
454 assert_eq!(read_once(&data), 42u8);
455 }
456 }
457
458 #[ktest]
459 fn read_write_u16() {
460 let mut data: u16 = 0;
461 let val: u16 = 0x1234;
462 unsafe {
464 write_once(&mut data, val);
465 assert_eq!(read_once(&data), val);
466 }
467 }
468
469 #[ktest]
470 fn read_write_u32() {
471 let mut data: u32 = 0;
472 let val: u32 = 0x12345678;
473 unsafe {
475 write_once(&mut data, val);
476 assert_eq!(read_once(&data), val);
477 }
478 }
479
480 #[ktest]
481 fn read_write_u64() {
482 let mut data: u64 = 0;
483 let val: u64 = 0xDEADBEEFCAFEBABE;
484 unsafe {
486 write_once(&mut data, val);
487 assert_eq!(read_once(&data), val);
488 }
489 }
490
491 #[ktest]
492 fn boundary_overlap() {
493 let mut data: [u8; 2] = [0xAA, 0xBB];
494 unsafe {
496 write_once(&mut data[0], 0x11u8);
497 assert_eq!(data[0], 0x11);
498 assert_eq!(data[1], 0xBB);
499 }
500 }
501
502 fn fill_pattern(buf: &mut [u8]) {
503 for (idx, byte) in buf.iter_mut().enumerate() {
504 *byte = (idx as u8).wrapping_mul(3).wrapping_add(1);
505 }
506 }
507
508 fn run_copy_from_case(src_offset: usize, dst_offset: usize, len: usize) {
509 let mut src = [0u8; 64];
510 let mut dst = [0u8; 64];
511 fill_pattern(&mut src);
512
513 let src_ptr = unsafe { src.as_ptr().add(src_offset) };
515 let dst_ptr = unsafe { dst.as_mut_ptr().add(dst_offset) };
517
518 unsafe { copy_from_mmio(dst_ptr, src_ptr, len) };
520
521 assert_eq!(
522 &dst[dst_offset..dst_offset + len],
523 &src[src_offset..src_offset + len]
524 );
525 }
526
527 fn run_copy_to_case(src_offset: usize, dst_offset: usize, len: usize) {
528 let mut src = [0u8; 64];
529 let mut dst = [0u8; 64];
530 fill_pattern(&mut src);
531
532 let src_ptr = unsafe { src.as_ptr().add(src_offset) };
534 let dst_ptr = unsafe { dst.as_mut_ptr().add(dst_offset) };
536
537 unsafe { copy_to_mmio(src_ptr, dst_ptr, len) };
539
540 assert_eq!(
541 &dst[dst_offset..dst_offset + len],
542 &src[src_offset..src_offset + len]
543 );
544 }
545
546 #[ktest]
547 fn copy_from_alignment_and_sizes() {
548 let word_size = size_of::<usize>();
549 let sizes = [
550 0,
551 1,
552 word_size.saturating_sub(1),
553 word_size,
554 word_size + 1,
555 word_size * 2 + 3,
556 ];
557 let offsets = [0, 1, 2];
558
559 for &len in &sizes {
560 for &src_offset in &offsets {
561 for &dst_offset in &offsets {
562 if src_offset + len <= 64 && dst_offset + len <= 64 {
563 run_copy_from_case(src_offset, dst_offset, len);
564 }
565 }
566 }
567 }
568 }
569
570 #[ktest]
571 fn copy_to_alignment_and_sizes() {
572 let word_size = size_of::<usize>();
573 let sizes = [
574 0,
575 1,
576 word_size.saturating_sub(1),
577 word_size,
578 word_size + 1,
579 word_size * 2 + 3,
580 ];
581 let offsets = [0, 1, 2];
582
583 for &len in &sizes {
584 for &src_offset in &offsets {
585 for &dst_offset in &offsets {
586 if src_offset + len <= 64 && dst_offset + len <= 64 {
587 run_copy_to_case(src_offset, dst_offset, len);
588 }
589 }
590 }
591 }
592 }
593}