Skip to content

Commit b28913e

Browse files
committed
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
2 parents dd03650 + 0073a17 commit b28913e

10 files changed

Lines changed: 534 additions & 195 deletions

File tree

drivers/gpu/nova-core/gsp.rs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,12 @@ struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
4747
unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
4848

4949
impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
50-
/// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
51-
fn new(start: DmaAddress) -> Result<Self> {
52-
let mut ptes = [0u64; NUM_PAGES];
53-
for (i, pte) in ptes.iter_mut().enumerate() {
54-
*pte = start
55-
.checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
56-
.ok_or(EOVERFLOW)?;
57-
}
58-
59-
Ok(Self(ptes))
50+
/// Returns the page table entry for `index`, for a mapping starting at `start`.
51+
// TODO: Replace with `IoView` projection once available.
52+
fn entry(start: DmaAddress, index: usize) -> Result<u64> {
53+
start
54+
.checked_add(num::usize_as_u64(index) << GSP_PAGE_SHIFT)
55+
.ok_or(EOVERFLOW)
6056
}
6157
}
6258

@@ -86,16 +82,22 @@ impl LogBuffer {
8682
NUM_PAGES * GSP_PAGE_SIZE,
8783
GFP_KERNEL | __GFP_ZERO,
8884
)?);
89-
let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?;
85+
86+
let start_addr = obj.0.dma_handle();
9087

9188
// SAFETY: `obj` has just been created and we are its sole user.
92-
unsafe {
93-
// Copy the self-mapping PTE at the expected location.
89+
let pte_region = unsafe {
9490
obj.0
95-
.as_slice_mut(size_of::<u64>(), size_of_val(&ptes))?
96-
.copy_from_slice(ptes.as_bytes())
91+
.as_slice_mut(size_of::<u64>(), NUM_PAGES * size_of::<u64>())?
9792
};
9893

94+
// Write values one by one to avoid an on-stack instance of `PteArray`.
95+
for (i, chunk) in pte_region.chunks_exact_mut(size_of::<u64>()).enumerate() {
96+
let pte_value = PteArray::<0>::entry(start_addr, i)?;
97+
98+
chunk.copy_from_slice(&pte_value.to_ne_bytes());
99+
}
100+
99101
Ok(obj)
100102
}
101103
}
@@ -143,14 +145,14 @@ impl Gsp {
143145
// _kgspInitLibosLoggingStructures (allocates memory for buffers)
144146
// kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
145147
dma_write!(
146-
libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0)
147-
)?;
148+
libos, [0]?, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0)
149+
);
148150
dma_write!(
149-
libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0)
150-
)?;
151-
dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
152-
dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(cmdq))?;
153-
dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?;
151+
libos, [1]?, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0)
152+
);
153+
dma_write!(libos, [2]?, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0));
154+
dma_write!(rmargs, [0]?.inner, fw::GspArgumentsCached::new(cmdq));
155+
dma_write!(libos, [3]?, LibosMemoryRegionInitArgument::new("RMARGS", rmargs));
154156
},
155157
}))
156158
})

drivers/gpu/nova-core/gsp/boot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl super::Gsp {
157157

158158
let wpr_meta =
159159
CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
160-
dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
160+
dma_write!(wpr_meta, [0]?, GspFwWprMeta::new(&gsp_fw, &fb_layout));
161161

162162
self.cmdq
163163
.send_command(bar, commands::SetSystemInfo::new(pdev))?;

drivers/gpu/nova-core/gsp/cmdq.rs

Lines changed: 33 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
use core::{
44
cmp,
5-
mem,
6-
sync::atomic::{
7-
fence,
8-
Ordering, //
9-
}, //
5+
mem, //
106
};
117

128
use kernel::{
@@ -146,30 +142,36 @@ static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
146142
#[repr(C)]
147143
// There is no struct defined for this in the open-gpu-kernel-source headers.
148144
// Instead it is defined by code in `GspMsgQueuesInit()`.
149-
struct Msgq {
145+
// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
146+
pub(super) struct Msgq {
150147
/// Header for sending messages, including the write pointer.
151-
tx: MsgqTxHeader,
148+
pub(super) tx: MsgqTxHeader,
152149
/// Header for receiving messages, including the read pointer.
153-
rx: MsgqRxHeader,
150+
pub(super) rx: MsgqRxHeader,
154151
/// The message queue proper.
155152
msgq: MsgqData,
156153
}
157154

158155
/// Structure shared between the driver and the GSP and containing the command and message queues.
159156
#[repr(C)]
160-
struct GspMem {
157+
// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
158+
pub(super) struct GspMem {
161159
/// Self-mapping page table entries.
162-
ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>,
160+
ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,
163161
/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
164162
/// write and read pointers that the CPU updates.
165163
///
166164
/// This member is read-only for the GSP.
167-
cpuq: Msgq,
165+
pub(super) cpuq: Msgq,
168166
/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
169167
/// write and read pointers that the GSP updates.
170168
///
171169
/// This member is read-only for the driver.
172-
gspq: Msgq,
170+
pub(super) gspq: Msgq,
171+
}
172+
173+
impl GspMem {
174+
const PTE_ARRAY_SIZE: usize = GSP_PAGE_SIZE / size_of::<u64>();
173175
}
174176

175177
// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
@@ -201,9 +203,19 @@ impl DmaGspMem {
201203

202204
let gsp_mem =
203205
CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
204-
dma_write!(gsp_mem[0].ptes = PteArray::new(gsp_mem.dma_handle())?)?;
205-
dma_write!(gsp_mem[0].cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES))?;
206-
dma_write!(gsp_mem[0].cpuq.rx = MsgqRxHeader::new())?;
206+
207+
let start = gsp_mem.dma_handle();
208+
// Write values one by one to avoid an on-stack instance of `PteArray`.
209+
for i in 0..GspMem::PTE_ARRAY_SIZE {
210+
dma_write!(gsp_mem, [0]?.ptes.0[i], PteArray::<0>::entry(start, i)?);
211+
}
212+
213+
dma_write!(
214+
gsp_mem,
215+
[0]?.cpuq.tx,
216+
MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES)
217+
);
218+
dma_write!(gsp_mem, [0]?.cpuq.rx, MsgqRxHeader::new());
207219

208220
Ok(Self(gsp_mem))
209221
}
@@ -317,12 +329,7 @@ impl DmaGspMem {
317329
//
318330
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
319331
fn gsp_write_ptr(&self) -> u32 {
320-
let gsp_mem = self.0.start_ptr();
321-
322-
// SAFETY:
323-
// - The 'CoherentAllocation' contains at least one object.
324-
// - By the invariants of `CoherentAllocation` the pointer is valid.
325-
(unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES)
332+
super::fw::gsp_mem::gsp_write_ptr(&self.0)
326333
}
327334

328335
// Returns the index of the memory page the GSP will read the next command from.
@@ -331,12 +338,7 @@ impl DmaGspMem {
331338
//
332339
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
333340
fn gsp_read_ptr(&self) -> u32 {
334-
let gsp_mem = self.0.start_ptr();
335-
336-
// SAFETY:
337-
// - The 'CoherentAllocation' contains at least one object.
338-
// - By the invariants of `CoherentAllocation` the pointer is valid.
339-
(unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES)
341+
super::fw::gsp_mem::gsp_read_ptr(&self.0)
340342
}
341343

342344
// Returns the index of the memory page the CPU can read the next message from.
@@ -345,27 +347,12 @@ impl DmaGspMem {
345347
//
346348
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
347349
fn cpu_read_ptr(&self) -> u32 {
348-
let gsp_mem = self.0.start_ptr();
349-
350-
// SAFETY:
351-
// - The ['CoherentAllocation'] contains at least one object.
352-
// - By the invariants of CoherentAllocation the pointer is valid.
353-
(unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES)
350+
super::fw::gsp_mem::cpu_read_ptr(&self.0)
354351
}
355352

356353
// Informs the GSP that it can send `elem_count` new pages into the message queue.
357354
fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
358-
let rptr = self.cpu_read_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES;
359-
360-
// Ensure read pointer is properly ordered.
361-
fence(Ordering::SeqCst);
362-
363-
let gsp_mem = self.0.start_ptr_mut();
364-
365-
// SAFETY:
366-
// - The 'CoherentAllocation' contains at least one object.
367-
// - By the invariants of `CoherentAllocation` the pointer is valid.
368-
unsafe { (*gsp_mem).cpuq.rx.set_read_ptr(rptr) };
355+
super::fw::gsp_mem::advance_cpu_read_ptr(&self.0, elem_count)
369356
}
370357

371358
// Returns the index of the memory page the CPU can write the next command to.
@@ -374,26 +361,12 @@ impl DmaGspMem {
374361
//
375362
// - The returned value is between `0` and `MSGQ_NUM_PAGES`.
376363
fn cpu_write_ptr(&self) -> u32 {
377-
let gsp_mem = self.0.start_ptr();
378-
379-
// SAFETY:
380-
// - The 'CoherentAllocation' contains at least one object.
381-
// - By the invariants of `CoherentAllocation` the pointer is valid.
382-
(unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES)
364+
super::fw::gsp_mem::cpu_write_ptr(&self.0)
383365
}
384366

385367
// Informs the GSP that it can process `elem_count` new pages from the command queue.
386368
fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
387-
let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES;
388-
let gsp_mem = self.0.start_ptr_mut();
389-
390-
// SAFETY:
391-
// - The 'CoherentAllocation' contains at least one object.
392-
// - By the invariants of `CoherentAllocation` the pointer is valid.
393-
unsafe { (*gsp_mem).cpuq.tx.set_write_ptr(wptr) };
394-
395-
// Ensure all command data is visible before triggering the GSP read.
396-
fence(Ordering::SeqCst);
369+
super::fw::gsp_mem::advance_cpu_write_ptr(&self.0, elem_count)
397370
}
398371
}
399372

drivers/gpu/nova-core/gsp/fw.rs

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,75 @@ use crate::{
4040
},
4141
};
4242

43+
// TODO: Replace with `IoView` projections once available; the `unwrap()` calls go away once we
44+
// switch to the new `dma::Coherent` API.
45+
pub(super) mod gsp_mem {
46+
use core::sync::atomic::{
47+
fence,
48+
Ordering, //
49+
};
50+
51+
use kernel::{
52+
dma::CoherentAllocation,
53+
dma_read,
54+
dma_write,
55+
prelude::*, //
56+
};
57+
58+
use crate::gsp::cmdq::{
59+
GspMem,
60+
MSGQ_NUM_PAGES, //
61+
};
62+
63+
pub(in crate::gsp) fn gsp_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
64+
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
65+
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
66+
}
67+
68+
pub(in crate::gsp) fn gsp_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
69+
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
70+
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
71+
}
72+
73+
pub(in crate::gsp) fn cpu_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
74+
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
75+
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
76+
}
77+
78+
pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
79+
let rptr = cpu_read_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
80+
81+
// Ensure read pointer is properly ordered.
82+
fence(Ordering::SeqCst);
83+
84+
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
85+
|| -> Result {
86+
dma_write!(qs, [0]?.cpuq.rx.0.readPtr, rptr);
87+
Ok(())
88+
}()
89+
.unwrap()
90+
}
91+
92+
pub(in crate::gsp) fn cpu_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
93+
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
94+
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
95+
}
96+
97+
pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
98+
let wptr = cpu_write_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
99+
100+
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
101+
|| -> Result {
102+
dma_write!(qs, [0]?.cpuq.tx.0.writePtr, wptr);
103+
Ok(())
104+
}()
105+
.unwrap();
106+
107+
// Ensure all command data is visible before triggering the GSP read.
108+
fence(Ordering::SeqCst);
109+
}
110+
}
111+
43112
/// Empty type to group methods related to heap parameters for running the GSP firmware.
44113
enum GspFwHeapParams {}
45114

@@ -708,22 +777,6 @@ impl MsgqTxHeader {
708777
entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
709778
})
710779
}
711-
712-
/// Returns the value of the write pointer for this queue.
713-
pub(crate) fn write_ptr(&self) -> u32 {
714-
let ptr = core::ptr::from_ref(&self.0.writePtr);
715-
716-
// SAFETY: `ptr` is a valid pointer to a `u32`.
717-
unsafe { ptr.read_volatile() }
718-
}
719-
720-
/// Sets the value of the write pointer for this queue.
721-
pub(crate) fn set_write_ptr(&mut self, val: u32) {
722-
let ptr = core::ptr::from_mut(&mut self.0.writePtr);
723-
724-
// SAFETY: `ptr` is a valid pointer to a `u32`.
725-
unsafe { ptr.write_volatile(val) }
726-
}
727780
}
728781

729782
// SAFETY: Padding is explicit and does not contain uninitialized data.
@@ -739,22 +792,6 @@ impl MsgqRxHeader {
739792
pub(crate) fn new() -> Self {
740793
Self(Default::default())
741794
}
742-
743-
/// Returns the value of the read pointer for this queue.
744-
pub(crate) fn read_ptr(&self) -> u32 {
745-
let ptr = core::ptr::from_ref(&self.0.readPtr);
746-
747-
// SAFETY: `ptr` is a valid pointer to a `u32`.
748-
unsafe { ptr.read_volatile() }
749-
}
750-
751-
/// Sets the value of the read pointer for this queue.
752-
pub(crate) fn set_read_ptr(&mut self, val: u32) {
753-
let ptr = core::ptr::from_mut(&mut self.0.readPtr);
754-
755-
// SAFETY: `ptr` is a valid pointer to a `u32`.
756-
unsafe { ptr.write_volatile(val) }
757-
}
758795
}
759796

760797
// SAFETY: Padding is explicit and does not contain uninitialized data.

0 commit comments

Comments
 (0)