[RFC PATCH] drm: panthor: add dev_coredumpv support

Alice Ryhl aliceryhl at google.com
Tue Jul 23 09:53:57 UTC 2024


On Thu, Jul 11, 2024 at 12:52 AM Daniel Almeida
<daniel.almeida at collabora.com> wrote:
>
> Dump the state of the GPU. This feature is useful for debugging purposes.
> ---
> Hi everybody!
>
> For those looking for a branch instead, see [0].
>
> I know this patch has (possibly many) issues. It is meant as a
> discussion around the GEM abstractions for now. In particular, I am
> aware of the series introducing Rust support for vmalloc and friends -
> that is some very nice work! :)
>
> Danilo, as we've spoken before, I find it hard to work with `rust: drm:
> gem: Add GEM object abstraction`. My patch is based on v1, but IIUC
> the issue remains in v2: it is not possible to build a gem::ObjectRef
> from a bindings::drm_gem_object*.
>
> Furthermore, gem::IntoGEMObject contains a Driver: drv::Driver
> associated type:
>
> ```
> +/// Trait that represents a GEM object subtype
> +pub trait IntoGEMObject: Sized + crate::private::Sealed {
> +    /// Owning driver for this type
> +    type Driver: drv::Driver;
> +
> ```
>
> While this does work for Asahi and Nova - two drivers that are written
> entirely in Rust - it is a blocker for any partially-converted drivers.
> This is because there is no drv::Driver at all, only Rust functions that
> are called from an existing C driver.
>
> IMHO, are unlikely to see full rewrites of any existing C code. But
> partial convertions allows companies to write new features entirely in
> Rust, or to migrate to Rust in small steps. For this reason, I think we
> should strive to treat partially-converted drivers as first-class
> citizens.
>
> [0]: https://gitlab.collabora.com/dwlsalmeida/for-upstream/-/tree/panthor-devcoredump?ref_type=heads
>
>  drivers/gpu/drm/panthor/Kconfig         |  13 ++
>  drivers/gpu/drm/panthor/Makefile        |   2 +
>  drivers/gpu/drm/panthor/dump.rs         | 294 ++++++++++++++++++++++++
>  drivers/gpu/drm/panthor/lib.rs          |  10 +
>  drivers/gpu/drm/panthor/panthor_mmu.c   |  39 ++++
>  drivers/gpu/drm/panthor/panthor_mmu.h   |   3 +
>  drivers/gpu/drm/panthor/panthor_rs.h    |  40 ++++
>  drivers/gpu/drm/panthor/panthor_sched.c |  28 ++-
>  drivers/gpu/drm/panthor/regs.rs         | 264 +++++++++++++++++++++
>  rust/bindings/bindings_helper.h         |   3 +
>  10 files changed, 695 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/panthor/dump.rs
>  create mode 100644 drivers/gpu/drm/panthor/lib.rs
>  create mode 100644 drivers/gpu/drm/panthor/panthor_rs.h
>  create mode 100644 drivers/gpu/drm/panthor/regs.rs
>
> diff --git a/drivers/gpu/drm/panthor/Kconfig b/drivers/gpu/drm/panthor/Kconfig
> index 55b40ad07f3b..78d34e516f5b 100644
> --- a/drivers/gpu/drm/panthor/Kconfig
> +++ b/drivers/gpu/drm/panthor/Kconfig
> @@ -21,3 +21,16 @@ config DRM_PANTHOR
>
>           Note that the Mali-G68 and Mali-G78, while Valhall architecture, will
>           be supported with the panfrost driver as they are not CSF GPUs.
> +
> +config DRM_PANTHOR_RS
> +       bool "Panthor Rust components"
> +       depends on DRM_PANTHOR
> +       depends on RUST
> +       help
> +         Enable Panthor's Rust components
> +
> +config DRM_PANTHOR_COREDUMP
> +       bool "Panthor devcoredump support"
> +       depends on DRM_PANTHOR_RS
> +       help
> +         Dump the GPU state through devcoredump for debugging purposes
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
> index 15294719b09c..10387b02cd69 100644
> --- a/drivers/gpu/drm/panthor/Makefile
> +++ b/drivers/gpu/drm/panthor/Makefile
> @@ -11,4 +11,6 @@ panthor-y := \
>         panthor_mmu.o \
>         panthor_sched.o
>
> +panthor-$(CONFIG_DRM_PANTHOR_RS) += lib.o
>  obj-$(CONFIG_DRM_PANTHOR) += panthor.o
> +
> diff --git a/drivers/gpu/drm/panthor/dump.rs b/drivers/gpu/drm/panthor/dump.rs
> new file mode 100644
> index 000000000000..77fe5f420300
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/dump.rs
> @@ -0,0 +1,294 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPDX-FileCopyrightText: Copyright Collabora 2024
> +
> +//! Dump the GPU state to a file, so we can figure out what went wrong if it
> +//! crashes.
> +//!
> +//! The dump is comprised of the following sections:
> +//!
> +//! Registers,
> +//! Firmware interface (TODO)
> +//! Buffer objects (the whole VM)
> +//!
> +//! Each section is preceded by a header that describes it. Most importantly,
> +//! each header starts with a magic number that should be used by userspace to
> +//! when decoding.
> +//!
> +
> +use alloc::DumpAllocator;
> +use kernel::bindings;
> +use kernel::prelude::*;
> +
> +use crate::regs;
> +use crate::regs::GpuRegister;
> +
> +// PANT
> +const MAGIC: u32 = 0x544e4150;
> +
> +#[derive(Copy, Clone)]
> +#[repr(u32)]
> +enum HeaderType {
> +    /// A register dump
> +    Registers,
> +    /// The VM data,
> +    Vm,
> +    /// A dump of the firmware interface
> +    _FirmwareInterface,
> +}
> +
> +#[repr(C)]
> +pub(crate) struct DumpArgs {
> +    dev: *mut bindings::device,
> +    /// The slot for the job
> +    slot: i32,
> +    /// The active buffer objects
> +    bos: *mut *mut bindings::drm_gem_object,
> +    /// The number of active buffer objects
> +    bo_count: usize,
> +    /// The base address of the registers to use when reading.
> +    reg_base_addr: *mut core::ffi::c_void,
> +}
> +
> +#[repr(C)]
> +pub(crate) struct Header {
> +    magic: u32,
> +    ty: HeaderType,
> +    header_size: u32,
> +    data_size: u32,
> +}
> +
> +#[repr(C)]
> +#[derive(Clone, Copy)]
> +pub(crate) struct RegisterDump {
> +    register: GpuRegister,
> +    value: u32,
> +}
> +
> +/// The registers to dump
> +const REGISTERS: [GpuRegister; 18] = [
> +    regs::SHADER_READY_LO,
> +    regs::SHADER_READY_HI,
> +    regs::TILER_READY_LO,
> +    regs::TILER_READY_HI,
> +    regs::L2_READY_LO,
> +    regs::L2_READY_HI,
> +    regs::JOB_INT_MASK,
> +    regs::JOB_INT_STAT,
> +    regs::MMU_INT_MASK,
> +    regs::MMU_INT_STAT,
> +    regs::as_transtab_lo(0),
> +    regs::as_transtab_hi(0),
> +    regs::as_memattr_lo(0),
> +    regs::as_memattr_hi(0),
> +    regs::as_faultstatus(0),
> +    regs::as_faultaddress_lo(0),
> +    regs::as_faultaddress_hi(0),
> +    regs::as_status(0),
> +];
> +
> +mod alloc {
> +    use core::ptr::NonNull;
> +
> +    use kernel::bindings;
> +    use kernel::prelude::*;
> +
> +    use crate::dump::Header;
> +    use crate::dump::HeaderType;
> +    use crate::dump::MAGIC;
> +
> +    pub(crate) struct DumpAllocator {
> +        mem: NonNull<core::ffi::c_void>,
> +        pos: usize,
> +        capacity: usize,
> +    }
> +
> +    impl DumpAllocator {
> +        pub(crate) fn new(size: usize) -> Result<Self> {
> +            if isize::try_from(size).unwrap() == isize::MAX {
> +                return Err(EINVAL);
> +            }
> +
> +            // Let's cheat a bit here, since there is no Rust vmalloc allocator
> +            // for the time being.
> +            //
> +            // Safety: just a FFI call to alloc memory
> +            let mem = NonNull::new(unsafe {
> +                bindings::__vmalloc_noprof(
> +                    size.try_into().unwrap(),
> +                    bindings::GFP_KERNEL | bindings::GFP_NOWAIT | 1 << bindings::___GFP_NORETRY_BIT,
> +                )
> +            });
> +
> +            let mem = match mem {
> +                Some(buffer) => buffer,
> +                None => return Err(ENOMEM),
> +            };
> +
> +            // Ssfety: just a FFI call to zero out the memory. Mem and size were
> +            // used to allocate the memory above.
> +            unsafe { core::ptr::write_bytes(mem.as_ptr(), 0, size) };
> +            Ok(Self {
> +                mem,
> +                pos: 0,
> +                capacity: size,
> +            })
> +        }
> +
> +        fn alloc_mem(&mut self, size: usize) -> Option<*mut u8> {
> +            assert!(size % 8 == 0, "Allocation size must be 8-byte aligned");
> +            if isize::try_from(size).unwrap() == isize::MAX {
> +                return None;
> +            } else if self.pos + size > self.capacity {
> +                kernel::pr_debug!("DumpAllocator out of memory");
> +                None
> +            } else {
> +                let offset = self.pos;
> +                self.pos += size;
> +
> +                // Safety: we know that this is a valid allocation, so
> +                // dereferencing is safe. We don't ever return two pointers to
> +                // the same address, so we adhere to the aliasing rules. We make
> +                // sure that the memory is zero-initialized before being handed
> +                // out (this happens when the allocator is first created) and we
> +                // enforce a 8 byte alignment rule.
> +                Some(unsafe { self.mem.as_ptr().offset(offset as isize) as *mut u8 })
> +            }
> +        }
> +
> +        pub(crate) fn alloc<T>(&mut self) -> Option<&mut T> {
> +            let mem = self.alloc_mem(core::mem::size_of::<T>())? as *mut T;
> +            // Safety: we uphold safety guarantees in alloc_mem(), so this is
> +            // safe to dereference.

This code doesn't properly handle when T requires a large alignment.

> +            Some(unsafe { &mut *mem })
> +        }
> +
> +        pub(crate) fn alloc_bytes(&mut self, num_bytes: usize) -> Option<&mut [u8]> {
> +            let mem = self.alloc_mem(num_bytes)?;
> +
> +            // Safety: we uphold safety guarantees in alloc_mem(), so this is
> +            // safe to build a slice
> +            Some(unsafe { core::slice::from_raw_parts_mut(mem, num_bytes) })
> +        }

Using references for functions that allocate is generally wrong.
References imply that you don't have ownership of the memory, but
allocator functions would normally return ownership of the allocation.
As-is, the code seems to leak these allocations.

> +        pub(crate) fn alloc_header(&mut self, ty: HeaderType, data_size: u32) -> &mut Header {
> +            let hdr: &mut Header = self.alloc().unwrap();
> +            hdr.magic = MAGIC;
> +            hdr.ty = ty;
> +            hdr.header_size = core::mem::size_of::<Header>() as u32;
> +            hdr.data_size = data_size;
> +            hdr
> +        }
> +
> +        pub(crate) fn is_end(&self) -> bool {
> +            self.pos == self.capacity
> +        }
> +
> +        pub(crate) fn dump(self) -> (NonNull<core::ffi::c_void>, usize) {
> +            (self.mem, self.capacity)
> +        }
> +    }
> +}
> +
> +fn dump_registers(alloc: &mut DumpAllocator, args: &DumpArgs) {
> +    let sz = core::mem::size_of_val(&REGISTERS);
> +    alloc.alloc_header(HeaderType::Registers, sz.try_into().unwrap());
> +
> +    for reg in &REGISTERS {
> +        let dumped_reg: &mut RegisterDump = alloc.alloc().unwrap();
> +        dumped_reg.register = *reg;
> +        dumped_reg.value = reg.read(args.reg_base_addr);
> +    }
> +}
> +
> +fn dump_bo(alloc: &mut DumpAllocator, bo: &mut bindings::drm_gem_object) {
> +    let mut map = bindings::iosys_map::default();
> +
> +    // Safety: we trust the kernel to provide a valid BO.
> +    let ret = unsafe { bindings::drm_gem_vmap_unlocked(bo, &mut map as _) };
> +    if ret != 0 {
> +        pr_warn!("Failed to map BO");
> +        return;
> +    }
> +
> +    let sz = bo.size;
> +
> +    // Safety: we know that the vaddr is valid and we know the BO size.
> +    let mapped_bo: &mut [u8] =
> +        unsafe { core::slice::from_raw_parts_mut(map.__bindgen_anon_1.vaddr as *mut _, sz) };

You don't write to this memory, so I would avoid the mutable reference.

> +    alloc.alloc_header(HeaderType::Vm, sz as u32);
> +
> +    let bo_data = alloc.alloc_bytes(sz).unwrap();
> +    bo_data.copy_from_slice(&mapped_bo[..]);
> +
> +    // Safety: BO is valid and was previously mapped.
> +    unsafe { bindings::drm_gem_vunmap_unlocked(bo, &mut map as _) };

You don't need `as _` here. You can just pass a mutable reference and
Rust will automatically cast it to raw pointer.

> +}
> +
> +/// Dumps the current state of the GPU to a file
> +///
> +/// # Safety
> +///
> +/// `Args` must be aligned and non-null.
> +/// All fields of `DumpArgs` must be valid.
> +#[no_mangle]
> +pub(crate) extern "C" fn panthor_core_dump(args: *const DumpArgs) -> core::ffi::c_int {
> +    assert!(!args.is_null());
> +    // Safety: we checked whether the pointer was null. It is assumed to be
> +    // aligned as per the safety requirements.
> +    let args = unsafe { &*args };

Creating a reference requires that it isn't dangling, so the safety
requirements should require that.

Also, panthor_core_dump should be unsafe.

> +    //
> +    // TODO: Ideally, we would use the safe GEM abstraction from the kernel
> +    // crate, but I see no way to create a drm::gem::ObjectRef from a
> +    // bindings::drm_gem_object. drm::gem::IntoGEMObject is only implemented for
> +    // drm::gem::Object, which means that new references can only be created
> +    // from a Rust-owned GEM object.
> +    //
> +    // It also has a has a `type Driver: drv::Driver` associated type, from
> +    // which it can access the `File` associated type. But not all GEM functions
> +    // take a file, though. For example, `drm_gem_vmap_unlocked` (used here)
> +    // does not.
> +    //
> +    // This associated type is a blocker here, because there is no actual
> +    // drv::Driver. We're only implementing a few functions in Rust.
> +    let mut bos = match Vec::with_capacity(args.bo_count, GFP_KERNEL) {
> +        Ok(bos) => bos,
> +        Err(_) => return ENOMEM.to_errno(),
> +    };
> +    for i in 0..args.bo_count {
> +        // Safety: `args` is assumed valid as per the safety requirements.
> +        // `bos` is a valid pointer to a valid array of valid pointers.
> +        let bo = unsafe { &mut **args.bos.add(i) };
> +        bos.push(bo, GFP_KERNEL).unwrap();
> +    }
> +
> +    let mut sz = core::mem::size_of::<Header>();
> +    sz += REGISTERS.len() * core::mem::size_of::<RegisterDump>();
> +
> +    for bo in &mut *bos {
> +        sz += core::mem::size_of::<Header>();
> +        sz += bo.size;
> +    }
> +
> +    // Everything must fit within this allocation, otherwise it was miscomputed.
> +    let mut alloc = match DumpAllocator::new(sz) {
> +        Ok(alloc) => alloc,
> +        Err(e) => return e.to_errno(),
> +    };
> +
> +    dump_registers(&mut alloc, &args);
> +    for bo in bos {
> +        dump_bo(&mut alloc, bo);
> +    }
> +
> +    if !alloc.is_end() {
> +        pr_warn!("DumpAllocator: wrong allocation size");
> +    }
> +
> +    let (mem, size) = alloc.dump();
> +
> +    // Safety: `mem` is a valid pointer to a valid allocation of `size` bytes.
> +    unsafe { bindings::dev_coredumpv(args.dev, mem.as_ptr(), size, bindings::GFP_KERNEL) };
> +
> +    0
> +}
> diff --git a/drivers/gpu/drm/panthor/lib.rs b/drivers/gpu/drm/panthor/lib.rs
> new file mode 100644
> index 000000000000..faef8662d0f5
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/lib.rs
> @@ -0,0 +1,10 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPDX-FileCopyrightText: Copyright Collabora 2024
> +
> +//! The Rust components of the Panthor driver
> +
> +#[cfg(CONFIG_DRM_PANTHOR_COREDUMP)]
> +mod dump;
> +mod regs;
> +
> +const __LOG_PREFIX: &[u8] = b"panthor\0";
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
> index fa0a002b1016..f8934de41ffa 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.c
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.c
> @@ -2,6 +2,8 @@
>  /* Copyright 2019 Linaro, Ltd, Rob Herring <robh at kernel.org> */
>  /* Copyright 2023 Collabora ltd. */
>
> +#include "drm/drm_gem.h"
> +#include "linux/gfp_types.h"
>  #include <drm/drm_debugfs.h>
>  #include <drm/drm_drv.h>
>  #include <drm/drm_exec.h>
> @@ -2619,6 +2621,43 @@ int panthor_vm_prepare_mapped_bos_resvs(struct drm_exec *exec, struct panthor_vm
>         return drm_gpuvm_prepare_objects(&vm->base, exec, slot_count);
>  }
>
> +/**
> + * panthor_vm_bo_dump() - Dump the VM BOs for debugging purposes.
> + *
> + *
> + * @vm: VM targeted by the GPU job.
> + * @count: The number of BOs returned
> + *
> + * Return: an array of pointers to the BOs backing the whole VM.
> + */
> +struct drm_gem_object **
> +panthor_vm_dump(struct panthor_vm *vm, u32 *count)
> +{
> +       struct drm_gpuva *va, *next;
> +       struct drm_gem_object **objs;
> +       *count = 0;
> +       u32 i = 0;
> +
> +       mutex_lock(&vm->op_lock);
> +       drm_gpuvm_for_each_va_safe(va, next, &vm->base) {
> +               (*count)++;
> +       }
> +
> +       objs = kcalloc(*count, sizeof(struct drm_gem_object *), GFP_KERNEL);
> +       if (!objs) {
> +               mutex_unlock(&vm->op_lock);
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       drm_gpuvm_for_each_va_safe(va, next, &vm->base) {
> +               objs[i] = va->gem.obj;
> +               i++;
> +       }
> +       mutex_unlock(&vm->op_lock);
> +
> +       return objs;
> +}
> +
>  /**
>   * panthor_mmu_unplug() - Unplug the MMU logic
>   * @ptdev: Device.
> diff --git a/drivers/gpu/drm/panthor/panthor_mmu.h b/drivers/gpu/drm/panthor/panthor_mmu.h
> index f3c1ed19f973..e9369c19e5b5 100644
> --- a/drivers/gpu/drm/panthor/panthor_mmu.h
> +++ b/drivers/gpu/drm/panthor/panthor_mmu.h
> @@ -50,6 +50,9 @@ int panthor_vm_add_bos_resvs_deps_to_job(struct panthor_vm *vm,
>  void panthor_vm_add_job_fence_to_bos_resvs(struct panthor_vm *vm,
>                                            struct drm_sched_job *job);
>
> +struct drm_gem_object **
> +panthor_vm_dump(struct panthor_vm *vm, u32 *count);
> +
>  struct dma_resv *panthor_vm_resv(struct panthor_vm *vm);
>  struct drm_gem_object *panthor_vm_root_gem(struct panthor_vm *vm);
>
> diff --git a/drivers/gpu/drm/panthor/panthor_rs.h b/drivers/gpu/drm/panthor/panthor_rs.h
> new file mode 100644
> index 000000000000..024db09be9a1
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/panthor_rs.h
> @@ -0,0 +1,40 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPDX-FileCopyrightText: Copyright Collabora 2024
> +
> +#include <drm/drm_gem.h>
> +
> +struct PanthorDumpArgs {
> +       struct device *dev;
> +       /**
> +   * The slot for the job
> +   */
> +       s32 slot;
> +       /**
> +   * The active buffer objects
> +   */
> +       struct drm_gem_object **bos;
> +       /**
> +   * The number of active buffer objects
> +   */
> +       size_t bo_count;
> +       /**
> +   * The base address of the registers to use when reading.
> +   */
> +       void *reg_base_addr;
> +};
> +
> +/**
> + * Dumps the current state of the GPU to a file
> + *
> + * # Safety
> + *
> + * All fields of `DumpArgs` must be valid.
> + */
> +#ifdef CONFIG_DRM_PANTHOR_RS
> +int panthor_core_dump(const struct PanthorDumpArgs *args);
> +#else
> +inline int panthor_core_dump(const struct PanthorDumpArgs *args)
> +{
> +       return 0;
> +}
> +#endif
> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index 79ffcbc41d78..39e1654d930e 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
> @@ -1,6 +1,9 @@
>  // SPDX-License-Identifier: GPL-2.0 or MIT
>  /* Copyright 2023 Collabora ltd. */
>
> +#include "drm/drm_gem.h"
> +#include "linux/gfp_types.h"
> +#include "linux/slab.h"
>  #include <drm/drm_drv.h>
>  #include <drm/drm_exec.h>
>  #include <drm/drm_gem_shmem_helper.h>
> @@ -31,6 +34,7 @@
>  #include "panthor_mmu.h"
>  #include "panthor_regs.h"
>  #include "panthor_sched.h"
> +#include "panthor_rs.h"
>
>  /**
>   * DOC: Scheduler
> @@ -2805,6 +2809,27 @@ static void group_sync_upd_work(struct work_struct *work)
>         group_put(group);
>  }
>
> +static void dump_job(struct panthor_device *dev, struct panthor_job *job)
> +{
> +       struct panthor_vm *vm = job->group->vm;
> +       struct drm_gem_object **objs;
> +       u32 count;
> +
> +       objs = panthor_vm_dump(vm, &count);
> +
> +       if (!IS_ERR(objs)) {
> +               struct PanthorDumpArgs args = {
> +                       .dev = job->group->ptdev->base.dev,
> +                       .bos = objs,
> +                       .bo_count = count,
> +                       .reg_base_addr = dev->iomem,
> +               };
> +               panthor_core_dump(&args);
> +               kfree(objs);
> +       }
> +}
> +
> +
>  static struct dma_fence *
>  queue_run_job(struct drm_sched_job *sched_job)
>  {
> @@ -2929,7 +2954,7 @@ queue_run_job(struct drm_sched_job *sched_job)
>         }
>
>         done_fence = dma_fence_get(job->done_fence);
> -
> +       dump_job(ptdev, job);
>  out_unlock:
>         mutex_unlock(&sched->lock);
>         pm_runtime_mark_last_busy(ptdev->base.dev);
> @@ -2950,6 +2975,7 @@ queue_timedout_job(struct drm_sched_job *sched_job)
>         drm_warn(&ptdev->base, "job timeout\n");
>
>         drm_WARN_ON(&ptdev->base, atomic_read(&sched->reset.in_progress));
> +       dump_job(ptdev, job);
>
>         queue_stop(queue, job);
>
> diff --git a/drivers/gpu/drm/panthor/regs.rs b/drivers/gpu/drm/panthor/regs.rs
> new file mode 100644
> index 000000000000..514bc9ee2856
> --- /dev/null
> +++ b/drivers/gpu/drm/panthor/regs.rs
> @@ -0,0 +1,264 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPDX-FileCopyrightText: Copyright Collabora 2024
> +// SPDX-FileCopyrightText: (C) COPYRIGHT 2010-2022 ARM Limited. All rights reserved.
> +
> +//! The registers for Panthor, extracted from panthor_regs.h
> +
> +#![allow(unused_macros, unused_imports, dead_code)]
> +
> +use kernel::bindings;
> +
> +use core::ops::Add;
> +use core::ops::Shl;
> +use core::ops::Shr;
> +
> +#[repr(transparent)]
> +#[derive(Clone, Copy)]
> +pub(crate) struct GpuRegister(u64);
> +
> +impl GpuRegister {
> +    pub(crate) fn read(&self, iomem: *const core::ffi::c_void) -> u32 {
> +        // Safety: `reg` represents a valid address
> +        unsafe {
> +            let addr = iomem.offset(self.0 as isize);
> +            bindings::readl_relaxed(addr as *const _)
> +        }
> +    }
> +}
> +
> +pub(crate) const fn bit(index: u64) -> u64 {
> +    1 << index
> +}
> +pub(crate) const fn genmask(high: u64, low: u64) -> u64 {
> +    ((1 << (high - low + 1)) - 1) << low
> +}
> +
> +pub(crate) const GPU_ID: GpuRegister = GpuRegister(0x0);
> +pub(crate) const fn gpu_arch_major(x: u64) -> GpuRegister {
> +    GpuRegister((x) >> 28)
> +}
> +pub(crate) const fn gpu_arch_minor(x: u64) -> GpuRegister {
> +    GpuRegister((x) & genmask(27, 24) >> 24)
> +}
> +pub(crate) const fn gpu_arch_rev(x: u64) -> GpuRegister {
> +    GpuRegister((x) & genmask(23, 20) >> 20)
> +}
> +pub(crate) const fn gpu_prod_major(x: u64) -> GpuRegister {
> +    GpuRegister((x) & genmask(19, 16) >> 16)
> +}
> +pub(crate) const fn gpu_ver_major(x: u64) -> GpuRegister {
> +    GpuRegister((x) & genmask(15, 12) >> 12)
> +}
> +pub(crate) const fn gpu_ver_minor(x: u64) -> GpuRegister {
> +    GpuRegister((x) & genmask(11, 4) >> 4)
> +}
> +pub(crate) const fn gpu_ver_status(x: u64) -> GpuRegister {
> +    GpuRegister(x & genmask(3, 0))
> +}
> +pub(crate) const GPU_L2_FEATURES: GpuRegister = GpuRegister(0x4);
> +pub(crate) const fn gpu_l2_features_line_size(x: u64) -> GpuRegister {
> +    GpuRegister(1 << ((x) & genmask(7, 0)))
> +}
> +pub(crate) const GPU_CORE_FEATURES: GpuRegister = GpuRegister(0x8);
> +pub(crate) const GPU_TILER_FEATURES: GpuRegister = GpuRegister(0xc);
> +pub(crate) const GPU_MEM_FEATURES: GpuRegister = GpuRegister(0x10);
> +pub(crate) const GROUPS_L2_COHERENT: GpuRegister = GpuRegister(bit(0));
> +pub(crate) const GPU_MMU_FEATURES: GpuRegister = GpuRegister(0x14);
> +pub(crate) const fn gpu_mmu_features_va_bits(x: u64) -> GpuRegister {
> +    GpuRegister((x) & genmask(7, 0))
> +}
> +pub(crate) const fn gpu_mmu_features_pa_bits(x: u64) -> GpuRegister {
> +    GpuRegister(((x) >> 8) & genmask(7, 0))
> +}
> +pub(crate) const GPU_AS_PRESENT: GpuRegister = GpuRegister(0x18);
> +pub(crate) const GPU_CSF_ID: GpuRegister = GpuRegister(0x1c);
> +pub(crate) const GPU_INT_RAWSTAT: GpuRegister = GpuRegister(0x20);
> +pub(crate) const GPU_INT_CLEAR: GpuRegister = GpuRegister(0x24);
> +pub(crate) const GPU_INT_MASK: GpuRegister = GpuRegister(0x28);
> +pub(crate) const GPU_INT_STAT: GpuRegister = GpuRegister(0x2c);
> +pub(crate) const GPU_IRQ_FAULT: GpuRegister = GpuRegister(bit(0));
> +pub(crate) const GPU_IRQ_PROTM_FAULT: GpuRegister = GpuRegister(bit(1));
> +pub(crate) const GPU_IRQ_RESET_COMPLETED: GpuRegister = GpuRegister(bit(8));
> +pub(crate) const GPU_IRQ_POWER_CHANGED: GpuRegister = GpuRegister(bit(9));
> +pub(crate) const GPU_IRQ_POWER_CHANGED_ALL: GpuRegister = GpuRegister(bit(10));
> +pub(crate) const GPU_IRQ_CLEAN_CACHES_COMPLETED: GpuRegister = GpuRegister(bit(17));
> +pub(crate) const GPU_IRQ_DOORBELL_MIRROR: GpuRegister = GpuRegister(bit(18));
> +pub(crate) const GPU_IRQ_MCU_STATUS_CHANGED: GpuRegister = GpuRegister(bit(19));
> +pub(crate) const GPU_CMD: GpuRegister = GpuRegister(0x30);
> +const fn gpu_cmd_def(ty: u64, payload: u64) -> u64 {
> +    (ty) | ((payload) << 8)
> +}
> +pub(crate) const fn gpu_soft_reset() -> GpuRegister {
> +    GpuRegister(gpu_cmd_def(1, 1))
> +}
> +pub(crate) const fn gpu_hard_reset() -> GpuRegister {
> +    GpuRegister(gpu_cmd_def(1, 2))
> +}
> +pub(crate) const CACHE_CLEAN: GpuRegister = GpuRegister(bit(0));
> +pub(crate) const CACHE_INV: GpuRegister = GpuRegister(bit(1));
> +pub(crate) const GPU_STATUS: GpuRegister = GpuRegister(0x34);
> +pub(crate) const GPU_STATUS_ACTIVE: GpuRegister = GpuRegister(bit(0));
> +pub(crate) const GPU_STATUS_PWR_ACTIVE: GpuRegister = GpuRegister(bit(1));
> +pub(crate) const GPU_STATUS_PAGE_FAULT: GpuRegister = GpuRegister(bit(4));
> +pub(crate) const GPU_STATUS_PROTM_ACTIVE: GpuRegister = GpuRegister(bit(7));
> +pub(crate) const GPU_STATUS_DBG_ENABLED: GpuRegister = GpuRegister(bit(8));
> +pub(crate) const GPU_FAULT_STATUS: GpuRegister = GpuRegister(0x3c);
> +pub(crate) const GPU_FAULT_ADDR_LO: GpuRegister = GpuRegister(0x40);
> +pub(crate) const GPU_FAULT_ADDR_HI: GpuRegister = GpuRegister(0x44);
> +pub(crate) const GPU_PWR_KEY: GpuRegister = GpuRegister(0x50);
> +pub(crate) const GPU_PWR_KEY_UNLOCK: GpuRegister = GpuRegister(0x2968a819);
> +pub(crate) const GPU_PWR_OVERRIDE0: GpuRegister = GpuRegister(0x54);
> +pub(crate) const GPU_PWR_OVERRIDE1: GpuRegister = GpuRegister(0x58);
> +pub(crate) const GPU_TIMESTAMP_OFFSET_LO: GpuRegister = GpuRegister(0x88);
> +pub(crate) const GPU_TIMESTAMP_OFFSET_HI: GpuRegister = GpuRegister(0x8c);
> +pub(crate) const GPU_CYCLE_COUNT_LO: GpuRegister = GpuRegister(0x90);
> +pub(crate) const GPU_CYCLE_COUNT_HI: GpuRegister = GpuRegister(0x94);
> +pub(crate) const GPU_TIMESTAMP_LO: GpuRegister = GpuRegister(0x98);
> +pub(crate) const GPU_TIMESTAMP_HI: GpuRegister = GpuRegister(0x9c);
> +pub(crate) const GPU_THREAD_MAX_THREADS: GpuRegister = GpuRegister(0xa0);
> +pub(crate) const GPU_THREAD_MAX_WORKGROUP_SIZE: GpuRegister = GpuRegister(0xa4);
> +pub(crate) const GPU_THREAD_MAX_BARRIER_SIZE: GpuRegister = GpuRegister(0xa8);
> +pub(crate) const GPU_THREAD_FEATURES: GpuRegister = GpuRegister(0xac);
> +pub(crate) const fn gpu_texture_features(n: u64) -> GpuRegister {
> +    GpuRegister(0xB0 + ((n) * 4))
> +}
> +pub(crate) const GPU_SHADER_PRESENT_LO: GpuRegister = GpuRegister(0x100);
> +pub(crate) const GPU_SHADER_PRESENT_HI: GpuRegister = GpuRegister(0x104);
> +pub(crate) const GPU_TILER_PRESENT_LO: GpuRegister = GpuRegister(0x110);
> +pub(crate) const GPU_TILER_PRESENT_HI: GpuRegister = GpuRegister(0x114);
> +pub(crate) const GPU_L2_PRESENT_LO: GpuRegister = GpuRegister(0x120);
> +pub(crate) const GPU_L2_PRESENT_HI: GpuRegister = GpuRegister(0x124);
> +pub(crate) const SHADER_READY_LO: GpuRegister = GpuRegister(0x140);
> +pub(crate) const SHADER_READY_HI: GpuRegister = GpuRegister(0x144);
> +pub(crate) const TILER_READY_LO: GpuRegister = GpuRegister(0x150);
> +pub(crate) const TILER_READY_HI: GpuRegister = GpuRegister(0x154);
> +pub(crate) const L2_READY_LO: GpuRegister = GpuRegister(0x160);
> +pub(crate) const L2_READY_HI: GpuRegister = GpuRegister(0x164);
> +pub(crate) const SHADER_PWRON_LO: GpuRegister = GpuRegister(0x180);
> +pub(crate) const SHADER_PWRON_HI: GpuRegister = GpuRegister(0x184);
> +pub(crate) const TILER_PWRON_LO: GpuRegister = GpuRegister(0x190);
> +pub(crate) const TILER_PWRON_HI: GpuRegister = GpuRegister(0x194);
> +pub(crate) const L2_PWRON_LO: GpuRegister = GpuRegister(0x1a0);
> +pub(crate) const L2_PWRON_HI: GpuRegister = GpuRegister(0x1a4);
> +pub(crate) const SHADER_PWROFF_LO: GpuRegister = GpuRegister(0x1c0);
> +pub(crate) const SHADER_PWROFF_HI: GpuRegister = GpuRegister(0x1c4);
> +pub(crate) const TILER_PWROFF_LO: GpuRegister = GpuRegister(0x1d0);
> +pub(crate) const TILER_PWROFF_HI: GpuRegister = GpuRegister(0x1d4);
> +pub(crate) const L2_PWROFF_LO: GpuRegister = GpuRegister(0x1e0);
> +pub(crate) const L2_PWROFF_HI: GpuRegister = GpuRegister(0x1e4);
> +pub(crate) const SHADER_PWRTRANS_LO: GpuRegister = GpuRegister(0x200);
> +pub(crate) const SHADER_PWRTRANS_HI: GpuRegister = GpuRegister(0x204);
> +pub(crate) const TILER_PWRTRANS_LO: GpuRegister = GpuRegister(0x210);
> +pub(crate) const TILER_PWRTRANS_HI: GpuRegister = GpuRegister(0x214);
> +pub(crate) const L2_PWRTRANS_LO: GpuRegister = GpuRegister(0x220);
> +pub(crate) const L2_PWRTRANS_HI: GpuRegister = GpuRegister(0x224);
> +pub(crate) const SHADER_PWRACTIVE_LO: GpuRegister = GpuRegister(0x240);
> +pub(crate) const SHADER_PWRACTIVE_HI: GpuRegister = GpuRegister(0x244);
> +pub(crate) const TILER_PWRACTIVE_LO: GpuRegister = GpuRegister(0x250);
> +pub(crate) const TILER_PWRACTIVE_HI: GpuRegister = GpuRegister(0x254);
> +pub(crate) const L2_PWRACTIVE_LO: GpuRegister = GpuRegister(0x260);
> +pub(crate) const L2_PWRACTIVE_HI: GpuRegister = GpuRegister(0x264);
> +pub(crate) const GPU_REVID: GpuRegister = GpuRegister(0x280);
> +pub(crate) const GPU_COHERENCY_FEATURES: GpuRegister = GpuRegister(0x300);
> +pub(crate) const GPU_COHERENCY_PROTOCOL: GpuRegister = GpuRegister(0x304);
> +pub(crate) const GPU_COHERENCY_ACE: GpuRegister = GpuRegister(0);
> +pub(crate) const GPU_COHERENCY_ACE_LITE: GpuRegister = GpuRegister(1);
> +pub(crate) const GPU_COHERENCY_NONE: GpuRegister = GpuRegister(31);
> +pub(crate) const MCU_CONTROL: GpuRegister = GpuRegister(0x700);
> +pub(crate) const MCU_CONTROL_ENABLE: GpuRegister = GpuRegister(1);
> +pub(crate) const MCU_CONTROL_AUTO: GpuRegister = GpuRegister(2);
> +pub(crate) const MCU_CONTROL_DISABLE: GpuRegister = GpuRegister(0);
> +pub(crate) const MCU_STATUS: GpuRegister = GpuRegister(0x704);
> +pub(crate) const MCU_STATUS_DISABLED: GpuRegister = GpuRegister(0);
> +pub(crate) const MCU_STATUS_ENABLED: GpuRegister = GpuRegister(1);
> +pub(crate) const MCU_STATUS_HALT: GpuRegister = GpuRegister(2);
> +pub(crate) const MCU_STATUS_FATAL: GpuRegister = GpuRegister(3);
> +pub(crate) const JOB_INT_RAWSTAT: GpuRegister = GpuRegister(0x1000);
> +pub(crate) const JOB_INT_CLEAR: GpuRegister = GpuRegister(0x1004);
> +pub(crate) const JOB_INT_MASK: GpuRegister = GpuRegister(0x1008);
> +pub(crate) const JOB_INT_STAT: GpuRegister = GpuRegister(0x100c);
> +pub(crate) const JOB_INT_GLOBAL_IF: GpuRegister = GpuRegister(bit(31));
> +pub(crate) const fn job_int_csg_if(x: u64) -> GpuRegister {
> +    GpuRegister(bit(x))
> +}
> +pub(crate) const MMU_INT_RAWSTAT: GpuRegister = GpuRegister(0x2000);
> +pub(crate) const MMU_INT_CLEAR: GpuRegister = GpuRegister(0x2004);
> +pub(crate) const MMU_INT_MASK: GpuRegister = GpuRegister(0x2008);
> +pub(crate) const MMU_INT_STAT: GpuRegister = GpuRegister(0x200c);
> +pub(crate) const MMU_BASE: GpuRegister = GpuRegister(0x2400);
> +pub(crate) const MMU_AS_SHIFT: GpuRegister = GpuRegister(6);
> +const fn mmu_as(as_: u64) -> u64 {
> +    MMU_BASE.0 + ((as_) << MMU_AS_SHIFT.0)
> +}
> +pub(crate) const fn as_transtab_lo(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x0)
> +}
> +pub(crate) const fn as_transtab_hi(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x4)
> +}
> +pub(crate) const fn as_memattr_lo(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x8)
> +}
> +pub(crate) const fn as_memattr_hi(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0xC)
> +}
> +pub(crate) const fn as_memattr_aarch64_inner_alloc_expl(w: u64, r: u64) -> GpuRegister {
> +    GpuRegister((3 << 2) | (if w > 0 { bit(0) } else { 0 } | (if r > 0 { bit(1) } else { 0 })))
> +}
> +pub(crate) const fn as_lockaddr_lo(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x10)
> +}
> +pub(crate) const fn as_lockaddr_hi(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x14)
> +}
> +pub(crate) const fn as_command(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x18)
> +}
> +pub(crate) const AS_COMMAND_NOP: GpuRegister = GpuRegister(0);
> +pub(crate) const AS_COMMAND_UPDATE: GpuRegister = GpuRegister(1);
> +pub(crate) const AS_COMMAND_LOCK: GpuRegister = GpuRegister(2);
> +pub(crate) const AS_COMMAND_UNLOCK: GpuRegister = GpuRegister(3);
> +pub(crate) const AS_COMMAND_FLUSH_PT: GpuRegister = GpuRegister(4);
> +pub(crate) const AS_COMMAND_FLUSH_MEM: GpuRegister = GpuRegister(5);
> +pub(crate) const fn as_faultstatus(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x1C)
> +}
> +pub(crate) const fn as_faultaddress_lo(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x20)
> +}
> +pub(crate) const fn as_faultaddress_hi(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x24)
> +}
> +pub(crate) const fn as_status(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x28)
> +}
> +pub(crate) const AS_STATUS_AS_ACTIVE: GpuRegister = GpuRegister(bit(0));
> +pub(crate) const fn as_transcfg_lo(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x30)
> +}
> +pub(crate) const fn as_transcfg_hi(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x34)
> +}
> +pub(crate) const fn as_transcfg_ina_bits(x: u64) -> GpuRegister {
> +    GpuRegister((x) << 6)
> +}
> +pub(crate) const fn as_transcfg_outa_bits(x: u64) -> GpuRegister {
> +    GpuRegister((x) << 14)
> +}
> +pub(crate) const AS_TRANSCFG_SL_CONCAT: GpuRegister = GpuRegister(bit(22));
> +pub(crate) const AS_TRANSCFG_PTW_RA: GpuRegister = GpuRegister(bit(30));
> +pub(crate) const AS_TRANSCFG_DISABLE_HIER_AP: GpuRegister = GpuRegister(bit(33));
> +pub(crate) const AS_TRANSCFG_DISABLE_AF_FAULT: GpuRegister = GpuRegister(bit(34));
> +pub(crate) const AS_TRANSCFG_WXN: GpuRegister = GpuRegister(bit(35));
> +pub(crate) const AS_TRANSCFG_XREADABLE: GpuRegister = GpuRegister(bit(36));
> +pub(crate) const fn as_faultextra_lo(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x38)
> +}
> +pub(crate) const fn as_faultextra_hi(as_: u64) -> GpuRegister {
> +    GpuRegister(mmu_as(as_) + 0x3C)
> +}
> +pub(crate) const CSF_GPU_LATEST_FLUSH_ID: GpuRegister = GpuRegister(0x10000);
> +pub(crate) const fn csf_doorbell(i: u64) -> GpuRegister {
> +    GpuRegister(0x80000 + ((i) * 0x10000))
> +}
> +pub(crate) const CSF_GLB_DOORBELL_ID: GpuRegister = GpuRegister(0);
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index b245db8d5a87..4ee4b97e7930 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -12,15 +12,18 @@
>  #include <drm/drm_gem.h>
>  #include <drm/drm_ioctl.h>
>  #include <kunit/test.h>
> +#include <linux/devcoredump.h>
>  #include <linux/errname.h>
>  #include <linux/ethtool.h>
>  #include <linux/jiffies.h>
> +#include <linux/iosys-map.h>
>  #include <linux/mdio.h>
>  #include <linux/pci.h>
>  #include <linux/phy.h>
>  #include <linux/refcount.h>
>  #include <linux/sched.h>
>  #include <linux/slab.h>
> +#include <linux/vmalloc.h>
>  #include <linux/wait.h>
>  #include <linux/workqueue.h>
>
> --
> 2.45.2
>
>


More information about the dri-devel mailing list