[PATCH 3/9] drm/tyr: add MappedBo, a kernel BO with an always-valid CPU mapping
From: Laura Nao
Date: Tue Sep 15 2026 - 07:00:10 EST
From: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
Firmware sections need CPU access at well-defined points: once at load
time to copy the section payload in, and, for the shared section, for
the lifetime of the driver to talk to the CSF interface blocks.
Introduce MappedBo, which pairs a KernelBo with one persistent vmap so
the GPU mapping and the CPU mapping share a single lifetime, and hold it
in Section instead of the bare KernelBo. Section payload initialization
now writes through the persistent mapping.
This is the foundation for resolving firmware-reported MCU virtual
addresses into validated views of the shared section: the object that
owns both mappings is the natural place for those checks to live.
Signed-off-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
Signed-off-by: Laura Nao <laura.nao@xxxxxxxxxxxxx>
---
drivers/gpu/drm/tyr/fw.rs | 14 ++++++-------
drivers/gpu/drm/tyr/gem.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 56 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs
index 19bdeee858ce..fb4d47ab35a3 100644
--- a/drivers/gpu/drm/tyr/fw.rs
+++ b/drivers/gpu/drm/tyr/fw.rs
@@ -144,7 +144,7 @@ struct Section<'drm> {
// Keep the BO backing this firmware section so that both the
// GPU mapping and CPU mapping remain valid until the Section is dropped.
#[expect(dead_code)]
- mem: gem::KernelBo<'drm>,
+ mem: Arc<gem::MappedBo<'drm>>,
}
/// Loaded firmware with sections mapped into MCU VM.
@@ -171,13 +171,13 @@ fn drop(&mut self) {
}
impl<'drm> Firmware<'drm> {
- fn init_section_mem(dev: &Device, mem: &mut KernelBo<'drm>, data: &KVec<u8>) -> Result {
+ fn init_section_mem(dev: &Device, mem: &gem::MappedBo<'drm>, data: &KVec<u8>) -> Result {
if data.is_empty() {
return Ok(());
}
- let vmap = mem.bo().vmap::<0>()?;
- let size = mem.bo().size();
+ let vmap = mem.vmap();
+ let size = mem.size();
if data.len() > size {
dev_err!(dev, "fw section {} bigger than BO {}", data.len(), size);
@@ -235,13 +235,13 @@ pub(crate) fn new(
let va = u64::from(parsed.va.start);
- let mut mem = KernelBo::new(
+ let mem = gem::MappedBo::new(KernelBo::new(
ddev,
vm.clone(),
size,
KernelBoVaAlloc::Explicit(va),
parsed.vm_map_flags,
- )?;
+ )?)?;
let section_start = parsed.data_range.start as usize;
let section_end = parsed.data_range.end as usize;
@@ -252,7 +252,7 @@ pub(crate) fn new(
let bytes = fw_data.get(section_start..section_end).ok_or(EINVAL)?;
data.extend_from_slice(bytes, GFP_KERNEL)?;
- Self::init_section_mem(dev, &mut mem, &data)?;
+ Self::init_section_mem(dev, &mem, &data)?;
sections.push(Section { data, mem }, GFP_KERNEL)?;
}
diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs
index 3bf3787f5c3f..4763b5b2cd80 100644
--- a/drivers/gpu/drm/tyr/gem.rs
+++ b/drivers/gpu/drm/tyr/gem.rs
@@ -136,6 +136,12 @@ pub(crate) fn new(
pub(crate) fn bo(&self) -> &Bo {
&self.bo
}
+
+ /// Returns the GPU virtual address range occupied by this buffer.
+ #[expect(dead_code)]
+ pub(crate) fn va_range(&self) -> Range<u64> {
+ self.va_range.clone()
+ }
}
impl Drop for KernelBo<'_> {
@@ -158,3 +164,46 @@ fn drop(&mut self) {
}
}
}
+
+/// A kernel-owned buffer object with an always-valid kernel (CPU) mapping.
+///
+/// This pairs a [`KernelBo`] with a persistent vmap of its backing GEM object,
+/// so the GPU mapping and the CPU mapping share one lifetime. Consumers that
+/// need CPU access to the buffer contents (e.g. the firmware interface blocks
+/// in the CSF shared section) hold an `Arc<MappedBo>` instead of creating
+/// short-lived vmaps at every use site.
+pub(crate) struct MappedBo<'drm> {
+ /// Persistent CPU mapping of `kernel_bo`'s backing object.
+ ///
+ /// Declared before `kernel_bo` so the mapping is dropped first.
+ vmap: shmem::VMapOwned<BoData>,
+ /// The underlying kernel-owned buffer object.
+ #[expect(dead_code)]
+ kernel_bo: KernelBo<'drm>,
+}
+
+impl<'drm> MappedBo<'drm> {
+ /// Wraps `kernel_bo` together with a persistent CPU mapping of its buffer.
+ pub(crate) fn new(kernel_bo: KernelBo<'drm>) -> Result<Arc<Self>> {
+ let vmap = kernel_bo.bo.owned_vmap::<0>()?;
+ Ok(Arc::new(Self { vmap, kernel_bo }, GFP_KERNEL)?)
+ }
+
+ /// Returns the persistent CPU mapping of the buffer.
+ pub(crate) fn vmap(&self) -> &shmem::VMapOwned<BoData> {
+ &self.vmap
+ }
+
+ /// Returns the GPU virtual address range occupied by the buffer.
+ pub(crate) fn va_range(&self) -> Range<u64> {
+ self.kernel_bo.va_range()
+ }
+}
+
+impl core::ops::Deref for MappedBo<'_> {
+ type Target = Bo;
+
+ fn deref(&self) -> &Bo {
+ self.vmap.owner()
+ }
+}
--
2.39.5