diff options
author | Dave Airlie <airlied@redhat.com> | 2024-01-05 16:42:27 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2024-01-11 11:18:01 +1000 |
commit | 24253b0045e1607b81cb8b7e0cb81be05b38bad4 (patch) | |
tree | f179b66af0c3bf18715c0e32bbc4ac9cf64cc53d | |
parent | 9f17b68893b623634dcfac7ff56544825247ef82 (diff) |
start parsing bios headers.
the vec is probably a bad idea
this dumps out the BIOS version from the bit i table.
this now finds the pmuEp table for fwsec
-rw-r--r-- | drivers/gpu/drm/nova/bios.rs | 428 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/gpu.rs | 66 | ||||
-rw-r--r-- | drivers/gpu/drm/nova/nova_drv.rs | 45 |
3 files changed, 522 insertions, 17 deletions
diff --git a/drivers/gpu/drm/nova/bios.rs b/drivers/gpu/drm/nova/bios.rs new file mode 100644 index 000000000000..30085b9f4273 --- /dev/null +++ b/drivers/gpu/drm/nova/bios.rs @@ -0,0 +1,428 @@ + +use kernel::io_mem::IoMem; +use kernel::prelude::*; + +use alloc::vec::Vec; +use crate::BAR_SIZE; + +use core::ptr::read_unaligned; +const PROM_OFFSET: usize = 0x300000; + +#[repr(C)] +#[derive(Default)] +struct BitEntry { + id: u8, + version: u8, + length: u16, + offset: u16, +} + +#[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: Vec<u8>, +} + +impl Bios { + pub(crate) fn new() -> Self + { + Self { + image0_size : 0, + imaged_addr : 0, + bmp_offset : 0, + bit_offset : 0, + bios_vec: Default::default(), + } + } + + 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() as *const u8; + 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: &Vec<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(bios: &Bios, id: u8, bit_entry: &mut BitEntry) -> Result + { + let mut entries = bios.rd08((bios.bit_offset + 0x0a) as isize); + let rec_size = bios.rd08((bios.bit_offset + 0x09) as isize); + let mut entry = (bios.bit_offset + 0x0c) as isize; + + while { let tmp = entries; entries -= 1; tmp != 0 } { + let idx = bios.rd08(entry); + if idx == id { + bit_entry.id = bios.rd08(entry); + bit_entry.version = bios.rd08(entry + 1); + bit_entry.length = bios.rd16(entry + 2); + bit_entry.offset = bios.rd16(entry + 4); + return Ok(()); + } + entry += (rec_size as usize) as isize; + } + Err(ENOENT) + } + + fn pmu_te(bios: &Bios, ver: &mut u8, hdr: &mut u8, cnt: &mut u8, len: &mut u8) -> u32 + { + let mut bit_p: BitEntry = Default::default(); + + let mut data = 0; + + Self::bit_entry(bios, 112 as u8, &mut bit_p); + if bit_p.id != 112 as u8 { + return 0; + } + + if bit_p.version == 2 && bit_p.length >= 4 { + data = bios.rd32(bit_p.offset as isize); + } + if data != 0 { + *ver = bios.rd08(data as isize); + *hdr = bios.rd08((data + 0x01) as isize); + *len = bios.rd08((data + 0x02) as isize); + *cnt = bios.rd08((data + 0x03) as isize); + } + data + } + + fn pmu_ee(bios: &Bios, 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(bios, ver, hdr, &mut cnt, &mut len); + if data != 0 && idx < cnt { + data = data + (*hdr as u32) + ((idx as u32 * len as u32) as u32); + *hdr = len; + return data; + } + 0 + } + + fn pmu_ep(bios: &Bios, idx: u8, ver: &mut u8, hdr: &mut u8, info: &mut BiosPmuE) -> u32 + { + let mut data = Self::pmu_ee(bios, idx, ver, hdr); + if data != 0 { + info.pmutype = bios.rd08(data as isize); + info.data = bios.rd32((data + 0x02) as isize); + } + data as u32 + } + + + fn pcir_te(bios: &Bios, offset: isize, ver: &mut u8, hdr: &mut u16) -> u32 + { + let mut data = bios.rd16(offset + 0x18) as u32; + if data != 0 { + data += offset as u32; + match bios.rd32(data as isize) { + 0x52494350 | 0x53494752 | 0x5344504e => { + *hdr = bios.rd16((data + 0x0a) as isize); + *ver = bios.rd08((data + 0x0c) as isize); + } + _ => { + pr_debug!("{:#x} Unknown PCIR signature {:#x}\n", + data, bios.rd32(data as isize)); + data = 0; + *hdr = 0; + *ver = 0; + } + } + } + data as u32 + } + + fn pcir_tp(bios: &Bios, offset: isize, ver: &mut u8, hdr: &mut u16, info: &mut BiosPcirT) -> u32 + { + let data = Self::pcir_te(bios, offset, ver, hdr); + if data != 0 { + info.vendor_id = bios.rd16((data + 0x04) as isize); + info.device_id = bios.rd16((data + 0x06) as isize); + info.class_code[0] = bios.rd08((data + 0x0d) as isize); + info.class_code[1] = bios.rd08((data + 0x0e) as isize); + info.class_code[2] = bios.rd08((data + 0x0f) as isize); + info.image_size = (bios.rd16((data + 0x10) as isize) as u32) * 512; + info.image_type = bios.rd08((data + 0x14) as isize); + info.last = bios.rd08((data + 0x15) as isize) != 0; + } + data as u32 + } + + fn npde_te(bios: &Bios, 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(bios, offset, &mut ver, &mut hdr, &mut pcir); + data = (data + (hdr as u32) + 0x0f) & !0x0f; + if data != 0 { + match bios.rd32(data as isize) { + 0x4544504e => {} + _ => { + pr_debug!("{:#x} Unknown NPDE signature {:#x}\n", + data, bios.rd32(data as isize)); + data = 0; + } + } + } + data + } + + fn npde_tp(bios: &Bios, offset: isize, info: &mut BiosNpdeT) -> u32 + { + let data = Self::npde_te(bios, offset); + if data != 0 { + info.image_size = (bios.rd16((data + 0x08) as isize) as u32) * 512; + info.last = (bios.rd08((data + 0x0a) as isize) & 0x80) != 0; + } + data + } + + fn imagen(bios: &Bios, image: &mut BiosImage) -> bool + { + let data = bios.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(bios, 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(bios, 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(vec: &mut Vec<u8>, bar: &IoMem<BAR_SIZE>, offset: usize, length: usize) -> Result + { + vec.try_resize(offset + length, 0)?; + for i in (offset..offset+length).step_by(4) { + let ptr: *mut u32 = vec.as_mut_ptr() as *mut u32; + unsafe { *(ptr.offset((i / 4) as isize)) = bar.readl(PROM_OFFSET + i); } + } + Ok(()) + } + + pub(crate) fn probe(&mut self, bar: &IoMem<BAR_SIZE>) -> Result + { + /* just do PROM probe */ + /* hardcoded lots */ + 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(&mut self.bios_vec, bar, image.base, image.base + 4096)?; + + let imaged_addr = self.imaged_addr; + self.imaged_addr = 0; + if Self::imagen(&self, &mut image) == false { + 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(&mut self.bios_vec, 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 = Vec::new();//vec![];//0xff, 0x7f, 0x78, 0x86, 0x00]; + bmp_vec.try_push(0xff); + bmp_vec.try_push(0x7f); + bmp_vec.try_push(0x78); + bmp_vec.try_push(0x86); + bmp_vec.try_push(0x00); + 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 = Vec::new();//ec![0xff, 0xb8, 'B', 'I', 'T']; + bit_vec.try_push(0xff); + bit_vec.try_push(0xb8); + bit_vec.try_push('B' as u8); + bit_vec.try_push('I' as u8); + bit_vec.try_push('T' as u8); + 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(); + + Bios::bit_entry(self, 'i' as u8, &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); + 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(self, 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); + } + } + Ok(()) + } +} diff --git a/drivers/gpu/drm/nova/gpu.rs b/drivers/gpu/drm/nova/gpu.rs new file mode 100644 index 000000000000..e53118d69570 --- /dev/null +++ b/drivers/gpu/drm/nova/gpu.rs @@ -0,0 +1,66 @@ + +use kernel::{ + bindings, + error::Error, + error::Result, + io_mem::IoMem, + c_str, + device::{self, Device, RawDevice}, + firmware::{Firmware}, + pci, +}; + +use crate::bios::*; + +use crate::BAR_SIZE; + +pub(crate) enum CardType { + TU100 = 0x160, + GA100 = 0x170, + AD100 = 0x190, +} + +pub(crate) struct Gpu { + booter_load_fw: Firmware, + booter_unload_fw: Firmware, + gsp_fw: Firmware, + card_type: CardType, + bar: IoMem<BAR_SIZE>, + bios: Bios +} + +impl Gpu { + pub(crate) fn new(card_type: CardType, bar: IoMem<BAR_SIZE>) -> Self + { + Self { + booter_load_fw: Firmware::new(), + booter_unload_fw: Firmware::new(), + gsp_fw: Firmware::new(), + card_type: card_type, + bar: bar, + bios: Bios::new(), + } + } + + pub(crate) fn init(&mut self, dev: &mut pci::Device) -> Result { + match self.booter_load_fw.request(c_str!("nvidia/ad102/gsp/booter_load-535.113.01.bin"), dev) { + Err(e) => return Err(e), + Ok(_) => () + } + + match self.booter_unload_fw.request(c_str!("nvidia/ad102/gsp/booter_unload-535.113.01.bin"), dev) { + Err(e) => return Err(e), + Ok(_) => () + } + + match self.gsp_fw.request(c_str!("nvidia/ad102/gsp/gsp-535.113.01.bin"), dev) { + Err(e) => return Err(e), + Ok(_) => (), + } + + self.bios.probe(&self.bar)?; + + self.bios.find_fwsec()?; + Ok(()) + } +} diff --git a/drivers/gpu/drm/nova/nova_drv.rs b/drivers/gpu/drm/nova/nova_drv.rs index c4b5b6cd7e89..506ecc9f71e5 100644 --- a/drivers/gpu/drm/nova/nova_drv.rs +++ b/drivers/gpu/drm/nova/nova_drv.rs @@ -1,6 +1,11 @@ /// Nova GPU Driver // For now, catch 'em all! + +mod gpu; +mod bios; + +use crate::gpu::CardType; use alloc::boxed::Box; use core::{ cell::UnsafeCell, @@ -26,8 +31,12 @@ use kernel::{ types::AtomicOptionalBoxedPtr, }; +use gpu::Gpu; struct NovaDevice; +/// BAR Size +pub(crate) const BAR_SIZE: usize = 16777216; + impl pci::Driver for NovaDevice { define_pci_id_table! { @@ -35,16 +44,16 @@ impl pci::Driver for NovaDevice { [ (pci::DeviceId::new(bindings::PCI_VENDOR_ID_NVIDIA, bindings::PCI_ANY_ID as u32), None) ] } - fn probe(_dev: &mut pci::Device, _id_info: Option<&Self::IdInfo>) -> Result { + fn probe(dev: &mut pci::Device, _id_info: Option<&Self::IdInfo>) -> Result { pr_info!("probe()\n"); - _dev.enable_device_mem()?; - _dev.set_master(); + dev.enable_device_mem()?; + dev.set_master(); - let bars = _dev.select_bars(bindings::IORESOURCE_MEM.into()); + let bars = dev.select_bars(bindings::IORESOURCE_MEM.into()); - let res = _dev.take_resource(0).ok_or(ENXIO)?; - let bar = unsafe { IoMem::<16777216>::try_new(res) }?; + let res = dev.take_resource(0).ok_or(ENXIO)?; + let bar = unsafe { IoMem::<BAR_SIZE>::try_new(res) }?; let boot0 = u64::from_le(bar.readq(0)); @@ -55,19 +64,21 @@ impl pci::Driver for NovaDevice { let chipset = (boot0 & 0x1ff00000) >> 20; let chiprev = boot0 & 0xff; - match chipset & 0x1f0 { - 0x160 => pr_info!("TU100"), - 0x170 => pr_info!("GA100"), - 0x190 => pr_info!("AD100"), + let card_type = match chipset & 0x1f0 { + 0x160 => { pr_info!("TU100"); CardType::TU100 }, + 0x170 => { pr_info!("GA100"); CardType::GA100 }, + 0x190 => { pr_info!("AD100"); CardType::AD100 }, _ => return Err(ENODEV) - } + }; + + let mut gpu = Gpu::new(card_type, bar); - let mut fw = Firmware::new(); - match fw.request(c_str!("nvidia/ad102/gsp/booter_load-535.113.01.bin"), _dev) { - Err(e) => return Err(e), - Ok(()) => pr_info!("loaded fw: {} ", fw.size()) - } - Ok(()) + let _ = gpu.init(dev)?; +// match self.booter_load_fw.request(c_str!("nvidia/ad102/gsp/booter_load-535.113.01.bin"), dev) { +// Err(e) => return Err(e), +// Ok(()) => pr_info!("loaded fw: {} ", self.booter_load_fw.size()) + // } + Ok(()) } fn remove(_data: &Self::Data) { |