Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • lyudess/linux
1 result
Show changes
Showing
with 2160 additions and 20 deletions
......@@ -129,6 +129,9 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
struct drm_printer *p, unsigned int indent);
extern const struct vm_operations_struct drm_gem_shmem_vm_ops;
vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf);
void drm_gem_shmem_vm_open(struct vm_area_struct *vma);
void drm_gem_shmem_vm_close(struct vm_area_struct *vma);
/*
* GEM object functions
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2025 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
* Copyright (c) 2025 The Linux Foundation
*
* A "simple" faux bus that allows devices to be created and added
* automatically to it. This is to be used whenever you need to create a
* device that is not associated with any "real" system resources, and do
* not want to have to deal with a bus/driver binding logic. It is
* intended to be very simple, with only a create and a destroy function
* available.
*/
#ifndef _FAUX_DEVICE_H_
#define _FAUX_DEVICE_H_
#include <linux/container_of.h>
#include <linux/device.h>
/**
* struct faux_device - a "faux" device
* @dev: internal struct device of the object
*
* A simple faux device that can be created/destroyed. To be used when a
* driver only needs to have a device to "hang" something off. This can be
* used for downloading firmware or other basic tasks. Use this instead of
* a struct platform_device if the device has no resources assigned to
* it at all.
*/
struct faux_device {
struct device dev;
};
#define to_faux_device(x) container_of_const((x), struct faux_device, dev)
/**
* struct faux_device_ops - a set of callbacks for a struct faux_device
* @probe: called when a faux device is probed by the driver core
* before the device is fully bound to the internal faux bus
* code. If probe succeeds, return 0, otherwise return a
* negative error number to stop the probe sequence from
* succeeding.
* @remove: called when a faux device is removed from the system
*
* Both @probe and @remove are optional, if not needed, set to NULL.
*/
struct faux_device_ops {
int (*probe)(struct faux_device *faux_dev);
void (*remove)(struct faux_device *faux_dev);
};
struct faux_device *faux_device_create(const char *name,
struct device *parent,
const struct faux_device_ops *faux_ops);
struct faux_device *faux_device_create_with_groups(const char *name,
struct device *parent,
const struct faux_device_ops *faux_ops,
const struct attribute_group **groups);
void faux_device_destroy(struct faux_device *faux_dev);
static inline void *faux_device_get_drvdata(const struct faux_device *faux_dev)
{
return dev_get_drvdata(&faux_dev->dev);
}
static inline void faux_device_set_drvdata(struct faux_device *faux_dev, void *data)
{
dev_set_drvdata(&faux_dev->dev, data);
}
#endif /* _FAUX_DEVICE_H_ */
......@@ -421,6 +421,8 @@ enum drm_mode_subconnector {
#define DRM_MODE_CONNECTOR_WRITEBACK 18
#define DRM_MODE_CONNECTOR_SPI 19
#define DRM_MODE_CONNECTOR_USB 20
/* Update this if you add new types */
#define _DRM_MODE_CONNECTOR_COUNT 21
/**
* struct drm_mode_get_connector - Get connector metadata.
......
......@@ -6,20 +6,36 @@
* Sorted alphabetically.
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_mode_object.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <kunit/test.h>
#include <linux/blk-mq.h>
#include <linux/blk_types.h>
#include <linux/blkdev.h>
#include <linux/cred.h>
#include <linux/dma-resv.h>
#include <linux/device/faux.h>
#include <linux/errname.h>
#include <linux/ethtool.h>
#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/iosys-map.h>
#include <linux/mdio.h>
#include <linux/of_device.h>
#include <linux/pci.h>
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/dma-resv.h>
int rust_helper_dma_resv_lock(struct dma_resv *obj, struct ww_acquire_ctx *ctx)
{
return dma_resv_lock(obj, ctx);
}
void rust_helper_dma_resv_unlock(struct dma_resv *obj)
{
dma_resv_unlock(obj);
}
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_atomic.h>
void rust_helper_drm_atomic_state_get(struct drm_atomic_state *state)
{
drm_atomic_state_get(state);
}
void rust_helper_drm_atomic_state_put(struct drm_atomic_state *state)
{
drm_atomic_state_put(state);
}
// Macros for generating one repetitive atomic state accessors (like drm_atomic_get_new_plane_state)
#define STATE_FUNC(type, tense) \
struct drm_ ## type ## _state *rust_helper_drm_atomic_get_ ## tense ## _ ## type ## _state( \
const struct drm_atomic_state *state, \
struct drm_ ## type *type \
) { \
return drm_atomic_get_## tense ## _ ## type ## _state(state, type); \
}
#define STATE_FUNCS(type) \
STATE_FUNC(type, new); \
STATE_FUNC(type, old);
STATE_FUNCS(plane);
STATE_FUNCS(crtc);
STATE_FUNCS(connector);
#undef STATE_FUNCS
#undef STATE_FUNC
// SPDX-License-Identifier: GPL-2.0
#ifdef CONFIG_DRM_KMS_HELPER
#include "atomic.c"
#include "vblank.c"
#endif
#include "gem.c"
#ifdef CONFIG_DRM_GEM_SHMEM_HELPER
#include "gem_shmem_helper.c"
#endif
#include "vma_manager.c"
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_gem.h>
#include <drm/drm_vma_manager.h>
void rust_helper_drm_gem_object_get(struct drm_gem_object *obj)
{
......@@ -12,8 +10,3 @@ void rust_helper_drm_gem_object_put(struct drm_gem_object *obj)
{
drm_gem_object_put(obj);
}
__u64 rust_helper_drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
{
return drm_vma_node_offset_addr(node);
}
#include <drm/drm_gem_shmem_helper.h>
void rust_helper_drm_gem_shmem_object_free(struct drm_gem_object *obj)
{
return drm_gem_shmem_object_free(obj);
}
void rust_helper_drm_gem_shmem_object_print_info(struct drm_printer *p, unsigned int indent,
const struct drm_gem_object *obj)
{
drm_gem_shmem_object_print_info(p, indent, obj);
}
int rust_helper_drm_gem_shmem_object_pin(struct drm_gem_object *obj)
{
return drm_gem_shmem_object_pin(obj);
}
void rust_helper_drm_gem_shmem_object_unpin(struct drm_gem_object *obj)
{
drm_gem_shmem_object_unpin(obj);
}
struct sg_table *rust_helper_drm_gem_shmem_object_get_sg_table(struct drm_gem_object *obj)
{
return drm_gem_shmem_object_get_sg_table(obj);
}
int rust_helper_drm_gem_shmem_object_vmap(struct drm_gem_object *obj,
struct iosys_map *map)
{
return drm_gem_shmem_object_vmap(obj, map);
}
void rust_helper_drm_gem_shmem_object_vunmap(struct drm_gem_object *obj,
struct iosys_map *map)
{
drm_gem_shmem_object_vunmap(obj, map);
}
int rust_helper_drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
return drm_gem_shmem_object_mmap(obj, vma);
}
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_vblank.h>
struct drm_vblank_crtc *rust_helper_drm_crtc_vblank_crtc(struct drm_crtc *crtc)
{
return drm_crtc_vblank_crtc(crtc);
}
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_vma_manager.h>
__u64 rust_helper_drm_vma_node_offset_addr(struct drm_vma_offset_node *node)
{
return drm_vma_node_offset_addr(node);
}
......@@ -11,8 +11,11 @@
#include "bug.c"
#include "build_assert.c"
#include "build_bug.c"
#ifdef CONFIG_DMA_SHARED_BUFFER
#include "dma-resv.c"
#endif
#include "device.c"
#include "drm.c"
#include "drm/drm.c"
#include "err.c"
#include "io.c"
#include "irq.c"
......@@ -24,6 +27,7 @@
#include "rbtree.c"
#include "rcu.c"
#include "refcount.c"
#include "scatterlist.c"
#include "signal.c"
#include "slab.c"
#include "spinlock.c"
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/scatterlist.h>
dma_addr_t rust_helper_sg_dma_address(const struct scatterlist *sg)
{
return sg_dma_address(sg);
}
int rust_helper_sg_dma_len(const struct scatterlist *sg)
{
return sg_dma_len(sg);
}
......@@ -5,14 +5,23 @@
//! C header: [`include/linux/drm/drm_device.h`](srctree/include/linux/drm/drm_device.h)
use crate::{
bindings, device, drm,
drm::drv::AllocImpl,
bindings, device,
drm::{
drv::AllocImpl,
self,
kms::private::KmsImpl as KmsImplPrivate
},
error::code::*,
error::from_err_ptr,
error::Result,
types::{ARef, AlwaysRefCounted, ForeignOwnable, Opaque},
sync::*,
};
use core::{
ffi::c_void,
marker::PhantomData,
ptr::NonNull
};
use core::{ffi::c_void, marker::PhantomData, ptr::NonNull};
#[cfg(CONFIG_DRM_LEGACY)]
macro_rules! drm_legacy_fields {
......@@ -152,6 +161,16 @@ impl<T: drm::drv::Driver> Device<T> {
// SAFETY: `Self::data` is always converted and set on device creation.
unsafe { <T::Data as ForeignOwnable>::from_foreign(drm.raw_data()) };
}
/// Returns a reference to the `event` spinlock
pub(crate) fn event_lock(&self) -> &SpinLockIrq<()> {
// SAFETY: `event_lock` is initialized for as long as `self` is exposed to users
unsafe { SpinLockIrq::from_raw(&mut (*self.as_raw()).event_lock) }
}
pub(crate) const fn has_kms() -> bool {
<T::Kms as KmsImplPrivate>::MODE_CONFIG_OPS.is_some()
}
}
// SAFETY: DRM device objects are always reference counted and the get/put functions
......
......@@ -8,7 +8,11 @@ use crate::{
alloc::flags::*,
bindings,
devres::Devres,
drm,
drm::{
self,
kms::{private::KmsImpl as KmsImplPrivate, ModeConfigInfo}
},
device,
error::{Error, Result},
private::Sealed,
str::CStr,
......@@ -121,6 +125,17 @@ pub struct AllocOps {
pub trait AllocImpl: Sealed + drm::gem::IntoGEMObject {
/// The C callback operations for this memory manager.
const ALLOC_OPS: AllocOps;
/// Setup the fbdev implementation for a KMS driver using this [`AllocImpl`].
///
/// # Safety
///
/// This function must only be called once immediately after registration by the DRM crate.
#[cfg(CONFIG_DRM_FBDEV_EMULATION = "y")]
unsafe fn setup_fbdev(
drm: &drm::device::Device<Self::Driver>,
mode_config_info: &ModeConfigInfo
);
}
/// The DRM `Driver` trait.
......@@ -137,11 +152,17 @@ pub trait Driver {
/// The type used to manage memory for this driver.
///
/// Should be either `drm::gem::Object<T>` or `drm::gem::shmem::Object<T>`.
type Object: AllocImpl;
type Object: AllocImpl<Driver = Self> where Self: Sized;
/// The type used to represent a DRM File (client)
type File: drm::file::DriverFile;
/// The KMS implementation for this driver.
///
/// Drivers that wish to support KMS should pass their implementation of `drm::kms::KmsDriver`
/// here. Drivers which do not have KMS support can simply pass `drm::kms::NoKms` here.
type Kms: drm::kms::KmsImpl<Driver = Self> where Self: Sized;
/// Driver metadata
const INFO: DriverInfo;
......@@ -159,21 +180,39 @@ pub struct Registration<T: Driver>(ARef<drm::device::Device<T>>);
impl<T: Driver> Registration<T> {
/// Creates a new [`Registration`] and registers it.
pub fn new(drm: ARef<drm::device::Device<T>>, flags: usize) -> Result<Self> {
pub fn new(dev: &device::Device, data: T::Data, flags: usize) -> Result<Self> {
let drm = drm::device::Device::<T>::new(dev, data)?;
let has_kms = drm::device::Device::<T>::has_kms();
let mode_config_info = if has_kms {
// SAFETY: We have yet to register this device
Some(unsafe { T::Kms::setup_kms(&drm)? })
} else {
None
};
// SAFETY: Safe by the invariants of `drm::device::Device`.
let ret = unsafe { bindings::drm_dev_register(drm.as_raw(), flags as u64) };
if ret < 0 {
return Err(Error::from_errno(ret));
}
#[cfg(CONFIG_DRM_FBDEV_EMULATION = "y")]
if has_kms {
if let Some(ref info) = mode_config_info {
// SAFETY: We just registered the device above, and this is within the DRM create
unsafe { T::Object::setup_fbdev(&drm, info) };
}
}
Ok(Self(drm))
}
/// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to `Devres`.
pub fn new_foreign_owned(drm: ARef<drm::device::Device<T>>, flags: usize) -> Result {
let reg = Registration::<T>::new(drm.clone(), flags)?;
pub fn new_foreign_owned(dev: &device::Device, data: T::Data, flags: usize) -> Result {
let reg = Registration::<T>::new(dev, data, flags)?;
Devres::new_foreign_owned(drm.as_ref(), reg, GFP_KERNEL)
Devres::new_foreign_owned(dev, reg, GFP_KERNEL)
}
/// Returns a reference to the `Device` instance for this registration.
......@@ -195,5 +234,11 @@ impl<T: Driver> Drop for Registration<T> {
// SAFETY: Safe by the invariant of `ARef<drm::device::Device<T>>`. The existance of this
// `Registration` also guarantees the this `drm::device::Device` is actually registered.
unsafe { bindings::drm_dev_unregister(self.0.as_raw()) };
if drm::device::Device::<T>::has_kms() {
// SAFETY: We just checked above that KMS was setup for this device, so this is safe to
// call
unsafe { bindings::drm_atomic_helper_shutdown(self.0.as_raw()) }
}
}
}
use bindings;
/// Return a fourcc format code
const fn fourcc_code(a: u8, b: u8, c: u8, d: u8) -> u32 {
(a as u32) | (b as u32) << 8 | (c as u32) << 16 | (d as u32) << 24
}
// TODO: We manually import this because we don't have a reasonable way of getting constants from
// function-like macros in bindgen yet.
pub(crate) const FORMAT_MOD_INVALID: u64 = 0xffffffffffffff;
// TODO: Figure out a more automated way of importing this
pub const XRGB888: u32 = fourcc_code(b'X', b'R', b'2', b'4');
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct FormatInfo {
inner: bindings::drm_format_info,
}
impl FormatInfo {
// SAFETY: `ptr` must point to a valid instance of a `bindings::drm_format_info`
pub(super) unsafe fn from_raw<'a>(ptr: *const bindings::drm_format_info) -> &'a Self {
// SAFETY: Our data layout is identical
unsafe { &*ptr.cast() }
}
/// The number of color planes (1 to 3)
pub const fn num_planes(&self) -> u8 {
self.inner.num_planes
}
/// Does the format embed an alpha component?
pub const fn has_alpha(&self) -> bool {
self.inner.has_alpha
}
/// The total number of components (color planes + alpha channel, if there is one)
pub const fn num_components(&self) -> u8 {
self.num_planes() + self.has_alpha() as u8
}
/// Number of bytes per block (per plane), where blocks are defined as a rectangle of pixels
/// which are stored next to each other in a byte aligned memory region.
pub fn char_per_block(&self) -> &[u8] {
// SAFETY: The union we access here is just for descriptive purposes on the C side, both
// members are identical in data layout
unsafe { &self.inner.__bindgen_anon_1.char_per_block[..self.num_components() as _] }
}
}
impl AsRef<bindings::drm_format_info> for FormatInfo {
fn as_ref(&self) -> &bindings::drm_format_info {
&self.inner
}
}
impl From<bindings::drm_format_info> for FormatInfo {
fn from(value: bindings::drm_format_info) -> Self {
Self { inner: value }
}
}
......@@ -4,10 +4,13 @@
//!
//! C header: [`include/linux/drm/drm_gem.h`](srctree/include/linux/drm/drm_gem.h)
#[cfg(CONFIG_DRM_GEM_SHMEM_HELPER = "y")]
pub mod shmem;
use crate::{
alloc::flags::*,
bindings,
drm::{device, drv, file},
drm::{device, drv, file, kms},
error::{to_result, Result},
prelude::*,
};
......@@ -197,8 +200,6 @@ impl<T: IntoGEMObject> BaseObject for T {}
#[pin_data]
pub struct Object<T: DriverObject> {
obj: bindings::drm_gem_object,
// The DRM core ensures the Device exists as long as its objects exist, so we don't need to
// manage the reference count here.
dev: *const bindings::drm_device,
#[pin]
inner: T,
......@@ -300,6 +301,21 @@ impl<T: DriverObject> drv::AllocImpl for Object<T> {
dumb_create: None,
dumb_map_offset: None,
};
#[cfg(CONFIG_DRM_FBDEV_EMULATION = "y")]
#[inline]
unsafe fn setup_fbdev(
drm: &device::Device<T::Driver>,
mode_config_info: &kms::ModeConfigInfo
) {
// SAFETY:
// - This implementation of `AllocImpl` is proof that this driver is using the GEM dma
// helpers.
// - `as_raw()` always returns a pointer to an initialized DRM device.
// - Our safety contract ensures that `drm` was just registered, and that we're being called
// from the DRM crate
unsafe { bindings::drm_fbdev_dma_setup(drm.as_raw(), mode_config_info.preferred_depth) };
}
}
/// A reference-counted shared reference to a base GEM object.
......
// SPDX-License-Identifier: GPL-2.0
//! DRM GEM shmem helper objects
//!
//! C header: [`include/linux/drm/drm_gem_shmem_helper.h`](../../../../include/linux/drm/drm_gem_shmem_helper.h)
use crate::drm::{device, drv, gem, kms};
use crate::{
error::{from_err_ptr, to_result},
prelude::*,
};
use core::{
marker::{PhantomData, PhantomPinned},
mem,
mem::MaybeUninit,
ops::{Deref, DerefMut},
slice,
};
use gem::BaseObject;
/// Trait which must be implemented by drivers using shmem-backed GEM objects.
pub trait DriverObject: gem::BaseDriverObject<Object<Self>> {
/// Parent `Driver` for this object.
type Driver: drv::Driver;
}
// FIXME: This is terrible and I don't know how to avoid it
#[cfg(CONFIG_NUMA)]
macro_rules! vm_numa_fields {
( $($field:ident: $val:expr),* $(,)? ) => {
bindings::vm_operations_struct {
$( $field: $val ),*,
set_policy: None,
get_policy: None,
}
}
}
#[cfg(not(CONFIG_NUMA))]
macro_rules! vm_numa_fields {
( $($field:ident: $val:expr),* $(,)? ) => {
bindings::vm_operations_struct {
$( $field: $val ),*
}
}
}
const SHMEM_VM_OPS: bindings::vm_operations_struct = vm_numa_fields! {
open: Some(bindings::drm_gem_shmem_vm_open),
close: Some(bindings::drm_gem_shmem_vm_close),
may_split: None,
mremap: None,
mprotect: None,
fault: Some(bindings::drm_gem_shmem_fault),
huge_fault: None,
map_pages: None,
pagesize: None,
page_mkwrite: None,
pfn_mkwrite: None,
access: None,
name: None,
find_special_page: None,
};
/// A shmem-backed GEM object.
#[repr(C)]
#[pin_data]
pub struct Object<T: DriverObject> {
obj: bindings::drm_gem_shmem_object,
// The DRM core ensures the Device exists as long as its objects exist, so we don't need to
// manage the reference count here.
dev: *const bindings::drm_device,
#[pin]
inner: T,
}
// SAFETY: This struct is safe to zero-initialize
unsafe impl init::Zeroable for bindings::drm_gem_shmem_object {}
unsafe extern "C" fn gem_create_object<T: DriverObject>(
dev: *mut bindings::drm_device,
size: usize,
) -> *mut bindings::drm_gem_object {
let p = unsafe {
bindings::krealloc(
core::ptr::null(),
Object::<T>::SIZE,
bindings::GFP_KERNEL | bindings::__GFP_ZERO,
) as *mut Object<T>
};
if p.is_null() {
return ENOMEM.to_ptr();
}
let init = try_pin_init!(Object {
obj <- init::zeroed(),
// SAFETY: GEM ensures the device lives as long as its objects live
inner <- T::new(unsafe { device::Device::borrow(dev)}, size),
dev,
});
// SAFETY: p is a valid pointer to an uninitialized Object<T>.
if let Err(e) = unsafe { init.__pinned_init(p) } {
// SAFETY: p is a valid pointer from `krealloc` and __pinned_init guarantees we can dealloc it.
unsafe { bindings::kfree(p as *mut _) };
return e.to_ptr();
}
// SAFETY: drm_gem_shmem_object is safe to zero-init, and
// the rest of Object has been initialized
let new: &mut Object<T> = unsafe { &mut *(p as *mut _) };
new.obj.base.funcs = &Object::<T>::VTABLE;
&mut new.obj.base
}
unsafe extern "C" fn free_callback<T: DriverObject>(obj: *mut bindings::drm_gem_object) {
// SAFETY: All of our objects are Object<T>.
let p = unsafe {
let shmem = crate::container_of!(obj, bindings::drm_gem_shmem_object, base)
as *mut bindings::drm_gem_shmem_object;
crate::container_of!(shmem, Object<T>, obj) as *mut Object<T>
};
// SAFETY: p is never used after this
unsafe {
core::ptr::drop_in_place(&mut (*p).inner);
}
// SAFETY: This pointer has to be valid, since p is valid
unsafe {
bindings::drm_gem_shmem_free(&mut (*p).obj);
}
}
impl<T: DriverObject> Object<T> {
/// The size of this object's structure.
const SIZE: usize = mem::size_of::<Self>();
/// `drm_gem_object_funcs` vtable suitable for GEM shmem objects.
const VTABLE: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs {
free: Some(free_callback::<T>),
open: Some(super::open_callback::<T, Object<T>>),
close: Some(super::close_callback::<T, Object<T>>),
print_info: Some(bindings::drm_gem_shmem_object_print_info),
export: None,
pin: Some(bindings::drm_gem_shmem_object_pin),
unpin: Some(bindings::drm_gem_shmem_object_unpin),
get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table),
vmap: Some(bindings::drm_gem_shmem_object_vmap),
vunmap: Some(bindings::drm_gem_shmem_object_vunmap),
mmap: Some(bindings::drm_gem_shmem_object_mmap),
status: None,
vm_ops: &SHMEM_VM_OPS,
evict: None,
rss: None,
};
// SAFETY: Must only be used with DRM functions that are thread-safe
unsafe fn mut_shmem(&self) -> *mut bindings::drm_gem_shmem_object {
&self.obj as *const _ as *mut _
}
/// Create a new shmem-backed DRM object of the given size.
pub fn new(dev: &device::Device<T::Driver>, size: usize) -> Result<gem::UniqueObjectRef<Self>> {
// SAFETY: This function can be called as long as the ALLOC_OPS are set properly
// for this driver, and the gem_create_object is called.
let p = unsafe { bindings::drm_gem_shmem_create(dev.as_raw(), size) };
// SAFETY: `p` was created by the last call which uses our ALLOC_OPS to create the gem, so
// we're guaranteed `p` resides within an `Object<T>`
let p = unsafe { crate::container_of!(p, Object<T>, obj) as *mut _ };
// SAFETY: The gem_create_object callback ensures this is a valid Object<T>,
// so we can take a unique reference to it.
let obj_ref = gem::UniqueObjectRef { ptr: p, _p: PhantomPinned };
Ok(obj_ref)
}
/// Returns the `Device` that owns this GEM object.
pub fn dev(&self) -> &device::Device<T::Driver> {
// SAFETY: GEM ensures that the device outlives its objects, so we can
// just borrow here.
unsafe { device::Device::borrow(self.dev) }
}
/// Creates (if necessary) and returns a scatter-gather table of DMA pages for this object.
///
/// This will pin the object in memory.
pub fn sg_table(&self) -> Result<SGTable<T>> {
// SAFETY: drm_gem_shmem_get_pages_sgt is thread-safe.
let sgt = from_err_ptr(unsafe { bindings::drm_gem_shmem_get_pages_sgt(self.mut_shmem()) })?;
Ok(SGTable {
sgt,
_owner: self.reference(),
})
}
/// Creates and returns a virtual kernel memory mapping for this object.
pub fn vmap(&self) -> Result<VMap<T>> {
let mut map: MaybeUninit<bindings::iosys_map> = MaybeUninit::uninit();
// SAFETY: drm_gem_shmem_vmap can be called with the DMA reservation lock held
to_result(unsafe {
let resv = self.obj.base.resv as *const _ as *mut _;
bindings::dma_resv_lock(resv, core::ptr::null_mut());
let ret = bindings::drm_gem_shmem_vmap(self.mut_shmem(), map.as_mut_ptr());
bindings::dma_resv_unlock(resv);
ret
})?;
// SAFETY: if drm_gem_shmem_vmap did not fail, map is initialized now
let map = unsafe { map.assume_init() };
Ok(VMap {
map,
owner: self.reference(),
})
}
/// Set the write-combine flag for this object.
///
/// Should be called before any mappings are made.
pub fn set_wc(&mut self, map_wc: bool) {
unsafe { (*self.mut_shmem()).set_map_wc(map_wc) };
}
}
impl<T: DriverObject> Deref for Object<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: DriverObject> DerefMut for Object<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: DriverObject> crate::private::Sealed for Object<T> {}
impl<T: DriverObject> gem::IntoGEMObject for Object<T> {
type Driver = T::Driver;
fn gem_obj(&self) -> &bindings::drm_gem_object {
&self.obj.base
}
fn from_gem_obj(obj: *mut bindings::drm_gem_object) -> *mut Object<T> {
// SAFETY: Guaranteed by type invariance
let shmem = unsafe {
crate::container_of!(obj, bindings::drm_gem_shmem_object, base)
as *mut bindings::drm_gem_shmem_object;
};
// SAFETY: Guaranteed by type invariance
unsafe { crate::container_of!(&shmem, Object<T>, obj) as *mut Object<T> }
}
}
impl<T: DriverObject> drv::AllocImpl for Object<T> {
const ALLOC_OPS: drv::AllocOps = drv::AllocOps {
gem_create_object: Some(gem_create_object::<T>),
prime_handle_to_fd: None,
prime_fd_to_handle: None,
gem_prime_import: None,
gem_prime_import_sg_table: Some(bindings::drm_gem_shmem_prime_import_sg_table),
dumb_create: Some(bindings::drm_gem_shmem_dumb_create),
dumb_map_offset: None,
};
#[cfg(CONFIG_DRM_FBDEV_EMULATION = "y")]
#[inline]
unsafe fn setup_fbdev(
drm: &device::Device<T::Driver>,
mode_config_info: &kms::ModeConfigInfo
) {
// SAFETY:
// - This implementation of `AllocImpl` is proof that this driver is using the GEM shmem
// helpers.
// - `as_raw()` always returns a pointer to an initialized DRM device.
// - Our safety contract ensures that `drm` was just registered, and that we're being called
// from the DRM crate.
unsafe { bindings::drm_fbdev_shmem_setup(drm.as_raw(), mode_config_info.preferred_depth) };
}
}
/// A virtual mapping for a shmem-backed GEM object in kernel address space.
pub struct VMap<T: DriverObject> {
map: bindings::iosys_map,
owner: gem::ObjectRef<Object<T>>,
}
impl<T: DriverObject> VMap<T> {
/// Returns a const raw pointer to the start of the mapping.
pub fn as_ptr(&self) -> *const core::ffi::c_void {
// SAFETY: The shmem helpers always return non-iomem maps
unsafe { self.map.__bindgen_anon_1.vaddr }
}
/// Returns a mutable raw pointer to the start of the mapping.
pub fn as_mut_ptr(&mut self) -> *mut core::ffi::c_void {
// SAFETY: The shmem helpers always return non-iomem maps
unsafe { self.map.__bindgen_anon_1.vaddr }
}
/// Returns a byte slice view of the mapping.
pub fn as_slice(&self) -> &[u8] {
// SAFETY: The vmap maps valid memory up to the owner size
unsafe { slice::from_raw_parts(self.as_ptr() as *const u8, self.owner.size()) }
}
/// Returns mutable a byte slice view of the mapping.
pub fn as_mut_slice(&mut self) -> &mut [u8] {
// SAFETY: The vmap maps valid memory up to the owner size
unsafe { slice::from_raw_parts_mut(self.as_mut_ptr() as *mut u8, self.owner.size()) }
}
/// Borrows a reference to the object that owns this virtual mapping.
pub fn owner(&self) -> &gem::ObjectRef<Object<T>> {
&self.owner
}
}
impl<T: DriverObject> Drop for VMap<T> {
fn drop(&mut self) {
// SAFETY: This function is safe to call with the DMA reservation lock held
unsafe {
let resv = self.owner.obj.base.resv as *const _ as *mut _;
bindings::dma_resv_lock(resv, core::ptr::null_mut());
bindings::drm_gem_shmem_vunmap(self.owner.mut_shmem(), &mut self.map);
bindings::dma_resv_unlock(resv);
}
}
}
/// SAFETY: `iosys_map` objects are safe to send across threads.
unsafe impl<T: DriverObject> Send for VMap<T> {}
unsafe impl<T: DriverObject> Sync for VMap<T> {}
/// A single scatter-gather entry, representing a span of pages in the device's DMA address space.
///
/// For devices not behind a standalone IOMMU, this corresponds to physical addresses.
#[repr(transparent)]
pub struct SGEntry(bindings::scatterlist);
impl SGEntry {
/// Returns the starting DMA address of this span
pub fn dma_address(&self) -> usize {
(unsafe { bindings::sg_dma_address(&self.0) }) as usize
}
/// Returns the length of this span in bytes
pub fn dma_len(&self) -> usize {
(unsafe { bindings::sg_dma_len(&self.0) }) as usize
}
}
/// A scatter-gather table of DMA address spans for a GEM shmem object.
///
/// # Invariants
/// `sgt` must be a valid pointer to the `sg_table`, which must correspond to the owned
/// object in `_owner` (which ensures it remains valid).
pub struct SGTable<T: DriverObject> {
sgt: *const bindings::sg_table,
_owner: gem::ObjectRef<Object<T>>,
}
impl<T: DriverObject> SGTable<T> {
/// Returns an iterator through the SGTable's entries
pub fn iter(&'_ self) -> SGTableIter<'_> {
SGTableIter {
left: unsafe { (*self.sgt).nents } as usize,
sg: unsafe { (*self.sgt).sgl },
_p: PhantomData,
}
}
}
impl<'a, T: DriverObject> IntoIterator for &'a SGTable<T> {
type Item = &'a SGEntry;
type IntoIter = SGTableIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
/// SAFETY: `sg_table` objects are safe to send across threads.
unsafe impl<T: DriverObject> Send for SGTable<T> {}
unsafe impl<T: DriverObject> Sync for SGTable<T> {}
/// An iterator through `SGTable` entries.
///
/// # Invariants
/// `sg` must be a valid pointer to the scatterlist, which must outlive our lifetime.
pub struct SGTableIter<'a> {
sg: *mut bindings::scatterlist,
left: usize,
_p: PhantomData<&'a ()>,
}
impl<'a> Iterator for SGTableIter<'a> {
type Item = &'a SGEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
None
} else {
let sg = self.sg;
self.sg = unsafe { bindings::sg_next(self.sg) };
self.left -= 1;
Some(unsafe { &(*(sg as *const SGEntry)) })
}
}
}
This diff is collapsed.
This diff is collapsed.