From 66954d997d801743156f5eeda7a23ba1d9b6c098 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Thu, 18 Jul 2024 10:54:23 -0500 Subject: [PATCH] nouveau/headers: Add an nv_push crate in Rust Part-of: --- src/nouveau/headers/meson.build | 12 ++ src/nouveau/headers/nv_push_rs/lib.rs | 222 ++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 src/nouveau/headers/nv_push_rs/lib.rs diff --git a/src/nouveau/headers/meson.build b/src/nouveau/headers/meson.build index 29a5b17520a..759e8831a21 100644 --- a/src/nouveau/headers/meson.build +++ b/src/nouveau/headers/meson.build @@ -151,4 +151,16 @@ if with_nouveau_vk idep_nvidia_headers_rs = declare_dependency( link_with : _nvidia_headers_rs, ) + + _libnv_push_rs = static_library( + 'nv_push_rs', + files('nv_push_rs/lib.rs'), + gnu_symbol_visibility: 'hidden', + rust_abi: 'rust', + dependencies: [idep_nvidia_headers_rs], + ) + + idep_nv_push_rs = declare_dependency( + link_with : _libnv_push_rs, + ) endif diff --git a/src/nouveau/headers/nv_push_rs/lib.rs b/src/nouveau/headers/nv_push_rs/lib.rs new file mode 100644 index 00000000000..d3a5955279f --- /dev/null +++ b/src/nouveau/headers/nv_push_rs/lib.rs @@ -0,0 +1,222 @@ +// Copyright © 2024 Collabora, Ltd. +// SPDX-License-Identifier: MIT + +use nvidia_headers::ArrayMthd; +use nvidia_headers::Mthd; + +pub const MAX_MTHD_LEN: u16 = 0x1fff; +pub const MAX_MTHD_ADDR: u16 = 0x7fff; + +fn class_to_subc(class: u16) -> u8 { + match class & 0xff { + 0x97 => 0, + 0xc0 => 1, + 0x39 => 2, + 0x2d => 3, + 0xb5 => 4, + _ => panic!("Invalid class: {class}"), + } +} + +#[repr(u8)] +enum MthdType { + /// Each dword increments the address by one + NInc = 1, + /// The first dword increments the address by one + OneInc = 3, + /// Instead of a length, stores 13 bits of immediate data + Immd = 4, + /// The address is not incremented + ZeroInc = 5, +} + +impl TryFrom for MthdType { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(MthdType::NInc), + 3 => Ok(MthdType::OneInc), + 4 => Ok(MthdType::Immd), + 5 => Ok(MthdType::ZeroInc), + _ => Err("Invalid method type"), + } + } +} + +/// A method header. +/// +/// Methods start with a header that can encode the `IncType`, the subclass, +/// an address and the size. Optionally, the header can contain an address +/// and an immediate instead. +#[repr(transparent)] +struct MthdHeader(u32); + +impl MthdHeader { + fn from_bits_mut(bits: &mut u32) -> &mut Self { + // This is always safe beause a reference is always safe to + // derefence. + unsafe { &mut *(bits as *mut u32 as *mut MthdHeader) } + } + + fn to_bits(self) -> u32 { + self.0 + } + + fn new(mthd_type: MthdType, subc: u8, addr: u16, data: u16) -> Self { + debug_assert!(subc <= 0x7); + debug_assert!(addr & 0x3 == 0 && addr <= MAX_MTHD_ADDR); + debug_assert!(data <= MAX_MTHD_LEN); + + let mthd_type: u32 = (mthd_type as u8).into(); + let subc: u32 = subc.into(); + let addr: u32 = addr.into(); + let data: u32 = data.into(); + + Self((mthd_type << 29) | (data << 16) | (subc << 13) | (addr >> 2)) + } + + fn new_immd(immd: u16, subc: u8, addr: u16) -> Self { + Self::new(MthdType::Immd, subc, addr, immd) + } + + fn mthd_type(&self) -> MthdType { + ((self.0 >> 29) as u8).try_into().unwrap() + } + + fn set_mthd_type(&mut self, mthd_type: MthdType) { + self.0 &= 0x1fffffff; + self.0 |= (mthd_type as u8 as u32) << 29; + } + + fn subc(&self) -> u8 { + (self.0 >> 13 & 0x7) as u8 + } + + fn addr(&self) -> u16 { + ((self.0 & 0x1fff) << 2) as u16 + } + + fn len(&self) -> u16 { + debug_assert!(!matches!(self.mthd_type(), MthdType::Immd)); + (self.0 >> 16 & 0x1fff) as u16 + } + + fn set_len(&mut self, len: u16) { + debug_assert!(len <= MAX_MTHD_LEN); + self.0 &= 0xe000ffff; + self.0 |= u32::from(len) << 16; + } + + fn add_len(&mut self, count: u16) { + let new_len = self.len() + count; + self.set_len(new_len); + } +} + +pub struct Push { + /// The internal memory. Has to be uploaded to a BO through flush(). + mem: Vec, + /// Last DW that is an incrementing type or usize::MAX + last_inc: usize, +} + +impl Push { + /// Instantiates a new push buffer. + pub fn new() -> Self { + Self { + mem: Vec::new(), + last_inc: usize::MAX, + } + } + + fn push_mthd_bits(&mut self, subc: u8, addr: u16, bits: u32) { + let current_len = self.mem.len(); + if let Some(last) = self.mem.get_mut(self.last_inc) { + let last = MthdHeader::from_bits_mut(last); + debug_assert!(last.len() >= 1); + debug_assert!( + self.last_inc + usize::from(last.len()) + 1 == current_len + ); + if subc == last.subc() { + match last.mthd_type() { + MthdType::NInc => { + if addr == last.addr() + last.len() * 4 { + last.add_len(1); + self.mem.push(bits); + return; + } else if last.len() == 1 && addr == last.addr() { + last.set_mthd_type(MthdType::ZeroInc); + last.add_len(1); + self.mem.push(bits); + return; + } else if last.len() == 2 && addr == last.addr() + 4 { + last.set_mthd_type(MthdType::OneInc); + last.add_len(1); + self.mem.push(bits); + return; + } + } + MthdType::ZeroInc => { + if addr == last.addr() { + last.add_len(1); + self.mem.push(bits); + return; + } + } + MthdType::OneInc => { + if addr == last.addr() + 4 { + last.add_len(1); + self.mem.push(bits); + return; + } + } + _ => (), + } + } + } + + // Otherwise, we need a new method header. + // + // Methods that use 13bits or lower can be encoded as immediates + // directly. + if bits <= 0x1fff { + self.last_inc = usize::MAX; + let header = MthdHeader::new_immd(bits as u16, subc, addr); + self.mem.push(header.to_bits()); + } else { + self.last_inc = self.mem.len(); + let header = MthdHeader::new(MthdType::NInc, subc, addr, 1); + self.mem.push(header.to_bits()); + self.mem.push(bits); + } + } + + pub fn push_method(&mut self, mthd: M) { + self.push_mthd_bits(class_to_subc(M::CLASS), M::ADDR, mthd.to_bits()); + } + + pub fn push_array_method(&mut self, i: usize, mthd: M) { + self.push_mthd_bits( + class_to_subc(M::CLASS), + M::addr(i), + mthd.to_bits(), + ); + } + + /// Push an array of dwords into the push buffer + pub fn push_inline_data(&mut self, data: &[u32]) { + if self.last_inc != usize::MAX { + panic!("Inline data must only be placed after a method header"); + } + self.mem.extend_from_slice(data); + } +} + +impl std::ops::Deref for Push { + type Target = [u32]; + + fn deref(&self) -> &[u32] { + &self.mem + } +}