344 lines · 10.5 KB · Rust
RawDownload
// 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,} 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) elf: 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 elf = {            const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();            let elf_size = u64::from_safe_cast(gsp_fw.size);            let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);             FbRange(elf_addr..elf_addr + elf_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 = elf                .start                .checked_sub(wpr2_heap_size)                .ok_or(EOVERFLOW)?                .align_down(WPR2_HEAP_DOWN_ALIGN);             FbRange(wpr2_heap_addr..(elf.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,            elf,            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,        })    }}