diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs index 16e143658c51..02efdcf78d89 100644 --- a/drivers/gpu/nova-core/bitfield.rs +++ b/drivers/gpu/nova-core/bitfield.rs @@ -314,12 +314,11 @@ fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result { /// Returns a value for the bitfield where all fields are set to their default value. impl ::core::default::Default for $name { fn default() -> Self { - #[allow(unused_mut)] - let mut value = Self(Default::default()); + let value = Self(Default::default()); ::kernel::macros::paste!( $( - value.[](Default::default()); + let value = value.[](Default::default()); )* ); diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index e0315fda576b..33927af4134c 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -11,6 +11,7 @@ }, dma::{ Coherent, + CoherentBox, DmaAddress, DmaMask, // }, @@ -613,8 +614,24 @@ fn dma_load + FalconDmaLoadable>( bar: &Bar0, fw: &F, ) -> Result { - // Create DMA object with firmware content as the source of the DMA engine. - let dma_obj = Coherent::from_slice(dev, fw.as_slice(), GFP_KERNEL)?; + // DMA object with firmware content as the source of the DMA engine. + let dma_obj = { + let fw_slice = fw.as_slice(); + + // DMA copies are done in chunks of `MEM_BLOCK_ALIGNMENT`, so pad the length + // accordingly and fill with `0`. + let mut dma_obj = CoherentBox::zeroed_slice( + dev, + fw_slice.len().next_multiple_of(MEM_BLOCK_ALIGNMENT), + GFP_KERNEL, + )?; + + // PANIC: `dma_obj` has been created with a length equal to or larger than + // `fw_slice.len()`, so the range `..fw_slice.len()` is valid. + dma_obj[..fw_slice.len()].copy_from_slice(fw_slice); + + dma_obj.into() + }; self.dma_reset(bar); bar.update(regs::NV_PFALCON_FBIF_TRANSCFG::of::().at(0), |v| { diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs index 6f707b3d1a54..18f356c9178e 100644 --- a/drivers/gpu/nova-core/gsp/boot.rs +++ b/drivers/gpu/nova-core/gsp/boot.rs @@ -195,7 +195,7 @@ pub(crate) fn boot( Some(wpr_handle as u32), Some((wpr_handle >> 32) as u32), )?; - dev_dbg!(pdev, "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n", mbox0, mbox1); + dev_dbg!(pdev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); if mbox0 != 0 { dev_err!(pdev, "Booter-load failed with error {:#x}\n", mbox0); diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 2224896ccc89..569bb1a2501c 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -17,6 +17,7 @@ }, new_mutex, prelude::*, + ptr, sync::{ aref::ARef, Mutex, // @@ -255,37 +256,46 @@ fn new(dev: &device::Device) -> Result { /// As the message queue is a circular buffer, the region may be discontiguous in memory. In /// that case the second slice will have a non-zero length. fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) { - let tx = self.cpu_write_ptr() as usize; - let rx = self.gsp_read_ptr() as usize; + let tx = self.cpu_write_ptr(); + let rx = self.gsp_read_ptr(); + + // Pointer to the first entry of the CPU message queue. + let data = ptr::project!(mut self.0.as_mut_ptr(), .cpuq.msgq.data[0]); + + let (tail_end, wrap_end) = if rx == 0 { + // The write area is non-wrapping, and stops at the second-to-last entry of the command + // queue (to leave the last one empty). + (MSGQ_NUM_PAGES - 1, 0) + } else if rx <= tx { + // The write area wraps and continues until `rx - 1`. + (MSGQ_NUM_PAGES, rx - 1) + } else { + // The write area doesn't wrap and stops at `rx - 1`. + (rx - 1, 0) + }; // SAFETY: - // - We will only access the driver-owned part of the shared memory. - // - Per the safety statement of the function, no concurrent access will be performed. - let gsp_mem = unsafe { &mut *self.0.as_mut() }; - // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `< MSGQ_NUM_PAGES`. - let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx); - - // The area starting at `tx` and ending at `rx - 2` modulo MSGQ_NUM_PAGES, inclusive, - // belongs to the driver for writing. - - if rx == 0 { - // Since `rx` is zero, leave an empty slot at end of the buffer. - let last = after_tx.len() - 1; - (&mut after_tx[..last], &mut []) - } else if rx <= tx { - // The area is discontiguous and we leave an empty slot before `rx`. - // PANIC: - // - The index `rx - 1` is non-negative because `rx != 0` in this branch. - // - The index does not exceed `before_tx.len()` (which equals `tx`) because - // `rx <= tx` in this branch. - (after_tx, &mut before_tx[..(rx - 1)]) - } else { - // The area is contiguous and we leave an empty slot before `rx`. - // PANIC: - // - The index `rx - tx - 1` is non-negative because `rx > tx` in this branch. - // - The index does not exceed `after_tx.len()` (which is `MSGQ_NUM_PAGES - tx`) - // because `rx < MSGQ_NUM_PAGES` by the `gsp_read_ptr` invariant. - (&mut after_tx[..(rx - tx - 1)], &mut []) + // - `data` was created from a valid pointer, and `rx` and `tx` are in the + // `0..MSGQ_NUM_PAGES` range per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, + // thus the created slices are valid. + // - The area starting at `tx` and ending at `rx - 2` modulo `MSGQ_NUM_PAGES`, + // inclusive, belongs to the driver for writing and is not accessed concurrently by + // the GSP. + // - The caller holds a reference to `self` for as long as the returned slices are live, + // meaning the CPU write pointer cannot be advanced and thus that the returned area + // remains exclusive to the CPU for the duration of the slices. + // - The created slices point to non-overlapping sub-ranges of `data` in all + // branches (in the `rx <= tx` case, the second slice ends at `rx - 1` which is strictly + // less than `tx` where the first slice starts; in the other cases the second slice is + // empty), so creating two `&mut` references from them does not violate aliasing rules. + unsafe { + ( + core::slice::from_raw_parts_mut( + data.add(num::u32_as_usize(tx)), + num::u32_as_usize(tail_end - tx), + ), + core::slice::from_raw_parts_mut(data, num::u32_as_usize(wrap_end)), + ) } } @@ -308,26 +318,38 @@ fn driver_write_area_size(&self) -> usize { /// As the message queue is a circular buffer, the region may be discontiguous in memory. In /// that case the second slice will have a non-zero length. fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) { - let tx = self.gsp_write_ptr() as usize; - let rx = self.cpu_read_ptr() as usize; + let tx = self.gsp_write_ptr(); + let rx = self.cpu_read_ptr(); + + // Pointer to the first entry of the GSP message queue. + let data = ptr::project!(self.0.as_ptr(), .gspq.msgq.data[0]); + + let (tail_end, wrap_end) = if rx <= tx { + // Read area is non-wrapping and stops right before `tx`. + (tx, 0) + } else { + // Read area is wrapping and stops right before `tx`. + (MSGQ_NUM_PAGES, tx) + }; // SAFETY: - // - We will only access the driver-owned part of the shared memory. - // - Per the safety statement of the function, no concurrent access will be performed. - let gsp_mem = unsafe { &*self.0.as_ptr() }; - let data = &gsp_mem.gspq.msgq.data; - - // The area starting at `rx` and ending at `tx - 1` modulo MSGQ_NUM_PAGES, inclusive, - // belongs to the driver for reading. - // PANIC: - // - per the invariant of `cpu_read_ptr`, `rx < MSGQ_NUM_PAGES` - // - per the invariant of `gsp_write_ptr`, `tx < MSGQ_NUM_PAGES` - if rx <= tx { - // The area is contiguous. - (&data[rx..tx], &[]) - } else { - // The area is discontiguous. - (&data[rx..], &data[..tx]) + // - `data` was created from a valid pointer, and `rx` and `tx` are in the + // `0..MSGQ_NUM_PAGES` range per the invariants of `gsp_write_ptr` and `cpu_read_ptr`, + // thus the created slices are valid. + // - The area starting at `rx` and ending at `tx - 1` modulo `MSGQ_NUM_PAGES`, + // inclusive, belongs to the driver for reading and is not accessed concurrently by + // the GSP. + // - The caller holds a reference to `self` for as long as the returned slices are live, + // meaning the CPU read pointer cannot be advanced and thus that the returned area + // remains exclusive to the CPU for the duration of the slices. + unsafe { + ( + core::slice::from_raw_parts( + data.add(num::u32_as_usize(rx)), + num::u32_as_usize(tail_end - rx), + ), + core::slice::from_raw_parts(data, num::u32_as_usize(wrap_end)), + ) } } diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index 3e3fa5b72524..ebda28e596c5 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -507,12 +507,7 @@ fn new(dev: &device::Device, data: &[u8]) -> Result { if data.len() >= 30 { // Read size_of_block at offset 0x1A. - size_of_block = Some( - u32::from(data[29]) << 24 - | u32::from(data[28]) << 16 - | u32::from(data[27]) << 8 - | u32::from(data[26]), - ); + size_of_block = Some(u32::from_le_bytes([data[26], data[27], data[28], data[29]])); } // For NBSI images, try to read the nbsiDataOffset at offset 0x16.