Skip to content

Commit e476ae6

Browse files
committed
kernel/platform: Implement mmio_read and mmio_write for native platform
Introduce `mmio_write` and `mmio_read` for native platform. This is useful when developing or testing drivers that rely on MMIO, like virtio. It was not possible to implement these two function using generics because SvsmPlatform struct uses the `dyn` trait. It is also not possible to iterate and read byte by byte as this would violate the virtio spec, where is stated that all multi-byte long registers must be read in one operation, therefore making it impossible to implement a virtio driver. Signed-off-by: Luigi Leonardi <leonardi@redhat.com>
1 parent dfdf3bd commit e476ae6

1 file changed

Lines changed: 113 additions & 5 deletions

File tree

kernel/src/platform/native.rs

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,123 @@ impl SvsmPlatform for NativePlatform {
233233
Ok(())
234234
}
235235

236-
unsafe fn mmio_write(&self, _vaddr: VirtAddr, _data: &[u8]) -> Result<(), SvsmError> {
237-
unimplemented!()
236+
/// Perform a write to a memory-mapped IO area
237+
///
238+
/// This function expects data to be 1, 2, 4 or 8 bytes long.
239+
///
240+
/// It is not possible to loop and write one byte at a time because mmio devices (e.g., those emulated by QEMU)
241+
/// expect certain registers to be written with a single operation. Using a generic on SvsmPlatform is
242+
/// not possible because it uses the dyn trait.
243+
///
244+
/// # Safety
245+
///
246+
/// Caller must ensure that `vaddr` points to a properly aligned memory location and the
247+
/// memory accessed is part of a valid MMIO range.
248+
unsafe fn mmio_write(&self, vaddr: VirtAddr, data: &[u8]) -> Result<(), SvsmError> {
249+
match data.len() {
250+
1 => {
251+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
252+
unsafe {
253+
mmio_write_type::<u8>(vaddr, data);
254+
}
255+
}
256+
2 => {
257+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
258+
unsafe {
259+
mmio_write_type::<u16>(vaddr, data);
260+
}
261+
}
262+
4 => {
263+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
264+
unsafe {
265+
mmio_write_type::<u32>(vaddr, data);
266+
}
267+
}
268+
8 => {
269+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
270+
unsafe {
271+
mmio_write_type::<u64>(vaddr, data);
272+
}
273+
}
274+
_ => return Err(SvsmError::InvalidBytes),
275+
};
276+
277+
Ok(())
238278
}
239279

280+
/// Perform a read from a memory-mapped IO area
281+
///
282+
/// This function expects reads to be 1, 2, 4 or 8 bytes long.
283+
///
284+
/// It is not possible to loop and read one byte at a time because mmio devices (e.g., those emulated by QEMU)
285+
/// expect certain registers to be read with a single operation. Using a generic on SvsmPlatform is
286+
/// not possible because it uses the dyn trait.
287+
///
288+
/// # Safety
289+
///
290+
/// Caller must ensure that `vaddr` points to a properly aligned memory location and the
291+
/// memory accessed is part of a valid MMIO range.
240292
unsafe fn mmio_read(
241293
&self,
242-
_vaddr: VirtAddr,
243-
_data: &mut [MaybeUninit<u8>],
294+
vaddr: VirtAddr,
295+
data: &mut [MaybeUninit<u8>],
244296
) -> Result<(), SvsmError> {
245-
unimplemented!()
297+
match data.len() {
298+
1 => {
299+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
300+
unsafe {
301+
mmio_read_type::<u8>(vaddr, data);
302+
}
303+
}
304+
2 => {
305+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
306+
unsafe {
307+
mmio_read_type::<u16>(vaddr, data);
308+
}
309+
}
310+
4 => {
311+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
312+
unsafe {
313+
mmio_read_type::<u32>(vaddr, data);
314+
}
315+
}
316+
8 => {
317+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
318+
unsafe {
319+
mmio_read_type::<u64>(vaddr, data);
320+
}
321+
}
322+
_ => return Err(SvsmError::InvalidBytes),
323+
};
324+
325+
Ok(())
246326
}
247327
}
328+
329+
unsafe fn mmio_write_type<T: Copy>(vaddr: VirtAddr, data: &[u8]) {
330+
let data_ptr = data.as_ptr().cast::<T>();
331+
let ptr = vaddr.as_mut_ptr::<T>();
332+
333+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
334+
unsafe {
335+
if data_ptr.is_aligned() {
336+
ptr.write_volatile(data_ptr.read());
337+
} else {
338+
ptr.write_volatile(data_ptr.read_unaligned());
339+
}
340+
};
341+
}
342+
343+
unsafe fn mmio_read_type<T>(vaddr: VirtAddr, data: &mut [MaybeUninit<u8>]) {
344+
let data_ptr = data.as_mut_ptr().cast::<T>();
345+
let ptr = vaddr.as_mut_ptr::<T>();
346+
347+
// SAFETY: We are trusting the caller to ensure validity of `vaddr`.
348+
unsafe {
349+
if data_ptr.is_aligned() {
350+
data_ptr.write(ptr.read_volatile());
351+
} else {
352+
data_ptr.write_unaligned(ptr.read_volatile());
353+
}
354+
};
355+
}

0 commit comments

Comments
 (0)