diff options
author | Dave Airlie <airlied@redhat.com> | 2024-03-20 16:24:17 +0100 |
---|---|---|
committer | Danilo Krummrich <dakr@kernel.org> | 2024-08-13 16:18:57 +0200 |
commit | 02ef3ce2d934c185e6f02e488624a39fbc3befbd (patch) | |
tree | 6d4803f598ad2906386a0d4809b181f8377dc6f9 /drivers/gpu | |
parent | 904585b12c778822d5d26a9e5e753381a8092f03 (diff) |
nova: start parsing bios headersnova
Co-authored-by: Danilo Krummrich <dakr@redhat.com>
Signed-off-by: Danilo Krummrich <dakr@redhat.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nova/bios.rs | 476 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/driver.rs | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/fwsec.rs | 37 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/gpu.rs | 33 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/nova.rs | 2 |
5 files changed, 551 insertions, 5 deletions
diff --git a/drivers/gpu/drm/nova/bios.rs b/drivers/gpu/drm/nova/bios.rs new file mode 100644 index 000000000000..b3e8a8435308 --- /dev/null +++ b/drivers/gpu/drm/nova/bios.rs @@ -0,0 +1,476 @@ +use kernel::{ + alloc::{flags::*, Flags}, + devres::Devres, + prelude::*, + revocable::RevocableGuard, +}; + +use crate::driver::Bar0; +use crate::fwsec::FalconUCodeDescV3; + +const PROM_OFFSET: usize = 0x300000; + +#[repr(C)] +#[derive(Default)] +struct BitEntry { + id: u8, + version: u8, + length: u16, + offset: u16, +} + +#[allow(dead_code)] +#[derive(Default)] +struct BiosPcirT { + vendor_id: u16, + device_id: u16, + class_code: [u8; 3], + image_size: u32, + image_rev: u16, + image_type: u8, + last: bool, +} + +#[derive(Default)] +struct BiosNpdeT { + image_size: u32, + last: bool, +} + +#[derive(Default)] +struct BiosPmuE { + pmutype: u8, + data: u32, +} + +#[derive(Default)] +struct BiosImage { + base: usize, + size: usize, + itype: u8, + last: bool, +} + +pub(crate) struct Bios { + image0_size: isize, + imaged_addr: usize, + + bmp_offset: usize, + bit_offset: usize, + + bios_vec: KVec<u8>, + bios_vec_gfp: Flags, +} + +impl Bios { + pub(crate) fn new() -> Self { + Self { + image0_size: 0, + imaged_addr: 0, + bmp_offset: 0, + bit_offset: 0, + bios_vec: Default::default(), + bios_vec_gfp: GFP_KERNEL | __GFP_ZERO, + } + } + + fn rd32(&self, offset: isize) -> u32 { + let mut addr = offset; + if addr >= self.image0_size && self.imaged_addr != 0 { + addr -= self.image0_size; + addr += self.imaged_addr as isize; + } + let ptr: *const u32 = (self.bios_vec.as_ptr() as usize + addr as usize) as *const u32; + unsafe { core::ptr::read_unaligned(ptr) } + } + + fn rd16(&self, offset: isize) -> u16 { + let mut addr = offset; + if addr >= self.image0_size && self.imaged_addr != 0 { + addr -= self.image0_size; + addr += self.imaged_addr as isize; + } + let ptr: *const u16 = (self.bios_vec.as_ptr() as usize + addr as usize) as *const u16; + unsafe { core::ptr::read_unaligned(ptr) } + } + + fn rd08(&self, offset: isize) -> u8 { + let mut addr = offset; + if addr >= self.image0_size && self.imaged_addr != 0 { + addr -= self.image0_size; + addr += self.imaged_addr as isize; + } + let ptr: *const u8 = self.bios_vec.as_ptr(); + unsafe { *(ptr.offset(addr)) } + } + + fn ptr(&self, offset: isize) -> *const u8 { + let mut addr = offset; + if addr >= self.image0_size && self.imaged_addr != 0 { + addr -= self.image0_size; + addr += self.imaged_addr as isize; + } + (self.bios_vec.as_ptr() as usize + addr as usize) as *const u8 + } + + fn findbytes(&self, needle: &[u8]) -> usize { + for i in 0..self.bios_vec.len() - needle.len() { + let mut found = false; + for j in 0..needle.len() { + if self.bios_vec[i + j] != needle[j] { + break; + } + if j == needle.len() - 1 { + found = true; + } + } + if found { + return i; + } + } + 0 + } + + fn bit_entry(&self, id: u8, bit_entry: &mut BitEntry) -> Result { + let mut entries = self.rd08((self.bit_offset + 0x0a) as isize); + let rec_size = self.rd08((self.bit_offset + 0x09) as isize); + let mut entry = (self.bit_offset + 0x0c) as isize; + + while { + let tmp = entries; + entries -= 1; + tmp != 0 + } { + let idx = self.rd08(entry); + if idx == id { + bit_entry.id = self.rd08(entry); + bit_entry.version = self.rd08(entry + 1); + bit_entry.length = self.rd16(entry + 2); + bit_entry.offset = self.rd16(entry + 4); + return Ok(()); + } + entry += (rec_size as usize) as isize; + } + Err(ENOENT) + } + + fn pmu_te(&self, ver: &mut u8, hdr: &mut u8, cnt: &mut u8, len: &mut u8) -> u32 { + let mut bit_p: BitEntry = Default::default(); + + let mut data = 0; + + let _ = self.bit_entry(112_u8, &mut bit_p); + if bit_p.id != 112_u8 { + return 0; + } + + if bit_p.version == 2 && bit_p.length >= 4 { + data = self.rd32(bit_p.offset as isize); + } + if data != 0 { + *ver = self.rd08(data as isize); + *hdr = self.rd08((data + 0x01) as isize); + *len = self.rd08((data + 0x02) as isize); + *cnt = self.rd08((data + 0x03) as isize); + } + data + } + + fn pmu_ee(&self, idx: u8, ver: &mut u8, hdr: &mut u8) -> u32 { + let mut cnt: u8 = 0; + let mut len: u8 = 0; + let mut data = self.pmu_te(ver, hdr, &mut cnt, &mut len); + if data != 0 && idx < cnt { + data = data + (*hdr as u32) + (idx as u32 * len as u32); + *hdr = len; + return data; + } + 0 + } + + fn pmu_ep(&self, idx: u8, ver: &mut u8, hdr: &mut u8, info: &mut BiosPmuE) -> u32 { + let data = self.pmu_ee(idx, ver, hdr); + if data != 0 { + info.pmutype = self.rd08(data as isize); + info.data = self.rd32((data + 0x02) as isize); + } + data + } + + fn pcir_te(&self, offset: isize, ver: &mut u8, hdr: &mut u16) -> u32 { + let mut data = self.rd16(offset + 0x18) as u32; + if data != 0 { + data += offset as u32; + match self.rd32(data as isize) { + 0x52494350 | 0x53494752 | 0x5344504e => { + *hdr = self.rd16((data + 0x0a) as isize); + *ver = self.rd08((data + 0x0c) as isize); + } + _ => { + pr_debug!( + "{:#x} Unknown PCIR signature {:#x}\n", + data, + self.rd32(data as isize) + ); + data = 0; + *hdr = 0; + *ver = 0; + } + } + } + data + } + + fn pcir_tp(&self, offset: isize, ver: &mut u8, hdr: &mut u16, info: &mut BiosPcirT) -> u32 { + let data = self.pcir_te(offset, ver, hdr); + if data != 0 { + info.vendor_id = self.rd16((data + 0x04) as isize); + info.device_id = self.rd16((data + 0x06) as isize); + info.class_code[0] = self.rd08((data + 0x0d) as isize); + info.class_code[1] = self.rd08((data + 0x0e) as isize); + info.class_code[2] = self.rd08((data + 0x0f) as isize); + info.image_size = (self.rd16((data + 0x10) as isize) as u32) * 512; + info.image_type = self.rd08((data + 0x14) as isize); + info.last = self.rd08((data + 0x15) as isize) != 0; + } + data + } + + fn npde_te(&self, offset: isize) -> u32 { + let mut pcir: BiosPcirT = Default::default(); + let mut ver: u8 = 0; + let mut hdr: u16 = 0; + let mut data = self.pcir_tp(offset, &mut ver, &mut hdr, &mut pcir); + data = (data + (hdr as u32) + 0x0f) & !0x0f; + if data != 0 { + match self.rd32(data as isize) { + 0x4544504e => {} + _ => { + pr_debug!( + "{:#x} Unknown NPDE signature {:#x}\n", + data, + self.rd32(data as isize) + ); + data = 0; + } + } + } + data + } + + fn npde_tp(&self, offset: isize, info: &mut BiosNpdeT) -> u32 { + let data = self.npde_te(offset); + if data != 0 { + info.image_size = (self.rd16((data + 0x08) as isize) as u32) * 512; + info.last = (self.rd08((data + 0x0a) as isize) & 0x80) != 0; + } + data + } + + fn imagen(&self, image: &mut BiosImage) -> bool { + let data = self.rd16(image.base as isize); + + pr_info!("bios {:#x}", data); + + match data { + 0xaa55 | 0xbb77 | 0x4e56 => {} + _ => return false, + }; + + let mut pcir: BiosPcirT = Default::default(); + let mut ver: u8 = 0; + let mut hdr: u16 = 0; + + let data = self.pcir_tp(image.base as isize, &mut ver, &mut hdr, &mut pcir); + if data == 0 { + return false; + } + image.size = pcir.image_size as usize; + image.itype = pcir.image_type; + image.last = pcir.last; + + if pcir.image_type != 0x70 { + let mut npde: BiosNpdeT = Default::default(); + let data = self.npde_tp(image.base as isize, &mut npde); + if data != 0 { + image.size = npde.image_size as usize; + image.last = npde.last; + } + } else { + image.last = true; + } + true + } + + fn fetch(&mut self, bar: &RevocableGuard<'_, Bar0>, offset: usize, length: usize) -> Result { + let vec = &mut self.bios_vec; + let gfp = self.bios_vec_gfp; + + vec.resize(offset + length, 0, gfp)?; + for i in (offset..offset + length).step_by(4) { + let ptr: *mut u32 = vec.as_mut_ptr() as *mut u32; + unsafe { + *(ptr.add(i / 4)) = bar.try_readl(PROM_OFFSET + i)?; + } + } + Ok(()) + } + + pub(crate) fn probe(&mut self, bar: &Devres<Bar0>) -> Result { + /* just do PROM probe */ + /* hardcoded lots */ + let bar = bar.try_access().ok_or(ENXIO)?; + let mut data = bar.readl(0x88000 + 0x50); + data &= !0x00000001; + bar.writel(data, 0x88000 + 0x50); + + let mut image: BiosImage = Default::default(); + let mut idx = 0; + loop { + self.fetch(&bar, image.base, image.base + 4096)?; + + let imaged_addr = self.imaged_addr; + self.imaged_addr = 0; + if !self.imagen(&mut image) { + self.imaged_addr = imaged_addr; + break; + } + + if idx == 0 { + self.image0_size = image.size as isize; + } + + pr_info!( + "bios pcir: {:#x} {:#x} {}\n", + image.size, + image.itype, + image.last + ); + + if image.size > 0x100000 { + break; + } + + if image.itype == 0xe0 { + self.imaged_addr = image.base; + } + + self.fetch(&bar, image.base, image.size)?; + + if image.last { + self.imaged_addr = imaged_addr; + break; + } + + image.base += image.size; + if image.base > 0x100000 { + break; + } + idx += 1; + } + + pr_info!("bios size {:#x}\n", self.bios_vec.len()); + let mut bmp_vec = KVec::new(); //vec![];//0xff, 0x7f, 0x78, 0x86, 0x00]; + bmp_vec.push(0xff, GFP_KERNEL)?; + bmp_vec.push(0x7f, GFP_KERNEL)?; + bmp_vec.push(0x78, GFP_KERNEL)?; + bmp_vec.push(0x86, GFP_KERNEL)?; + bmp_vec.push(0x00, GFP_KERNEL)?; + self.bmp_offset = self.findbytes(&bmp_vec); + pr_info!( + "bmp offset is {:#x} {:#x}\n", + bmp_vec.len(), + self.bmp_offset + ); + + let mut bit_vec = KVec::new(); //ec![0xff, 0xb8, 'B', 'I', 'T']; + bit_vec.push(0xff, GFP_KERNEL)?; + bit_vec.push(0xb8, GFP_KERNEL)?; + bit_vec.push(b'B', GFP_KERNEL)?; + bit_vec.push(b'I', GFP_KERNEL)?; + bit_vec.push(b'T', GFP_KERNEL)?; + self.bit_offset = self.findbytes(&bit_vec); + pr_info!( + "bit offset is {:#x} {:#x}\n", + bit_vec.len(), + self.bit_offset + ); + + let mut bit_entry: BitEntry = Default::default(); + + self.bit_entry(b'i', &mut bit_entry)?; + pr_info!("bit i {:#x} {:#x}\n", bit_entry.offset, bit_entry.length); + + if bit_entry.length >= 4 { + let major = self.rd08((bit_entry.offset + 3) as isize); + let chip = self.rd08((bit_entry.offset + 2) as isize); + let minor = self.rd08((bit_entry.offset + 1) as isize); + #[allow(clippy::identity_op)] + let micro = self.rd08((bit_entry.offset + 0) as isize); + let patch = self.rd08((bit_entry.offset + 4) as isize); + + pr_info!( + "version {:x}:{:x}:{:x}:{:x}:{:x}\n", + major, + chip, + minor, + micro, + patch + ); + } + + Ok(()) + } + + pub(crate) fn find_fwsec(&mut self) -> Result { + // find fwsec? + let mut idx = 0; + let mut ver = 0; + let mut hdr = 0; + let mut pmue: BiosPmuE = Default::default(); + let mut found: i8 = -1; + loop { + let data = self.pmu_ep(idx, &mut ver, &mut hdr, &mut pmue); + if data == 0 { + break; + } + if pmue.pmutype == 0x85 { + found = idx as i8; + break; + } + idx += 1; + } + + match found { + -1 => { + return Err(ENOENT); + } + _ => { + pr_info!("pmu found idx {}\n", found); + } + } + + let desc_hdr = self.rd32(pmue.data as isize); + pr_info!( + "flcn {:#x} {} {}\n", + desc_hdr, + (desc_hdr & 0xffff0000) >> 16, + (desc_hdr & 0xff00) >> 8 + ); + + let ucode_ptr = self.ptr(pmue.data as isize); + unsafe { + let ucodedesc: *const FalconUCodeDescV3 = ucode_ptr as *const FalconUCodeDescV3; + pr_info!( + "ucode desc stored {:#x}, imem {:#x} {:#x} {:#x} dmem {:#x} {:#x}\n", + (*ucodedesc).StoredSize, + (*ucodedesc).IMEMPhysBase, + (*ucodedesc).IMEMLoadSize, + (*ucodedesc).IMEMVirtBase, + (*ucodedesc).DMEMPhysBase, + (*ucodedesc).DMEMLoadSize + ); + } + Ok(()) + } +} diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index 69d0efeb125e..8f5753f04dba 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -22,7 +22,7 @@ pub(crate) struct NovaData { pub(crate) pdev: pci::Device, } -const BAR0_SIZE: usize = 8; +const BAR0_SIZE: usize = 0x1000000; pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>; const INFO: drm::drv::DriverInfo = drm::drv::DriverInfo { @@ -49,9 +49,13 @@ impl pci::Driver for NovaDriver { pdev.set_master(); let bar = pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova"))?; + + let gpu = Gpu::new(pdev, bar)?; + gpu.init()?; + let data = Arc::new( NovaData { - gpu: Gpu::new(pdev, bar)?, + gpu, pdev: pdev.clone(), }, GFP_KERNEL, diff --git a/drivers/gpu/drm/nova/fwsec.rs b/drivers/gpu/drm/nova/fwsec.rs new file mode 100644 index 000000000000..9b0d8cc1e057 --- /dev/null +++ b/drivers/gpu/drm/nova/fwsec.rs @@ -0,0 +1,37 @@ +#![allow(non_snake_case)] +#[allow(dead_code)] +#[repr(C)] +pub(crate) struct FalconUCodeDescV2 { + Hdr: u32, + StoredSize: u32, + UncompressedSize: u32, + VirtualEntry: u32, + InterfaceOffset: u32, + IMEMPhysBase: u32, + IMEMLoadSize: u32, + IMEMVirtBase: u32, + IMEMSecBase: u32, + IMEMSecSize: u32, + DMEMOffset: u32, + DMEMLoadSize: u32, + altIMEMLoadSize: u32, + altDMEMLoadSize: u32, +} + +#[repr(C)] +pub(crate) struct FalconUCodeDescV3 { + pub(crate) Hdr: u32, + pub(crate) StoredSize: u32, + PKCDataOffset: u32, + InterfaceOffset: u32, + pub(crate) IMEMPhysBase: u32, + pub(crate) IMEMLoadSize: u32, + pub(crate) IMEMVirtBase: u32, + pub(crate) DMEMPhysBase: u32, + pub(crate) DMEMLoadSize: u32, + EngineIdMask: u16, + UcodeId: u8, + SignatureCount: u8, + SignatureVersions: u16, + Reserved: u16, +} diff --git a/drivers/gpu/drm/nova/gpu.rs b/drivers/gpu/drm/nova/gpu.rs index f3dc126b0b12..e6a282dd2858 100644 --- a/drivers/gpu/drm/nova/gpu.rs +++ b/drivers/gpu/drm/nova/gpu.rs @@ -1,13 +1,21 @@ // SPDX-License-Identifier: GPL-2.0 use kernel::{ - alloc::flags::*, device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, - str::CString, sync::Arc, + alloc::flags::*, + device, + devres::Devres, + error::code::*, + firmware, fmt, pci, + prelude::*, + str::CString, + sync::{Arc, Mutex}, }; use crate::driver::Bar0; use core::fmt::Debug; +use crate::bios::*; + /// Enum representing the GPU chipset. #[derive(Debug)] pub(crate) enum Chipset { @@ -66,6 +74,8 @@ pub(crate) struct Gpu { /// MMIO mapping of PCI BAR 0 bar: Devres<Bar0>, fw: Firmware, + #[pin] + bios: Mutex<Bios>, } // TODO replace with something like derive(FromPrimitive) @@ -169,6 +179,23 @@ impl Gpu { spec.boot0 ); - Arc::pin_init(try_pin_init!(Self { spec, bar, fw }), GFP_KERNEL) + Arc::pin_init( + try_pin_init!(Self { + spec, + bar, + fw, + bios <- kernel::new_mutex!(Bios::new()), + }), + GFP_KERNEL, + ) + } + + pub(crate) fn init(&self) -> Result { + let mut bios = self.bios.lock(); + + bios.probe(&self.bar)?; + bios.find_fwsec()?; + + Ok(()) } } diff --git a/drivers/gpu/drm/nova/nova.rs b/drivers/gpu/drm/nova/nova.rs index c675be404d9b..1c88df4fe3af 100644 --- a/drivers/gpu/drm/nova/nova.rs +++ b/drivers/gpu/drm/nova/nova.rs @@ -2,8 +2,10 @@ //! Nova GPU Driver +mod bios; mod driver; mod file; +mod fwsec; mod gem; mod gpu; |