summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-03-13 10:39:57 +1000
committerDave Airlie <airlied@redhat.com>2026-03-13 10:40:17 +1000
commitb28913e897edfeedc4e33b03b28068b27d002e6c (patch)
tree37f1b32aed1ddcc88cf6dab0f4e98bed628a3e82 /drivers/gpu
parentdd0365021be3e8693680bb1d2616edf7d5e17800 (diff)
parent0073a17b466684413ac87cf8ff6c19560db44e7a (diff)
Merge tag 'drm-rust-fixes-2026-03-12' of https://gitlab.freedesktop.org/drm/rust/kernel into drm-fixes
Core Changes: - Fix safety issue in dma_read! and dma_write!. Driver Changes (Nova Core): - Fix UB in DmaGspMem pointer accessors. - Fix stack overflow in GSP memory allocation. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/abNBSol3CLRCqlkZ@google.com
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/nova-core/gsp.rs46
-rw-r--r--drivers/gpu/nova-core/gsp/boot.rs2
-rw-r--r--drivers/gpu/nova-core/gsp/cmdq.rs93
-rw-r--r--drivers/gpu/nova-core/gsp/fw.rs101
4 files changed, 127 insertions, 115 deletions
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 174feaca0a6b..c69adaa92bbe 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -47,16 +47,12 @@ struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
- /// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
- fn new(start: DmaAddress) -> Result<Self> {
- let mut ptes = [0u64; NUM_PAGES];
- for (i, pte) in ptes.iter_mut().enumerate() {
- *pte = start
- .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
- .ok_or(EOVERFLOW)?;
- }
-
- Ok(Self(ptes))
+ /// Returns the page table entry for `index`, for a mapping starting at `start`.
+ // TODO: Replace with `IoView` projection once available.
+ fn entry(start: DmaAddress, index: usize) -> Result<u64> {
+ start
+ .checked_add(num::usize_as_u64(index) << GSP_PAGE_SHIFT)
+ .ok_or(EOVERFLOW)
}
}
@@ -86,16 +82,22 @@ impl LogBuffer {
NUM_PAGES * GSP_PAGE_SIZE,
GFP_KERNEL | __GFP_ZERO,
)?);
- let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?;
+
+ let start_addr = obj.0.dma_handle();
// SAFETY: `obj` has just been created and we are its sole user.
- unsafe {
- // Copy the self-mapping PTE at the expected location.
+ let pte_region = unsafe {
obj.0
- .as_slice_mut(size_of::<u64>(), size_of_val(&ptes))?
- .copy_from_slice(ptes.as_bytes())
+ .as_slice_mut(size_of::<u64>(), NUM_PAGES * size_of::<u64>())?
};
+ // Write values one by one to avoid an on-stack instance of `PteArray`.
+ for (i, chunk) in pte_region.chunks_exact_mut(size_of::<u64>()).enumerate() {
+ let pte_value = PteArray::<0>::entry(start_addr, i)?;
+
+ chunk.copy_from_slice(&pte_value.to_ne_bytes());
+ }
+
Ok(obj)
}
}
@@ -143,14 +145,14 @@ impl Gsp {
// _kgspInitLibosLoggingStructures (allocates memory for buffers)
// kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
dma_write!(
- libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0)
- )?;
+ libos, [0]?, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0)
+ );
dma_write!(
- libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0)
- )?;
- dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
- dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(cmdq))?;
- dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?;
+ libos, [1]?, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0)
+ );
+ dma_write!(libos, [2]?, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0));
+ dma_write!(rmargs, [0]?.inner, fw::GspArgumentsCached::new(cmdq));
+ dma_write!(libos, [3]?, LibosMemoryRegionInitArgument::new("RMARGS", rmargs));
},
}))
})
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index be427fe26a58..94833f7996e8 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -157,7 +157,7 @@ impl super::Gsp {
let wpr_meta =
CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
- dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
+ dma_write!(wpr_meta, [0]?, GspFwWprMeta::new(&gsp_fw, &fb_layout));
self.cmdq
.send_command(bar, commands::SetSystemInfo::new(pdev))?;
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 46819a82a51a..03a4f3599849 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -2,11 +2,7 @@
use core::{
cmp,
- mem,
- sync::atomic::{
- fence,
- Ordering, //
- }, //
+ mem, //
};
use kernel::{
@@ -146,30 +142,36 @@ static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
#[repr(C)]
// There is no struct defined for this in the open-gpu-kernel-source headers.
// Instead it is defined by code in `GspMsgQueuesInit()`.
-struct Msgq {
+// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
+pub(super) struct Msgq {
/// Header for sending messages, including the write pointer.
- tx: MsgqTxHeader,
+ pub(super) tx: MsgqTxHeader,
/// Header for receiving messages, including the read pointer.
- rx: MsgqRxHeader,
+ pub(super) rx: MsgqRxHeader,
/// The message queue proper.
msgq: MsgqData,
}
/// Structure shared between the driver and the GSP and containing the command and message queues.
#[repr(C)]
-struct GspMem {
+// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
+pub(super) struct GspMem {
/// Self-mapping page table entries.
- ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>,
+ ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,
/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
/// write and read pointers that the CPU updates.
///
/// This member is read-only for the GSP.
- cpuq: Msgq,
+ pub(super) cpuq: Msgq,
/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
/// write and read pointers that the GSP updates.
///
/// This member is read-only for the driver.
- gspq: Msgq,
+ pub(super) gspq: Msgq,
+}
+
+impl GspMem {
+ const PTE_ARRAY_SIZE: usize = GSP_PAGE_SIZE / size_of::<u64>();
}
// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
@@ -201,9 +203,19 @@ impl DmaGspMem {
let gsp_mem =
CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
- dma_write!(gsp_mem[0].ptes = PteArray::new(gsp_mem.dma_handle())?)?;
- dma_write!(gsp_mem[0].cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES))?;
- dma_write!(gsp_mem[0].cpuq.rx = MsgqRxHeader::new())?;
+
+ let start = gsp_mem.dma_handle();
+ // Write values one by one to avoid an on-stack instance of `PteArray`.
+ for i in 0..GspMem::PTE_ARRAY_SIZE {
+ dma_write!(gsp_mem, [0]?.ptes.0[i], PteArray::<0>::entry(start, i)?);
+ }
+
+ dma_write!(
+ gsp_mem,
+ [0]?.cpuq.tx,
+ MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES)
+ );
+ dma_write!(gsp_mem, [0]?.cpuq.rx, MsgqRxHeader::new());
Ok(Self(gsp_mem))
}
@@ -317,12 +329,7 @@ impl DmaGspMem {
//
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn gsp_write_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- (unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::gsp_write_ptr(&self.0)
}
// Returns the index of the memory page the GSP will read the next command from.
@@ -331,12 +338,7 @@ impl DmaGspMem {
//
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn gsp_read_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- (unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::gsp_read_ptr(&self.0)
}
// Returns the index of the memory page the CPU can read the next message from.
@@ -345,27 +347,12 @@ impl DmaGspMem {
//
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn cpu_read_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The ['CoherentAllocation'] contains at least one object.
- // - By the invariants of CoherentAllocation the pointer is valid.
- (unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::cpu_read_ptr(&self.0)
}
// Informs the GSP that it can send `elem_count` new pages into the message queue.
fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
- let rptr = self.cpu_read_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES;
-
- // Ensure read pointer is properly ordered.
- fence(Ordering::SeqCst);
-
- let gsp_mem = self.0.start_ptr_mut();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- unsafe { (*gsp_mem).cpuq.rx.set_read_ptr(rptr) };
+ super::fw::gsp_mem::advance_cpu_read_ptr(&self.0, elem_count)
}
// Returns the index of the memory page the CPU can write the next command to.
@@ -374,26 +361,12 @@ impl DmaGspMem {
//
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn cpu_write_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- (unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::cpu_write_ptr(&self.0)
}
// Informs the GSP that it can process `elem_count` new pages from the command queue.
fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
- let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES;
- let gsp_mem = self.0.start_ptr_mut();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- unsafe { (*gsp_mem).cpuq.tx.set_write_ptr(wptr) };
-
- // Ensure all command data is visible before triggering the GSP read.
- fence(Ordering::SeqCst);
+ super::fw::gsp_mem::advance_cpu_write_ptr(&self.0, elem_count)
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 83ff91614e36..040b30ec3089 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -40,6 +40,75 @@ use crate::{
},
};
+// TODO: Replace with `IoView` projections once available; the `unwrap()` calls go away once we
+// switch to the new `dma::Coherent` API.
+pub(super) mod gsp_mem {
+ use core::sync::atomic::{
+ fence,
+ Ordering, //
+ };
+
+ use kernel::{
+ dma::CoherentAllocation,
+ dma_read,
+ dma_write,
+ prelude::*, //
+ };
+
+ use crate::gsp::cmdq::{
+ GspMem,
+ MSGQ_NUM_PAGES, //
+ };
+
+ pub(in crate::gsp) fn gsp_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn gsp_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn cpu_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
+ let rptr = cpu_read_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
+
+ // Ensure read pointer is properly ordered.
+ fence(Ordering::SeqCst);
+
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result {
+ dma_write!(qs, [0]?.cpuq.rx.0.readPtr, rptr);
+ Ok(())
+ }()
+ .unwrap()
+ }
+
+ pub(in crate::gsp) fn cpu_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
+ let wptr = cpu_write_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
+
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result {
+ dma_write!(qs, [0]?.cpuq.tx.0.writePtr, wptr);
+ Ok(())
+ }()
+ .unwrap();
+
+ // Ensure all command data is visible before triggering the GSP read.
+ fence(Ordering::SeqCst);
+ }
+}
+
/// Empty type to group methods related to heap parameters for running the GSP firmware.
enum GspFwHeapParams {}
@@ -708,22 +777,6 @@ impl MsgqTxHeader {
entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
})
}
-
- /// Returns the value of the write pointer for this queue.
- pub(crate) fn write_ptr(&self) -> u32 {
- let ptr = core::ptr::from_ref(&self.0.writePtr);
-
- // SAFETY: `ptr` is a valid pointer to a `u32`.
- unsafe { ptr.read_volatile() }
- }
-
- /// Sets the value of the write pointer for this queue.
- pub(crate) fn set_write_ptr(&mut self, val: u32) {
- let ptr = core::ptr::from_mut(&mut self.0.writePtr);
-
- // SAFETY: `ptr` is a valid pointer to a `u32`.
- unsafe { ptr.write_volatile(val) }
- }
}
// SAFETY: Padding is explicit and does not contain uninitialized data.
@@ -739,22 +792,6 @@ impl MsgqRxHeader {
pub(crate) fn new() -> Self {
Self(Default::default())
}
-
- /// Returns the value of the read pointer for this queue.
- pub(crate) fn read_ptr(&self) -> u32 {
- let ptr = core::ptr::from_ref(&self.0.readPtr);
-
- // SAFETY: `ptr` is a valid pointer to a `u32`.
- unsafe { ptr.read_volatile() }
- }
-
- /// Sets the value of the read pointer for this queue.
- pub(crate) fn set_read_ptr(&mut self, val: u32) {
- let ptr = core::ptr::from_mut(&mut self.0.readPtr);
-
- // SAFETY: `ptr` is a valid pointer to a `u32`.
- unsafe { ptr.write_volatile(val) }
- }
}
// SAFETY: Padding is explicit and does not contain uninitialized data.