mirror of https://github.com/torvalds/linux.git
Nova changes for v6.16
auxiliary:
- bus abstractions
- implementation for driver registration
- add sample driver
drm:
- implement __drm_dev_alloc()
- DRM core infrastructure Rust abstractions
- device, driver and registration
- DRM IOCTL
- DRM File
- GEM object
- IntoGEMObject rework
- generically implement AlwaysRefCounted through IntoGEMObject
- refactor unsound from_gem_obj() into as_ref()
- refactor into_gem_obj() into as_raw()
driver-core:
- merge topic/device-context-2025-04-17 from driver-core tree
- implement Devres::access()
- fix: doctest build under `!CONFIG_PCI`
- accessor for Device::parent()
- fix: conditionally expect `dead_code` for `parent()`
- impl TryFrom<&Device> bus devices (PCI, platform)
nova-core:
- remove completed Vec extentions from task list
- register auxiliary device for nova-drm
- derive useful traits for Chipset
- add missing GA100 chipset
- take &Device<Bound> in Gpu::new()
- infrastructure to generate register definitions
- fix register layout of NV_PMC_BOOT_0
- move Firmware into own (Rust) module
- fix: select AUXILIARY_BUS
nova-drm:
- initial driver skeleton (depends on drm and auxiliary bus
abstractions)
- fix: select AUXILIARY_BUS
Rust (dependencies):
- implement Opaque::zeroed()
- implement Revocable::try_access_with()
- implement Revocable::access()
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQS2q/xV6QjXAdC7k+1FlHeO1qrKLgUCaCw8MQAKCRBFlHeO1qrK
LjWvAP9kg3wJuJtO5KT7RsEk/fsPXoHy0QuB45v/R7yWdehsrwEA7WPoR/3TfDqN
Ydq1JMMHw9lrDqaSNcMMw5K8zYs4oQQ=
=FRbl
-----END PGP SIGNATURE-----
Merge tag 'nova-next-v6.16-2025-05-20' of https://gitlab.freedesktop.org/drm/nova into drm-next
Nova changes for v6.16
auxiliary:
- bus abstractions
- implementation for driver registration
- add sample driver
drm:
- implement __drm_dev_alloc()
- DRM core infrastructure Rust abstractions
- device, driver and registration
- DRM IOCTL
- DRM File
- GEM object
- IntoGEMObject rework
- generically implement AlwaysRefCounted through IntoGEMObject
- refactor unsound from_gem_obj() into as_ref()
- refactor into_gem_obj() into as_raw()
driver-core:
- merge topic/device-context-2025-04-17 from driver-core tree
- implement Devres::access()
- fix: doctest build under `!CONFIG_PCI`
- accessor for Device::parent()
- fix: conditionally expect `dead_code` for `parent()`
- impl TryFrom<&Device> bus devices (PCI, platform)
nova-core:
- remove completed Vec extentions from task list
- register auxiliary device for nova-drm
- derive useful traits for Chipset
- add missing GA100 chipset
- take &Device<Bound> in Gpu::new()
- infrastructure to generate register definitions
- fix register layout of NV_PMC_BOOT_0
- move Firmware into own (Rust) module
- fix: select AUXILIARY_BUS
nova-drm:
- initial driver skeleton (depends on drm and auxiliary bus
abstractions)
- fix: select AUXILIARY_BUS
Rust (dependencies):
- implement Opaque::zeroed()
- implement Revocable::try_access_with()
- implement Revocable::access()
From: Danilo Krummrich <dakr@kernel.org>
Link: https://lore.kernel.org/r/aCxAf3RqQAXLDhAj@cassiopeiae
This commit is contained in:
commit
c4f8ac095f
|
|
@ -102,7 +102,13 @@ Usage:
|
|||
let boot0 = Boot0::read(&bar);
|
||||
pr_info!("Revision: {}\n", boot0.revision());
|
||||
|
||||
Note: a work-in-progress implementation currently resides in
|
||||
`drivers/gpu/nova-core/regs/macros.rs` and is used in nova-core. It would be
|
||||
nice to improve it (possibly using proc macros) and move it to the `kernel`
|
||||
crate so it can be used by other components as well.
|
||||
|
||||
| Complexity: Advanced
|
||||
| Contact: Alexandre Courbot
|
||||
|
||||
Delay / Sleep abstractions
|
||||
--------------------------
|
||||
|
|
@ -190,16 +196,6 @@ Rust abstraction for debugfs APIs.
|
|||
| Reference: Export GSP log buffers
|
||||
| Complexity: Intermediate
|
||||
|
||||
Vec extensions
|
||||
--------------
|
||||
|
||||
Implement ``Vec::truncate`` and ``Vec::resize``.
|
||||
|
||||
Currently this is used for some experimental code to parse the vBIOS.
|
||||
|
||||
| Reference vBIOS support
|
||||
| Complexity: Beginner
|
||||
|
||||
GPU (general)
|
||||
=============
|
||||
|
||||
|
|
|
|||
17
MAINTAINERS
17
MAINTAINERS
|
|
@ -3880,6 +3880,9 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
|
|||
F: Documentation/driver-api/auxiliary_bus.rst
|
||||
F: drivers/base/auxiliary.c
|
||||
F: include/linux/auxiliary_bus.h
|
||||
F: rust/helpers/auxiliary.c
|
||||
F: rust/kernel/auxiliary.rs
|
||||
F: samples/rust/rust_driver_auxiliary.rs
|
||||
|
||||
AUXILIARY DISPLAY DRIVERS
|
||||
M: Andy Shevchenko <andy@kernel.org>
|
||||
|
|
@ -7607,6 +7610,18 @@ T: git https://gitlab.freedesktop.org/drm/nova.git nova-next
|
|||
F: Documentation/gpu/nova/
|
||||
F: drivers/gpu/nova-core/
|
||||
|
||||
DRM DRIVER FOR NVIDIA GPUS [RUST]
|
||||
M: Danilo Krummrich <dakr@kernel.org>
|
||||
L: nouveau@lists.freedesktop.org
|
||||
S: Supported
|
||||
Q: https://patchwork.freedesktop.org/project/nouveau/
|
||||
B: https://gitlab.freedesktop.org/drm/nova/-/issues
|
||||
C: irc://irc.oftc.net/nouveau
|
||||
T: git https://gitlab.freedesktop.org/drm/nova.git nova-next
|
||||
F: Documentation/gpu/nova/
|
||||
F: drivers/gpu/drm/nova/
|
||||
F: include/uapi/drm/nova_drm.h
|
||||
|
||||
DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS
|
||||
M: Stefan Mavrodiev <stefan@olimex.com>
|
||||
S: Maintained
|
||||
|
|
@ -7820,6 +7835,7 @@ F: Documentation/devicetree/bindings/display/
|
|||
F: Documentation/devicetree/bindings/gpu/
|
||||
F: Documentation/gpu/
|
||||
F: drivers/gpu/
|
||||
F: rust/kernel/drm/
|
||||
F: include/drm/
|
||||
F: include/linux/vga*
|
||||
F: include/uapi/drm/
|
||||
|
|
@ -7836,6 +7852,7 @@ F: Documentation/devicetree/bindings/gpu/
|
|||
F: Documentation/gpu/
|
||||
F: drivers/gpu/drm/
|
||||
F: drivers/gpu/vga/
|
||||
F: rust/kernel/drm/
|
||||
F: include/drm/drm
|
||||
F: include/linux/vga*
|
||||
F: include/uapi/drm/
|
||||
|
|
|
|||
|
|
@ -274,6 +274,8 @@ source "drivers/gpu/drm/amd/amdgpu/Kconfig"
|
|||
|
||||
source "drivers/gpu/drm/nouveau/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/nova/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/i915/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/xe/Kconfig"
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
|
|||
obj-$(CONFIG_DRM_VGEM) += vgem/
|
||||
obj-$(CONFIG_DRM_VKMS) += vkms/
|
||||
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
||||
obj-$(CONFIG_DRM_NOVA) += nova/
|
||||
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
||||
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
|
||||
obj-$(CONFIG_DRM_GMA500) += gma500/
|
||||
|
|
|
|||
|
|
@ -829,6 +829,47 @@ void *__devm_drm_dev_alloc(struct device *parent,
|
|||
}
|
||||
EXPORT_SYMBOL(__devm_drm_dev_alloc);
|
||||
|
||||
/**
|
||||
* __drm_dev_alloc - Allocation of a &drm_device instance
|
||||
* @parent: Parent device object
|
||||
* @driver: DRM driver
|
||||
* @size: the size of the struct which contains struct drm_device
|
||||
* @offset: the offset of the &drm_device within the container.
|
||||
*
|
||||
* This should *NOT* be by any drivers, but is a dedicated interface for the
|
||||
* corresponding Rust abstraction.
|
||||
*
|
||||
* This is the same as devm_drm_dev_alloc(), but without the corresponding
|
||||
* resource management through the parent device, but not the same as
|
||||
* drm_dev_alloc(), since the latter is the deprecated version, which does not
|
||||
* support subclassing.
|
||||
*
|
||||
* Returns: A pointer to new DRM device, or an ERR_PTR on failure.
|
||||
*/
|
||||
void *__drm_dev_alloc(struct device *parent,
|
||||
const struct drm_driver *driver,
|
||||
size_t size, size_t offset)
|
||||
{
|
||||
void *container;
|
||||
struct drm_device *drm;
|
||||
int ret;
|
||||
|
||||
container = kzalloc(size, GFP_KERNEL);
|
||||
if (!container)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
drm = container + offset;
|
||||
ret = drm_dev_init(drm, driver, parent);
|
||||
if (ret) {
|
||||
kfree(container);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
drmm_add_final_kfree(drm, container);
|
||||
|
||||
return container;
|
||||
}
|
||||
EXPORT_SYMBOL(__drm_dev_alloc);
|
||||
|
||||
/**
|
||||
* drm_dev_alloc - Allocate new DRM device
|
||||
* @driver: DRM driver to allocate device for
|
||||
|
|
@ -844,22 +885,7 @@ EXPORT_SYMBOL(__devm_drm_dev_alloc);
|
|||
struct drm_device *drm_dev_alloc(const struct drm_driver *driver,
|
||||
struct device *parent)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = drm_dev_init(dev, driver, parent);
|
||||
if (ret) {
|
||||
kfree(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drmm_add_final_kfree(dev, dev);
|
||||
|
||||
return dev;
|
||||
return __drm_dev_alloc(parent, driver, sizeof(struct drm_device), 0);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dev_alloc);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
config DRM_NOVA
|
||||
tristate "Nova DRM driver"
|
||||
depends on DRM=y
|
||||
depends on PCI
|
||||
depends on RUST
|
||||
select AUXILIARY_BUS
|
||||
default n
|
||||
help
|
||||
Choose this if you want to build the Nova DRM driver for Nvidia
|
||||
GSP-based GPUs.
|
||||
|
||||
This driver is work in progress and may not be functional.
|
||||
|
||||
If M is selected, the module will be called nova.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_DRM_NOVA) += nova.o
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use kernel::{auxiliary, c_str, device::Core, drm, drm::gem, drm::ioctl, prelude::*, types::ARef};
|
||||
|
||||
use crate::file::File;
|
||||
use crate::gem::NovaObject;
|
||||
|
||||
pub(crate) struct NovaDriver {
|
||||
#[expect(unused)]
|
||||
drm: ARef<drm::Device<Self>>,
|
||||
}
|
||||
|
||||
/// Convienence type alias for the DRM device type for this driver
|
||||
pub(crate) type NovaDevice = drm::Device<NovaDriver>;
|
||||
|
||||
#[pin_data]
|
||||
pub(crate) struct NovaData {
|
||||
pub(crate) adev: ARef<auxiliary::Device>,
|
||||
}
|
||||
|
||||
const INFO: drm::DriverInfo = drm::DriverInfo {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
patchlevel: 0,
|
||||
name: c_str!("nova"),
|
||||
desc: c_str!("Nvidia Graphics"),
|
||||
};
|
||||
|
||||
const NOVA_CORE_MODULE_NAME: &CStr = c_str!("NovaCore");
|
||||
const AUXILIARY_NAME: &CStr = c_str!("nova-drm");
|
||||
|
||||
kernel::auxiliary_device_table!(
|
||||
AUX_TABLE,
|
||||
MODULE_AUX_TABLE,
|
||||
<NovaDriver as auxiliary::Driver>::IdInfo,
|
||||
[(
|
||||
auxiliary::DeviceId::new(NOVA_CORE_MODULE_NAME, AUXILIARY_NAME),
|
||||
()
|
||||
)]
|
||||
);
|
||||
|
||||
impl auxiliary::Driver for NovaDriver {
|
||||
type IdInfo = ();
|
||||
const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
|
||||
|
||||
fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
|
||||
let data = try_pin_init!(NovaData { adev: adev.into() });
|
||||
|
||||
let drm = drm::Device::<Self>::new(adev.as_ref(), data)?;
|
||||
drm::Registration::new_foreign_owned(&drm, adev.as_ref(), 0)?;
|
||||
|
||||
Ok(KBox::new(Self { drm }, GFP_KERNEL)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[vtable]
|
||||
impl drm::Driver for NovaDriver {
|
||||
type Data = NovaData;
|
||||
type File = File;
|
||||
type Object = gem::Object<NovaObject>;
|
||||
|
||||
const INFO: drm::DriverInfo = INFO;
|
||||
|
||||
kernel::declare_drm_ioctls! {
|
||||
(NOVA_GETPARAM, drm_nova_getparam, ioctl::RENDER_ALLOW, File::get_param),
|
||||
(NOVA_GEM_CREATE, drm_nova_gem_create, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_create),
|
||||
(NOVA_GEM_INFO, drm_nova_gem_info, ioctl::AUTH | ioctl::RENDER_ALLOW, File::gem_info),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use crate::driver::{NovaDevice, NovaDriver};
|
||||
use crate::gem::NovaObject;
|
||||
use crate::uapi::{GemCreate, GemInfo, Getparam};
|
||||
use kernel::{
|
||||
alloc::flags::*,
|
||||
drm::{self, gem::BaseObject},
|
||||
pci,
|
||||
prelude::*,
|
||||
types::Opaque,
|
||||
uapi,
|
||||
};
|
||||
|
||||
pub(crate) struct File;
|
||||
|
||||
impl drm::file::DriverFile for File {
|
||||
type Driver = NovaDriver;
|
||||
|
||||
fn open(_dev: &NovaDevice) -> Result<Pin<KBox<Self>>> {
|
||||
Ok(KBox::new(Self, GFP_KERNEL)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// IOCTL: get_param: Query GPU / driver metadata.
|
||||
pub(crate) fn get_param(
|
||||
dev: &NovaDevice,
|
||||
getparam: &Opaque<uapi::drm_nova_getparam>,
|
||||
_file: &drm::File<File>,
|
||||
) -> Result<u32> {
|
||||
let adev = &dev.adev;
|
||||
let parent = adev.parent().ok_or(ENOENT)?;
|
||||
let pdev: &pci::Device = parent.try_into()?;
|
||||
let getparam: &Getparam = getparam.into();
|
||||
|
||||
let value = match getparam.param() as u32 {
|
||||
uapi::NOVA_GETPARAM_VRAM_BAR_SIZE => pdev.resource_len(1)?,
|
||||
_ => return Err(EINVAL),
|
||||
};
|
||||
|
||||
getparam.set_value(value);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// IOCTL: gem_create: Create a new DRM GEM object.
|
||||
pub(crate) fn gem_create(
|
||||
dev: &NovaDevice,
|
||||
req: &Opaque<uapi::drm_nova_gem_create>,
|
||||
file: &drm::File<File>,
|
||||
) -> Result<u32> {
|
||||
let req: &GemCreate = req.into();
|
||||
let obj = NovaObject::new(dev, req.size().try_into()?)?;
|
||||
|
||||
req.set_handle(obj.create_handle(file)?);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// IOCTL: gem_info: Query GEM metadata.
|
||||
pub(crate) fn gem_info(
|
||||
_dev: &NovaDevice,
|
||||
req: &Opaque<uapi::drm_nova_gem_info>,
|
||||
file: &drm::File<File>,
|
||||
) -> Result<u32> {
|
||||
let req: &GemInfo = req.into();
|
||||
let bo = NovaObject::lookup_handle(file, req.handle())?;
|
||||
|
||||
req.set_size(bo.size().try_into()?);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use kernel::{
|
||||
drm,
|
||||
drm::{gem, gem::BaseObject},
|
||||
prelude::*,
|
||||
types::ARef,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
driver::{NovaDevice, NovaDriver},
|
||||
file::File,
|
||||
};
|
||||
|
||||
/// GEM Object inner driver data
|
||||
#[pin_data]
|
||||
pub(crate) struct NovaObject {}
|
||||
|
||||
impl gem::BaseDriverObject<gem::Object<NovaObject>> for NovaObject {
|
||||
fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit<Self, Error> {
|
||||
try_pin_init!(NovaObject {})
|
||||
}
|
||||
}
|
||||
|
||||
impl gem::DriverObject for NovaObject {
|
||||
type Driver = NovaDriver;
|
||||
}
|
||||
|
||||
impl NovaObject {
|
||||
/// Create a new DRM GEM object.
|
||||
pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result<ARef<gem::Object<Self>>> {
|
||||
let aligned_size = size.next_multiple_of(1 << 12);
|
||||
|
||||
if size == 0 || size > aligned_size {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
gem::Object::new(dev, aligned_size)
|
||||
}
|
||||
|
||||
/// Look up a GEM object handle for a `File` and return an `ObjectRef` for it.
|
||||
#[inline]
|
||||
pub(crate) fn lookup_handle(
|
||||
file: &drm::File<File>,
|
||||
handle: u32,
|
||||
) -> Result<ARef<gem::Object<Self>>> {
|
||||
gem::Object::lookup_handle(file, handle)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Nova DRM Driver
|
||||
|
||||
mod driver;
|
||||
mod file;
|
||||
mod gem;
|
||||
mod uapi;
|
||||
|
||||
use crate::driver::NovaDriver;
|
||||
|
||||
kernel::module_auxiliary_driver! {
|
||||
type: NovaDriver,
|
||||
name: "Nova",
|
||||
author: "Danilo Krummrich",
|
||||
description: "Nova GPU driver",
|
||||
license: "GPL v2",
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use kernel::uapi;
|
||||
|
||||
// TODO Work out some common infrastructure to avoid boilerplate code for uAPI abstractions.
|
||||
|
||||
macro_rules! define_uapi_abstraction {
|
||||
($name:ident <= $inner:ty) => {
|
||||
#[repr(transparent)]
|
||||
pub struct $name(::kernel::types::Opaque<$inner>);
|
||||
|
||||
impl ::core::convert::From<&::kernel::types::Opaque<$inner>> for &$name {
|
||||
fn from(value: &::kernel::types::Opaque<$inner>) -> Self {
|
||||
// SAFETY: `Self` is a transparent wrapper of `$inner`.
|
||||
unsafe { ::core::mem::transmute(value) }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_uapi_abstraction!(Getparam <= uapi::drm_nova_getparam);
|
||||
|
||||
impl Getparam {
|
||||
pub fn param(&self) -> u64 {
|
||||
// SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`.
|
||||
unsafe { (*self.0.get()).param }
|
||||
}
|
||||
|
||||
pub fn set_value(&self, v: u64) {
|
||||
// SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_getparam`.
|
||||
unsafe { (*self.0.get()).value = v };
|
||||
}
|
||||
}
|
||||
|
||||
define_uapi_abstraction!(GemCreate <= uapi::drm_nova_gem_create);
|
||||
|
||||
impl GemCreate {
|
||||
pub fn size(&self) -> u64 {
|
||||
// SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`.
|
||||
unsafe { (*self.0.get()).size }
|
||||
}
|
||||
|
||||
pub fn set_handle(&self, handle: u32) {
|
||||
// SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_create`.
|
||||
unsafe { (*self.0.get()).handle = handle };
|
||||
}
|
||||
}
|
||||
|
||||
define_uapi_abstraction!(GemInfo <= uapi::drm_nova_gem_info);
|
||||
|
||||
impl GemInfo {
|
||||
pub fn handle(&self) -> u32 {
|
||||
// SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`.
|
||||
unsafe { (*self.0.get()).handle }
|
||||
}
|
||||
|
||||
pub fn set_size(&self, size: u64) {
|
||||
// SAFETY: `self.get()` is a valid pointer to a `struct drm_nova_gem_info`.
|
||||
unsafe { (*self.0.get()).size = size };
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ config NOVA_CORE
|
|||
depends on PCI
|
||||
depends on RUST
|
||||
depends on RUST_FW_LOADER_ABSTRACTIONS
|
||||
select AUXILIARY_BUS
|
||||
default n
|
||||
help
|
||||
Choose this if you want to build the Nova Core driver for Nvidia
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use kernel::{bindings, c_str, device::Core, pci, prelude::*};
|
||||
use kernel::{auxiliary, bindings, c_str, device::Core, pci, prelude::*};
|
||||
|
||||
use crate::gpu::Gpu;
|
||||
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
pub(crate) struct NovaCore {
|
||||
#[pin]
|
||||
pub(crate) gpu: Gpu,
|
||||
_reg: auxiliary::Registration,
|
||||
}
|
||||
|
||||
const BAR0_SIZE: usize = 8;
|
||||
|
|
@ -38,6 +39,12 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self
|
|||
let this = KBox::pin_init(
|
||||
try_pin_init!(Self {
|
||||
gpu <- Gpu::new(pdev, bar)?,
|
||||
_reg: auxiliary::Registration::new(
|
||||
pdev.as_ref(),
|
||||
c_str!("nova-drm"),
|
||||
0, // TODO: Once it lands, use XArray; for now we don't use the ID.
|
||||
crate::MODULE_NAME
|
||||
)?,
|
||||
}),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,49 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use crate::gpu;
|
||||
//! Contains structures and functions dedicated to the parsing, building and patching of firmwares
|
||||
//! to be loaded into a given execution unit.
|
||||
|
||||
use kernel::device;
|
||||
use kernel::firmware;
|
||||
use kernel::prelude::*;
|
||||
use kernel::str::CString;
|
||||
|
||||
use crate::gpu;
|
||||
use crate::gpu::Chipset;
|
||||
|
||||
pub(crate) const FIRMWARE_VERSION: &str = "535.113.01";
|
||||
|
||||
/// Structure encapsulating the firmware blobs required for the GPU to operate.
|
||||
#[expect(dead_code)]
|
||||
pub(crate) struct Firmware {
|
||||
booter_load: firmware::Firmware,
|
||||
booter_unload: firmware::Firmware,
|
||||
bootloader: firmware::Firmware,
|
||||
gsp: firmware::Firmware,
|
||||
}
|
||||
|
||||
impl Firmware {
|
||||
pub(crate) fn new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware> {
|
||||
let mut chip_name = CString::try_from_fmt(fmt!("{}", chipset))?;
|
||||
chip_name.make_ascii_lowercase();
|
||||
|
||||
let request = |name_| {
|
||||
CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
|
||||
.and_then(|path| firmware::Firmware::request(&path, dev))
|
||||
};
|
||||
|
||||
Ok(Firmware {
|
||||
booter_load: request("booter_load")?,
|
||||
booter_unload: request("booter_unload")?,
|
||||
bootloader: request("bootloader")?,
|
||||
gsp: request("gsp")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
|
||||
|
||||
impl<const N: usize> ModInfoBuilder<N> {
|
||||
const VERSION: &'static str = "535.113.01";
|
||||
|
||||
const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
|
||||
ModInfoBuilder(
|
||||
self.0
|
||||
|
|
@ -17,7 +53,7 @@ const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
|
|||
.push("/gsp/")
|
||||
.push(fw)
|
||||
.push("-")
|
||||
.push(Self::VERSION)
|
||||
.push(FIRMWARE_VERSION)
|
||||
.push(".bin"),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use kernel::{
|
||||
device, devres::Devres, error::code::*, firmware, fmt, pci, prelude::*, str::CString,
|
||||
};
|
||||
use kernel::{device, devres::Devres, error::code::*, pci, prelude::*};
|
||||
|
||||
use crate::driver::Bar0;
|
||||
use crate::firmware::{Firmware, FIRMWARE_VERSION};
|
||||
use crate::regs;
|
||||
use crate::util;
|
||||
use core::fmt;
|
||||
|
|
@ -13,7 +12,7 @@ macro_rules! define_chipset {
|
|||
({ $($variant:ident = $value:expr),* $(,)* }) =>
|
||||
{
|
||||
/// Enum representation of the GPU chipset.
|
||||
#[derive(fmt::Debug)]
|
||||
#[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub(crate) enum Chipset {
|
||||
$($variant = $value),*,
|
||||
}
|
||||
|
|
@ -54,6 +53,7 @@ fn try_from(value: u32) -> Result<Self, Self::Error> {
|
|||
TU117 = 0x167,
|
||||
TU116 = 0x168,
|
||||
// Ampere
|
||||
GA100 = 0x170,
|
||||
GA102 = 0x172,
|
||||
GA103 = 0x173,
|
||||
GA104 = 0x174,
|
||||
|
|
@ -73,7 +73,7 @@ pub(crate) fn arch(&self) -> Architecture {
|
|||
Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
|
||||
Architecture::Turing
|
||||
}
|
||||
Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
|
||||
Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
|
||||
Architecture::Ampere
|
||||
}
|
||||
Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
|
||||
|
|
@ -100,9 +100,22 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|||
/// Enum representation of the GPU generation.
|
||||
#[derive(fmt::Debug)]
|
||||
pub(crate) enum Architecture {
|
||||
Turing,
|
||||
Ampere,
|
||||
Ada,
|
||||
Turing = 0x16,
|
||||
Ampere = 0x17,
|
||||
Ada = 0x19,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Architecture {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self> {
|
||||
match value {
|
||||
0x16 => Ok(Self::Turing),
|
||||
0x17 => Ok(Self::Ampere),
|
||||
0x19 => Ok(Self::Ada),
|
||||
_ => Err(ENODEV),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Revision {
|
||||
|
|
@ -111,10 +124,10 @@ pub(crate) struct Revision {
|
|||
}
|
||||
|
||||
impl Revision {
|
||||
fn from_boot0(boot0: regs::Boot0) -> Self {
|
||||
fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self {
|
||||
Self {
|
||||
major: boot0.major_rev(),
|
||||
minor: boot0.minor_rev(),
|
||||
major: boot0.major_revision(),
|
||||
minor: boot0.minor_revision(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -133,45 +146,16 @@ pub(crate) struct Spec {
|
|||
}
|
||||
|
||||
impl Spec {
|
||||
fn new(bar: &Devres<Bar0>) -> Result<Spec> {
|
||||
let bar = bar.try_access().ok_or(ENXIO)?;
|
||||
let boot0 = regs::Boot0::read(&bar);
|
||||
fn new(bar: &Bar0) -> Result<Spec> {
|
||||
let boot0 = regs::NV_PMC_BOOT_0::read(bar);
|
||||
|
||||
Ok(Self {
|
||||
chipset: boot0.chipset().try_into()?,
|
||||
chipset: boot0.chipset()?,
|
||||
revision: Revision::from_boot0(boot0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure encapsulating the firmware blobs required for the GPU to operate.
|
||||
#[expect(dead_code)]
|
||||
pub(crate) struct Firmware {
|
||||
booter_load: firmware::Firmware,
|
||||
booter_unload: firmware::Firmware,
|
||||
bootloader: firmware::Firmware,
|
||||
gsp: firmware::Firmware,
|
||||
}
|
||||
|
||||
impl Firmware {
|
||||
fn new(dev: &device::Device, spec: &Spec, ver: &str) -> Result<Firmware> {
|
||||
let mut chip_name = CString::try_from_fmt(fmt!("{}", spec.chipset))?;
|
||||
chip_name.make_ascii_lowercase();
|
||||
|
||||
let request = |name_| {
|
||||
CString::try_from_fmt(fmt!("nvidia/{}/gsp/{}-{}.bin", &*chip_name, name_, ver))
|
||||
.and_then(|path| firmware::Firmware::request(&path, dev))
|
||||
};
|
||||
|
||||
Ok(Firmware {
|
||||
booter_load: request("booter_load")?,
|
||||
booter_unload: request("booter_unload")?,
|
||||
bootloader: request("bootloader")?,
|
||||
gsp: request("gsp")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure holding the resources required to operate the GPU.
|
||||
#[pin_data]
|
||||
pub(crate) struct Gpu {
|
||||
|
|
@ -182,9 +166,13 @@ pub(crate) struct Gpu {
|
|||
}
|
||||
|
||||
impl Gpu {
|
||||
pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<Self>> {
|
||||
let spec = Spec::new(&bar)?;
|
||||
let fw = Firmware::new(pdev.as_ref(), &spec, "535.113.01")?;
|
||||
pub(crate) fn new(
|
||||
pdev: &pci::Device<device::Bound>,
|
||||
devres_bar: Devres<Bar0>,
|
||||
) -> Result<impl PinInit<Self>> {
|
||||
let bar = devres_bar.access(pdev.as_ref())?;
|
||||
let spec = Spec::new(bar)?;
|
||||
let fw = Firmware::new(pdev.as_ref(), spec.chipset, FIRMWARE_VERSION)?;
|
||||
|
||||
dev_info!(
|
||||
pdev.as_ref(),
|
||||
|
|
@ -194,6 +182,10 @@ pub(crate) fn new(pdev: &pci::Device, bar: Devres<Bar0>) -> Result<impl PinInit<
|
|||
spec.revision
|
||||
);
|
||||
|
||||
Ok(pin_init!(Self { spec, bar, fw }))
|
||||
Ok(pin_init!(Self {
|
||||
spec,
|
||||
bar: devres_bar,
|
||||
fw
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
mod regs;
|
||||
mod util;
|
||||
|
||||
pub(crate) const MODULE_NAME: &kernel::str::CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
|
||||
|
||||
kernel::module_pci_driver! {
|
||||
type: driver::NovaCore,
|
||||
name: "NovaCore",
|
||||
|
|
|
|||
|
|
@ -1,55 +1,39 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use crate::driver::Bar0;
|
||||
// Required to retain the original register names used by OpenRM, which are all capital snake case
|
||||
// but are mapped to types.
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
// TODO
|
||||
//
|
||||
// Create register definitions via generic macros. See task "Generic register
|
||||
// abstraction" in Documentation/gpu/nova/core/todo.rst.
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
const BOOT0_OFFSET: usize = 0x00000000;
|
||||
use crate::gpu::{Architecture, Chipset};
|
||||
use kernel::prelude::*;
|
||||
|
||||
// 3:0 - chipset minor revision
|
||||
const BOOT0_MINOR_REV_SHIFT: u8 = 0;
|
||||
const BOOT0_MINOR_REV_MASK: u32 = 0x0000000f;
|
||||
/* PMC */
|
||||
|
||||
// 7:4 - chipset major revision
|
||||
const BOOT0_MAJOR_REV_SHIFT: u8 = 4;
|
||||
const BOOT0_MAJOR_REV_MASK: u32 = 0x000000f0;
|
||||
register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU" {
|
||||
3:0 minor_revision as u8, "Minor revision of the chip";
|
||||
7:4 major_revision as u8, "Major revision of the chip";
|
||||
8:8 architecture_1 as u8, "MSB of the architecture";
|
||||
23:20 implementation as u8, "Implementation version of the architecture";
|
||||
28:24 architecture_0 as u8, "Lower bits of the architecture";
|
||||
});
|
||||
|
||||
// 23:20 - chipset implementation Identifier (depends on architecture)
|
||||
const BOOT0_IMPL_SHIFT: u8 = 20;
|
||||
const BOOT0_IMPL_MASK: u32 = 0x00f00000;
|
||||
|
||||
// 28:24 - chipset architecture identifier
|
||||
const BOOT0_ARCH_MASK: u32 = 0x1f000000;
|
||||
|
||||
// 28:20 - chipset identifier (virtual register field combining BOOT0_IMPL and
|
||||
// BOOT0_ARCH)
|
||||
const BOOT0_CHIPSET_SHIFT: u8 = BOOT0_IMPL_SHIFT;
|
||||
const BOOT0_CHIPSET_MASK: u32 = BOOT0_IMPL_MASK | BOOT0_ARCH_MASK;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct Boot0(u32);
|
||||
|
||||
impl Boot0 {
|
||||
#[inline]
|
||||
pub(crate) fn read(bar: &Bar0) -> Self {
|
||||
Self(bar.read32(BOOT0_OFFSET))
|
||||
impl NV_PMC_BOOT_0 {
|
||||
/// Combines `architecture_0` and `architecture_1` to obtain the architecture of the chip.
|
||||
pub(crate) fn architecture(self) -> Result<Architecture> {
|
||||
Architecture::try_from(
|
||||
self.architecture_0() | (self.architecture_1() << Self::ARCHITECTURE_0.len()),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn chipset(&self) -> u32 {
|
||||
(self.0 & BOOT0_CHIPSET_MASK) >> BOOT0_CHIPSET_SHIFT
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn minor_rev(&self) -> u8 {
|
||||
((self.0 & BOOT0_MINOR_REV_MASK) >> BOOT0_MINOR_REV_SHIFT) as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn major_rev(&self) -> u8 {
|
||||
((self.0 & BOOT0_MAJOR_REV_MASK) >> BOOT0_MAJOR_REV_SHIFT) as u8
|
||||
/// Combines `architecture` and `implementation` to obtain a code unique to the chipset.
|
||||
pub(crate) fn chipset(self) -> Result<Chipset> {
|
||||
self.architecture()
|
||||
.map(|arch| {
|
||||
((arch as u32) << Self::IMPLEMENTATION.len()) | self.implementation() as u32
|
||||
})
|
||||
.and_then(Chipset::try_from)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,380 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Macro to define register layout and accessors.
|
||||
//!
|
||||
//! A single register typically includes several fields, which are accessed through a combination
|
||||
//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
|
||||
//! not all possible field values are necessarily valid.
|
||||
//!
|
||||
//! The macro in this module allow to define, using an intruitive and readable syntax, a dedicated
|
||||
//! type for each register with its own field accessors that can return an error is a field's value
|
||||
//! is invalid.
|
||||
|
||||
/// Defines a dedicated type for a register with an absolute offset, alongside with getter and
|
||||
/// setter methods for its fields and methods to read and write it from an `Io` region.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```no_run
|
||||
/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
|
||||
/// 3:0 minor_revision as u8, "Minor revision of the chip";
|
||||
/// 7:4 major_revision as u8, "Major revision of the chip";
|
||||
/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
|
||||
/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 less
|
||||
/// significant bits of the register. Each field can be accessed and modified using accessor
|
||||
/// methods:
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Read from the register's defined offset (0x100).
|
||||
/// let boot0 = BOOT_0::read(&bar);
|
||||
/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
|
||||
///
|
||||
/// // `Chipset::try_from` will be called with the value of the field and returns an error if the
|
||||
/// // value is invalid.
|
||||
/// let chipset = boot0.chipset()?;
|
||||
///
|
||||
/// // Update some fields and write the value back.
|
||||
/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
|
||||
///
|
||||
/// // Or just read and update the register in a single step:
|
||||
/// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
|
||||
/// ```
|
||||
///
|
||||
/// Fields can be defined as follows:
|
||||
///
|
||||
/// - `as <type>` simply returns the field value casted as the requested integer type, typically
|
||||
/// `u32`, `u16`, `u8` or `bool`. Note that `bool` fields must have a range of 1 bit.
|
||||
/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
|
||||
/// the result.
|
||||
/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
|
||||
/// and returns the result. This is useful on fields for which not all values are value.
|
||||
///
|
||||
/// The documentation strings are optional. If present, they will be added to the type's
|
||||
/// definition, or the field getter and setter methods they are attached to.
|
||||
///
|
||||
/// Putting a `+` before the address of the register makes it relative to a base: the `read` and
|
||||
/// `write` methods take a `base` argument that is added to the specified address before access,
|
||||
/// and `try_read` and `try_write` methods are also created, allowing access with offsets unknown
|
||||
/// at compile-time:
|
||||
///
|
||||
/// ```no_run
|
||||
/// register!(CPU_CTL @ +0x0000010, "CPU core control" {
|
||||
/// 0:0 start as bool, "Start the CPU core";
|
||||
/// });
|
||||
///
|
||||
/// // Flip the `start` switch for the CPU core which base address is at `CPU_BASE`.
|
||||
/// let cpuctl = CPU_CTL::read(&bar, CPU_BASE);
|
||||
/// pr_info!("CPU CTL: {:#x}", cpuctl);
|
||||
/// cpuctl.set_start(true).write(&bar, CPU_BASE);
|
||||
/// ```
|
||||
macro_rules! register {
|
||||
// Creates a register at a fixed offset of the MMIO space.
|
||||
(
|
||||
$name:ident @ $offset:literal $(, $comment:literal)? {
|
||||
$($fields:tt)*
|
||||
}
|
||||
) => {
|
||||
register!(@common $name $(, $comment)?);
|
||||
register!(@field_accessors $name { $($fields)* });
|
||||
register!(@io $name @ $offset);
|
||||
};
|
||||
|
||||
// Creates a register at a relative offset from a base address.
|
||||
(
|
||||
$name:ident @ + $offset:literal $(, $comment:literal)? {
|
||||
$($fields:tt)*
|
||||
}
|
||||
) => {
|
||||
register!(@common $name $(, $comment)?);
|
||||
register!(@field_accessors $name { $($fields)* });
|
||||
register!(@io$name @ + $offset);
|
||||
};
|
||||
|
||||
// Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, `BitOr`,
|
||||
// and conversion to regular `u32`).
|
||||
(@common $name:ident $(, $comment:literal)?) => {
|
||||
$(
|
||||
#[doc=$comment]
|
||||
)?
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub(crate) struct $name(u32);
|
||||
|
||||
// TODO: display the raw hex value, then the value of all the fields. This requires
|
||||
// matching the fields, which will complexify the syntax considerably...
|
||||
impl ::core::fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
f.debug_tuple(stringify!($name))
|
||||
.field(&format_args!("0x{0:x}", &self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::BitOr for $name {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::convert::From<$name> for u32 {
|
||||
fn from(reg: $name) -> u32 {
|
||||
reg.0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Defines all the field getter/methods methods for `$name`.
|
||||
(
|
||||
@field_accessors $name:ident {
|
||||
$($hi:tt:$lo:tt $field:ident as $type:tt
|
||||
$(?=> $try_into_type:ty)?
|
||||
$(=> $into_type:ty)?
|
||||
$(, $comment:literal)?
|
||||
;
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$(
|
||||
register!(@check_field_bounds $hi:$lo $field as $type);
|
||||
)*
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl $name {
|
||||
$(
|
||||
register!(@field_accessor $name $hi:$lo $field as $type
|
||||
$(?=> $try_into_type)?
|
||||
$(=> $into_type)?
|
||||
$(, $comment)?
|
||||
;
|
||||
);
|
||||
)*
|
||||
}
|
||||
};
|
||||
|
||||
// Boolean fields must have `$hi == $lo`.
|
||||
(@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
|
||||
#[allow(clippy::eq_op)]
|
||||
const _: () = {
|
||||
kernel::build_assert!(
|
||||
$hi == $lo,
|
||||
concat!("boolean field `", stringify!($field), "` covers more than one bit")
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
// Non-boolean fields must have `$hi >= $lo`.
|
||||
(@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
|
||||
#[allow(clippy::eq_op)]
|
||||
const _: () = {
|
||||
kernel::build_assert!(
|
||||
$hi >= $lo,
|
||||
concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
// Catches fields defined as `bool` and convert them into a boolean value.
|
||||
(
|
||||
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
|
||||
$(, $comment:literal)?;
|
||||
) => {
|
||||
register!(
|
||||
@leaf_accessor $name $hi:$lo $field as bool
|
||||
{ |f| <$into_type>::from(if f != 0 { true } else { false }) }
|
||||
$into_type => $into_type $(, $comment)?;
|
||||
);
|
||||
};
|
||||
|
||||
// Shortcut for fields defined as `bool` without the `=>` syntax.
|
||||
(
|
||||
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
|
||||
) => {
|
||||
register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
|
||||
};
|
||||
|
||||
// Catches the `?=>` syntax for non-boolean fields.
|
||||
(
|
||||
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
|
||||
$(, $comment:literal)?;
|
||||
) => {
|
||||
register!(@leaf_accessor $name $hi:$lo $field as $type
|
||||
{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
|
||||
::core::result::Result<
|
||||
$try_into_type,
|
||||
<$try_into_type as ::core::convert::TryFrom<$type>>::Error
|
||||
>
|
||||
$(, $comment)?;);
|
||||
};
|
||||
|
||||
// Catches the `=>` syntax for non-boolean fields.
|
||||
(
|
||||
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
|
||||
$(, $comment:literal)?;
|
||||
) => {
|
||||
register!(@leaf_accessor $name $hi:$lo $field as $type
|
||||
{ |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
|
||||
};
|
||||
|
||||
// Shortcut for fields defined as non-`bool` without the `=>` or `?=>` syntax.
|
||||
(
|
||||
@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
|
||||
$(, $comment:literal)?;
|
||||
) => {
|
||||
register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
|
||||
};
|
||||
|
||||
// Generates the accessor methods for a single field.
|
||||
(
|
||||
@leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:ty
|
||||
{ $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
|
||||
) => {
|
||||
kernel::macros::paste!(
|
||||
const [<$field:upper>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
|
||||
const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
|
||||
const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
|
||||
);
|
||||
|
||||
$(
|
||||
#[doc="Returns the value of this field:"]
|
||||
#[doc=$comment]
|
||||
)?
|
||||
#[inline]
|
||||
pub(crate) fn $field(self) -> $res_type {
|
||||
kernel::macros::paste!(
|
||||
const MASK: u32 = $name::[<$field:upper _MASK>];
|
||||
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
|
||||
);
|
||||
let field = ((self.0 & MASK) >> SHIFT);
|
||||
|
||||
$process(field)
|
||||
}
|
||||
|
||||
kernel::macros::paste!(
|
||||
$(
|
||||
#[doc="Sets the value of this field:"]
|
||||
#[doc=$comment]
|
||||
)?
|
||||
#[inline]
|
||||
pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
|
||||
const MASK: u32 = $name::[<$field:upper _MASK>];
|
||||
const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
|
||||
let value = ((value as u32) << SHIFT) & MASK;
|
||||
self.0 = (self.0 & !MASK) | value;
|
||||
|
||||
self
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Creates the IO accessors for a fixed offset register.
|
||||
(@io $name:ident @ $offset:literal) => {
|
||||
#[allow(dead_code)]
|
||||
impl $name {
|
||||
#[inline]
|
||||
pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
{
|
||||
Self(io.read32($offset))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
{
|
||||
io.write32(self.0, $offset)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn alter<const SIZE: usize, T, F>(
|
||||
io: &T,
|
||||
f: F,
|
||||
) where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
F: ::core::ops::FnOnce(Self) -> Self,
|
||||
{
|
||||
let reg = f(Self::read(io));
|
||||
reg.write(io);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create the IO accessors for a relative offset register.
|
||||
(@io $name:ident @ + $offset:literal) => {
|
||||
#[allow(dead_code)]
|
||||
impl $name {
|
||||
#[inline]
|
||||
pub(crate) fn read<const SIZE: usize, T>(
|
||||
io: &T,
|
||||
base: usize,
|
||||
) -> Self where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
{
|
||||
Self(io.read32(base + $offset))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn write<const SIZE: usize, T>(
|
||||
self,
|
||||
io: &T,
|
||||
base: usize,
|
||||
) where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
{
|
||||
io.write32(self.0, base + $offset)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn alter<const SIZE: usize, T, F>(
|
||||
io: &T,
|
||||
base: usize,
|
||||
f: F,
|
||||
) where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
F: ::core::ops::FnOnce(Self) -> Self,
|
||||
{
|
||||
let reg = f(Self::read(io, base));
|
||||
reg.write(io, base);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_read<const SIZE: usize, T>(
|
||||
io: &T,
|
||||
base: usize,
|
||||
) -> ::kernel::error::Result<Self> where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
{
|
||||
io.try_read32(base + $offset).map(Self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_write<const SIZE: usize, T>(
|
||||
self,
|
||||
io: &T,
|
||||
base: usize,
|
||||
) -> ::kernel::error::Result<()> where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
{
|
||||
io.try_write32(self.0, base + $offset)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_alter<const SIZE: usize, T, F>(
|
||||
io: &T,
|
||||
base: usize,
|
||||
f: F,
|
||||
) -> ::kernel::error::Result<()> where
|
||||
T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
|
||||
F: ::core::ops::FnOnce(Self) -> Self,
|
||||
{
|
||||
let reg = f(Self::try_read(io, base)?);
|
||||
reg.try_write(io, base)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -473,6 +473,11 @@ drmm_cgroup_register_region(struct drm_device *dev,
|
|||
|
||||
struct drm_device *drm_dev_alloc(const struct drm_driver *driver,
|
||||
struct device *parent);
|
||||
|
||||
void *__drm_dev_alloc(struct device *parent,
|
||||
const struct drm_driver *driver,
|
||||
size_t size, size_t offset);
|
||||
|
||||
int drm_dev_register(struct drm_device *dev, unsigned long flags);
|
||||
void drm_dev_unregister(struct drm_device *dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#ifndef __NOVA_DRM_H__
|
||||
#define __NOVA_DRM_H__
|
||||
|
||||
#include "drm.h"
|
||||
|
||||
/* DISCLAIMER: Do not use, this is not a stable uAPI.
|
||||
*
|
||||
* This uAPI serves only testing purposes as long as this driver is still in
|
||||
* development. It is required to implement and test infrastructure which is
|
||||
* upstreamed in the context of this driver. See also [1].
|
||||
*
|
||||
* [1] https://lore.kernel.org/dri-devel/Zfsj0_tb-0-tNrJy@cassiopeiae/T/#u
|
||||
*/
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NOVA_GETPARAM_VRAM_BAR_SIZE
|
||||
*
|
||||
* Query the VRAM BAR size in bytes.
|
||||
*/
|
||||
#define NOVA_GETPARAM_VRAM_BAR_SIZE 0x1
|
||||
|
||||
/**
|
||||
* struct drm_nova_getparam - query GPU and driver metadata
|
||||
*/
|
||||
struct drm_nova_getparam {
|
||||
/**
|
||||
* @param: The identifier of the parameter to query.
|
||||
*/
|
||||
__u64 param;
|
||||
|
||||
/**
|
||||
* @value: The value for the specified parameter.
|
||||
*/
|
||||
__u64 value;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_nova_gem_create - create a new DRM GEM object
|
||||
*/
|
||||
struct drm_nova_gem_create {
|
||||
/**
|
||||
* @handle: The handle of the new DRM GEM object.
|
||||
*/
|
||||
__u32 handle;
|
||||
|
||||
/**
|
||||
* @pad: 32 bit padding, should be 0.
|
||||
*/
|
||||
__u32 pad;
|
||||
|
||||
/**
|
||||
* @size: The size of the new DRM GEM object.
|
||||
*/
|
||||
__u64 size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_nova_gem_info - query DRM GEM object metadata
|
||||
*/
|
||||
struct drm_nova_gem_info {
|
||||
/**
|
||||
* @handle: The handle of the DRM GEM object to query.
|
||||
*/
|
||||
__u32 handle;
|
||||
|
||||
/**
|
||||
* @pad: 32 bit padding, should be 0.
|
||||
*/
|
||||
__u32 pad;
|
||||
|
||||
/**
|
||||
* @size: The size of the DRM GEM obejct.
|
||||
*/
|
||||
__u64 size;
|
||||
};
|
||||
|
||||
#define DRM_NOVA_GETPARAM 0x00
|
||||
#define DRM_NOVA_GEM_CREATE 0x01
|
||||
#define DRM_NOVA_GEM_INFO 0x02
|
||||
|
||||
/* Note: this is an enum so that it can be resolved by Rust bindgen. */
|
||||
enum {
|
||||
DRM_IOCTL_NOVA_GETPARAM = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GETPARAM,
|
||||
struct drm_nova_getparam),
|
||||
DRM_IOCTL_NOVA_GEM_CREATE = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GEM_CREATE,
|
||||
struct drm_nova_gem_create),
|
||||
DRM_IOCTL_NOVA_GEM_INFO = DRM_IOWR(DRM_COMMAND_BASE + DRM_NOVA_GEM_INFO,
|
||||
struct drm_nova_gem_info),
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NOVA_DRM_H__ */
|
||||
|
|
@ -6,7 +6,13 @@
|
|||
* Sorted alphabetically.
|
||||
*/
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_ioctl.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/blk-mq.h>
|
||||
#include <linux/blk_types.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
|
@ -55,3 +61,4 @@ const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO;
|
|||
const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM;
|
||||
const gfp_t RUST_CONST_HELPER___GFP_NOWARN = ___GFP_NOWARN;
|
||||
const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL;
|
||||
const fop_flags_t RUST_CONST_HELPER_FOP_UNSIGNED_OFFSET = FOP_UNSIGNED_OFFSET;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
|
||||
void rust_helper_auxiliary_set_drvdata(struct auxiliary_device *adev, void *data)
|
||||
{
|
||||
auxiliary_set_drvdata(adev, data);
|
||||
}
|
||||
|
||||
void *rust_helper_auxiliary_get_drvdata(struct auxiliary_device *adev)
|
||||
{
|
||||
return auxiliary_get_drvdata(adev);
|
||||
}
|
||||
|
||||
void rust_helper_auxiliary_device_uninit(struct auxiliary_device *adev)
|
||||
{
|
||||
return auxiliary_device_uninit(adev);
|
||||
}
|
||||
|
||||
void rust_helper_auxiliary_device_delete(struct auxiliary_device *adev)
|
||||
{
|
||||
return auxiliary_device_delete(adev);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_vma_manager.h>
|
||||
|
||||
#ifdef CONFIG_DRM
|
||||
|
||||
void rust_helper_drm_gem_object_get(struct drm_gem_object *obj)
|
||||
{
|
||||
drm_gem_object_get(obj);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
* Sorted alphabetically.
|
||||
*/
|
||||
|
||||
#include "auxiliary.c"
|
||||
#include "blk.c"
|
||||
#include "bug.c"
|
||||
#include "build_assert.c"
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
#include "cred.c"
|
||||
#include "device.c"
|
||||
#include "dma.c"
|
||||
#include "drm.c"
|
||||
#include "err.c"
|
||||
#include "fs.c"
|
||||
#include "io.c"
|
||||
|
|
|
|||
|
|
@ -16,3 +16,8 @@ resource_size_t rust_helper_pci_resource_len(struct pci_dev *pdev, int bar)
|
|||
{
|
||||
return pci_resource_len(pdev, bar);
|
||||
}
|
||||
|
||||
bool rust_helper_dev_is_pci(const struct device *dev)
|
||||
{
|
||||
return dev_is_pci(dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,3 +11,8 @@ void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
|
|||
{
|
||||
platform_set_drvdata(pdev, data);
|
||||
}
|
||||
|
||||
bool rust_helper_dev_is_platform(const struct device *dev)
|
||||
{
|
||||
return dev_is_platform(dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,360 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Abstractions for the auxiliary bus.
|
||||
//!
|
||||
//! C header: [`include/linux/auxiliary_bus.h`](srctree/include/linux/auxiliary_bus.h)
|
||||
|
||||
use crate::{
|
||||
bindings, container_of, device,
|
||||
device_id::RawDeviceId,
|
||||
driver,
|
||||
error::{to_result, Result},
|
||||
prelude::*,
|
||||
str::CStr,
|
||||
types::{ForeignOwnable, Opaque},
|
||||
ThisModule,
|
||||
};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ptr::{addr_of_mut, NonNull},
|
||||
};
|
||||
|
||||
/// An adapter for the registration of auxiliary drivers.
|
||||
pub struct Adapter<T: Driver>(T);
|
||||
|
||||
// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
|
||||
// a preceding call to `register` has been successful.
|
||||
unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
|
||||
type RegType = bindings::auxiliary_driver;
|
||||
|
||||
unsafe fn register(
|
||||
adrv: &Opaque<Self::RegType>,
|
||||
name: &'static CStr,
|
||||
module: &'static ThisModule,
|
||||
) -> Result {
|
||||
// SAFETY: It's safe to set the fields of `struct auxiliary_driver` on initialization.
|
||||
unsafe {
|
||||
(*adrv.get()).name = name.as_char_ptr();
|
||||
(*adrv.get()).probe = Some(Self::probe_callback);
|
||||
(*adrv.get()).remove = Some(Self::remove_callback);
|
||||
(*adrv.get()).id_table = T::ID_TABLE.as_ptr();
|
||||
}
|
||||
|
||||
// SAFETY: `adrv` is guaranteed to be a valid `RegType`.
|
||||
to_result(unsafe {
|
||||
bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr())
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn unregister(adrv: &Opaque<Self::RegType>) {
|
||||
// SAFETY: `adrv` is guaranteed to be a valid `RegType`.
|
||||
unsafe { bindings::auxiliary_driver_unregister(adrv.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Driver + 'static> Adapter<T> {
|
||||
extern "C" fn probe_callback(
|
||||
adev: *mut bindings::auxiliary_device,
|
||||
id: *const bindings::auxiliary_device_id,
|
||||
) -> kernel::ffi::c_int {
|
||||
// SAFETY: The auxiliary bus only ever calls the probe callback with a valid pointer to a
|
||||
// `struct auxiliary_device`.
|
||||
//
|
||||
// INVARIANT: `adev` is valid for the duration of `probe_callback()`.
|
||||
let adev = unsafe { &*adev.cast::<Device<device::Core>>() };
|
||||
|
||||
// SAFETY: `DeviceId` is a `#[repr(transparent)`] wrapper of `struct auxiliary_device_id`
|
||||
// and does not add additional invariants, so it's safe to transmute.
|
||||
let id = unsafe { &*id.cast::<DeviceId>() };
|
||||
let info = T::ID_TABLE.info(id.index());
|
||||
|
||||
match T::probe(adev, info) {
|
||||
Ok(data) => {
|
||||
// Let the `struct auxiliary_device` own a reference of the driver's private data.
|
||||
// SAFETY: By the type invariant `adev.as_raw` returns a valid pointer to a
|
||||
// `struct auxiliary_device`.
|
||||
unsafe { bindings::auxiliary_set_drvdata(adev.as_raw(), data.into_foreign()) };
|
||||
}
|
||||
Err(err) => return Error::to_errno(err),
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
extern "C" fn remove_callback(adev: *mut bindings::auxiliary_device) {
|
||||
// SAFETY: The auxiliary bus only ever calls the remove callback with a valid pointer to a
|
||||
// `struct auxiliary_device`.
|
||||
let ptr = unsafe { bindings::auxiliary_get_drvdata(adev) };
|
||||
|
||||
// SAFETY: `remove_callback` is only ever called after a successful call to
|
||||
// `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
|
||||
// `KBox<T>` pointer created through `KBox::into_foreign`.
|
||||
drop(unsafe { KBox::<T>::from_foreign(ptr) });
|
||||
}
|
||||
}
|
||||
|
||||
/// Declares a kernel module that exposes a single auxiliary driver.
|
||||
#[macro_export]
|
||||
macro_rules! module_auxiliary_driver {
|
||||
($($f:tt)*) => {
|
||||
$crate::module_driver!(<T>, $crate::auxiliary::Adapter<T>, { $($f)* });
|
||||
};
|
||||
}
|
||||
|
||||
/// Abstraction for `bindings::auxiliary_device_id`.
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DeviceId(bindings::auxiliary_device_id);
|
||||
|
||||
impl DeviceId {
|
||||
/// Create a new [`DeviceId`] from name.
|
||||
pub const fn new(modname: &'static CStr, name: &'static CStr) -> Self {
|
||||
let name = name.as_bytes_with_nul();
|
||||
let modname = modname.as_bytes_with_nul();
|
||||
|
||||
// TODO: Replace with `bindings::auxiliary_device_id::default()` once stabilized for
|
||||
// `const`.
|
||||
//
|
||||
// SAFETY: FFI type is valid to be zero-initialized.
|
||||
let mut id: bindings::auxiliary_device_id = unsafe { core::mem::zeroed() };
|
||||
|
||||
let mut i = 0;
|
||||
while i < modname.len() {
|
||||
id.name[i] = modname[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Reuse the space of the NULL terminator.
|
||||
id.name[i - 1] = b'.';
|
||||
|
||||
let mut j = 0;
|
||||
while j < name.len() {
|
||||
id.name[i] = name[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
// * `DeviceId` is a `#[repr(transparent)`] wrapper of `auxiliary_device_id` and does not add
|
||||
// additional invariants, so it's safe to transmute to `RawType`.
|
||||
// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
|
||||
unsafe impl RawDeviceId for DeviceId {
|
||||
type RawType = bindings::auxiliary_device_id;
|
||||
|
||||
const DRIVER_DATA_OFFSET: usize =
|
||||
core::mem::offset_of!(bindings::auxiliary_device_id, driver_data);
|
||||
|
||||
fn index(&self) -> usize {
|
||||
self.0.driver_data
|
||||
}
|
||||
}
|
||||
|
||||
/// IdTable type for auxiliary drivers.
|
||||
pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
|
||||
|
||||
/// Create a auxiliary `IdTable` with its alias for modpost.
|
||||
#[macro_export]
|
||||
macro_rules! auxiliary_device_table {
|
||||
($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
|
||||
const $table_name: $crate::device_id::IdArray<
|
||||
$crate::auxiliary::DeviceId,
|
||||
$id_info_type,
|
||||
{ $table_data.len() },
|
||||
> = $crate::device_id::IdArray::new($table_data);
|
||||
|
||||
$crate::module_device_table!("auxiliary", $module_table_name, $table_name);
|
||||
};
|
||||
}
|
||||
|
||||
/// The auxiliary driver trait.
|
||||
///
|
||||
/// Drivers must implement this trait in order to get an auxiliary driver registered.
|
||||
pub trait Driver {
|
||||
/// The type holding information about each device id supported by the driver.
|
||||
///
|
||||
/// TODO: Use associated_type_defaults once stabilized:
|
||||
///
|
||||
/// type IdInfo: 'static = ();
|
||||
type IdInfo: 'static;
|
||||
|
||||
/// The table of device ids supported by the driver.
|
||||
const ID_TABLE: IdTable<Self::IdInfo>;
|
||||
|
||||
/// Auxiliary driver probe.
|
||||
///
|
||||
/// Called when an auxiliary device is matches a corresponding driver.
|
||||
fn probe(dev: &Device<device::Core>, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
|
||||
}
|
||||
|
||||
/// The auxiliary device representation.
|
||||
///
|
||||
/// This structure represents the Rust abstraction for a C `struct auxiliary_device`. The
|
||||
/// implementation abstracts the usage of an already existing C `struct auxiliary_device` within
|
||||
/// Rust code that we get passed from the C side.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// A [`Device`] instance represents a valid `struct auxiliary_device` created by the C portion of
|
||||
/// the kernel.
|
||||
#[repr(transparent)]
|
||||
pub struct Device<Ctx: device::DeviceContext = device::Normal>(
|
||||
Opaque<bindings::auxiliary_device>,
|
||||
PhantomData<Ctx>,
|
||||
);
|
||||
|
||||
impl<Ctx: device::DeviceContext> Device<Ctx> {
|
||||
fn as_raw(&self) -> *mut bindings::auxiliary_device {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
/// Returns the auxiliary device' id.
|
||||
pub fn id(&self) -> u32 {
|
||||
// SAFETY: By the type invariant `self.as_raw()` is a valid pointer to a
|
||||
// `struct auxiliary_device`.
|
||||
unsafe { (*self.as_raw()).id }
|
||||
}
|
||||
|
||||
/// Returns a reference to the parent [`device::Device`], if any.
|
||||
pub fn parent(&self) -> Option<&device::Device> {
|
||||
let ptr: *const Self = self;
|
||||
// CAST: `Device<Ctx: DeviceContext>` types are transparent to each other.
|
||||
let ptr: *const Device = ptr.cast();
|
||||
// SAFETY: `ptr` was derived from `&self`.
|
||||
let this = unsafe { &*ptr };
|
||||
|
||||
this.as_ref().parent()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
extern "C" fn release(dev: *mut bindings::device) {
|
||||
// SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
|
||||
// embedded in `struct auxiliary_device`.
|
||||
let adev = unsafe { container_of!(dev, bindings::auxiliary_device, dev) }.cast_mut();
|
||||
|
||||
// SAFETY: `adev` points to the memory that has been allocated in `Registration::new`, via
|
||||
// `KBox::new(Opaque::<bindings::auxiliary_device>::zeroed(), GFP_KERNEL)`.
|
||||
let _ = unsafe { KBox::<Opaque<bindings::auxiliary_device>>::from_raw(adev.cast()) };
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
|
||||
// argument.
|
||||
kernel::impl_device_context_deref!(unsafe { Device });
|
||||
kernel::impl_device_context_into_aref!(Device);
|
||||
|
||||
// SAFETY: Instances of `Device` are always reference-counted.
|
||||
unsafe impl crate::types::AlwaysRefCounted for Device {
|
||||
fn inc_ref(&self) {
|
||||
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
|
||||
unsafe { bindings::get_device(self.as_ref().as_raw()) };
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||||
// CAST: `Self` a transparent wrapper of `bindings::auxiliary_device`.
|
||||
let adev: *mut bindings::auxiliary_device = obj.cast().as_ptr();
|
||||
|
||||
// SAFETY: By the type invariant of `Self`, `adev` is a pointer to a valid
|
||||
// `struct auxiliary_device`.
|
||||
let dev = unsafe { addr_of_mut!((*adev).dev) };
|
||||
|
||||
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
||||
unsafe { bindings::put_device(dev) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
|
||||
fn as_ref(&self) -> &device::Device<Ctx> {
|
||||
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
|
||||
// `struct auxiliary_device`.
|
||||
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
|
||||
|
||||
// SAFETY: `dev` points to a valid `struct device`.
|
||||
unsafe { device::Device::as_ref(dev) }
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
|
||||
unsafe impl Send for Device {}
|
||||
|
||||
// SAFETY: `Device` can be shared among threads because all methods of `Device`
|
||||
// (i.e. `Device<Normal>) are thread safe.
|
||||
unsafe impl Sync for Device {}
|
||||
|
||||
/// The registration of an auxiliary device.
|
||||
///
|
||||
/// This type represents the registration of a [`struct auxiliary_device`]. When an instance of this
|
||||
/// type is dropped, its respective auxiliary device will be unregistered from the system.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// `self.0` always holds a valid pointer to an initialized and registered
|
||||
/// [`struct auxiliary_device`].
|
||||
pub struct Registration(NonNull<bindings::auxiliary_device>);
|
||||
|
||||
impl Registration {
|
||||
/// Create and register a new auxiliary device.
|
||||
pub fn new(parent: &device::Device, name: &CStr, id: u32, modname: &CStr) -> Result<Self> {
|
||||
let boxed = KBox::new(Opaque::<bindings::auxiliary_device>::zeroed(), GFP_KERNEL)?;
|
||||
let adev = boxed.get();
|
||||
|
||||
// SAFETY: It's safe to set the fields of `struct auxiliary_device` on initialization.
|
||||
unsafe {
|
||||
(*adev).dev.parent = parent.as_raw();
|
||||
(*adev).dev.release = Some(Device::release);
|
||||
(*adev).name = name.as_char_ptr();
|
||||
(*adev).id = id;
|
||||
}
|
||||
|
||||
// SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`,
|
||||
// which has not been initialized yet.
|
||||
unsafe { bindings::auxiliary_device_init(adev) };
|
||||
|
||||
// Now that `adev` is initialized, leak the `Box`; the corresponding memory will be freed
|
||||
// by `Device::release` when the last reference to the `struct auxiliary_device` is dropped.
|
||||
let _ = KBox::into_raw(boxed);
|
||||
|
||||
// SAFETY:
|
||||
// - `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`, which has
|
||||
// been initialialized,
|
||||
// - `modname.as_char_ptr()` is a NULL terminated string.
|
||||
let ret = unsafe { bindings::__auxiliary_device_add(adev, modname.as_char_ptr()) };
|
||||
if ret != 0 {
|
||||
// SAFETY: `adev` is guaranteed to be a valid pointer to a `struct auxiliary_device`,
|
||||
// which has been initialialized.
|
||||
unsafe { bindings::auxiliary_device_uninit(adev) };
|
||||
|
||||
return Err(Error::from_errno(ret));
|
||||
}
|
||||
|
||||
// SAFETY: `adev` is guaranteed to be non-null, since the `KBox` was allocated successfully.
|
||||
//
|
||||
// INVARIANT: The device will remain registered until `auxiliary_device_delete()` is called,
|
||||
// which happens in `Self::drop()`.
|
||||
Ok(Self(unsafe { NonNull::new_unchecked(adev) }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Registration {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: By the type invariant of `Self`, `self.0.as_ptr()` is a valid registered
|
||||
// `struct auxiliary_device`.
|
||||
unsafe { bindings::auxiliary_device_delete(self.0.as_ptr()) };
|
||||
|
||||
// This drops the reference we acquired through `auxiliary_device_init()`.
|
||||
//
|
||||
// SAFETY: By the type invariant of `Self`, `self.0.as_ptr()` is a valid registered
|
||||
// `struct auxiliary_device`.
|
||||
unsafe { bindings::auxiliary_device_uninit(self.0.as_ptr()) };
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `Registration` of a `struct auxiliary_device` can be released from any thread.
|
||||
unsafe impl Send for Registration {}
|
||||
|
||||
// SAFETY: `Registration` does not expose any methods or fields that need synchronization.
|
||||
unsafe impl Sync for Registration {}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
str::CStr,
|
||||
types::{ARef, Opaque},
|
||||
};
|
||||
use core::{fmt, ptr};
|
||||
use core::{fmt, marker::PhantomData, ptr};
|
||||
|
||||
#[cfg(CONFIG_PRINTK)]
|
||||
use crate::c_str;
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
/// `bindings::device::release` is valid to be called from any thread, hence `ARef<Device>` can be
|
||||
/// dropped from any thread.
|
||||
#[repr(transparent)]
|
||||
pub struct Device(Opaque<bindings::device>);
|
||||
pub struct Device<Ctx: DeviceContext = Normal>(Opaque<bindings::device>, PhantomData<Ctx>);
|
||||
|
||||
impl Device {
|
||||
/// Creates a new reference-counted abstraction instance of an existing `struct device` pointer.
|
||||
|
|
@ -59,12 +59,33 @@ pub unsafe fn get_device(ptr: *mut bindings::device) -> ARef<Self> {
|
|||
// SAFETY: By the safety requirements ptr is valid
|
||||
unsafe { Self::as_ref(ptr) }.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: DeviceContext> Device<Ctx> {
|
||||
/// Obtain the raw `struct device *`.
|
||||
pub(crate) fn as_raw(&self) -> *mut bindings::device {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
/// Returns a reference to the parent device, if any.
|
||||
#[cfg_attr(not(CONFIG_AUXILIARY_BUS), expect(dead_code))]
|
||||
pub(crate) fn parent(&self) -> Option<&Self> {
|
||||
// SAFETY:
|
||||
// - By the type invariant `self.as_raw()` is always valid.
|
||||
// - The parent device is only ever set at device creation.
|
||||
let parent = unsafe { (*self.as_raw()).parent };
|
||||
|
||||
if parent.is_null() {
|
||||
None
|
||||
} else {
|
||||
// SAFETY:
|
||||
// - Since `parent` is not NULL, it must be a valid pointer to a `struct device`.
|
||||
// - `parent` is valid for the lifetime of `self`, since a `struct device` holds a
|
||||
// reference count of its parent.
|
||||
Some(unsafe { Self::as_ref(parent) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a raw C `struct device` pointer to a `&'a Device`.
|
||||
///
|
||||
/// # Safety
|
||||
|
|
@ -189,6 +210,11 @@ pub fn property_present(&self, name: &CStr) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
|
||||
// argument.
|
||||
kernel::impl_device_context_deref!(unsafe { Device });
|
||||
kernel::impl_device_context_into_aref!(Device);
|
||||
|
||||
// SAFETY: Instances of `Device` are always reference-counted.
|
||||
unsafe impl crate::types::AlwaysRefCounted for Device {
|
||||
fn inc_ref(&self) {
|
||||
|
|
@ -225,16 +251,95 @@ pub trait DeviceContext: private::Sealed {}
|
|||
/// any of the bus callbacks, such as `probe()`.
|
||||
pub struct Core;
|
||||
|
||||
/// The [`Bound`] context is the context of a bus specific device reference when it is guaranteed to
|
||||
/// be bound for the duration of its lifetime.
|
||||
pub struct Bound;
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for super::Bound {}
|
||||
impl Sealed for super::Core {}
|
||||
impl Sealed for super::Normal {}
|
||||
}
|
||||
|
||||
impl DeviceContext for Bound {}
|
||||
impl DeviceContext for Core {}
|
||||
impl DeviceContext for Normal {}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
|
||||
/// generic argument of `$device`.
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_device_context_deref {
|
||||
(unsafe { $device:ident, $src:ty => $dst:ty }) => {
|
||||
impl ::core::ops::Deref for $device<$src> {
|
||||
type Target = $device<$dst>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
let ptr: *const Self = self;
|
||||
|
||||
// CAST: `$device<$src>` and `$device<$dst>` transparently wrap the same type by the
|
||||
// safety requirement of the macro.
|
||||
let ptr = ptr.cast::<Self::Target>();
|
||||
|
||||
// SAFETY: `ptr` was derived from `&self`.
|
||||
unsafe { &*ptr }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement [`core::ops::Deref`] traits for allowed [`DeviceContext`] conversions of a (bus
|
||||
/// specific) device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
|
||||
/// generic argument of `$device`.
|
||||
#[macro_export]
|
||||
macro_rules! impl_device_context_deref {
|
||||
(unsafe { $device:ident }) => {
|
||||
// SAFETY: This macro has the exact same safety requirement as
|
||||
// `__impl_device_context_deref!`.
|
||||
::kernel::__impl_device_context_deref!(unsafe {
|
||||
$device,
|
||||
$crate::device::Core => $crate::device::Bound
|
||||
});
|
||||
|
||||
// SAFETY: This macro has the exact same safety requirement as
|
||||
// `__impl_device_context_deref!`.
|
||||
::kernel::__impl_device_context_deref!(unsafe {
|
||||
$device,
|
||||
$crate::device::Bound => $crate::device::Normal
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_device_context_into_aref {
|
||||
($src:ty, $device:tt) => {
|
||||
impl ::core::convert::From<&$device<$src>> for $crate::types::ARef<$device> {
|
||||
fn from(dev: &$device<$src>) -> Self {
|
||||
(&**dev).into()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement [`core::convert::From`], such that all `&Device<Ctx>` can be converted to an
|
||||
/// `ARef<Device>`.
|
||||
#[macro_export]
|
||||
macro_rules! impl_device_context_into_aref {
|
||||
($device:tt) => {
|
||||
::kernel::__impl_device_context_into_aref!($crate::device::Core, $device);
|
||||
::kernel::__impl_device_context_into_aref!($crate::device::Bound, $device);
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! dev_printk {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
use crate::{
|
||||
alloc::Flags,
|
||||
bindings,
|
||||
device::Device,
|
||||
device::{Bound, Device},
|
||||
error::{Error, Result},
|
||||
ffi::c_void,
|
||||
prelude::*,
|
||||
|
|
@ -45,7 +45,7 @@ struct DevresInner<T> {
|
|||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::{Io, IoRaw}};
|
||||
/// # use kernel::{bindings, c_str, device::{Bound, Device}, devres::Devres, io::{Io, IoRaw}};
|
||||
/// # use core::ops::Deref;
|
||||
///
|
||||
/// // See also [`pci::Bar`] for a real example.
|
||||
|
|
@ -83,13 +83,10 @@ struct DevresInner<T> {
|
|||
/// unsafe { Io::from_raw(&self.0) }
|
||||
/// }
|
||||
/// }
|
||||
/// # fn no_run() -> Result<(), Error> {
|
||||
/// # // SAFETY: Invalid usage; just for the example to get an `ARef<Device>` instance.
|
||||
/// # let dev = unsafe { Device::get_device(core::ptr::null_mut()) };
|
||||
///
|
||||
/// # fn no_run(dev: &Device<Bound>) -> Result<(), Error> {
|
||||
/// // SAFETY: Invalid usage for example purposes.
|
||||
/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
|
||||
/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?;
|
||||
/// let devres = Devres::new(dev, iomem, GFP_KERNEL)?;
|
||||
///
|
||||
/// let res = devres.try_access().ok_or(ENXIO)?;
|
||||
/// res.write8(0x42, 0x0);
|
||||
|
|
@ -99,7 +96,7 @@ struct DevresInner<T> {
|
|||
pub struct Devres<T>(Arc<DevresInner<T>>);
|
||||
|
||||
impl<T> DevresInner<T> {
|
||||
fn new(dev: &Device, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
|
||||
fn new(dev: &Device<Bound>, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
|
||||
let inner = Arc::pin_init(
|
||||
pin_init!( DevresInner {
|
||||
dev: dev.into(),
|
||||
|
|
@ -171,7 +168,7 @@ fn remove_action(this: &Arc<Self>) {
|
|||
impl<T> Devres<T> {
|
||||
/// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the
|
||||
/// returned `Devres` instance' `data` will be revoked once the device is detached.
|
||||
pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
|
||||
pub fn new(dev: &Device<Bound>, data: T, flags: Flags) -> Result<Self> {
|
||||
let inner = DevresInner::new(dev, data, flags)?;
|
||||
|
||||
Ok(Devres(inner))
|
||||
|
|
@ -179,11 +176,50 @@ pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
|
|||
|
||||
/// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data`
|
||||
/// is owned by devres and will be revoked / dropped, once the device is detached.
|
||||
pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result {
|
||||
pub fn new_foreign_owned(dev: &Device<Bound>, data: T, flags: Flags) -> Result {
|
||||
let _ = DevresInner::new(dev, data, flags)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Obtain `&'a T`, bypassing the [`Revocable`].
|
||||
///
|
||||
/// This method allows to directly obtain a `&'a T`, bypassing the [`Revocable`], by presenting
|
||||
/// a `&'a Device<Bound>` of the same [`Device`] this [`Devres`] instance has been created with.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An error is returned if `dev` does not match the same [`Device`] this [`Devres`] instance
|
||||
/// has been created with.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #![cfg(CONFIG_PCI)]
|
||||
/// # use kernel::{device::Core, devres::Devres, pci};
|
||||
///
|
||||
/// fn from_core(dev: &pci::Device<Core>, devres: Devres<pci::Bar<0x4>>) -> Result {
|
||||
/// let bar = devres.access(dev.as_ref())?;
|
||||
///
|
||||
/// let _ = bar.read32(0x0);
|
||||
///
|
||||
/// // might_sleep()
|
||||
///
|
||||
/// bar.write32(0x42, 0x0);
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn access<'a>(&'a self, dev: &'a Device<Bound>) -> Result<&'a T> {
|
||||
if self.0.dev.as_raw() != dev.as_raw() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
// SAFETY: `dev` being the same device as the device this `Devres` has been created for
|
||||
// proves that `self.0.data` hasn't been revoked and is guaranteed to not be revoked as
|
||||
// long as `dev` lives; `dev` lives at least as long as `self`.
|
||||
Ok(unsafe { self.deref().access() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Devres<T> {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
use crate::{
|
||||
bindings, build_assert,
|
||||
device::Device,
|
||||
device::{Bound, Device},
|
||||
error::code::*,
|
||||
error::Result,
|
||||
transmute::{AsBytes, FromBytes},
|
||||
|
|
@ -22,10 +22,10 @@
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::device::Device;
|
||||
/// # use kernel::device::{Bound, Device};
|
||||
/// use kernel::dma::{attrs::*, CoherentAllocation};
|
||||
///
|
||||
/// # fn test(dev: &Device) -> Result {
|
||||
/// # fn test(dev: &Device<Bound>) -> Result {
|
||||
/// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN;
|
||||
/// let c: CoherentAllocation<u64> =
|
||||
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, attribs)?;
|
||||
|
|
@ -143,16 +143,16 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::device::Device;
|
||||
/// # use kernel::device::{Bound, Device};
|
||||
/// use kernel::dma::{attrs::*, CoherentAllocation};
|
||||
///
|
||||
/// # fn test(dev: &Device) -> Result {
|
||||
/// # fn test(dev: &Device<Bound>) -> Result {
|
||||
/// let c: CoherentAllocation<u64> =
|
||||
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
|
||||
/// # Ok::<(), Error>(()) }
|
||||
/// ```
|
||||
pub fn alloc_attrs(
|
||||
dev: &Device,
|
||||
dev: &Device<Bound>,
|
||||
count: usize,
|
||||
gfp_flags: kernel::alloc::Flags,
|
||||
dma_attrs: Attrs,
|
||||
|
|
@ -194,7 +194,7 @@ pub fn alloc_attrs(
|
|||
/// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the
|
||||
/// `dma_attrs` is 0 by default.
|
||||
pub fn alloc_coherent(
|
||||
dev: &Device,
|
||||
dev: &Device<Bound>,
|
||||
count: usize,
|
||||
gfp_flags: kernel::alloc::Flags,
|
||||
) -> Result<CoherentAllocation<T>> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
|
||||
//! DRM device.
|
||||
//!
|
||||
//! C header: [`include/linux/drm/drm_device.h`](srctree/include/linux/drm/drm_device.h)
|
||||
|
||||
use crate::{
|
||||
bindings, device, drm,
|
||||
drm::driver::AllocImpl,
|
||||
error::from_err_ptr,
|
||||
error::Result,
|
||||
prelude::*,
|
||||
types::{ARef, AlwaysRefCounted, Opaque},
|
||||
};
|
||||
use core::{mem, ops::Deref, ptr, ptr::NonNull};
|
||||
|
||||
#[cfg(CONFIG_DRM_LEGACY)]
|
||||
macro_rules! drm_legacy_fields {
|
||||
( $($field:ident: $val:expr),* $(,)? ) => {
|
||||
bindings::drm_driver {
|
||||
$( $field: $val ),*,
|
||||
firstopen: None,
|
||||
preclose: None,
|
||||
dma_ioctl: None,
|
||||
dma_quiescent: None,
|
||||
context_dtor: None,
|
||||
irq_handler: None,
|
||||
irq_preinstall: None,
|
||||
irq_postinstall: None,
|
||||
irq_uninstall: None,
|
||||
get_vblank_counter: None,
|
||||
enable_vblank: None,
|
||||
disable_vblank: None,
|
||||
dev_priv_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(CONFIG_DRM_LEGACY))]
|
||||
macro_rules! drm_legacy_fields {
|
||||
( $($field:ident: $val:expr),* $(,)? ) => {
|
||||
bindings::drm_driver {
|
||||
$( $field: $val ),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A typed DRM device with a specific `drm::Driver` implementation.
|
||||
///
|
||||
/// The device is always reference-counted.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// `self.dev` is a valid instance of a `struct device`.
|
||||
#[repr(C)]
|
||||
#[pin_data]
|
||||
pub struct Device<T: drm::Driver> {
|
||||
dev: Opaque<bindings::drm_device>,
|
||||
#[pin]
|
||||
data: T::Data,
|
||||
}
|
||||
|
||||
impl<T: drm::Driver> Device<T> {
|
||||
const VTABLE: bindings::drm_driver = drm_legacy_fields! {
|
||||
load: None,
|
||||
open: Some(drm::File::<T::File>::open_callback),
|
||||
postclose: Some(drm::File::<T::File>::postclose_callback),
|
||||
unload: None,
|
||||
release: None,
|
||||
master_set: None,
|
||||
master_drop: None,
|
||||
debugfs_init: None,
|
||||
gem_create_object: T::Object::ALLOC_OPS.gem_create_object,
|
||||
prime_handle_to_fd: T::Object::ALLOC_OPS.prime_handle_to_fd,
|
||||
prime_fd_to_handle: T::Object::ALLOC_OPS.prime_fd_to_handle,
|
||||
gem_prime_import: T::Object::ALLOC_OPS.gem_prime_import,
|
||||
gem_prime_import_sg_table: T::Object::ALLOC_OPS.gem_prime_import_sg_table,
|
||||
dumb_create: T::Object::ALLOC_OPS.dumb_create,
|
||||
dumb_map_offset: T::Object::ALLOC_OPS.dumb_map_offset,
|
||||
show_fdinfo: None,
|
||||
fbdev_probe: None,
|
||||
|
||||
major: T::INFO.major,
|
||||
minor: T::INFO.minor,
|
||||
patchlevel: T::INFO.patchlevel,
|
||||
name: T::INFO.name.as_char_ptr() as *mut _,
|
||||
desc: T::INFO.desc.as_char_ptr() as *mut _,
|
||||
|
||||
driver_features: drm::driver::FEAT_GEM,
|
||||
ioctls: T::IOCTLS.as_ptr(),
|
||||
num_ioctls: T::IOCTLS.len() as i32,
|
||||
fops: &Self::GEM_FOPS as _,
|
||||
};
|
||||
|
||||
const GEM_FOPS: bindings::file_operations = drm::gem::create_fops();
|
||||
|
||||
/// Create a new `drm::Device` for a `drm::Driver`.
|
||||
pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> {
|
||||
// SAFETY:
|
||||
// - `VTABLE`, as a `const` is pinned to the read-only section of the compilation,
|
||||
// - `dev` is valid by its type invarants,
|
||||
let raw_drm: *mut Self = unsafe {
|
||||
bindings::__drm_dev_alloc(
|
||||
dev.as_raw(),
|
||||
&Self::VTABLE,
|
||||
mem::size_of::<Self>(),
|
||||
mem::offset_of!(Self, dev),
|
||||
)
|
||||
}
|
||||
.cast();
|
||||
let raw_drm = NonNull::new(from_err_ptr(raw_drm)?).ok_or(ENOMEM)?;
|
||||
|
||||
// SAFETY: `raw_drm` is a valid pointer to `Self`.
|
||||
let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) };
|
||||
|
||||
// SAFETY:
|
||||
// - `raw_data` is a valid pointer to uninitialized memory.
|
||||
// - `raw_data` will not move until it is dropped.
|
||||
unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| {
|
||||
// SAFETY: `__drm_dev_alloc()` was successful, hence `raw_drm` must be valid and the
|
||||
// refcount must be non-zero.
|
||||
unsafe { bindings::drm_dev_put(ptr::addr_of_mut!((*raw_drm.as_ptr()).dev).cast()) };
|
||||
})?;
|
||||
|
||||
// SAFETY: The reference count is one, and now we take ownership of that reference as a
|
||||
// `drm::Device`.
|
||||
Ok(unsafe { ARef::from_raw(raw_drm) })
|
||||
}
|
||||
|
||||
pub(crate) fn as_raw(&self) -> *mut bindings::drm_device {
|
||||
self.dev.get()
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `ptr` must be a valid pointer to a `struct device` embedded in `Self`.
|
||||
unsafe fn from_drm_device(ptr: *const bindings::drm_device) -> *mut Self {
|
||||
// SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a
|
||||
// `struct drm_device` embedded in `Self`.
|
||||
unsafe { crate::container_of!(ptr, Self, dev) }.cast_mut()
|
||||
}
|
||||
|
||||
/// Not intended to be called externally, except via declare_drm_ioctls!()
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count,
|
||||
/// i.e. it must be ensured that the reference count of the C `struct drm_device` `ptr` points
|
||||
/// to can't drop to zero, for the duration of this function call and the entire duration when
|
||||
/// the returned reference exists.
|
||||
///
|
||||
/// Additionally, callers must ensure that the `struct device`, `ptr` is pointing to, is
|
||||
/// embedded in `Self`.
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn as_ref<'a>(ptr: *const bindings::drm_device) -> &'a Self {
|
||||
// SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a
|
||||
// `struct drm_device` embedded in `Self`.
|
||||
let ptr = unsafe { Self::from_drm_device(ptr) };
|
||||
|
||||
// SAFETY: `ptr` is valid by the safety requirements of this function.
|
||||
unsafe { &*ptr.cast() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: drm::Driver> Deref for Device<T> {
|
||||
type Target = T::Data;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: DRM device objects are always reference counted and the get/put functions
|
||||
// satisfy the requirements.
|
||||
unsafe impl<T: drm::Driver> AlwaysRefCounted for Device<T> {
|
||||
fn inc_ref(&self) {
|
||||
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
|
||||
unsafe { bindings::drm_dev_get(self.as_raw()) };
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||||
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
||||
unsafe { bindings::drm_dev_put(obj.cast().as_ptr()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: drm::Driver> AsRef<device::Device> for Device<T> {
|
||||
fn as_ref(&self) -> &device::Device {
|
||||
// SAFETY: `bindings::drm_device::dev` is valid as long as the DRM device itself is valid,
|
||||
// which is guaranteed by the type invariant.
|
||||
unsafe { device::Device::as_ref((*self.as_raw()).dev) }
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `drm::Device` can be released from any thread.
|
||||
unsafe impl<T: drm::Driver> Send for Device<T> {}
|
||||
|
||||
// SAFETY: A `drm::Device` can be shared among threads because all immutable methods are protected
|
||||
// by the synchronization in `struct drm_device`.
|
||||
unsafe impl<T: drm::Driver> Sync for Device<T> {}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
|
||||
//! DRM driver core.
|
||||
//!
|
||||
//! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/drm_drv.h)
|
||||
|
||||
use crate::{
|
||||
bindings, device,
|
||||
devres::Devres,
|
||||
drm,
|
||||
error::{to_result, Result},
|
||||
prelude::*,
|
||||
str::CStr,
|
||||
types::ARef,
|
||||
};
|
||||
use macros::vtable;
|
||||
|
||||
/// Driver use the GEM memory manager. This should be set for all modern drivers.
|
||||
pub(crate) const FEAT_GEM: u32 = bindings::drm_driver_feature_DRIVER_GEM;
|
||||
|
||||
/// Information data for a DRM Driver.
|
||||
pub struct DriverInfo {
|
||||
/// Driver major version.
|
||||
pub major: i32,
|
||||
/// Driver minor version.
|
||||
pub minor: i32,
|
||||
/// Driver patchlevel version.
|
||||
pub patchlevel: i32,
|
||||
/// Driver name.
|
||||
pub name: &'static CStr,
|
||||
/// Driver description.
|
||||
pub desc: &'static CStr,
|
||||
}
|
||||
|
||||
/// Internal memory management operation set, normally created by memory managers (e.g. GEM).
|
||||
pub struct AllocOps {
|
||||
pub(crate) gem_create_object: Option<
|
||||
unsafe extern "C" fn(
|
||||
dev: *mut bindings::drm_device,
|
||||
size: usize,
|
||||
) -> *mut bindings::drm_gem_object,
|
||||
>,
|
||||
pub(crate) prime_handle_to_fd: Option<
|
||||
unsafe extern "C" fn(
|
||||
dev: *mut bindings::drm_device,
|
||||
file_priv: *mut bindings::drm_file,
|
||||
handle: u32,
|
||||
flags: u32,
|
||||
prime_fd: *mut core::ffi::c_int,
|
||||
) -> core::ffi::c_int,
|
||||
>,
|
||||
pub(crate) prime_fd_to_handle: Option<
|
||||
unsafe extern "C" fn(
|
||||
dev: *mut bindings::drm_device,
|
||||
file_priv: *mut bindings::drm_file,
|
||||
prime_fd: core::ffi::c_int,
|
||||
handle: *mut u32,
|
||||
) -> core::ffi::c_int,
|
||||
>,
|
||||
pub(crate) gem_prime_import: Option<
|
||||
unsafe extern "C" fn(
|
||||
dev: *mut bindings::drm_device,
|
||||
dma_buf: *mut bindings::dma_buf,
|
||||
) -> *mut bindings::drm_gem_object,
|
||||
>,
|
||||
pub(crate) gem_prime_import_sg_table: Option<
|
||||
unsafe extern "C" fn(
|
||||
dev: *mut bindings::drm_device,
|
||||
attach: *mut bindings::dma_buf_attachment,
|
||||
sgt: *mut bindings::sg_table,
|
||||
) -> *mut bindings::drm_gem_object,
|
||||
>,
|
||||
pub(crate) dumb_create: Option<
|
||||
unsafe extern "C" fn(
|
||||
file_priv: *mut bindings::drm_file,
|
||||
dev: *mut bindings::drm_device,
|
||||
args: *mut bindings::drm_mode_create_dumb,
|
||||
) -> core::ffi::c_int,
|
||||
>,
|
||||
pub(crate) dumb_map_offset: Option<
|
||||
unsafe extern "C" fn(
|
||||
file_priv: *mut bindings::drm_file,
|
||||
dev: *mut bindings::drm_device,
|
||||
handle: u32,
|
||||
offset: *mut u64,
|
||||
) -> core::ffi::c_int,
|
||||
>,
|
||||
}
|
||||
|
||||
/// Trait for memory manager implementations. Implemented internally.
|
||||
pub trait AllocImpl: super::private::Sealed + drm::gem::IntoGEMObject {
|
||||
/// The C callback operations for this memory manager.
|
||||
const ALLOC_OPS: AllocOps;
|
||||
}
|
||||
|
||||
/// The DRM `Driver` trait.
|
||||
///
|
||||
/// This trait must be implemented by drivers in order to create a `struct drm_device` and `struct
|
||||
/// drm_driver` to be registered in the DRM subsystem.
|
||||
#[vtable]
|
||||
pub trait Driver {
|
||||
/// Context data associated with the DRM driver
|
||||
type Data: Sync + Send;
|
||||
|
||||
/// The type used to manage memory for this driver.
|
||||
type Object: AllocImpl;
|
||||
|
||||
/// The type used to represent a DRM File (client)
|
||||
type File: drm::file::DriverFile;
|
||||
|
||||
/// Driver metadata
|
||||
const INFO: DriverInfo;
|
||||
|
||||
/// IOCTL list. See `kernel::drm::ioctl::declare_drm_ioctls!{}`.
|
||||
const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor];
|
||||
}
|
||||
|
||||
/// The registration type of a `drm::Device`.
|
||||
///
|
||||
/// Once the `Registration` structure is dropped, the device is unregistered.
|
||||
pub struct Registration<T: Driver>(ARef<drm::Device<T>>);
|
||||
|
||||
impl<T: Driver> Registration<T> {
|
||||
/// Creates a new [`Registration`] and registers it.
|
||||
fn new(drm: &drm::Device<T>, flags: usize) -> Result<Self> {
|
||||
// SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Device`.
|
||||
to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags) })?;
|
||||
|
||||
Ok(Self(drm.into()))
|
||||
}
|
||||
|
||||
/// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to
|
||||
/// [`Devres`].
|
||||
pub fn new_foreign_owned(
|
||||
drm: &drm::Device<T>,
|
||||
dev: &device::Device<device::Bound>,
|
||||
flags: usize,
|
||||
) -> Result {
|
||||
if drm.as_ref().as_raw() != dev.as_raw() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
let reg = Registration::<T>::new(drm, flags)?;
|
||||
Devres::new_foreign_owned(dev, reg, GFP_KERNEL)
|
||||
}
|
||||
|
||||
/// Returns a reference to the `Device` instance for this registration.
|
||||
pub fn device(&self) -> &drm::Device<T> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between
|
||||
// threads, hence it's safe to share it.
|
||||
unsafe impl<T: Driver> Sync for Registration<T> {}
|
||||
|
||||
// SAFETY: Registration with and unregistration from the DRM subsystem can happen from any thread.
|
||||
unsafe impl<T: Driver> Send for Registration<T> {}
|
||||
|
||||
impl<T: Driver> Drop for Registration<T> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: Safe by the invariant of `ARef<drm::Device<T>>`. The existence of this
|
||||
// `Registration` also guarantees the this `drm::Device` is actually registered.
|
||||
unsafe { bindings::drm_dev_unregister(self.0.as_raw()) };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
|
||||
//! DRM File objects.
|
||||
//!
|
||||
//! C header: [`include/linux/drm/drm_file.h`](srctree/include/linux/drm/drm_file.h)
|
||||
|
||||
use crate::{bindings, drm, error::Result, prelude::*, types::Opaque};
|
||||
use core::marker::PhantomData;
|
||||
use core::pin::Pin;
|
||||
|
||||
/// Trait that must be implemented by DRM drivers to represent a DRM File (a client instance).
|
||||
pub trait DriverFile {
|
||||
/// The parent `Driver` implementation for this `DriverFile`.
|
||||
type Driver: drm::Driver;
|
||||
|
||||
/// Open a new file (called when a client opens the DRM device).
|
||||
fn open(device: &drm::Device<Self::Driver>) -> Result<Pin<KBox<Self>>>;
|
||||
}
|
||||
|
||||
/// An open DRM File.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// `self.0` is a valid instance of a `struct drm_file`.
|
||||
#[repr(transparent)]
|
||||
pub struct File<T: DriverFile>(Opaque<bindings::drm_file>, PhantomData<T>);
|
||||
|
||||
impl<T: DriverFile> File<T> {
|
||||
#[doc(hidden)]
|
||||
/// Not intended to be called externally, except via declare_drm_ioctls!()
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `raw_file` must be a valid pointer to an open `struct drm_file`, opened through `T::open`.
|
||||
pub unsafe fn as_ref<'a>(ptr: *mut bindings::drm_file) -> &'a File<T> {
|
||||
// SAFETY: `raw_file` is valid by the safety requirements of this function.
|
||||
unsafe { &*ptr.cast() }
|
||||
}
|
||||
|
||||
pub(super) fn as_raw(&self) -> *mut bindings::drm_file {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
fn driver_priv(&self) -> *mut T {
|
||||
// SAFETY: By the type invariants of `Self`, `self.as_raw()` is always valid.
|
||||
unsafe { (*self.as_raw()).driver_priv }.cast()
|
||||
}
|
||||
|
||||
/// Return a pinned reference to the driver file structure.
|
||||
pub fn inner(&self) -> Pin<&T> {
|
||||
// SAFETY: By the type invariant the pointer `self.as_raw()` points to a valid and opened
|
||||
// `struct drm_file`, hence `driver_priv` has been properly initialized by `open_callback`.
|
||||
unsafe { Pin::new_unchecked(&*(self.driver_priv())) }
|
||||
}
|
||||
|
||||
/// The open callback of a `struct drm_file`.
|
||||
pub(crate) extern "C" fn open_callback(
|
||||
raw_dev: *mut bindings::drm_device,
|
||||
raw_file: *mut bindings::drm_file,
|
||||
) -> core::ffi::c_int {
|
||||
// SAFETY: A callback from `struct drm_driver::open` guarantees that
|
||||
// - `raw_dev` is valid pointer to a `struct drm_device`,
|
||||
// - the corresponding `struct drm_device` has been registered.
|
||||
let drm = unsafe { drm::Device::as_ref(raw_dev) };
|
||||
|
||||
// SAFETY: `raw_file` is a valid pointer to a `struct drm_file`.
|
||||
let file = unsafe { File::<T>::as_ref(raw_file) };
|
||||
|
||||
let inner = match T::open(drm) {
|
||||
Err(e) => {
|
||||
return e.to_errno();
|
||||
}
|
||||
Ok(i) => i,
|
||||
};
|
||||
|
||||
// SAFETY: This pointer is treated as pinned, and the Drop guarantee is upheld in
|
||||
// `postclose_callback()`.
|
||||
let driver_priv = KBox::into_raw(unsafe { Pin::into_inner_unchecked(inner) });
|
||||
|
||||
// SAFETY: By the type invariants of `Self`, `self.as_raw()` is always valid.
|
||||
unsafe { (*file.as_raw()).driver_priv = driver_priv.cast() };
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
/// The postclose callback of a `struct drm_file`.
|
||||
pub(crate) extern "C" fn postclose_callback(
|
||||
_raw_dev: *mut bindings::drm_device,
|
||||
raw_file: *mut bindings::drm_file,
|
||||
) {
|
||||
// SAFETY: This reference won't escape this function
|
||||
let file = unsafe { File::<T>::as_ref(raw_file) };
|
||||
|
||||
// SAFETY: `file.driver_priv` has been created in `open_callback` through `KBox::into_raw`.
|
||||
let _ = unsafe { KBox::from_raw(file.driver_priv()) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DriverFile> super::private::Sealed for File<T> {}
|
||||
|
|
@ -0,0 +1,328 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
|
||||
//! DRM GEM API
|
||||
//!
|
||||
//! C header: [`include/linux/drm/drm_gem.h`](srctree/include/linux/drm/drm_gem.h)
|
||||
|
||||
use crate::{
|
||||
alloc::flags::*,
|
||||
bindings, drm,
|
||||
drm::driver::{AllocImpl, AllocOps},
|
||||
error::{to_result, Result},
|
||||
prelude::*,
|
||||
types::{ARef, AlwaysRefCounted, Opaque},
|
||||
};
|
||||
use core::{mem, ops::Deref, ptr::NonNull};
|
||||
|
||||
/// GEM object functions, which must be implemented by drivers.
|
||||
pub trait BaseDriverObject<T: BaseObject>: Sync + Send + Sized {
|
||||
/// Create a new driver data object for a GEM object of a given size.
|
||||
fn new(dev: &drm::Device<T::Driver>, size: usize) -> impl PinInit<Self, Error>;
|
||||
|
||||
/// Open a new handle to an existing object, associated with a File.
|
||||
fn open(
|
||||
_obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object,
|
||||
_file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>,
|
||||
) -> Result {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Close a handle to an existing object, associated with a File.
|
||||
fn close(
|
||||
_obj: &<<T as IntoGEMObject>::Driver as drm::Driver>::Object,
|
||||
_file: &drm::File<<<T as IntoGEMObject>::Driver as drm::Driver>::File>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that represents a GEM object subtype
|
||||
pub trait IntoGEMObject: Sized + super::private::Sealed + AlwaysRefCounted {
|
||||
/// Owning driver for this type
|
||||
type Driver: drm::Driver;
|
||||
|
||||
/// Returns a reference to the raw `drm_gem_object` structure, which must be valid as long as
|
||||
/// this owning object is valid.
|
||||
fn as_raw(&self) -> *mut bindings::drm_gem_object;
|
||||
|
||||
/// Converts a pointer to a `struct drm_gem_object` into a reference to `Self`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - `self_ptr` must be a valid pointer to `Self`.
|
||||
/// - The caller promises that holding the immutable reference returned by this function does
|
||||
/// not violate rust's data aliasing rules and remains valid throughout the lifetime of `'a`.
|
||||
unsafe fn as_ref<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self;
|
||||
}
|
||||
|
||||
// SAFETY: All gem objects are refcounted.
|
||||
unsafe impl<T: IntoGEMObject> AlwaysRefCounted for T {
|
||||
fn inc_ref(&self) {
|
||||
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
|
||||
unsafe { bindings::drm_gem_object_get(self.as_raw()) };
|
||||
}
|
||||
|
||||
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||||
// SAFETY: We either hold the only refcount on `obj`, or one of many - meaning that no one
|
||||
// else could possibly hold a mutable reference to `obj` and thus this immutable reference
|
||||
// is safe.
|
||||
let obj = unsafe { obj.as_ref() }.as_raw();
|
||||
|
||||
// SAFETY:
|
||||
// - The safety requirements guarantee that the refcount is non-zero.
|
||||
// - We hold no references to `obj` now, making it safe for us to potentially deallocate it.
|
||||
unsafe { bindings::drm_gem_object_put(obj) };
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait which must be implemented by drivers using base GEM objects.
|
||||
pub trait DriverObject: BaseDriverObject<Object<Self>> {
|
||||
/// Parent `Driver` for this object.
|
||||
type Driver: drm::Driver;
|
||||
}
|
||||
|
||||
extern "C" fn open_callback<T: BaseDriverObject<U>, U: BaseObject>(
|
||||
raw_obj: *mut bindings::drm_gem_object,
|
||||
raw_file: *mut bindings::drm_file,
|
||||
) -> core::ffi::c_int {
|
||||
// SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`.
|
||||
let file = unsafe {
|
||||
drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file)
|
||||
};
|
||||
// SAFETY: `open_callback` is specified in the AllocOps structure for `Object<T>`, ensuring that
|
||||
// `raw_obj` is indeed contained within a `Object<T>`.
|
||||
let obj = unsafe {
|
||||
<<<U as IntoGEMObject>::Driver as drm::Driver>::Object as IntoGEMObject>::as_ref(raw_obj)
|
||||
};
|
||||
|
||||
match T::open(obj, file) {
|
||||
Err(e) => e.to_errno(),
|
||||
Ok(()) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn close_callback<T: BaseDriverObject<U>, U: BaseObject>(
|
||||
raw_obj: *mut bindings::drm_gem_object,
|
||||
raw_file: *mut bindings::drm_file,
|
||||
) {
|
||||
// SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`.
|
||||
let file = unsafe {
|
||||
drm::File::<<<U as IntoGEMObject>::Driver as drm::Driver>::File>::as_ref(raw_file)
|
||||
};
|
||||
// SAFETY: `close_callback` is specified in the AllocOps structure for `Object<T>`, ensuring
|
||||
// that `raw_obj` is indeed contained within a `Object<T>`.
|
||||
let obj = unsafe {
|
||||
<<<U as IntoGEMObject>::Driver as drm::Driver>::Object as IntoGEMObject>::as_ref(raw_obj)
|
||||
};
|
||||
|
||||
T::close(obj, file);
|
||||
}
|
||||
|
||||
impl<T: DriverObject> IntoGEMObject for Object<T> {
|
||||
type Driver = T::Driver;
|
||||
|
||||
fn as_raw(&self) -> *mut bindings::drm_gem_object {
|
||||
self.obj.get()
|
||||
}
|
||||
|
||||
unsafe fn as_ref<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self {
|
||||
// SAFETY: `obj` is guaranteed to be in an `Object<T>` via the safety contract of this
|
||||
// function
|
||||
unsafe { &*crate::container_of!(self_ptr, Object<T>, obj) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Base operations shared by all GEM object classes
|
||||
pub trait BaseObject: IntoGEMObject {
|
||||
/// Returns the size of the object in bytes.
|
||||
fn size(&self) -> usize {
|
||||
// SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `struct drm_gem_object`.
|
||||
unsafe { (*self.as_raw()).size }
|
||||
}
|
||||
|
||||
/// Creates a new handle for the object associated with a given `File`
|
||||
/// (or returns an existing one).
|
||||
fn create_handle(
|
||||
&self,
|
||||
file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>,
|
||||
) -> Result<u32> {
|
||||
let mut handle: u32 = 0;
|
||||
// SAFETY: The arguments are all valid per the type invariants.
|
||||
to_result(unsafe {
|
||||
bindings::drm_gem_handle_create(file.as_raw().cast(), self.as_raw(), &mut handle)
|
||||
})?;
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
/// Looks up an object by its handle for a given `File`.
|
||||
fn lookup_handle(
|
||||
file: &drm::File<<<Self as IntoGEMObject>::Driver as drm::Driver>::File>,
|
||||
handle: u32,
|
||||
) -> Result<ARef<Self>> {
|
||||
// SAFETY: The arguments are all valid per the type invariants.
|
||||
let ptr = unsafe { bindings::drm_gem_object_lookup(file.as_raw().cast(), handle) };
|
||||
if ptr.is_null() {
|
||||
return Err(ENOENT);
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
// - A `drm::Driver` can only have a single `File` implementation.
|
||||
// - `file` uses the same `drm::Driver` as `Self`.
|
||||
// - Therefore, we're guaranteed that `ptr` must be a gem object embedded within `Self`.
|
||||
// - And we check if the pointer is null befoe calling as_ref(), ensuring that `ptr` is a
|
||||
// valid pointer to an initialized `Self`.
|
||||
let obj = unsafe { Self::as_ref(ptr) };
|
||||
|
||||
// SAFETY:
|
||||
// - We take ownership of the reference of `drm_gem_object_lookup()`.
|
||||
// - Our `NonNull` comes from an immutable reference, thus ensuring it is a valid pointer to
|
||||
// `Self`.
|
||||
Ok(unsafe { ARef::from_raw(obj.into()) })
|
||||
}
|
||||
|
||||
/// Creates an mmap offset to map the object from userspace.
|
||||
fn create_mmap_offset(&self) -> Result<u64> {
|
||||
// SAFETY: The arguments are valid per the type invariant.
|
||||
to_result(unsafe { bindings::drm_gem_create_mmap_offset(self.as_raw()) })?;
|
||||
|
||||
// SAFETY: The arguments are valid per the type invariant.
|
||||
Ok(unsafe { bindings::drm_vma_node_offset_addr(&raw mut (*self.as_raw()).vma_node) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoGEMObject> BaseObject for T {}
|
||||
|
||||
/// A base GEM object.
|
||||
///
|
||||
/// Invariants
|
||||
///
|
||||
/// - `self.obj` is a valid instance of a `struct drm_gem_object`.
|
||||
/// - `self.dev` is always a valid pointer to a `struct drm_device`.
|
||||
#[repr(C)]
|
||||
#[pin_data]
|
||||
pub struct Object<T: DriverObject + Send + Sync> {
|
||||
obj: Opaque<bindings::drm_gem_object>,
|
||||
dev: NonNull<drm::Device<T::Driver>>,
|
||||
#[pin]
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T: DriverObject> Object<T> {
|
||||
/// The size of this object's structure.
|
||||
pub const SIZE: usize = mem::size_of::<Self>();
|
||||
|
||||
const OBJECT_FUNCS: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs {
|
||||
free: Some(Self::free_callback),
|
||||
open: Some(open_callback::<T, Object<T>>),
|
||||
close: Some(close_callback::<T, Object<T>>),
|
||||
print_info: None,
|
||||
export: None,
|
||||
pin: None,
|
||||
unpin: None,
|
||||
get_sg_table: None,
|
||||
vmap: None,
|
||||
vunmap: None,
|
||||
mmap: None,
|
||||
status: None,
|
||||
vm_ops: core::ptr::null_mut(),
|
||||
evict: None,
|
||||
rss: None,
|
||||
};
|
||||
|
||||
/// Create a new GEM object.
|
||||
pub fn new(dev: &drm::Device<T::Driver>, size: usize) -> Result<ARef<Self>> {
|
||||
let obj: Pin<KBox<Self>> = KBox::pin_init(
|
||||
try_pin_init!(Self {
|
||||
obj: Opaque::new(bindings::drm_gem_object::default()),
|
||||
data <- T::new(dev, size),
|
||||
// INVARIANT: The drm subsystem guarantees that the `struct drm_device` will live
|
||||
// as long as the GEM object lives.
|
||||
dev: dev.into(),
|
||||
}),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
|
||||
// SAFETY: `obj.as_raw()` is guaranteed to be valid by the initialization above.
|
||||
unsafe { (*obj.as_raw()).funcs = &Self::OBJECT_FUNCS };
|
||||
|
||||
// SAFETY: The arguments are all valid per the type invariants.
|
||||
to_result(unsafe { bindings::drm_gem_object_init(dev.as_raw(), obj.obj.get(), size) })?;
|
||||
|
||||
// SAFETY: We never move out of `Self`.
|
||||
let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(obj) });
|
||||
|
||||
// SAFETY: `ptr` comes from `KBox::into_raw` and hence can't be NULL.
|
||||
let ptr = unsafe { NonNull::new_unchecked(ptr) };
|
||||
|
||||
// SAFETY: We take over the initial reference count from `drm_gem_object_init()`.
|
||||
Ok(unsafe { ARef::from_raw(ptr) })
|
||||
}
|
||||
|
||||
/// Returns the `Device` that owns this GEM object.
|
||||
pub fn dev(&self) -> &drm::Device<T::Driver> {
|
||||
// SAFETY: The DRM subsystem guarantees that the `struct drm_device` will live as long as
|
||||
// the GEM object lives, hence the pointer must be valid.
|
||||
unsafe { self.dev.as_ref() }
|
||||
}
|
||||
|
||||
fn as_raw(&self) -> *mut bindings::drm_gem_object {
|
||||
self.obj.get()
|
||||
}
|
||||
|
||||
extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
|
||||
// SAFETY: All of our objects are of type `Object<T>`.
|
||||
let this = unsafe { crate::container_of!(obj, Self, obj) }.cast_mut();
|
||||
|
||||
// SAFETY: The C code only ever calls this callback with a valid pointer to a `struct
|
||||
// drm_gem_object`.
|
||||
unsafe { bindings::drm_gem_object_release(obj) };
|
||||
|
||||
// SAFETY: All of our objects are allocated via `KBox`, and we're in the
|
||||
// free callback which guarantees this object has zero remaining references,
|
||||
// so we can drop it.
|
||||
let _ = unsafe { KBox::from_raw(this) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DriverObject> super::private::Sealed for Object<T> {}
|
||||
|
||||
impl<T: DriverObject> Deref for Object<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DriverObject> AllocImpl for Object<T> {
|
||||
const ALLOC_OPS: AllocOps = AllocOps {
|
||||
gem_create_object: None,
|
||||
prime_handle_to_fd: None,
|
||||
prime_fd_to_handle: None,
|
||||
gem_prime_import: None,
|
||||
gem_prime_import_sg_table: None,
|
||||
dumb_create: None,
|
||||
dumb_map_offset: None,
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) const fn create_fops() -> bindings::file_operations {
|
||||
// SAFETY: As by the type invariant, it is safe to initialize `bindings::file_operations`
|
||||
// zeroed.
|
||||
let mut fops: bindings::file_operations = unsafe { core::mem::zeroed() };
|
||||
|
||||
fops.owner = core::ptr::null_mut();
|
||||
fops.open = Some(bindings::drm_open);
|
||||
fops.release = Some(bindings::drm_release);
|
||||
fops.unlocked_ioctl = Some(bindings::drm_ioctl);
|
||||
#[cfg(CONFIG_COMPAT)]
|
||||
{
|
||||
fops.compat_ioctl = Some(bindings::drm_compat_ioctl);
|
||||
}
|
||||
fops.poll = Some(bindings::drm_poll);
|
||||
fops.read = Some(bindings::drm_read);
|
||||
fops.llseek = Some(bindings::noop_llseek);
|
||||
fops.mmap = Some(bindings::drm_gem_mmap);
|
||||
fops.fop_flags = bindings::FOP_UNSIGNED_OFFSET;
|
||||
|
||||
fops
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
|
||||
//! DRM IOCTL definitions.
|
||||
//!
|
||||
//! C header: [`include/linux/drm/drm_ioctl.h`](srctree/include/linux/drm/drm_ioctl.h)
|
||||
|
||||
use crate::ioctl;
|
||||
|
||||
const BASE: u32 = uapi::DRM_IOCTL_BASE as u32;
|
||||
|
||||
/// Construct a DRM ioctl number with no argument.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub const fn IO(nr: u32) -> u32 {
|
||||
ioctl::_IO(BASE, nr)
|
||||
}
|
||||
|
||||
/// Construct a DRM ioctl number with a read-only argument.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub const fn IOR<T>(nr: u32) -> u32 {
|
||||
ioctl::_IOR::<T>(BASE, nr)
|
||||
}
|
||||
|
||||
/// Construct a DRM ioctl number with a write-only argument.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub const fn IOW<T>(nr: u32) -> u32 {
|
||||
ioctl::_IOW::<T>(BASE, nr)
|
||||
}
|
||||
|
||||
/// Construct a DRM ioctl number with a read-write argument.
|
||||
#[allow(non_snake_case)]
|
||||
#[inline(always)]
|
||||
pub const fn IOWR<T>(nr: u32) -> u32 {
|
||||
ioctl::_IOWR::<T>(BASE, nr)
|
||||
}
|
||||
|
||||
/// Descriptor type for DRM ioctls. Use the `declare_drm_ioctls!{}` macro to construct them.
|
||||
pub type DrmIoctlDescriptor = bindings::drm_ioctl_desc;
|
||||
|
||||
/// This is for ioctl which are used for rendering, and require that the file descriptor is either
|
||||
/// for a render node, or if it’s a legacy/primary node, then it must be authenticated.
|
||||
pub const AUTH: u32 = bindings::drm_ioctl_flags_DRM_AUTH;
|
||||
|
||||
/// This must be set for any ioctl which can change the modeset or display state. Userspace must
|
||||
/// call the ioctl through a primary node, while it is the active master.
|
||||
///
|
||||
/// Note that read-only modeset ioctl can also be called by unauthenticated clients, or when a
|
||||
/// master is not the currently active one.
|
||||
pub const MASTER: u32 = bindings::drm_ioctl_flags_DRM_MASTER;
|
||||
|
||||
/// Anything that could potentially wreak a master file descriptor needs to have this flag set.
|
||||
///
|
||||
/// Current that’s only for the SETMASTER and DROPMASTER ioctl, which e.g. logind can call to
|
||||
/// force a non-behaving master (display compositor) into compliance.
|
||||
///
|
||||
/// This is equivalent to callers with the SYSADMIN capability.
|
||||
pub const ROOT_ONLY: u32 = bindings::drm_ioctl_flags_DRM_ROOT_ONLY;
|
||||
|
||||
/// This is used for all ioctl needed for rendering only, for drivers which support render nodes.
|
||||
/// This should be all new render drivers, and hence it should be always set for any ioctl with
|
||||
/// `AUTH` set. Note though that read-only query ioctl might have this set, but have not set
|
||||
/// DRM_AUTH because they do not require authentication.
|
||||
pub const RENDER_ALLOW: u32 = bindings::drm_ioctl_flags_DRM_RENDER_ALLOW;
|
||||
|
||||
/// Internal structures used by the `declare_drm_ioctls!{}` macro. Do not use directly.
|
||||
#[doc(hidden)]
|
||||
pub mod internal {
|
||||
pub use bindings::drm_device;
|
||||
pub use bindings::drm_file;
|
||||
pub use bindings::drm_ioctl_desc;
|
||||
}
|
||||
|
||||
/// Declare the DRM ioctls for a driver.
|
||||
///
|
||||
/// Each entry in the list should have the form:
|
||||
///
|
||||
/// `(ioctl_number, argument_type, flags, user_callback),`
|
||||
///
|
||||
/// `argument_type` is the type name within the `bindings` crate.
|
||||
/// `user_callback` should have the following prototype:
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn foo(device: &kernel::drm::Device<Self>,
|
||||
/// data: &Opaque<uapi::argument_type>,
|
||||
/// file: &kernel::drm::File<Self::File>,
|
||||
/// ) -> Result<u32>
|
||||
/// ```
|
||||
/// where `Self` is the drm::drv::Driver implementation these ioctls are being declared within.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// kernel::declare_drm_ioctls! {
|
||||
/// (FOO_GET_PARAM, drm_foo_get_param, ioctl::RENDER_ALLOW, my_get_param_handler),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! declare_drm_ioctls {
|
||||
( $(($cmd:ident, $struct:ident, $flags:expr, $func:expr)),* $(,)? ) => {
|
||||
const IOCTLS: &'static [$crate::drm::ioctl::DrmIoctlDescriptor] = {
|
||||
use $crate::uapi::*;
|
||||
const _:() = {
|
||||
let i: u32 = $crate::uapi::DRM_COMMAND_BASE;
|
||||
// Assert that all the IOCTLs are in the right order and there are no gaps,
|
||||
// and that the size of the specified type is correct.
|
||||
$(
|
||||
let cmd: u32 = $crate::macros::concat_idents!(DRM_IOCTL_, $cmd);
|
||||
::core::assert!(i == $crate::ioctl::_IOC_NR(cmd));
|
||||
::core::assert!(core::mem::size_of::<$crate::uapi::$struct>() ==
|
||||
$crate::ioctl::_IOC_SIZE(cmd));
|
||||
let i: u32 = i + 1;
|
||||
)*
|
||||
};
|
||||
|
||||
let ioctls = &[$(
|
||||
$crate::drm::ioctl::internal::drm_ioctl_desc {
|
||||
cmd: $crate::macros::concat_idents!(DRM_IOCTL_, $cmd) as u32,
|
||||
func: {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern "C" fn $cmd(
|
||||
raw_dev: *mut $crate::drm::ioctl::internal::drm_device,
|
||||
raw_data: *mut ::core::ffi::c_void,
|
||||
raw_file: *mut $crate::drm::ioctl::internal::drm_file,
|
||||
) -> core::ffi::c_int {
|
||||
// SAFETY:
|
||||
// - The DRM core ensures the device lives while callbacks are being
|
||||
// called.
|
||||
// - The DRM device must have been registered when we're called through
|
||||
// an IOCTL.
|
||||
//
|
||||
// FIXME: Currently there is nothing enforcing that the types of the
|
||||
// dev/file match the current driver these ioctls are being declared
|
||||
// for, and it's not clear how to enforce this within the type system.
|
||||
let dev = $crate::drm::device::Device::as_ref(raw_dev);
|
||||
// SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we
|
||||
// asserted above matches the size of this type, and all bit patterns of
|
||||
// UAPI structs must be valid.
|
||||
let data = unsafe {
|
||||
&*(raw_data as *const $crate::types::Opaque<$crate::uapi::$struct>)
|
||||
};
|
||||
// SAFETY: This is just the DRM file structure
|
||||
let file = unsafe { $crate::drm::File::as_ref(raw_file) };
|
||||
|
||||
match $func(dev, data, file) {
|
||||
Err(e) => e.to_errno(),
|
||||
Ok(i) => i.try_into()
|
||||
.unwrap_or($crate::error::code::ERANGE.to_errno()),
|
||||
}
|
||||
}
|
||||
Some($cmd)
|
||||
},
|
||||
flags: $flags,
|
||||
name: $crate::c_str!(::core::stringify!($cmd)).as_char_ptr(),
|
||||
}
|
||||
),*];
|
||||
ioctls
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
|
||||
//! DRM subsystem abstractions.
|
||||
|
||||
pub mod device;
|
||||
pub mod driver;
|
||||
pub mod file;
|
||||
pub mod gem;
|
||||
pub mod ioctl;
|
||||
|
||||
pub use self::device::Device;
|
||||
pub use self::driver::Driver;
|
||||
pub use self::driver::DriverInfo;
|
||||
pub use self::driver::Registration;
|
||||
pub use self::file::File;
|
||||
|
||||
pub(crate) mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
|
@ -38,6 +38,8 @@
|
|||
pub use ffi;
|
||||
|
||||
pub mod alloc;
|
||||
#[cfg(CONFIG_AUXILIARY_BUS)]
|
||||
pub mod auxiliary;
|
||||
#[cfg(CONFIG_BLOCK)]
|
||||
pub mod block;
|
||||
#[doc(hidden)]
|
||||
|
|
@ -48,6 +50,8 @@
|
|||
pub mod devres;
|
||||
pub mod dma;
|
||||
pub mod driver;
|
||||
#[cfg(CONFIG_DRM = "y")]
|
||||
pub mod drm;
|
||||
pub mod error;
|
||||
pub mod faux;
|
||||
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
use crate::{
|
||||
alloc::flags::*,
|
||||
bindings, device,
|
||||
bindings, container_of, device,
|
||||
device_id::RawDeviceId,
|
||||
devres::Devres,
|
||||
driver,
|
||||
|
|
@ -360,11 +360,13 @@ fn deref(&self) -> &Self::Target {
|
|||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
impl<Ctx: device::DeviceContext> Device<Ctx> {
|
||||
fn as_raw(&self) -> *mut bindings::pci_dev {
|
||||
self.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Returns the PCI vendor ID.
|
||||
pub fn vendor_id(&self) -> u16 {
|
||||
// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
|
||||
|
|
@ -388,7 +390,9 @@ pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
|
|||
// - by its type invariant `self.as_raw` is always a valid pointer to a `struct pci_dev`.
|
||||
Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Device<device::Bound> {
|
||||
/// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks
|
||||
/// can be performed on compile time for offsets (plus the requested type size) < SIZE.
|
||||
pub fn iomap_region_sized<const SIZE: usize>(
|
||||
|
|
@ -422,25 +426,10 @@ pub fn set_master(&self) {
|
|||
}
|
||||
}
|
||||
|
||||
impl Deref for Device<device::Core> {
|
||||
type Target = Device;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
let ptr: *const Self = self;
|
||||
|
||||
// CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::pci_dev>`.
|
||||
let ptr = ptr.cast::<Device>();
|
||||
|
||||
// SAFETY: `ptr` was derived from `&self`.
|
||||
unsafe { &*ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Device<device::Core>> for ARef<Device> {
|
||||
fn from(dev: &Device<device::Core>) -> Self {
|
||||
(&**dev).into()
|
||||
}
|
||||
}
|
||||
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
|
||||
// argument.
|
||||
kernel::impl_device_context_deref!(unsafe { Device });
|
||||
kernel::impl_device_context_into_aref!(Device);
|
||||
|
||||
// SAFETY: Instances of `Device` are always reference-counted.
|
||||
unsafe impl crate::types::AlwaysRefCounted for Device {
|
||||
|
|
@ -455,8 +444,8 @@ unsafe fn dec_ref(obj: NonNull<Self>) {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<device::Device> for Device {
|
||||
fn as_ref(&self) -> &device::Device {
|
||||
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
|
||||
fn as_ref(&self) -> &device::Device<Ctx> {
|
||||
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
|
||||
// `struct pci_dev`.
|
||||
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
|
||||
|
|
@ -466,6 +455,26 @@ fn as_ref(&self) -> &device::Device {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &Device<Ctx> {
|
||||
type Error = kernel::error::Error;
|
||||
|
||||
fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
|
||||
// SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
|
||||
// `struct device`.
|
||||
if !unsafe { bindings::dev_is_pci(dev.as_raw()) } {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
// SAFETY: We've just verified that the bus type of `dev` equals `bindings::pci_bus_type`,
|
||||
// hence `dev` must be embedded in a valid `struct pci_dev` as guaranteed by the
|
||||
// corresponding C code.
|
||||
let pdev = unsafe { container_of!(dev.as_raw(), bindings::pci_dev, dev) };
|
||||
|
||||
// SAFETY: `pdev` is a valid pointer to a `struct pci_dev`.
|
||||
Ok(unsafe { &*pdev.cast() })
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
|
||||
unsafe impl Send for Device {}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,18 +5,17 @@
|
|||
//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
|
||||
|
||||
use crate::{
|
||||
bindings, device, driver,
|
||||
bindings, container_of, device, driver,
|
||||
error::{to_result, Result},
|
||||
of,
|
||||
prelude::*,
|
||||
str::CStr,
|
||||
types::{ARef, ForeignOwnable, Opaque},
|
||||
types::{ForeignOwnable, Opaque},
|
||||
ThisModule,
|
||||
};
|
||||
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
ptr::{addr_of_mut, NonNull},
|
||||
};
|
||||
|
||||
|
|
@ -184,31 +183,16 @@ pub struct Device<Ctx: device::DeviceContext = device::Normal>(
|
|||
PhantomData<Ctx>,
|
||||
);
|
||||
|
||||
impl Device {
|
||||
impl<Ctx: device::DeviceContext> Device<Ctx> {
|
||||
fn as_raw(&self) -> *mut bindings::platform_device {
|
||||
self.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Device<device::Core> {
|
||||
type Target = Device;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
let ptr: *const Self = self;
|
||||
|
||||
// CAST: `Device<Ctx>` is a transparent wrapper of `Opaque<bindings::platform_device>`.
|
||||
let ptr = ptr.cast::<Device>();
|
||||
|
||||
// SAFETY: `ptr` was derived from `&self`.
|
||||
unsafe { &*ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Device<device::Core>> for ARef<Device> {
|
||||
fn from(dev: &Device<device::Core>) -> Self {
|
||||
(&**dev).into()
|
||||
}
|
||||
}
|
||||
// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
|
||||
// argument.
|
||||
kernel::impl_device_context_deref!(unsafe { Device });
|
||||
kernel::impl_device_context_into_aref!(Device);
|
||||
|
||||
// SAFETY: Instances of `Device` are always reference-counted.
|
||||
unsafe impl crate::types::AlwaysRefCounted for Device {
|
||||
|
|
@ -223,8 +207,8 @@ unsafe fn dec_ref(obj: NonNull<Self>) {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<device::Device> for Device {
|
||||
fn as_ref(&self) -> &device::Device {
|
||||
impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
|
||||
fn as_ref(&self) -> &device::Device<Ctx> {
|
||||
// SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
|
||||
// `struct platform_device`.
|
||||
let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
|
||||
|
|
@ -234,6 +218,26 @@ fn as_ref(&self) -> &device::Device {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Ctx: device::DeviceContext> TryFrom<&device::Device<Ctx>> for &Device<Ctx> {
|
||||
type Error = kernel::error::Error;
|
||||
|
||||
fn try_from(dev: &device::Device<Ctx>) -> Result<Self, Self::Error> {
|
||||
// SAFETY: By the type invariant of `Device`, `dev.as_raw()` is a valid pointer to a
|
||||
// `struct device`.
|
||||
if !unsafe { bindings::dev_is_platform(dev.as_raw()) } {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
// SAFETY: We've just verified that the bus type of `dev` equals
|
||||
// `bindings::platform_bus_type`, hence `dev` must be embedded in a valid
|
||||
// `struct platform_device` as guaranteed by the corresponding C code.
|
||||
let pdev = unsafe { container_of!(dev.as_raw(), bindings::platform_device, dev) };
|
||||
|
||||
// SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
|
||||
Ok(unsafe { &*pdev.cast() })
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: A `Device` is always reference-counted and can be released from any thread.
|
||||
unsafe impl Send for Device {}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,34 @@ pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> Option<&'a
|
|||
}
|
||||
}
|
||||
|
||||
/// Tries to access the wrapped object and run a closure on it while the guard is held.
|
||||
///
|
||||
/// This is a convenience method to run short non-sleepable code blocks while ensuring the
|
||||
/// guard is dropped afterwards. [`Self::try_access`] carries the risk that the caller will
|
||||
/// forget to explicitly drop that returned guard before calling sleepable code; this method
|
||||
/// adds an extra safety to make sure it doesn't happen.
|
||||
///
|
||||
/// Returns [`None`] if the object has been revoked and is therefore no longer accessible, or
|
||||
/// the result of the closure wrapped in [`Some`]. If the closure returns a [`Result`] then the
|
||||
/// return type becomes `Option<Result<>>`, which can be inconvenient. Users are encouraged to
|
||||
/// define their own macro that turns the [`Option`] into a proper error code and flattens the
|
||||
/// inner result into it if it makes sense within their subsystem.
|
||||
pub fn try_access_with<R, F: FnOnce(&T) -> R>(&self, f: F) -> Option<R> {
|
||||
self.try_access().map(|t| f(&*t))
|
||||
}
|
||||
|
||||
/// Directly access the revocable wrapped object.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure this [`Revocable`] instance hasn't been revoked and won't be revoked
|
||||
/// as long as the returned `&T` lives.
|
||||
pub unsafe fn access(&self) -> &T {
|
||||
// SAFETY: By the safety requirement of this function it is guaranteed that
|
||||
// `self.data.get()` is a valid pointer to an instance of `T`.
|
||||
unsafe { &*self.data.get() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Callers must ensure that there are no more concurrent users of the revocable object.
|
||||
|
|
|
|||
|
|
@ -329,6 +329,14 @@ pub const fn uninit() -> Self {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new zeroed opaque value.
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
value: UnsafeCell::new(MaybeUninit::zeroed()),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an opaque pin-initializer from the given pin-initializer.
|
||||
pub fn pin_init(slot: impl PinInit<T>) -> impl PinInit<Self> {
|
||||
Self::ffi_init(|ptr: *mut T| {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
*/
|
||||
|
||||
#include <uapi/asm-generic/ioctl.h>
|
||||
#include <uapi/drm/drm.h>
|
||||
#include <uapi/drm/nova_drm.h>
|
||||
#include <uapi/linux/mdio.h>
|
||||
#include <uapi/linux/mii.h>
|
||||
#include <uapi/linux/ethtool.h>
|
||||
|
|
|
|||
|
|
@ -82,6 +82,18 @@ config SAMPLE_RUST_DRIVER_FAUX
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config SAMPLE_RUST_DRIVER_AUXILIARY
|
||||
tristate "Auxiliary Driver"
|
||||
depends on PCI
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
This option builds the Rust auxiliary driver sample.
|
||||
|
||||
To compile this as a module, choose M here:
|
||||
the module will be called rust_driver_auxiliary.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SAMPLE_RUST_HOSTPROGS
|
||||
bool "Host programs"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o
|
|||
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
|
||||
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
|
||||
obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o
|
||||
obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) += rust_driver_auxiliary.o
|
||||
|
||||
rust_print-y := rust_print_main.o rust_print_events.o
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Rust auxiliary driver sample (based on a PCI driver for QEMU's `pci-testdev`).
|
||||
//!
|
||||
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
|
||||
|
||||
use kernel::{
|
||||
auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, str::CStr,
|
||||
InPlaceModule,
|
||||
};
|
||||
|
||||
use pin_init::PinInit;
|
||||
|
||||
const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME;
|
||||
const AUXILIARY_NAME: &CStr = c_str!("auxiliary");
|
||||
|
||||
struct AuxiliaryDriver;
|
||||
|
||||
kernel::auxiliary_device_table!(
|
||||
AUX_TABLE,
|
||||
MODULE_AUX_TABLE,
|
||||
<AuxiliaryDriver as auxiliary::Driver>::IdInfo,
|
||||
[(auxiliary::DeviceId::new(MODULE_NAME, AUXILIARY_NAME), ())]
|
||||
);
|
||||
|
||||
impl auxiliary::Driver for AuxiliaryDriver {
|
||||
type IdInfo = ();
|
||||
|
||||
const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE;
|
||||
|
||||
fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
|
||||
dev_info!(
|
||||
adev.as_ref(),
|
||||
"Probing auxiliary driver for auxiliary device with id={}\n",
|
||||
adev.id()
|
||||
);
|
||||
|
||||
ParentDriver::connect(adev)?;
|
||||
|
||||
let this = KBox::new(Self, GFP_KERNEL)?;
|
||||
|
||||
Ok(this.into())
|
||||
}
|
||||
}
|
||||
|
||||
struct ParentDriver {
|
||||
_reg: [auxiliary::Registration; 2],
|
||||
}
|
||||
|
||||
kernel::pci_device_table!(
|
||||
PCI_TABLE,
|
||||
MODULE_PCI_TABLE,
|
||||
<ParentDriver as pci::Driver>::IdInfo,
|
||||
[(
|
||||
pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
|
||||
()
|
||||
)]
|
||||
);
|
||||
|
||||
impl pci::Driver for ParentDriver {
|
||||
type IdInfo = ();
|
||||
|
||||
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
|
||||
|
||||
fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
|
||||
let this = KBox::new(
|
||||
Self {
|
||||
_reg: [
|
||||
auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?,
|
||||
auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?,
|
||||
],
|
||||
},
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
|
||||
Ok(this.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentDriver {
|
||||
fn connect(adev: &auxiliary::Device) -> Result<()> {
|
||||
let parent = adev.parent().ok_or(EINVAL)?;
|
||||
let pdev: &pci::Device = parent.try_into()?;
|
||||
|
||||
dev_info!(
|
||||
adev.as_ref(),
|
||||
"Connect auxiliary {} with parent: VendorID={:#x}, DeviceID={:#x}\n",
|
||||
adev.id(),
|
||||
pdev.vendor_id(),
|
||||
pdev.device_id()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_data]
|
||||
struct SampleModule {
|
||||
#[pin]
|
||||
_pci_driver: driver::Registration<pci::Adapter<ParentDriver>>,
|
||||
#[pin]
|
||||
_aux_driver: driver::Registration<auxiliary::Adapter<AuxiliaryDriver>>,
|
||||
}
|
||||
|
||||
impl InPlaceModule for SampleModule {
|
||||
fn init(module: &'static kernel::ThisModule) -> impl PinInit<Self, Error> {
|
||||
try_pin_init!(Self {
|
||||
_pci_driver <- driver::Registration::new(MODULE_NAME, module),
|
||||
_aux_driver <- driver::Registration::new(MODULE_NAME, module),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: SampleModule,
|
||||
name: "rust_driver_auxiliary",
|
||||
author: "Danilo Krummrich",
|
||||
description: "Rust auxiliary driver",
|
||||
license: "GPL v2",
|
||||
}
|
||||
|
|
@ -83,12 +83,11 @@ fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> Result<Pin<KBox<Self>
|
|||
GFP_KERNEL,
|
||||
)?;
|
||||
|
||||
let bar = drvdata.bar.try_access().ok_or(ENXIO)?;
|
||||
|
||||
let bar = drvdata.bar.access(pdev.as_ref())?;
|
||||
dev_info!(
|
||||
pdev.as_ref(),
|
||||
"pci-testdev data-match count: {}\n",
|
||||
Self::testdev(info, &bar)?
|
||||
Self::testdev(info, bar)?
|
||||
);
|
||||
|
||||
Ok(drvdata.into())
|
||||
|
|
|
|||
Loading…
Reference in New Issue