summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2024-01-05 16:42:27 +1000
committerDave Airlie <airlied@redhat.com>2024-01-11 11:18:01 +1000
commit24253b0045e1607b81cb8b7e0cb81be05b38bad4 (patch)
treef179b66af0c3bf18715c0e32bbc4ac9cf64cc53d
parent9f17b68893b623634dcfac7ff56544825247ef82 (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.rs428
-rw-r--r--drivers/gpu/drm/nova/gpu.rs66
-rw-r--r--drivers/gpu/drm/nova/nova_drv.rs45
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) {