Implement `DmaStream::sync` on RISC-V platforms

This commit is contained in:
Zejun Zhao 2025-08-03 18:13:40 +08:00 committed by Tate, Hongliang Tian
parent 3353e53577
commit bfcb1d2c00
5 changed files with 78 additions and 27 deletions

View File

@ -224,6 +224,9 @@ fn build_kernel_elf(
// It makes running on Intel CPUs after Ivy Bridge (2012) faster, but much slower
// on older CPUs.
rustflags.push("-C target-feature=+ermsb");
} else if matches!(arch, Arch::RiscV64) {
// Enable the Zicbom extension for RISC-V to support cache block operations.
rustflags.push("-C target-feature=+zicbom");
}
let mut command = cargo();

View File

@ -7,7 +7,7 @@ use crate::{
mm::{
page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags as PrivFlags},
page_table::PageTableEntryTrait,
Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE,
DmaDirection, Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE,
},
Pod,
};
@ -102,6 +102,10 @@ pub(crate) fn tlb_flush_all_including_global() {
}
}
pub(crate) fn sync_dma_range(_range: Range<Vaddr>, _direction: DmaDirection) {
todo!("Implement DMA synchronization for LoongArch64 architecture");
}
/// Activates the given level 4 page table.
///
/// "pgdl" or "pgdh" register doesn't have a field that encodes the cache policy,

View File

@ -3,12 +3,17 @@
use alloc::fmt;
use core::ops::Range;
use spin::Once;
use crate::{
arch::cpu::extension::{has_extensions, IsaExtensions},
arch::{
boot::DEVICE_TREE,
cpu::extension::{has_extensions, IsaExtensions},
},
mm::{
page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags as PrivFlags},
page_table::PageTableEntryTrait,
Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE,
DmaDirection, Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE,
},
Pod,
};
@ -83,6 +88,50 @@ pub(crate) fn tlb_flush_all_including_global() {
riscv::asm::sfence_vma_all()
}
pub(crate) fn sync_dma_range(range: Range<Vaddr>, direction: DmaDirection) {
if has_extensions(IsaExtensions::ZICBOM) {
static CMO_MANAGEMENT_BLOCK_SIZE: Once<usize> = Once::new();
CMO_MANAGEMENT_BLOCK_SIZE.call_once(|| {
DEVICE_TREE
.get()
.unwrap()
.cpus()
.find(|cpu| cpu.property("mmu-type").is_some())
.expect("Failed to find an application CPU node in device tree")
.property("riscv,cbom-block-size")
.expect("Failed to find `riscv,cbom-block-size` property of the CPU node")
.as_usize()
.unwrap_or(64)
});
for addr in range.step_by(*CMO_MANAGEMENT_BLOCK_SIZE.get().unwrap()) {
// SAFETY: These are cache maintenance operations on a valid, owned
// memory range. They are required for correctness on systems with
// non-coherent DMA.
unsafe {
match direction {
DmaDirection::ToDevice => {
core::arch::asm!("cbo.clean ({})", in(reg) addr, options(nostack))
}
DmaDirection::FromDevice => {
core::arch::asm!("cbo.inval ({})", in(reg) addr, options(nostack));
}
DmaDirection::Bidirectional => {
core::arch::asm!("cbo.flush ({})", in(reg) addr, options(nostack));
}
}
}
}
// Ensure that all cache operations have completed before proceeding.
// SAFETY: Safe because it is only a memory fence.
unsafe {
core::arch::asm!("fence rw, rw", options(nostack));
}
} else {
// TODO: Implement DMA synchronization without ZICBOM support.
}
}
#[derive(Clone, Copy, Pod, Default)]
#[repr(C)]
pub(crate) struct PageTableEntry(usize);

View File

@ -13,7 +13,7 @@ use crate::{
mm::{
page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags as PrivFlags},
page_table::PageTableEntryTrait,
Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE,
DmaDirection, Paddr, PagingConstsTrait, PagingLevel, PodOnce, Vaddr, PAGE_SIZE,
},
Pod,
};
@ -110,6 +110,12 @@ pub(crate) fn tlb_flush_all_including_global() {
}
}
pub(crate) fn sync_dma_range(_range: Range<Vaddr>, _direction: DmaDirection) {
// The streaming DMA mapping in x86_64 is cache coherent, and does not
// require synchronization.
// Reference: <https://lwn.net/Articles/855328/>, <https://lwn.net/Articles/2265/>.
}
#[derive(Clone, Copy, Pod, Default)]
#[repr(C)]
pub(crate) struct PageTableEntry(usize);

View File

@ -26,8 +26,6 @@ use crate::{
pub struct DmaStream {
segment: USegment,
start_daddr: Daddr,
/// TODO: remove this field when on x86.
#[expect(unused)]
is_cache_coherent: bool,
direction: DmaDirection,
}
@ -123,28 +121,19 @@ impl DmaStream {
///
/// [`read_bytes`]: crate::mm::VmIo::read_bytes
/// [`write_bytes`]: crate::mm::VmIo::write_bytes
pub fn sync(&self, _byte_range: Range<usize>) -> Result<(), Error> {
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")]{
// The streaming DMA mapping in x86_64 is cache coherent, and does not require synchronization.
// Reference: <https://lwn.net/Articles/855328/>, <https://lwn.net/Articles/2265/>
Ok(())
} else {
if _byte_range.end > self.size() {
return Err(Error::InvalidArgs);
}
if self.is_cache_coherent {
return Ok(());
}
let _start_va = crate::mm::paddr_to_vaddr(self.segment.paddr()) as *const u8;
// TODO: Query the CPU for the cache line size via CPUID, we use 64 bytes as the cache line size here.
for _i in _byte_range.step_by(64) {
// TODO: Call the cache line flush command in the corresponding architecture.
todo!()
}
Ok(())
}
pub fn sync(&self, byte_range: Range<usize>) -> Result<(), Error> {
if byte_range.end > self.size() {
return Err(Error::InvalidArgs);
}
if self.is_cache_coherent {
return Ok(());
}
let start_vaddr = crate::mm::paddr_to_vaddr(self.segment.paddr());
let range = (start_vaddr + byte_range.start)..(start_vaddr + byte_range.end);
crate::arch::mm::sync_dma_range(range, self.direction);
Ok(())
}
}