Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345// SPDX-License-Identifier: GPL-2.0
use core::ops::{ Deref, Range, //};
use kernel::{ device, dma::CoherentHandle, fmt, io::Io, prelude::*, ptr::{ Alignable, Alignment, // }, sizes::*, //};
use crate::{ driver::Bar0, firmware::gsp::GspFirmware, gpu::Chipset, gsp, num::FromSafeCast, vgpu::VgpuState, //};
mod hal;mod regs;
/// Type holding the sysmem flush memory page, a page of memory to be written into the/// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency.////// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware/// memory-barrier operation that flushes all pending GPU-side memory writes that were done through/// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a/// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure/// this write is visible to the host and prevent driver timeouts, the falcon must perform a/// sysmembar operation to flush its writes.////// Because of this, the sysmem flush memory page must be registered as early as possible during/// driver initialization, and before any falcon is reset.///pub(crate) struct SysmemFlush<'sys> { /// Chipset we are operating on. chipset: Chipset, device: &'sys device::Device, bar: Bar0<'sys>, /// Keep the page alive as long as we need it. page: CoherentHandle<'sys>,}
impl<'sys> SysmemFlush<'sys> { /// Allocate a memory page and register it as the sysmem flush page. pub(crate) fn register( dev: &'sys device::Device<device::Bound>, bar: Bar0<'sys>, chipset: Chipset, ) -> Result<Self> { let page = CoherentHandle::alloc(dev, kernel::page::PAGE_SIZE, GFP_KERNEL)?;
hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_address())?;
Ok(Self { chipset, device: dev, bar, page, }) }}
impl Drop for SysmemFlush<'_> { fn drop(&mut self) { let hal = hal::fb_hal(self.chipset);
if hal.read_sysmem_flush_page(self.bar) == self.page.dma_address() { let _ = hal.write_sysmem_flush_page(self.bar, 0).inspect_err(|e| { dev_warn!( &self.device, "failed to unregister sysmem flush page: {:?}\n", e ) }); } else { // Another page has been registered after us for some reason - warn as this is a bug. dev_warn!( &self.device, "attempt to unregister a sysmem flush page that is not active\n" ); } }}
pub(crate) struct FbRange(Range<u64>);
impl FbRange { pub(crate) fn len(&self) -> u64 { self.0.end - self.0.start }}
impl From<Range<u64>> for FbRange { fn from(range: Range<u64>) -> Self { Self(range) }}
impl Deref for FbRange { type Target = Range<u64>;
fn deref(&self) -> &Self::Target { &self.0 }}
impl fmt::Debug for FbRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Use alternate format ({:#?}) to include size, compact format ({:?}) for just the range. if f.alternate() { let size = self.len();
if size < u64::SZ_1M { let size_kib = size / u64::SZ_1K; f.write_fmt(fmt!( "{:#x}..{:#x} ({} KiB)", self.0.start, self.0.end, size_kib )) } else { let size_mib = size / u64::SZ_1M; f.write_fmt(fmt!( "{:#x}..{:#x} ({} MiB)", self.0.start, self.0.end, size_mib )) } } else { f.write_fmt(fmt!("{:#x}..{:#x}", self.0.start, self.0.end)) } }}
/// Layout of the GPU framebuffer memory.////// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.#[derive(Debug)]pub(crate) struct FbRanges { /// Range of the framebuffer. Starts at `0`. pub(crate) fb: FbRange, /// VGA workspace, small area of reserved memory at the end of the framebuffer. pub(crate) vga_workspace: FbRange, /// FRTS range. pub(crate) frts: FbRange, /// Memory area containing the GSP bootloader image. pub(crate) boot: FbRange, /// Memory area containing the GSP firmware image. pub(crate) fw_image: FbRange, /// WPR2 heap. pub(crate) wpr2_heap: FbRange, /// WPR2 region range, starting with an instance of `GspFwWprMeta`. pub(crate) wpr2: FbRange, /// Non-WPR heap, located just below WPR2. pub(crate) non_wpr_heap: FbRange, /// Number of VF partitions. pub(crate) vf_partition_count: u8, /// PMU reserved memory size, in bytes. pub(crate) pmu_reserved_size: u32,}
impl FbRanges { /// Computes concrete framebuffer ranges required on non-FSP booting architectures. pub(crate) fn new( chipset: Chipset, bar: Bar0<'_>, gsp_fw: &GspFirmware<'_>, vgpu_state: VgpuState, ) -> Result<Self> { let hal = hal::fb_hal(chipset);
let fb = { let fb_size = hal.vidmem_size(bar);
FbRange(0..fb_size) };
let vga_workspace = { let vga_base = { const NV_PRAMIN_SIZE: u64 = u64::SZ_1M; let base = fb.end - NV_PRAMIN_SIZE;
if hal.supports_display(bar) { match bar .read(regs::NV_PDISP_VGA_WORKSPACE_BASE) .vga_workspace_addr() { Some(addr) => { if addr < base { const VBIOS_WORKSPACE_SIZE: u64 = u64::SZ_128K;
// Point workspace address to end of framebuffer. fb.end - VBIOS_WORKSPACE_SIZE } else { addr } } None => base, } } else { base } };
FbRange(vga_base..fb.end) };
let frts = { const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>(); let frts_size: u64 = hal.frts_size(); let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - frts_size;
FbRange(frts_base..frts_base + frts_size) };
let boot = { const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>(); let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size()); let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
FbRange(bootloader_base..bootloader_base + bootloader_size) };
let fw_image = { const FW_IMAGE_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>(); let fw_image_size = u64::from_safe_cast(gsp_fw.size()); let fw_image_addr = (boot.start - fw_image_size).align_down(FW_IMAGE_DOWN_ALIGN);
FbRange(fw_image_addr..fw_image_addr + fw_image_size) };
let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb.end)?;
let wpr2_heap = { const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); let wpr2_heap_addr = fw_image .start .checked_sub(wpr2_heap_size) .ok_or(EOVERFLOW)? .align_down(WPR2_HEAP_DOWN_ALIGN);
FbRange(wpr2_heap_addr..(fw_image.start).align_down(WPR2_HEAP_DOWN_ALIGN)) };
let wpr2 = { const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>())) .align_down(WPR2_DOWN_ALIGN);
FbRange(wpr2_addr..frts.end) };
let non_wpr_heap = { let non_wpr_heap_size = hal.non_wpr_heap_size(); FbRange(wpr2.start - non_wpr_heap_size..wpr2.start) };
Ok(Self { fb, vga_workspace, frts, boot, fw_image, wpr2_heap, wpr2, non_wpr_heap, vf_partition_count, pmu_reserved_size: hal.pmu_reserved_size(), }) }}
/// Reads the WPR2 memory region registers and returns the range if set./// Returns `None` if the WPR2 region is not set.pub(crate) fn wpr2_range(bar: Bar0<'_>) -> Option<Range<u64>> { let wpr2_hi = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI);
if !wpr2_hi.is_wpr2_set() { return None; }
let wpr2_lo = bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO);
Some(wpr2_lo.lower_bound()..wpr2_hi.higher_bound())}
/// Computes the number of VF partitions and the WPR2 heap size from the vGPU state.fn wpr2_heap_params(chipset: Chipset, vgpu_state: VgpuState, fb_size: u64) -> Result<(u8, u64)> { Ok(match vgpu_state { VgpuState::Disabled => ( 0, gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb_size)?, ), VgpuState::Enabled { total_vfs } => ( u8::try_from(total_vfs.get()).map_err(|_| EINVAL)?, gsp::LibosParams::vgpu_wpr_heap_size(), ), })}
/// Framebuffer region sizes needed for GSP-FMC boot.#[derive(Debug)]pub(crate) struct FbSizes { /// FRTS size, in bytes. pub(crate) frts_size: u64, /// WPR2 heap size, in bytes. pub(crate) wpr2_heap_size: u64, /// Non-WPR heap size, in bytes. pub(crate) non_wpr_heap_size: u64, /// PMU reserved memory size, in bytes. pub(crate) pmu_reserved_size: u32, /// Number of VF partitions. pub(crate) vf_partition_count: u8,}
impl FbSizes { /// Computes the framebuffer region sizes for GSP-FMC boot. pub(crate) fn new(chipset: Chipset, bar: Bar0<'_>, vgpu_state: VgpuState) -> Result<Self> { let hal = hal::fb_hal(chipset); let fb_size = hal.vidmem_size(bar); let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb_size)?;
Ok(Self { frts_size: hal.frts_size(), wpr2_heap_size, non_wpr_heap_size: hal.non_wpr_heap_size(), pmu_reserved_size: hal.pmu_reserved_size(), vf_partition_count, }) }}