use core::ops::{
Deref,
Range, };
use kernel::{
device,
dma::CoherentHandle,
fmt,
io::Io,
prelude::*,
ptr::{
Alignable,
Alignment, },
sizes::*,
sync::aref::ARef, };
use crate::{
driver::Bar0,
firmware::gsp::GspFirmware,
gpu::Chipset,
gsp,
num::{
usize_as_u64,
FromSafeCast, },
regs,
};
mod hal;
pub(crate) struct SysmemFlush {
chipset: Chipset,
device: ARef<device::Device>,
page: CoherentHandle,
}
impl SysmemFlush {
pub(crate) fn register(
dev: &device::Device<device::Bound>,
bar: &Bar0,
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_handle())?;
Ok(Self {
chipset,
device: dev.into(),
page,
})
}
pub(crate) fn unregister(&self, bar: &Bar0) {
let hal = hal::fb_hal(self.chipset);
if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() {
let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| {
dev_warn!(
&self.device,
"failed to unregister sysmem flush page: {:?}\n",
e
)
});
} else {
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 {
if f.alternate() {
let size = self.len();
if size < usize_as_u64(SZ_1M) {
let size_kib = size / usize_as_u64(SZ_1K);
f.write_fmt(fmt!(
"{:#x}..{:#x} ({} KiB)",
self.0.start,
self.0.end,
size_kib
))
} else {
let size_mib = size / usize_as_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))
}
}
}
#[derive(Debug)]
pub(crate) struct FbLayout {
pub(crate) fb: FbRange,
pub(crate) vga_workspace: FbRange,
pub(crate) frts: FbRange,
pub(crate) boot: FbRange,
pub(crate) elf: FbRange,
pub(crate) wpr2_heap: FbRange,
pub(crate) wpr2: FbRange,
pub(crate) heap: FbRange,
pub(crate) vf_partition_count: u8,
}
impl FbLayout {
pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> 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 = usize_as_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 = usize_as_u64(SZ_128K);
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>();
const FRTS_SIZE: u64 = usize_as_u64(SZ_1M);
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 wpr2_heap = {
const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
let wpr2_heap_size =
gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end);
let wpr2_heap_addr = (elf.start - wpr2_heap_size).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 heap = {
const HEAP_SIZE: u64 = usize_as_u64(SZ_1M);
FbRange(wpr2.start - HEAP_SIZE..wpr2.start)
};
Ok(Self {
fb,
vga_workspace,
frts,
boot,
elf,
wpr2_heap,
wpr2,
heap,
vf_partition_count: 0,
})
}
}