Merge branch 'driver-core-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git

This commit is contained in:
Stephen Rothwell 2025-01-13 13:56:40 +11:00
commit aadf687814
90 changed files with 2847 additions and 370 deletions

View File

@ -311,6 +311,7 @@ Code Seq# Include File Comments
<mailto:oe@port.de>
'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict!
'|' 00-7F linux/media.h
'|' 80-9F samples/ Any sample and example drivers
0x80 00-1F linux/fb.h
0x81 00-1F linux/vduse.h
0x89 00-06 arch/x86/include/asm/sockios.h

View File

@ -5363,6 +5363,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
F: drivers/char/
F: drivers/misc/
F: include/linux/miscdevice.h
F: samples/rust/rust_misc_device.rs
X: drivers/char/agp/
X: drivers/char/hw_random/
X: drivers/char/ipmi/
@ -7067,6 +7068,7 @@ F: include/linux/component.h
DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
R: "Rafael J. Wysocki" <rafael@kernel.org>
R: Danilo Krummrich <dakr@kernel.org>
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git
F: Documentation/core-api/kobject.rst
@ -7077,8 +7079,14 @@ F: include/linux/debugfs.h
F: include/linux/fwnode.h
F: include/linux/kobj*
F: include/linux/property.h
F: include/linux/sysfs.h
F: lib/kobj*
F: rust/kernel/device.rs
F: rust/kernel/device_id.rs
F: rust/kernel/devres.rs
F: rust/kernel/driver.rs
F: rust/kernel/platform.rs
F: samples/rust/rust_driver_platform.rs
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
M: Nishanth Menon <nm@ti.com>
@ -17600,6 +17608,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
F: Documentation/ABI/testing/sysfs-firmware-ofw
F: drivers/of/
F: include/linux/of*.h
F: rust/kernel/of.rs
F: scripts/dtc/
F: tools/testing/selftests/dt/
K: of_overlay_notifier_
@ -18200,6 +18209,8 @@ F: include/asm-generic/pci*
F: include/linux/of_pci.h
F: include/linux/pci*
F: include/uapi/linux/pci*
F: rust/kernel/pci.rs
F: samples/rust/rust_driver_pci.rs
PCIE BANDWIDTH CONTROLLER
M: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
@ -19796,6 +19807,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
F: Documentation/RCU/
F: include/linux/rcu*
F: kernel/rcu/
F: rust/kernel/sync/rcu.rs
X: Documentation/RCU/torture.rst
X: include/linux/srcu*.h
X: kernel/rcu/srcu*.c

View File

@ -195,7 +195,7 @@ void __iomem *ecardm_iomap(struct expansion_card *ec, unsigned int res,
unsigned long offset, unsigned long maxsize);
#define ecardm_iounmap(__ec, __addr) devm_iounmap(&(__ec)->dev, __addr)
extern struct bus_type ecard_bus_type;
extern const struct bus_type ecard_bus_type;
#define ECARD_DEV(_d) container_of((_d), struct expansion_card, dev)

View File

@ -1124,7 +1124,7 @@ static int ecard_match(struct device *_dev, const struct device_driver *_drv)
return ret;
}
struct bus_type ecard_bus_type = {
const struct bus_type ecard_bus_type = {
.name = "ecard",
.dev_groups = ecard_dev_groups,
.match = ecard_match,

View File

@ -815,7 +815,7 @@ static int opal_add_one_export(struct kobject *parent, const char *export_name,
sysfs_bin_attr_init(attr);
attr->attr.name = name;
attr->attr.mode = 0400;
attr->read = sysfs_bin_attr_simple_read;
attr->read_new = sysfs_bin_attr_simple_read;
attr->private = __va(vals[0]);
attr->size = vals[1];

View File

@ -419,13 +419,13 @@ struct vio_remove_node_data {
u64 node;
};
static int vio_md_node_match(struct device *dev, void *arg)
static int vio_md_node_match(struct device *dev, const void *arg)
{
struct vio_dev *vdev = to_vio_dev(dev);
struct vio_remove_node_data *node_data;
const struct vio_remove_node_data *node_data;
u64 node;
node_data = (struct vio_remove_node_data *)arg;
node_data = (const struct vio_remove_node_data *)arg;
node = vio_vdev_node(node_data->hp, vdev);

View File

@ -1138,6 +1138,7 @@ static void blkcg_fill_root_iostats(void)
blkg_iostat_set(&blkg->iostat.cur, &tmp);
u64_stats_update_end_irqrestore(&blkg->iostat.sync, flags);
}
class_dev_iter_exit(&iter);
}
static void blkcg_print_one_stat(struct blkcg_gq *blkg, struct seq_file *s)

View File

@ -354,7 +354,7 @@ static struct device *next_device(struct klist_iter *i)
* count in the supplied callback.
*/
int bus_for_each_dev(const struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
void *data, device_iter_t fn)
{
struct subsys_private *sp = bus_to_subsys(bus);
struct klist_iter i;
@ -402,9 +402,12 @@ struct device *bus_find_device(const struct bus_type *bus,
klist_iter_init_node(&sp->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)))
if (match(dev, data) && get_device(dev))
while ((dev = next_device(&i))) {
if (match(dev, data)) {
get_device(dev);
break;
}
}
klist_iter_exit(&i);
subsys_put(sp);
return dev;

View File

@ -323,8 +323,12 @@ void class_dev_iter_init(struct class_dev_iter *iter, const struct class *class,
struct subsys_private *sp = class_to_subsys(class);
struct klist_node *start_knode = NULL;
if (!sp)
memset(iter, 0, sizeof(*iter));
if (!sp) {
pr_crit("%s: class %p was not registered yet\n",
__func__, class);
return;
}
if (start)
start_knode = &start->p->knode_class;
@ -351,6 +355,9 @@ struct device *class_dev_iter_next(struct class_dev_iter *iter)
struct klist_node *knode;
struct device *dev;
if (!iter->sp)
return NULL;
while (1) {
knode = klist_next(&iter->ki);
if (!knode)
@ -395,7 +402,7 @@ EXPORT_SYMBOL_GPL(class_dev_iter_exit);
* code. There's no locking restriction.
*/
int class_for_each_device(const struct class *class, const struct device *start,
void *data, int (*fn)(struct device *, void *))
void *data, device_iter_t fn)
{
struct subsys_private *sp = class_to_subsys(class);
struct class_dev_iter iter;
@ -594,30 +601,10 @@ EXPORT_SYMBOL_GPL(class_compat_unregister);
* a bus device
* @cls: the compatibility class
* @dev: the target bus device
* @device_link: an optional device to which a "device" link should be created
*/
int class_compat_create_link(struct class_compat *cls, struct device *dev,
struct device *device_link)
int class_compat_create_link(struct class_compat *cls, struct device *dev)
{
int error;
error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
if (error)
return error;
/*
* Optionally add a "device" link (typically to the parent), as a
* class device would have one and we want to provide as much
* backwards compatibility as possible.
*/
if (device_link) {
error = sysfs_create_link(&dev->kobj, &device_link->kobj,
"device");
if (error)
sysfs_remove_link(cls->kobj, dev_name(dev));
}
return error;
return sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
}
EXPORT_SYMBOL_GPL(class_compat_create_link);
@ -626,14 +613,9 @@ EXPORT_SYMBOL_GPL(class_compat_create_link);
* a bus device
* @cls: the compatibility class
* @dev: the target bus device
* @device_link: an optional device to which a "device" link was previously
* created
*/
void class_compat_remove_link(struct class_compat *cls, struct device *dev,
struct device *device_link)
void class_compat_remove_link(struct class_compat *cls, struct device *dev)
{
if (device_link)
sysfs_remove_link(&dev->kobj, "device");
sysfs_remove_link(cls->kobj, dev_name(dev));
}
EXPORT_SYMBOL_GPL(class_compat_remove_link);

View File

@ -3980,7 +3980,7 @@ const char *device_get_devnode(const struct device *dev,
* other than 0, we break out and return that value.
*/
int device_for_each_child(struct device *parent, void *data,
int (*fn)(struct device *dev, void *data))
device_iter_t fn)
{
struct klist_iter i;
struct device *child;
@ -4010,7 +4010,7 @@ EXPORT_SYMBOL_GPL(device_for_each_child);
* other than 0, we break out and return that value.
*/
int device_for_each_child_reverse(struct device *parent, void *data,
int (*fn)(struct device *dev, void *data))
device_iter_t fn)
{
struct klist_iter i;
struct device *child;
@ -4043,14 +4043,14 @@ EXPORT_SYMBOL_GPL(device_for_each_child_reverse);
* device_for_each_child_reverse_from();
*/
int device_for_each_child_reverse_from(struct device *parent,
struct device *from, const void *data,
int (*fn)(struct device *, const void *))
struct device *from, void *data,
device_iter_t fn)
{
struct klist_iter i;
struct device *child;
int error = 0;
if (!parent->p)
if (!parent || !parent->p)
return 0;
klist_iter_init_node(&parent->p->klist_children, &i,
@ -4079,8 +4079,8 @@ EXPORT_SYMBOL_GPL(device_for_each_child_reverse_from);
*
* NOTE: you will need to drop the reference with put_device() after use.
*/
struct device *device_find_child(struct device *parent, void *data,
int (*match)(struct device *dev, void *data))
struct device *device_find_child(struct device *parent, const void *data,
device_match_t match)
{
struct klist_iter i;
struct device *child;
@ -4089,62 +4089,17 @@ struct device *device_find_child(struct device *parent, void *data,
return NULL;
klist_iter_init(&parent->p->klist_children, &i);
while ((child = next_device(&i)))
if (match(child, data) && get_device(child))
while ((child = next_device(&i))) {
if (match(child, data)) {
get_device(child);
break;
}
}
klist_iter_exit(&i);
return child;
}
EXPORT_SYMBOL_GPL(device_find_child);
/**
* device_find_child_by_name - device iterator for locating a child device.
* @parent: parent struct device
* @name: name of the child device
*
* This is similar to the device_find_child() function above, but it
* returns a reference to a device that has the name @name.
*
* NOTE: you will need to drop the reference with put_device() after use.
*/
struct device *device_find_child_by_name(struct device *parent,
const char *name)
{
struct klist_iter i;
struct device *child;
if (!parent)
return NULL;
klist_iter_init(&parent->p->klist_children, &i);
while ((child = next_device(&i)))
if (sysfs_streq(dev_name(child), name) && get_device(child))
break;
klist_iter_exit(&i);
return child;
}
EXPORT_SYMBOL_GPL(device_find_child_by_name);
static int match_any(struct device *dev, void *unused)
{
return 1;
}
/**
* device_find_any_child - device iterator for locating a child device, if any.
* @parent: parent struct device
*
* This is similar to the device_find_child() function above, but it
* returns a reference to a child device, if any.
*
* NOTE: you will need to drop the reference with put_device() after use.
*/
struct device *device_find_any_child(struct device *parent)
{
return device_find_child(parent, NULL, match_any);
}
EXPORT_SYMBOL_GPL(device_find_any_child);
int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
@ -5244,15 +5199,21 @@ int device_match_name(struct device *dev, const void *name)
}
EXPORT_SYMBOL_GPL(device_match_name);
int device_match_type(struct device *dev, const void *type)
{
return dev->type == type;
}
EXPORT_SYMBOL_GPL(device_match_type);
int device_match_of_node(struct device *dev, const void *np)
{
return dev->of_node == np;
return np && dev->of_node == np;
}
EXPORT_SYMBOL_GPL(device_match_of_node);
int device_match_fwnode(struct device *dev, const void *fwnode)
{
return dev_fwnode(dev) == fwnode;
return fwnode && dev_fwnode(dev) == fwnode;
}
EXPORT_SYMBOL_GPL(device_match_fwnode);
@ -5264,13 +5225,13 @@ EXPORT_SYMBOL_GPL(device_match_devt);
int device_match_acpi_dev(struct device *dev, const void *adev)
{
return ACPI_COMPANION(dev) == adev;
return adev && ACPI_COMPANION(dev) == adev;
}
EXPORT_SYMBOL(device_match_acpi_dev);
int device_match_acpi_handle(struct device *dev, const void *handle)
{
return ACPI_HANDLE(dev) == handle;
return handle && ACPI_HANDLE(dev) == handle;
}
EXPORT_SYMBOL(device_match_acpi_handle);

View File

@ -186,9 +186,9 @@ static ssize_t disabled_show(const struct class *class, const struct class_attri
* mutex_lock(&devcd->mutex);
*
*
* In the above diagram, It looks like disabled_store() would be racing with parallely
* In the above diagram, it looks like disabled_store() would be racing with parallelly
* running devcd_del() and result in memory abort while acquiring devcd->mutex which
* is called after kfree of devcd memory after dropping its last reference with
* is called after kfree of devcd memory after dropping its last reference with
* put_device(). However, this will not happens as fn(dev, data) runs
* with its own reference to device via klist_node so it is not its last reference.
* so, above situation would not occur.
@ -285,6 +285,8 @@ static void devcd_free_sgtable(void *data)
* @offset: start copy from @offset@ bytes from the head of the data
* in the given scatterlist
* @data_len: the length of the data in the sg_table
*
* Returns: the number of bytes copied
*/
static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset,
size_t buf_len, void *data,

View File

@ -750,25 +750,38 @@ int __devm_add_action(struct device *dev, void (*action)(void *), void *data, co
EXPORT_SYMBOL_GPL(__devm_add_action);
/**
* devm_remove_action() - removes previously added custom action
* devm_remove_action_nowarn() - removes previously added custom action
* @dev: Device that owns the action
* @action: Function implementing the action
* @data: Pointer to data passed to @action implementation
*
* Removes instance of @action previously added by devm_add_action().
* Both action and data should match one of the existing entries.
*
* In contrast to devm_remove_action(), this function does not WARN() if no
* entry could have been found.
*
* This should only be used if the action is contained in an object with
* independent lifetime management, e.g. the Devres rust abstraction.
*
* Causing the warning from regular driver code most likely indicates an abuse
* of the devres API.
*
* Returns: 0 on success, -ENOENT if no entry could have been found.
*/
void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
int devm_remove_action_nowarn(struct device *dev,
void (*action)(void *),
void *data)
{
struct action_devres devres = {
.data = data,
.action = action,
};
WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
&devres));
return devres_destroy(dev, devm_action_release, devm_action_match,
&devres);
}
EXPORT_SYMBOL_GPL(devm_remove_action);
EXPORT_SYMBOL_GPL(devm_remove_action_nowarn);
/**
* devm_release_action() - release previously added custom action

View File

@ -115,7 +115,7 @@ EXPORT_SYMBOL_GPL(driver_set_override);
* Iterate over the @drv's list of devices calling @fn for each one.
*/
int driver_for_each_device(struct device_driver *drv, struct device *start,
void *data, int (*fn)(struct device *, void *))
void *data, device_iter_t fn)
{
struct klist_iter i;
struct device *dev;
@ -160,9 +160,12 @@ struct device *driver_find_device(const struct device_driver *drv,
klist_iter_init_node(&drv->p->klist_devices, &i,
(start ? &start->p->knode_driver : NULL));
while ((dev = next_device(&i)))
if (match(dev, data) && get_device(dev))
while ((dev = next_device(&i))) {
if (match(dev, data)) {
get_device(dev);
break;
}
}
klist_iter_exit(&i);
return dev;
}

View File

@ -259,7 +259,7 @@ static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
}
static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -316,7 +316,7 @@ static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
* the driver as a firmware image.
**/
static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@ -356,11 +356,11 @@ out:
return retval;
}
static struct bin_attribute firmware_attr_data = {
static const struct bin_attribute firmware_attr_data = {
.attr = { .name = "data", .mode = 0644 },
.size = 0,
.read = firmware_data_read,
.write = firmware_data_write,
.read_new = firmware_data_read,
.write_new = firmware_data_write,
};
static struct attribute *fw_dev_attrs[] = {
@ -374,14 +374,14 @@ static struct attribute *fw_dev_attrs[] = {
NULL
};
static struct bin_attribute *fw_dev_bin_attrs[] = {
static const struct bin_attribute *const fw_dev_bin_attrs[] = {
&firmware_attr_data,
NULL
};
static const struct attribute_group fw_dev_attr_group = {
.attrs = fw_dev_attrs,
.bin_attrs = fw_dev_bin_attrs,
.bin_attrs_new = fw_dev_bin_attrs,
#ifdef CONFIG_FW_UPLOAD
.is_visible = fw_upload_is_visible,
#endif

View File

@ -12,6 +12,7 @@ config TEST_ASYNC_DRIVER_PROBE
config DM_KUNIT_TEST
tristate "KUnit Tests for the device model" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
config DRIVER_PE_KUNIT_TEST
tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS

View File

@ -1,8 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <kunit/platform_device.h>
#include <kunit/resource.h>
#include <linux/device.h>
#include <linux/device/bus.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#define DEVICE_NAME "test"
@ -217,7 +220,43 @@ static struct kunit_suite platform_device_devm_test_suite = {
.test_cases = platform_device_devm_tests,
};
kunit_test_suite(platform_device_devm_test_suite);
static void platform_device_find_by_null_test(struct kunit *test)
{
struct platform_device *pdev;
int ret;
pdev = kunit_platform_device_alloc(test, DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
ret = kunit_platform_device_add(test, pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, of_find_device_by_node(NULL), NULL);
KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_of_node(&platform_bus_type, NULL), NULL);
KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_fwnode(&platform_bus_type, NULL), NULL);
KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_acpi_dev(&platform_bus_type, NULL), NULL);
KUNIT_EXPECT_FALSE(test, device_match_of_node(&pdev->dev, NULL));
KUNIT_EXPECT_FALSE(test, device_match_fwnode(&pdev->dev, NULL));
KUNIT_EXPECT_FALSE(test, device_match_acpi_dev(&pdev->dev, NULL));
KUNIT_EXPECT_FALSE(test, device_match_acpi_handle(&pdev->dev, NULL));
}
static struct kunit_case platform_device_match_tests[] = {
KUNIT_CASE(platform_device_find_by_null_test),
{}
};
static struct kunit_suite platform_device_match_test_suite = {
.name = "platform-device-match",
.test_cases = platform_device_match_tests,
};
kunit_test_suites(
&platform_device_devm_test_suite,
&platform_device_match_test_suite,
);
MODULE_DESCRIPTION("Test module for platform devices");
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");

View File

@ -918,12 +918,12 @@ struct vdc_check_port_data {
char *type;
};
static int vdc_device_probed(struct device *dev, void *arg)
static int vdc_device_probed(struct device *dev, const void *arg)
{
struct vio_dev *vdev = to_vio_dev(dev);
struct vdc_check_port_data *port_data;
const struct vdc_check_port_data *port_data;
port_data = (struct vdc_check_port_data *)arg;
port_data = (const struct vdc_check_port_data *)arg;
if ((vdev->dev_no == port_data->dev_no) &&
(!(strcmp((char *)&vdev->type, port_data->type))) &&

View File

@ -22,8 +22,8 @@ struct fsl_mc_child_objs {
struct fsl_mc_obj_desc *child_array;
};
static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev,
struct fsl_mc_obj_desc *obj_desc)
static bool fsl_mc_device_match(const struct fsl_mc_device *mc_dev,
const struct fsl_mc_obj_desc *obj_desc)
{
return mc_dev->obj_desc.id == obj_desc->id &&
strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0;
@ -112,9 +112,9 @@ void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
}
EXPORT_SYMBOL_GPL(dprc_remove_devices);
static int __fsl_mc_device_match(struct device *dev, void *data)
static int __fsl_mc_device_match(struct device *dev, const void *data)
{
struct fsl_mc_obj_desc *obj_desc = data;
const struct fsl_mc_obj_desc *obj_desc = data;
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
return fsl_mc_device_match(mc_dev, obj_desc);

View File

@ -320,90 +320,90 @@ const struct bus_type fsl_mc_bus_type = {
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
struct device_type fsl_mc_bus_dprc_type = {
const struct device_type fsl_mc_bus_dprc_type = {
.name = "fsl_mc_bus_dprc"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprc_type);
struct device_type fsl_mc_bus_dpni_type = {
const struct device_type fsl_mc_bus_dpni_type = {
.name = "fsl_mc_bus_dpni"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpni_type);
struct device_type fsl_mc_bus_dpio_type = {
const struct device_type fsl_mc_bus_dpio_type = {
.name = "fsl_mc_bus_dpio"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpio_type);
struct device_type fsl_mc_bus_dpsw_type = {
const struct device_type fsl_mc_bus_dpsw_type = {
.name = "fsl_mc_bus_dpsw"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpsw_type);
struct device_type fsl_mc_bus_dpbp_type = {
const struct device_type fsl_mc_bus_dpbp_type = {
.name = "fsl_mc_bus_dpbp"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpbp_type);
struct device_type fsl_mc_bus_dpcon_type = {
const struct device_type fsl_mc_bus_dpcon_type = {
.name = "fsl_mc_bus_dpcon"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpcon_type);
struct device_type fsl_mc_bus_dpmcp_type = {
const struct device_type fsl_mc_bus_dpmcp_type = {
.name = "fsl_mc_bus_dpmcp"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmcp_type);
struct device_type fsl_mc_bus_dpmac_type = {
const struct device_type fsl_mc_bus_dpmac_type = {
.name = "fsl_mc_bus_dpmac"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmac_type);
struct device_type fsl_mc_bus_dprtc_type = {
const struct device_type fsl_mc_bus_dprtc_type = {
.name = "fsl_mc_bus_dprtc"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dprtc_type);
struct device_type fsl_mc_bus_dpseci_type = {
const struct device_type fsl_mc_bus_dpseci_type = {
.name = "fsl_mc_bus_dpseci"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type);
struct device_type fsl_mc_bus_dpdmux_type = {
const struct device_type fsl_mc_bus_dpdmux_type = {
.name = "fsl_mc_bus_dpdmux"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmux_type);
struct device_type fsl_mc_bus_dpdcei_type = {
const struct device_type fsl_mc_bus_dpdcei_type = {
.name = "fsl_mc_bus_dpdcei"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdcei_type);
struct device_type fsl_mc_bus_dpaiop_type = {
const struct device_type fsl_mc_bus_dpaiop_type = {
.name = "fsl_mc_bus_dpaiop"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpaiop_type);
struct device_type fsl_mc_bus_dpci_type = {
const struct device_type fsl_mc_bus_dpci_type = {
.name = "fsl_mc_bus_dpci"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpci_type);
struct device_type fsl_mc_bus_dpdmai_type = {
const struct device_type fsl_mc_bus_dpdmai_type = {
.name = "fsl_mc_bus_dpdmai"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmai_type);
struct device_type fsl_mc_bus_dpdbg_type = {
const struct device_type fsl_mc_bus_dpdbg_type = {
.name = "fsl_mc_bus_dpdbg"
};
EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdbg_type);
static struct device_type *fsl_mc_get_device_type(const char *type)
static const struct device_type *fsl_mc_get_device_type(const char *type)
{
static const struct {
struct device_type *dev_type;
const struct device_type *dev_type;
const char *type;
} dev_types[] = {
{ &fsl_mc_bus_dprc_type, "dprc" },

View File

@ -703,7 +703,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
return 0;
}
static int commit_reap(struct device *dev, const void *data)
static int commit_reap(struct device *dev, void *data)
{
struct cxl_port *port = to_cxl_port(dev->parent);
struct cxl_decoder *cxld;

View File

@ -252,9 +252,9 @@ static int devm_cxl_enable_mem(struct device *host, struct cxl_dev_state *cxlds)
}
/* require dvsec ranges to be covered by a locked platform window */
static int dvsec_range_allowed(struct device *dev, void *arg)
static int dvsec_range_allowed(struct device *dev, const void *arg)
{
struct range *dev_range = arg;
const struct range *dev_range = arg;
struct cxl_decoder *cxld;
if (!is_root_decoder(dev))

View File

@ -51,17 +51,6 @@ struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm_bridge, "CXL");
bool is_cxl_nvdimm_bridge(struct device *dev)
{
return dev->type == &cxl_nvdimm_bridge_type;
}
EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm_bridge, "CXL");
static int match_nvdimm_bridge(struct device *dev, void *data)
{
return is_cxl_nvdimm_bridge(dev);
}
/**
* cxl_find_nvdimm_bridge() - find a bridge device relative to a port
* @port: any descendant port of an nvdimm-bridge associated
@ -75,7 +64,9 @@ struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port)
if (!cxl_root)
return NULL;
dev = device_find_child(&cxl_root->port.dev, NULL, match_nvdimm_bridge);
dev = device_find_child(&cxl_root->port.dev,
&cxl_nvdimm_bridge_type,
device_match_type);
if (!dev)
return NULL;

View File

@ -778,7 +778,7 @@ out:
return rc;
}
static int check_commit_order(struct device *dev, const void *data)
static int check_commit_order(struct device *dev, void *data)
{
struct cxl_decoder *cxld = to_cxl_decoder(dev);
@ -792,7 +792,7 @@ static int check_commit_order(struct device *dev, const void *data)
return 0;
}
static int match_free_decoder(struct device *dev, void *data)
static int match_free_decoder(struct device *dev, const void *data)
{
struct cxl_port *port = to_cxl_port(dev->parent);
struct cxl_decoder *cxld;
@ -824,9 +824,9 @@ static int match_free_decoder(struct device *dev, void *data)
return 1;
}
static int match_auto_decoder(struct device *dev, void *data)
static int match_auto_decoder(struct device *dev, const void *data)
{
struct cxl_region_params *p = data;
const struct cxl_region_params *p = data;
struct cxl_decoder *cxld;
struct range *r;
@ -1733,10 +1733,12 @@ static struct cxl_port *next_port(struct cxl_port *port)
return port->parent_dport->port;
}
static int match_switch_decoder_by_range(struct device *dev, void *data)
static int match_switch_decoder_by_range(struct device *dev,
const void *data)
{
struct cxl_switch_decoder *cxlsd;
struct range *r1, *r2 = data;
const struct range *r1, *r2 = data;
if (!is_switch_decoder(dev))
return 0;
@ -3187,9 +3189,10 @@ err:
return rc;
}
static int match_root_decoder_by_range(struct device *dev, void *data)
static int match_root_decoder_by_range(struct device *dev,
const void *data)
{
struct range *r1, *r2 = data;
const struct range *r1, *r2 = data;
struct cxl_root_decoder *cxlrd;
if (!is_root_decoder(dev))
@ -3200,11 +3203,11 @@ static int match_root_decoder_by_range(struct device *dev, void *data)
return range_contains(r1, r2);
}
static int match_region_by_range(struct device *dev, void *data)
static int match_region_by_range(struct device *dev, const void *data)
{
struct cxl_region_params *p;
struct cxl_region *cxlr;
struct range *r = data;
const struct range *r = data;
int rc = 0;
if (!is_cxl_region(dev))

View File

@ -864,7 +864,6 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
struct cxl_port *port);
struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
bool is_cxl_nvdimm(struct device *dev);
bool is_cxl_nvdimm_bridge(struct device *dev);
int devm_cxl_add_nvdimm(struct cxl_port *parent_port, struct cxl_memdev *cxlmd);
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port);

View File

@ -988,7 +988,7 @@ int fw_device_set_broadcast_channel(struct device *dev, void *gen)
return 0;
}
static int compare_configuration_rom(struct device *dev, void *data)
static int compare_configuration_rom(struct device *dev, const void *data)
{
const struct fw_device *old = fw_device(dev);
const u32 *config_rom = data;
@ -1039,7 +1039,7 @@ static void fw_device_init(struct work_struct *work)
//
// serialize config_rom access.
scoped_guard(rwsem_read, &fw_device_rwsem) {
found = device_find_child(card->device, (void *)device->config_rom,
found = device_find_child(card->device, device->config_rom,
compare_configuration_rom);
}
if (found) {

View File

@ -238,10 +238,10 @@ static int scmi_dev_match(struct device *dev, const struct device_driver *drv)
return 0;
}
static int scmi_match_by_id_table(struct device *dev, void *data)
static int scmi_match_by_id_table(struct device *dev, const void *data)
{
struct scmi_device *sdev = to_scmi_dev(dev);
struct scmi_device_id *id_table = data;
const struct scmi_device_id *id_table = data;
return sdev->protocol_id == id_table->protocol_id &&
(id_table->name && !strcmp(sdev->name, id_table->name));

View File

@ -47,9 +47,9 @@ static long __init parse_acpi_path(const struct efi_dev_path *node,
return 0;
}
static int __init match_pci_dev(struct device *dev, void *data)
static int __init match_pci_dev(struct device *dev, const void *data)
{
unsigned int devfn = *(unsigned int *)data;
unsigned int devfn = *(const unsigned int *)data;
return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn;
}

View File

@ -413,11 +413,6 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip)
return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip);
}
static int gpio_sim_dev_match_fwnode(struct device *dev, void *data)
{
return device_match_fwnode(dev, data);
}
static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
{
struct gpio_sim_chip *chip;
@ -503,7 +498,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
if (ret)
return ret;
chip->dev = device_find_child(dev, swnode, gpio_sim_dev_match_fwnode);
chip->dev = device_find_child(dev, swnode, device_match_fwnode);
if (!chip->dev)
return -ENODEV;

View File

@ -358,7 +358,7 @@ static const struct of_device_id mtk_drm_of_ids[] = {
};
MODULE_DEVICE_TABLE(of, mtk_drm_of_ids);
static int mtk_drm_match(struct device *dev, void *data)
static int mtk_drm_match(struct device *dev, const void *data)
{
if (!strncmp(dev_name(dev), "mediatek-drm", sizeof("mediatek-drm") - 1))
return true;

View File

@ -332,7 +332,7 @@ static int hwmon_attr_base(enum hwmon_sensor_types type)
static DEFINE_MUTEX(hwmon_pec_mutex);
static int hwmon_match_device(struct device *dev, void *data)
static int hwmon_match_device(struct device *dev, const void *data)
{
return dev->class == &hwmon_class;
}

View File

@ -1306,10 +1306,10 @@ new_device_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_WO(new_device);
static int __i2c_find_user_addr(struct device *dev, void *addrp)
static int __i2c_find_user_addr(struct device *dev, const void *addrp)
{
struct i2c_client *client = i2c_verify_client(dev);
unsigned short addr = *(unsigned short *)addrp;
unsigned short addr = *(const unsigned short *)addrp;
return client && client->flags & I2C_CLIENT_USER &&
i2c_encode_flags_to_addr(client) == addr;

View File

@ -438,7 +438,7 @@ static int omnia_mcu_get_features(const struct i2c_client *mcu_client)
return reply;
}
static int omnia_match_mcu_client(struct device *dev, void *data)
static int omnia_match_mcu_client(struct device *dev, const void *data)
{
struct i2c_client *client;

View File

@ -125,7 +125,7 @@ static const struct hwmon_chip_info temp_chip_info = {
};
#endif
static int match_i2c_adap(struct device *dev, void *data)
static int match_i2c_adap(struct device *dev, const void *data)
{
return i2c_verify_adapter(dev) ? 1 : 0;
}
@ -141,7 +141,7 @@ static struct i2c_adapter *get_i2c_adap(struct platform_device *pdev)
return dev ? to_i2c_adapter(dev) : NULL;
}
static int match_spi_adap(struct device *dev, void *data)
static int match_spi_adap(struct device *dev, const void *data)
{
return to_spi_device(dev) ? 1 : 0;
}

View File

@ -42,7 +42,7 @@ struct mux_state {
unsigned int state;
};
static struct class mux_class = {
static const struct class mux_class = {
.name = "mux",
};

View File

@ -1212,7 +1212,7 @@ enum nd_ioctl_mode {
DIMM_IOCTL,
};
static int match_dimm(struct device *dev, void *data)
static int match_dimm(struct device *dev, const void *data)
{
long id = (long) data;

View File

@ -67,13 +67,6 @@ bool nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach,
return claimed;
}
static int namespace_match(struct device *dev, void *data)
{
char *name = data;
return strcmp(name, dev_name(dev)) == 0;
}
static bool is_idle(struct device *dev, struct nd_namespace_common *ndns)
{
struct nd_region *nd_region = to_nd_region(dev->parent);
@ -168,7 +161,7 @@ ssize_t nd_namespace_store(struct device *dev,
goto out;
}
found = device_find_child(dev->parent, name, namespace_match);
found = device_find_child_by_name(dev->parent, name);
if (!found) {
dev_dbg(dev, "'%s' not found under %s\n", name,
dev_name(dev->parent));

View File

@ -33,6 +33,11 @@
reg = <0x100>;
};
};
test-device@2 {
compatible = "test,rust-device";
reg = <0x2>;
};
};
platform-tests-2 {

View File

@ -1276,7 +1276,7 @@ static int pwm_export_child(struct device *pwmchip_dev, struct pwm_device *pwm)
return 0;
}
static int pwm_unexport_match(struct device *pwm_dev, void *data)
static int pwm_unexport_match(struct device *pwm_dev, const void *data)
{
return pwm_from_dev(pwm_dev) == data;
}

View File

@ -377,9 +377,9 @@ EXPORT_SYMBOL(rpmsg_get_mtu);
* this is used to make sure we're not creating rpmsg devices for channels
* that already exist.
*/
static int rpmsg_device_match(struct device *dev, void *data)
static int rpmsg_device_match(struct device *dev, const void *data)
{
struct rpmsg_channel_info *chinfo = data;
const struct rpmsg_channel_info *chinfo = data;
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src)

View File

@ -128,7 +128,7 @@ static int s390_vary_chpid(struct chp_id chpid, int on)
* Channel measurement related functions
*/
static ssize_t measurement_chars_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct channel_path *chp;
@ -142,11 +142,11 @@ static ssize_t measurement_chars_read(struct file *filp, struct kobject *kobj,
return memory_read_from_buffer(buf, count, &off, &chp->cmg_chars,
sizeof(chp->cmg_chars));
}
static BIN_ATTR_ADMIN_RO(measurement_chars, sizeof(struct cmg_chars));
static const BIN_ATTR_ADMIN_RO(measurement_chars, sizeof(struct cmg_chars));
static ssize_t measurement_chars_full_read(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
@ -196,22 +196,22 @@ static ssize_t chp_measurement_copy_block(void *buf, loff_t off, size_t count,
}
static ssize_t measurement_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
return chp_measurement_copy_block(buf, off, count, kobj, false);
}
static BIN_ATTR_ADMIN_RO(measurement, sizeof(struct cmg_entry));
static const BIN_ATTR_ADMIN_RO(measurement, sizeof(struct cmg_entry));
static ssize_t ext_measurement_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
const struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
return chp_measurement_copy_block(buf, off, count, kobj, true);
}
static BIN_ATTR_ADMIN_RO(ext_measurement, sizeof(struct cmg_ext_entry));
static const BIN_ATTR_ADMIN_RO(ext_measurement, sizeof(struct cmg_ext_entry));
static struct bin_attribute *measurement_attrs[] = {
static const struct bin_attribute *measurement_attrs[] = {
&bin_attr_measurement_chars,
&bin_attr_measurement_chars_full,
&bin_attr_measurement,
@ -435,7 +435,7 @@ static ssize_t speed_bps_show(struct device *dev,
static DEVICE_ATTR_RO(speed_bps);
static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
@ -448,10 +448,10 @@ static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
return rc;
}
static BIN_ATTR_RO(util_string,
sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
static const BIN_ATTR_RO(util_string,
sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
static struct bin_attribute *chp_bin_attrs[] = {
static const struct bin_attribute *const chp_bin_attrs[] = {
&bin_attr_util_string,
NULL,
};
@ -468,9 +468,9 @@ static struct attribute *chp_attrs[] = {
&dev_attr_speed_bps.attr,
NULL,
};
static struct attribute_group chp_attr_group = {
static const struct attribute_group chp_attr_group = {
.attrs = chp_attrs,
.bin_attrs = chp_bin_attrs,
.bin_attrs_new = chp_bin_attrs,
};
static const struct attribute_group *chp_attr_groups[] = {
&chp_attr_group,

View File

@ -7189,7 +7189,8 @@ exit_new_nt_list:
* 1: if flashnode entry is non-persistent
* 0: if flashnode entry is persistent
**/
static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev, void *data)
static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev,
const void *data)
{
struct iscsi_bus_flash_session *fnode_sess;

View File

@ -1324,7 +1324,7 @@ EXPORT_SYMBOL_GPL(iscsi_create_flashnode_conn);
* 1 on success
* 0 on failure
*/
static int iscsi_is_flashnode_conn_dev(struct device *dev, void *data)
static int iscsi_is_flashnode_conn_dev(struct device *dev, const void *data)
{
return dev->bus == &iscsi_flashnode_bus;
}
@ -1335,7 +1335,7 @@ static int iscsi_destroy_flashnode_conn(struct iscsi_bus_flash_conn *fnode_conn)
return 0;
}
static int flashnode_match_index(struct device *dev, void *data)
static int flashnode_match_index(struct device *dev, const void *data)
{
struct iscsi_bus_flash_session *fnode_sess = NULL;
int ret = 0;
@ -1344,7 +1344,7 @@ static int flashnode_match_index(struct device *dev, void *data)
goto exit_match_index;
fnode_sess = iscsi_dev_to_flash_session(dev);
ret = (fnode_sess->target_id == *((int *)data)) ? 1 : 0;
ret = (fnode_sess->target_id == *((const int *)data)) ? 1 : 0;
exit_match_index:
return ret;
@ -1389,8 +1389,8 @@ iscsi_get_flashnode_by_index(struct Scsi_Host *shost, uint32_t idx)
* %NULL on failure
*/
struct device *
iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data,
int (*fn)(struct device *dev, void *data))
iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data,
device_match_t fn)
{
return device_find_child(&shost->shost_gendev, data, fn);
}

View File

@ -328,7 +328,8 @@ void slim_report_absent(struct slim_device *sbdev)
}
EXPORT_SYMBOL_GPL(slim_report_absent);
static bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b)
static bool slim_eaddr_equal(const struct slim_eaddr *a,
const struct slim_eaddr *b)
{
return (a->manf_id == b->manf_id &&
a->prod_code == b->prod_code &&
@ -336,9 +337,9 @@ static bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b)
a->instance == b->instance);
}
static int slim_match_dev(struct device *dev, void *data)
static int slim_match_dev(struct device *dev, const void *data)
{
struct slim_eaddr *e_addr = data;
const struct slim_eaddr *e_addr = data;
struct slim_device *sbdev = to_slim_device(dev);
return slim_eaddr_equal(&sbdev->e_addr, e_addr);
@ -384,21 +385,13 @@ struct slim_device *slim_get_device(struct slim_controller *ctrl,
}
EXPORT_SYMBOL_GPL(slim_get_device);
static int of_slim_match_dev(struct device *dev, void *data)
{
struct device_node *np = data;
struct slim_device *sbdev = to_slim_device(dev);
return (sbdev->dev.of_node == np);
}
static struct slim_device *of_find_slim_device(struct slim_controller *ctrl,
struct device_node *np)
{
struct slim_device *sbdev;
struct device *dev;
dev = device_find_child(ctrl->dev, np, of_slim_match_dev);
dev = device_find_child(ctrl->dev, np, device_match_of_node);
if (dev) {
sbdev = to_slim_device(dev);
return sbdev;

View File

@ -472,7 +472,7 @@ struct tb_retimer_lookup {
u8 index;
};
static int retimer_match(struct device *dev, void *data)
static int retimer_match(struct device *dev, const void *data)
{
const struct tb_retimer_lookup *lookup = data;
struct tb_retimer *rt = tb_to_retimer(dev);

View File

@ -1026,7 +1026,7 @@ static int remove_missing_service(struct device *dev, void *data)
return 0;
}
static int find_service(struct device *dev, void *data)
static int find_service(struct device *dev, const void *data)
{
const struct tb_property *p = data;
struct tb_service *svc;

View File

@ -2365,9 +2365,9 @@ struct uart_match {
struct uart_driver *driver;
};
static int serial_match_port(struct device *dev, void *data)
static int serial_match_port(struct device *dev, const void *data)
{
struct uart_match *match = data;
const struct uart_match *match = data;
struct tty_driver *tty_drv = match->driver->tty_driver;
dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) +
match->port->line;

View File

@ -229,10 +229,10 @@ static const char * const usb_modes[] = {
/* ------------------------------------------------------------------------- */
/* Alternate Modes */
static int altmode_match(struct device *dev, void *data)
static int altmode_match(struct device *dev, const void *data)
{
struct typec_altmode *adev = to_typec_altmode(dev);
struct typec_device_id *id = data;
const struct typec_device_id *id = data;
if (!is_typec_altmode(dev))
return 0;
@ -1282,11 +1282,6 @@ const struct device_type typec_cable_dev_type = {
.release = typec_cable_release,
};
static int cable_match(struct device *dev, void *data)
{
return is_typec_cable(dev);
}
/**
* typec_cable_get - Get a reference to the USB Type-C cable
* @port: The USB Type-C Port the cable is connected to
@ -1298,7 +1293,8 @@ struct typec_cable *typec_cable_get(struct typec_port *port)
{
struct device *dev;
dev = device_find_child(&port->dev, NULL, cable_match);
dev = device_find_child(&port->dev, &typec_cable_dev_type,
device_match_type);
if (!dev)
return NULL;
@ -2028,16 +2024,12 @@ const struct device_type typec_port_dev_type = {
/* --------------------------------------- */
/* Driver callbacks to report role updates */
static int partner_match(struct device *dev, void *data)
{
return is_typec_partner(dev);
}
static struct typec_partner *typec_get_partner(struct typec_port *port)
{
struct device *dev;
dev = device_find_child(&port->dev, NULL, partner_match);
dev = device_find_child(&port->dev, &typec_partner_dev_type,
device_match_type);
if (!dev)
return NULL;
@ -2170,7 +2162,9 @@ void typec_set_pwr_opmode(struct typec_port *port,
sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode");
kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
partner_dev = device_find_child(&port->dev, NULL, partner_match);
partner_dev = device_find_child(&port->dev,
&typec_partner_dev_type,
device_match_type);
if (partner_dev) {
struct typec_partner *partner = to_typec_partner(partner_dev);
@ -2334,7 +2328,9 @@ int typec_get_negotiated_svdm_version(struct typec_port *port)
enum usb_pd_svdm_ver svdm_version;
struct device *partner_dev;
partner_dev = device_find_child(&port->dev, NULL, partner_match);
partner_dev = device_find_child(&port->dev,
&typec_partner_dev_type,
device_match_type);
if (!partner_dev)
return -ENODEV;
@ -2361,7 +2357,8 @@ int typec_get_cable_svdm_version(struct typec_port *port)
enum usb_pd_svdm_ver svdm_version;
struct device *cable_dev;
cable_dev = device_find_child(&port->dev, NULL, cable_match);
cable_dev = device_find_child(&port->dev, &typec_cable_dev_type,
device_match_type);
if (!cable_dev)
return -ENODEV;

View File

@ -76,7 +76,7 @@ int mdev_register_parent(struct mdev_parent *parent, struct device *dev,
if (ret)
return ret;
ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
ret = class_compat_create_link(mdev_bus_compat_class, dev);
if (ret)
dev_warn(dev, "Failed to create compatibility class link\n");
@ -98,7 +98,7 @@ void mdev_unregister_parent(struct mdev_parent *parent)
dev_info(parent->dev, "MDEV: Unregistering\n");
down_write(&parent->unreg_sem);
class_compat_remove_link(mdev_bus_compat_class, parent->dev, NULL);
class_compat_remove_link(mdev_bus_compat_class, parent->dev);
device_for_each_child(parent->dev, NULL, mdev_device_remove_cb);
parent_remove_sysfs_files(parent);
up_write(&parent->unreg_sem);

View File

@ -817,7 +817,7 @@ EXPORT_SYMBOL_GPL(sysfs_emit_at);
* Returns number of bytes written to @buf.
*/
ssize_t sysfs_bin_attr_simple_read(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
memcpy(buf, attr->private + off, count);

View File

@ -2,6 +2,7 @@
#ifndef _KUNIT_PLATFORM_DRIVER_H
#define _KUNIT_PLATFORM_DRIVER_H
struct completion;
struct kunit;
struct platform_device;
struct platform_driver;

View File

@ -399,7 +399,23 @@ void __iomem *devm_of_iomap(struct device *dev,
#endif
/* allows to add/remove a custom action to devres stack */
void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
int devm_remove_action_nowarn(struct device *dev, void (*action)(void *), void *data);
/**
* devm_remove_action() - removes previously added custom action
* @dev: Device that owns the action
* @action: Function implementing the action
* @data: Pointer to data passed to @action implementation
*
* Removes instance of @action previously added by devm_add_action().
* Both action and data should match one of the existing entries.
*/
static inline
void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
{
WARN_ON(devm_remove_action_nowarn(dev, action, data));
}
void devm_release_action(struct device *dev, void (*action)(void *), void *data);
int __devm_add_action(struct device *dev, void (*action)(void *), void *data, const char *name);
@ -1074,18 +1090,44 @@ void device_del(struct device *dev);
DEFINE_FREE(device_del, struct device *, if (_T) device_del(_T))
int device_for_each_child(struct device *dev, void *data,
int (*fn)(struct device *dev, void *data));
int device_for_each_child_reverse(struct device *dev, void *data,
int (*fn)(struct device *dev, void *data));
int device_for_each_child(struct device *parent, void *data,
device_iter_t fn);
int device_for_each_child_reverse(struct device *parent, void *data,
device_iter_t fn);
int device_for_each_child_reverse_from(struct device *parent,
struct device *from, const void *data,
int (*fn)(struct device *, const void *));
struct device *device_find_child(struct device *dev, void *data,
int (*match)(struct device *dev, void *data));
struct device *device_find_child_by_name(struct device *parent,
const char *name);
struct device *device_find_any_child(struct device *parent);
struct device *from, void *data,
device_iter_t fn);
struct device *device_find_child(struct device *parent, const void *data,
device_match_t match);
/**
* device_find_child_by_name - device iterator for locating a child device.
* @parent: parent struct device
* @name: name of the child device
*
* This is similar to the device_find_child() function above, but it
* returns a reference to a device that has the name @name.
*
* NOTE: you will need to drop the reference with put_device() after use.
*/
static inline struct device *device_find_child_by_name(struct device *parent,
const char *name)
{
return device_find_child(parent, name, device_match_name);
}
/**
* device_find_any_child - device iterator for locating a child device, if any.
* @parent: parent struct device
*
* This is similar to the device_find_child() function above, but it
* returns a reference to a child device, if any.
*
* NOTE: you will need to drop the reference with put_device() after use.
*/
static inline struct device *device_find_any_child(struct device *parent)
{
return device_find_child(parent, NULL, device_match_any);
}
int device_rename(struct device *dev, const char *new_name);
int device_move(struct device *dev, struct device *new_parent,

View File

@ -134,6 +134,7 @@ typedef int (*device_match_t)(struct device *dev, const void *data);
/* Generic device matching functions that all busses can use to match with */
int device_match_name(struct device *dev, const void *name);
int device_match_type(struct device *dev, const void *type);
int device_match_of_node(struct device *dev, const void *np);
int device_match_fwnode(struct device *dev, const void *fwnode);
int device_match_devt(struct device *dev, const void *pdevt);
@ -141,9 +142,12 @@ int device_match_acpi_dev(struct device *dev, const void *adev);
int device_match_acpi_handle(struct device *dev, const void *handle);
int device_match_any(struct device *dev, const void *unused);
/* Device iterating function type for various driver core for_each APIs */
typedef int (*device_iter_t)(struct device *dev, void *data);
/* iterator helpers for buses */
int bus_for_each_dev(const struct bus_type *bus, struct device *start, void *data,
int (*fn)(struct device *dev, void *data));
int bus_for_each_dev(const struct bus_type *bus, struct device *start,
void *data, device_iter_t fn);
struct device *bus_find_device(const struct bus_type *bus, struct device *start,
const void *data, device_match_t match);
/**

View File

@ -82,18 +82,16 @@ bool class_is_registered(const struct class *class);
struct class_compat;
struct class_compat *class_compat_register(const char *name);
void class_compat_unregister(struct class_compat *cls);
int class_compat_create_link(struct class_compat *cls, struct device *dev,
struct device *device_link);
void class_compat_remove_link(struct class_compat *cls, struct device *dev,
struct device *device_link);
int class_compat_create_link(struct class_compat *cls, struct device *dev);
void class_compat_remove_link(struct class_compat *cls, struct device *dev);
void class_dev_iter_init(struct class_dev_iter *iter, const struct class *class,
const struct device *start, const struct device_type *type);
struct device *class_dev_iter_next(struct class_dev_iter *iter);
void class_dev_iter_exit(struct class_dev_iter *iter);
int class_for_each_device(const struct class *class, const struct device *start, void *data,
int (*fn)(struct device *dev, void *data));
int class_for_each_device(const struct class *class, const struct device *start,
void *data, device_iter_t fn);
struct device *class_find_device(const struct class *class, const struct device *start,
const void *data, device_match_t match);

View File

@ -154,7 +154,7 @@ void driver_remove_file(const struct device_driver *driver,
int driver_set_override(struct device *dev, const char **override,
const char *s, size_t len);
int __must_check driver_for_each_device(struct device_driver *drv, struct device *start,
void *data, int (*fn)(struct device *dev, void *));
void *data, device_iter_t fn);
struct device *driver_find_device(const struct device_driver *drv,
struct device *start, const void *data,
device_match_t match);

View File

@ -438,21 +438,21 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev,
extern const struct bus_type fsl_mc_bus_type;
extern struct device_type fsl_mc_bus_dprc_type;
extern struct device_type fsl_mc_bus_dpni_type;
extern struct device_type fsl_mc_bus_dpio_type;
extern struct device_type fsl_mc_bus_dpsw_type;
extern struct device_type fsl_mc_bus_dpbp_type;
extern struct device_type fsl_mc_bus_dpcon_type;
extern struct device_type fsl_mc_bus_dpmcp_type;
extern struct device_type fsl_mc_bus_dpmac_type;
extern struct device_type fsl_mc_bus_dprtc_type;
extern struct device_type fsl_mc_bus_dpseci_type;
extern struct device_type fsl_mc_bus_dpdmux_type;
extern struct device_type fsl_mc_bus_dpdcei_type;
extern struct device_type fsl_mc_bus_dpaiop_type;
extern struct device_type fsl_mc_bus_dpci_type;
extern struct device_type fsl_mc_bus_dpdmai_type;
extern const struct device_type fsl_mc_bus_dprc_type;
extern const struct device_type fsl_mc_bus_dpni_type;
extern const struct device_type fsl_mc_bus_dpio_type;
extern const struct device_type fsl_mc_bus_dpsw_type;
extern const struct device_type fsl_mc_bus_dpbp_type;
extern const struct device_type fsl_mc_bus_dpcon_type;
extern const struct device_type fsl_mc_bus_dpmcp_type;
extern const struct device_type fsl_mc_bus_dpmac_type;
extern const struct device_type fsl_mc_bus_dprtc_type;
extern const struct device_type fsl_mc_bus_dpseci_type;
extern const struct device_type fsl_mc_bus_dpdmux_type;
extern const struct device_type fsl_mc_bus_dpdcei_type;
extern const struct device_type fsl_mc_bus_dpaiop_type;
extern const struct device_type fsl_mc_bus_dpci_type;
extern const struct device_type fsl_mc_bus_dpdmai_type;
static inline bool is_fsl_mc_bus_dprc(const struct fsl_mc_device *mc_dev)
{

View File

@ -293,7 +293,7 @@ __ATTRIBUTE_GROUPS(_name)
#define BIN_ATTRIBUTE_GROUPS(_name) \
static const struct attribute_group _name##_group = { \
.bin_attrs = _name##_attrs, \
.bin_attrs_new = _name##_attrs, \
}; \
__ATTRIBUTE_GROUPS(_name)
@ -511,7 +511,7 @@ __printf(3, 4)
int sysfs_emit_at(char *buf, int at, const char *fmt, ...);
ssize_t sysfs_bin_attr_simple_read(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count);
#else /* CONFIG_SYSFS */
@ -774,7 +774,7 @@ static inline int sysfs_emit_at(char *buf, int at, const char *fmt, ...)
static inline ssize_t sysfs_bin_attr_simple_read(struct file *file,
struct kobject *kobj,
struct bin_attribute *attr,
const struct bin_attribute *attr,
char *buf, loff_t off,
size_t count)
{

View File

@ -497,8 +497,8 @@ extern void iscsi_destroy_all_flashnode(struct Scsi_Host *shost);
extern int iscsi_flashnode_bus_match(struct device *dev,
const struct device_driver *drv);
extern struct device *
iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data,
int (*fn)(struct device *dev, void *data));
iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data,
device_match_t fn);
extern struct device *
iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess);

View File

@ -8001,17 +8001,6 @@ struct btf_module {
static LIST_HEAD(btf_modules);
static DEFINE_MUTEX(btf_module_mutex);
static ssize_t
btf_module_read(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t len)
{
const struct btf *btf = bin_attr->private;
memcpy(buf, btf->data + off, len);
return len;
}
static void purge_cand_cache(struct btf *btf);
static int btf_module_notify(struct notifier_block *nb, unsigned long op,
@ -8072,8 +8061,8 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
attr->attr.name = btf->name;
attr->attr.mode = 0444;
attr->size = btf->data_size;
attr->private = btf;
attr->read = btf_module_read;
attr->private = btf->data;
attr->read_new = sysfs_bin_attr_simple_read;
err = sysfs_create_bin_file(btf_kobj, attr);
if (err) {

View File

@ -12,24 +12,16 @@
extern char __start_BTF[];
extern char __stop_BTF[];
static ssize_t
btf_vmlinux_read(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t len)
{
memcpy(buf, __start_BTF + off, len);
return len;
}
static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = {
.attr = { .name = "vmlinux", .mode = 0444, },
.read = btf_vmlinux_read,
.read_new = sysfs_bin_attr_simple_read,
};
struct kobject *btf_kobj;
static int __init btf_vmlinux_init(void)
{
bin_attr_btf_vmlinux.private = __start_BTF;
bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF;
if (bin_attr_btf_vmlinux.size == 0)

View File

@ -29,25 +29,12 @@ asm (
extern char kernel_headers_data[];
extern char kernel_headers_data_end[];
static ssize_t
ikheaders_read(struct file *file, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t len)
{
memcpy(buf, &kernel_headers_data[off], len);
return len;
}
static struct bin_attribute kheaders_attr __ro_after_init = {
.attr = {
.name = "kheaders.tar.xz",
.mode = 0444,
},
.read = &ikheaders_read,
};
static struct bin_attribute kheaders_attr __ro_after_init =
__BIN_ATTR_SIMPLE_RO(kheaders.tar.xz, 0444);
static int __init ikheaders_init(void)
{
kheaders_attr.private = kernel_headers_data;
kheaders_attr.size = (kernel_headers_data_end -
kernel_headers_data);
return sysfs_create_bin_file(kernel_kobj, &kheaders_attr);

View File

@ -239,21 +239,7 @@ extern const void __start_notes;
extern const void __stop_notes;
#define notes_size (&__stop_notes - &__start_notes)
static ssize_t notes_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
memcpy(buf, &__start_notes + off, count);
return count;
}
static struct bin_attribute notes_attr __ro_after_init = {
.attr = {
.name = "notes",
.mode = S_IRUGO,
},
.read = &notes_read,
};
static __ro_after_init BIN_ATTR_SIMPLE_RO(notes);
struct kobject *kernel_kobj;
EXPORT_SYMBOL_GPL(kernel_kobj);
@ -307,8 +293,9 @@ static int __init ksysfs_init(void)
goto kset_exit;
if (notes_size > 0) {
notes_attr.size = notes_size;
error = sysfs_create_bin_file(kernel_kobj, &notes_attr);
bin_attr_notes.private = (void *)&__start_notes;
bin_attr_notes.size = notes_size;
error = sysfs_create_bin_file(kernel_kobj, &bin_attr_notes);
if (error)
goto group_exit;
}

View File

@ -190,7 +190,7 @@ static int add_notes_attrs(struct module *mod, const struct load_info *info)
nattr->attr.mode = 0444;
nattr->size = info->sechdrs[i].sh_size;
nattr->private = (void *)info->sechdrs[i].sh_addr;
nattr->read = sysfs_bin_attr_simple_read;
nattr->read_new = sysfs_bin_attr_simple_read;
*(gattr++) = nattr++;
}
++loaded;

View File

@ -1367,7 +1367,7 @@ static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn)
return dsa_switch_parse_ports_of(ds, dn);
}
static int dev_is_class(struct device *dev, void *class)
static int dev_is_class(struct device *dev, const void *class)
{
if (dev->class != NULL && !strcmp(dev->class->name, class))
return 1;

View File

@ -20,8 +20,11 @@
#include <linux/jump_label.h>
#include <linux/mdio.h>
#include <linux/miscdevice.h>
#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/phy.h>
#include <linux/pid_namespace.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/refcount.h>
#include <linux/sched.h>

10
rust/helpers/device.c Normal file
View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
int rust_helper_devm_add_action(struct device *dev,
void (*action)(void *),
void *data)
{
return devm_add_action(dev, action, data);
}

View File

@ -12,14 +12,19 @@
#include "build_assert.c"
#include "build_bug.c"
#include "cred.c"
#include "device.c"
#include "err.c"
#include "fs.c"
#include "io.c"
#include "jump_label.c"
#include "kunit.c"
#include "mutex.c"
#include "page.c"
#include "platform.c"
#include "pci.c"
#include "pid_namespace.c"
#include "rbtree.c"
#include "rcu.c"
#include "refcount.c"
#include "security.c"
#include "signal.c"

101
rust/helpers/io.c Normal file
View File

@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/io.h>
void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size)
{
return ioremap(offset, size);
}
void rust_helper_iounmap(volatile void __iomem *addr)
{
iounmap(addr);
}
u8 rust_helper_readb(const volatile void __iomem *addr)
{
return readb(addr);
}
u16 rust_helper_readw(const volatile void __iomem *addr)
{
return readw(addr);
}
u32 rust_helper_readl(const volatile void __iomem *addr)
{
return readl(addr);
}
#ifdef CONFIG_64BIT
u64 rust_helper_readq(const volatile void __iomem *addr)
{
return readq(addr);
}
#endif
void rust_helper_writeb(u8 value, volatile void __iomem *addr)
{
writeb(value, addr);
}
void rust_helper_writew(u16 value, volatile void __iomem *addr)
{
writew(value, addr);
}
void rust_helper_writel(u32 value, volatile void __iomem *addr)
{
writel(value, addr);
}
#ifdef CONFIG_64BIT
void rust_helper_writeq(u64 value, volatile void __iomem *addr)
{
writeq(value, addr);
}
#endif
u8 rust_helper_readb_relaxed(const volatile void __iomem *addr)
{
return readb_relaxed(addr);
}
u16 rust_helper_readw_relaxed(const volatile void __iomem *addr)
{
return readw_relaxed(addr);
}
u32 rust_helper_readl_relaxed(const volatile void __iomem *addr)
{
return readl_relaxed(addr);
}
#ifdef CONFIG_64BIT
u64 rust_helper_readq_relaxed(const volatile void __iomem *addr)
{
return readq_relaxed(addr);
}
#endif
void rust_helper_writeb_relaxed(u8 value, volatile void __iomem *addr)
{
writeb_relaxed(value, addr);
}
void rust_helper_writew_relaxed(u16 value, volatile void __iomem *addr)
{
writew_relaxed(value, addr);
}
void rust_helper_writel_relaxed(u32 value, volatile void __iomem *addr)
{
writel_relaxed(value, addr);
}
#ifdef CONFIG_64BIT
void rust_helper_writeq_relaxed(u64 value, volatile void __iomem *addr)
{
writeq_relaxed(value, addr);
}
#endif

18
rust/helpers/pci.c Normal file
View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/pci.h>
void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data)
{
pci_set_drvdata(pdev, data);
}
void *rust_helper_pci_get_drvdata(struct pci_dev *pdev)
{
return pci_get_drvdata(pdev);
}
resource_size_t rust_helper_pci_resource_len(struct pci_dev *pdev, int bar)
{
return pci_resource_len(pdev, bar);
}

13
rust/helpers/platform.c Normal file
View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/platform_device.h>
void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
{
return platform_get_drvdata(pdev);
}
void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
{
platform_set_drvdata(pdev, data);
}

13
rust/helpers/rcu.c Normal file
View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/rcupdate.h>
void rust_helper_rcu_read_lock(void)
{
rcu_read_lock();
}
void rust_helper_rcu_read_unlock(void)
{
rcu_read_unlock();
}

165
rust/kernel/device_id.rs Normal file
View File

@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0
//! Generic implementation of device IDs.
//!
//! Each bus / subsystem that matches device and driver through a bus / subsystem specific ID is
//! expected to implement [`RawDeviceId`].
use core::mem::MaybeUninit;
/// Marker trait to indicate a Rust device ID type represents a corresponding C device ID type.
///
/// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
/// guarantee (at compile-time) zero-termination of device id tables provided by drivers.
///
/// # Safety
///
/// Implementers must ensure that:
/// - `Self` is layout-compatible with [`RawDeviceId::RawType`]; i.e. it's safe to transmute to
/// `RawDeviceId`.
///
/// This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building
/// the ID table.
///
/// Ideally, this should be achieved using a const function that does conversion instead of
/// transmute; however, const trait functions relies on `const_trait_impl` unstable feature,
/// which is broken/gone in Rust 1.73.
///
/// - `DRIVER_DATA_OFFSET` is the offset of context/data field of the device ID (usually named
/// `driver_data`) of the device ID, the field is suitable sized to write a `usize` value.
///
/// Similar to the previous requirement, the data should ideally be added during `Self` to
/// `RawType` conversion, but there's currently no way to do it when using traits in const.
pub unsafe trait RawDeviceId {
/// The raw type that holds the device id.
///
/// Id tables created from [`Self`] are going to hold this type in its zero-terminated array.
type RawType: Copy;
/// The offset to the context/data field.
const DRIVER_DATA_OFFSET: usize;
/// The index stored at `DRIVER_DATA_OFFSET` of the implementor of the [`RawDeviceId`] trait.
fn index(&self) -> usize;
}
/// A zero-terminated device id array.
#[repr(C)]
pub struct RawIdArray<T: RawDeviceId, const N: usize> {
ids: [T::RawType; N],
sentinel: MaybeUninit<T::RawType>,
}
impl<T: RawDeviceId, const N: usize> RawIdArray<T, N> {
#[doc(hidden)]
pub const fn size(&self) -> usize {
core::mem::size_of::<Self>()
}
}
/// A zero-terminated device id array, followed by context data.
#[repr(C)]
pub struct IdArray<T: RawDeviceId, U, const N: usize> {
raw_ids: RawIdArray<T, N>,
id_infos: [U; N],
}
impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> {
/// Creates a new instance of the array.
///
/// The contents are derived from the given identifiers and context information.
pub const fn new(ids: [(T, U); N]) -> Self {
let mut raw_ids = [const { MaybeUninit::<T::RawType>::uninit() }; N];
let mut infos = [const { MaybeUninit::uninit() }; N];
let mut i = 0usize;
while i < N {
// SAFETY: by the safety requirement of `RawDeviceId`, we're guaranteed that `T` is
// layout-wise compatible with `RawType`.
raw_ids[i] = unsafe { core::mem::transmute_copy(&ids[i].0) };
// SAFETY: by the safety requirement of `RawDeviceId`, this would be effectively
// `raw_ids[i].driver_data = i;`.
unsafe {
raw_ids[i]
.as_mut_ptr()
.byte_offset(T::DRIVER_DATA_OFFSET as _)
.cast::<usize>()
.write(i);
}
// SAFETY: this is effectively a move: `infos[i] = ids[i].1`. We make a copy here but
// later forget `ids`.
infos[i] = MaybeUninit::new(unsafe { core::ptr::read(&ids[i].1) });
i += 1;
}
core::mem::forget(ids);
Self {
raw_ids: RawIdArray {
// SAFETY: this is effectively `array_assume_init`, which is unstable, so we use
// `transmute_copy` instead. We have initialized all elements of `raw_ids` so this
// `array_assume_init` is safe.
ids: unsafe { core::mem::transmute_copy(&raw_ids) },
sentinel: MaybeUninit::zeroed(),
},
// SAFETY: We have initialized all elements of `infos` so this `array_assume_init` is
// safe.
id_infos: unsafe { core::mem::transmute_copy(&infos) },
}
}
/// Reference to the contained [`RawIdArray`].
pub const fn raw_ids(&self) -> &RawIdArray<T, N> {
&self.raw_ids
}
}
/// A device id table.
///
/// This trait is only implemented by `IdArray`.
///
/// The purpose of this trait is to allow `&'static dyn IdArray<T, U>` to be in context when `N` in
/// `IdArray` doesn't matter.
pub trait IdTable<T: RawDeviceId, U> {
/// Obtain the pointer to the ID table.
fn as_ptr(&self) -> *const T::RawType;
/// Obtain the pointer to the bus specific device ID from an index.
fn id(&self, index: usize) -> &T::RawType;
/// Obtain the pointer to the driver-specific information from an index.
fn info(&self, index: usize) -> &U;
}
impl<T: RawDeviceId, U, const N: usize> IdTable<T, U> for IdArray<T, U, N> {
fn as_ptr(&self) -> *const T::RawType {
// This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance
// to access the sentinel.
(self as *const Self).cast()
}
fn id(&self, index: usize) -> &T::RawType {
&self.raw_ids.ids[index]
}
fn info(&self, index: usize) -> &U {
&self.id_infos[index]
}
}
/// Create device table alias for modpost.
#[macro_export]
macro_rules! module_device_table {
($table_type: literal, $module_table_name:ident, $table_name:ident) => {
#[rustfmt::skip]
#[export_name =
concat!("__mod_device_table__", $table_type,
"__", module_path!(),
"_", line!(),
"_", stringify!($table_name))
]
static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
};
}

201
rust/kernel/devres.rs Normal file
View File

@ -0,0 +1,201 @@
// SPDX-License-Identifier: GPL-2.0
//! Devres abstraction
//!
//! [`Devres`] represents an abstraction for the kernel devres (device resource management)
//! implementation.
use crate::{
alloc::Flags,
bindings,
device::Device,
error::{Error, Result},
ffi::c_void,
prelude::*,
revocable::Revocable,
sync::Arc,
types::ARef,
};
use core::ops::Deref;
#[pin_data]
struct DevresInner<T> {
dev: ARef<Device>,
callback: unsafe extern "C" fn(*mut c_void),
#[pin]
data: Revocable<T>,
}
/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
/// manage their lifetime.
///
/// [`Device`] bound resources should be freed when either the resource goes out of scope or the
/// [`Device`] is unbound respectively, depending on what happens first.
///
/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the
/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]).
///
/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource
/// anymore.
///
/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
/// [`Drop`] implementation.
///
/// # Example
///
/// ```no_run
/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::{Io, IoRaw}};
/// # use core::ops::Deref;
///
/// // See also [`pci::Bar`] for a real example.
/// struct IoMem<const SIZE: usize>(IoRaw<SIZE>);
///
/// impl<const SIZE: usize> IoMem<SIZE> {
/// /// # Safety
/// ///
/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
/// /// virtual address space.
/// unsafe fn new(paddr: usize) -> Result<Self>{
/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
/// // valid for `ioremap`.
/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE as _) };
/// if addr.is_null() {
/// return Err(ENOMEM);
/// }
///
/// Ok(IoMem(IoRaw::new(addr as _, SIZE)?))
/// }
/// }
///
/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
/// fn drop(&mut self) {
/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
/// unsafe { bindings::iounmap(self.0.addr() as _); };
/// }
/// }
///
/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
/// type Target = Io<SIZE>;
///
/// fn deref(&self) -> &Self::Target {
/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
/// 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()) };
///
/// // 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 res = devres.try_access().ok_or(ENXIO)?;
/// res.writel(0x42, 0x0);
/// # Ok(())
/// # }
/// ```
pub struct Devres<T>(Arc<DevresInner<T>>);
impl<T> DevresInner<T> {
fn new(dev: &Device, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
let inner = Arc::pin_init(
pin_init!( DevresInner {
dev: dev.into(),
callback: Self::devres_callback,
data <- Revocable::new(data),
}),
flags,
)?;
// Convert `Arc<DevresInner>` into a raw pointer and make devres own this reference until
// `Self::devres_callback` is called.
let data = inner.clone().into_raw();
// SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is
// detached.
let ret =
unsafe { bindings::devm_add_action(dev.as_raw(), Some(inner.callback), data as _) };
if ret != 0 {
// SAFETY: We just created another reference to `inner` in order to pass it to
// `bindings::devm_add_action`. If `bindings::devm_add_action` fails, we have to drop
// this reference accordingly.
let _ = unsafe { Arc::from_raw(data) };
return Err(Error::from_errno(ret));
}
Ok(inner)
}
fn as_ptr(&self) -> *const Self {
self as _
}
fn remove_action(this: &Arc<Self>) {
// SAFETY:
// - `self.inner.dev` is a valid `Device`,
// - the `action` and `data` pointers are the exact same ones as given to devm_add_action()
// previously,
// - `self` is always valid, even if the action has been released already.
let ret = unsafe {
bindings::devm_remove_action_nowarn(
this.dev.as_raw(),
Some(this.callback),
this.as_ptr() as _,
)
};
if ret == 0 {
// SAFETY: We leaked an `Arc` reference to devm_add_action() in `DevresInner::new`; if
// devm_remove_action_nowarn() was successful we can (and have to) claim back ownership
// of this reference.
let _ = unsafe { Arc::from_raw(this.as_ptr()) };
}
}
#[allow(clippy::missing_safety_doc)]
unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) {
let ptr = ptr as *mut DevresInner<T>;
// Devres owned this memory; now that we received the callback, drop the `Arc` and hence the
// reference.
// SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in
// `DevresInner::new`.
let inner = unsafe { Arc::from_raw(ptr) };
inner.data.revoke();
}
}
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> {
let inner = DevresInner::new(dev, data, flags)?;
Ok(Devres(inner))
}
/// 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 {
let _ = DevresInner::new(dev, data, flags)?;
Ok(())
}
}
impl<T> Deref for Devres<T> {
type Target = Revocable<T>;
fn deref(&self) -> &Self::Target {
&self.0.data
}
}
impl<T> Drop for Devres<T> {
fn drop(&mut self) {
DevresInner::remove_action(&self.0);
}
}

188
rust/kernel/driver.rs Normal file
View File

@ -0,0 +1,188 @@
// SPDX-License-Identifier: GPL-2.0
//! Generic support for drivers of different buses (e.g., PCI, Platform, Amba, etc.).
//!
//! Each bus / subsystem is expected to implement [`RegistrationOps`], which allows drivers to
//! register using the [`Registration`] class.
use crate::error::{Error, Result};
use crate::{device, init::PinInit, of, str::CStr, try_pin_init, types::Opaque, ThisModule};
use core::pin::Pin;
use macros::{pin_data, pinned_drop};
/// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform,
/// Amba, etc.) to provide the corresponding subsystem specific implementation to register /
/// unregister a driver of the particular type (`RegType`).
///
/// For instance, the PCI subsystem would set `RegType` to `bindings::pci_driver` and call
/// `bindings::__pci_register_driver` from `RegistrationOps::register` and
/// `bindings::pci_unregister_driver` from `RegistrationOps::unregister`.
///
/// # Safety
///
/// A call to [`RegistrationOps::unregister`] for a given instance of `RegType` is only valid if a
/// preceding call to [`RegistrationOps::register`] has been successful.
pub unsafe trait RegistrationOps {
/// The type that holds information about the registration. This is typically a struct defined
/// by the C portion of the kernel.
type RegType: Default;
/// Registers a driver.
///
/// # Safety
///
/// On success, `reg` must remain pinned and valid until the matching call to
/// [`RegistrationOps::unregister`].
unsafe fn register(
reg: &Opaque<Self::RegType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result;
/// Unregisters a driver previously registered with [`RegistrationOps::register`].
///
/// # Safety
///
/// Must only be called after a preceding successful call to [`RegistrationOps::register`] for
/// the same `reg`.
unsafe fn unregister(reg: &Opaque<Self::RegType>);
}
/// A [`Registration`] is a generic type that represents the registration of some driver type (e.g.
/// `bindings::pci_driver`). Therefore a [`Registration`] must be initialized with a type that
/// implements the [`RegistrationOps`] trait, such that the generic `T::register` and
/// `T::unregister` calls result in the subsystem specific registration calls.
///
///Once the `Registration` structure is dropped, the driver is unregistered.
#[pin_data(PinnedDrop)]
pub struct Registration<T: RegistrationOps> {
#[pin]
reg: Opaque<T::RegType>,
}
// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to
// share references to it with multiple threads as nothing can be done.
unsafe impl<T: RegistrationOps> Sync for Registration<T> {}
// SAFETY: Both registration and unregistration are implemented in C and safe to be performed from
// any thread, so `Registration` is `Send`.
unsafe impl<T: RegistrationOps> Send for Registration<T> {}
impl<T: RegistrationOps> Registration<T> {
/// Creates a new instance of the registration object.
pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit<Self, Error> {
try_pin_init!(Self {
reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| {
// SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write.
unsafe { ptr.write(T::RegType::default()) };
// SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write, and it has
// just been initialised above, so it's also valid for read.
let drv = unsafe { &*(ptr as *const Opaque<T::RegType>) };
// SAFETY: `drv` is guaranteed to be pinned until `T::unregister`.
unsafe { T::register(drv, name, module) }
}),
})
}
}
#[pinned_drop]
impl<T: RegistrationOps> PinnedDrop for Registration<T> {
fn drop(self: Pin<&mut Self>) {
// SAFETY: The existence of `self` guarantees that `self.reg` has previously been
// successfully registered with `T::register`
unsafe { T::unregister(&self.reg) };
}
}
/// Declares a kernel module that exposes a single driver.
///
/// It is meant to be used as a helper by other subsystems so they can more easily expose their own
/// macros.
#[macro_export]
macro_rules! module_driver {
(<$gen_type:ident>, $driver_ops:ty, { type: $type:ty, $($f:tt)* }) => {
type Ops<$gen_type> = $driver_ops;
#[$crate::prelude::pin_data]
struct DriverModule {
#[pin]
_driver: $crate::driver::Registration<Ops<$type>>,
}
impl $crate::InPlaceModule for DriverModule {
fn init(
module: &'static $crate::ThisModule
) -> impl $crate::init::PinInit<Self, $crate::error::Error> {
$crate::try_pin_init!(Self {
_driver <- $crate::driver::Registration::new(
<Self as $crate::ModuleMetadata>::NAME,
module,
),
})
}
}
$crate::prelude::module! {
type: DriverModule,
$($f)*
}
}
}
/// The bus independent adapter to match a drivers and a devices.
///
/// This trait should be implemented by the bus specific adapter, which represents the connection
/// of a device and a driver.
///
/// It provides bus independent functions for device / driver interactions.
pub trait Adapter {
/// The type holding driver private data about each device id supported by the driver.
type IdInfo: 'static;
/// The [`of::IdTable`] of the corresponding driver.
fn of_id_table() -> Option<of::IdTable<Self::IdInfo>>;
/// Returns the driver's private data from the matching entry in the [`of::IdTable`], if any.
///
/// If this returns `None`, it means there is no match with an entry in the [`of::IdTable`].
#[cfg(CONFIG_OF)]
fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
let table = Self::of_id_table()?;
// SAFETY:
// - `table` has static lifetime, hence it's valid for read,
// - `dev` is guaranteed to be valid while it's alive, and so is `pdev.as_ref().as_raw()`.
let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) };
if raw_id.is_null() {
None
} else {
// SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and
// does not add additional invariants, so it's safe to transmute.
let id = unsafe { &*raw_id.cast::<of::DeviceId>() };
Some(table.info(<of::DeviceId as crate::device_id::RawDeviceId>::index(id)))
}
}
#[cfg(not(CONFIG_OF))]
#[allow(missing_docs)]
fn of_id_info(_dev: &device::Device) -> Option<&'static Self::IdInfo> {
None
}
/// Returns the driver's private data from the matching entry of any of the ID tables, if any.
///
/// If this returns `None`, it means that there is no match in any of the ID tables directly
/// associated with a [`device::Device`].
fn id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> {
let id = Self::of_id_info(dev);
if id.is_some() {
return id;
}
None
}
}

260
rust/kernel/io.rs Normal file
View File

@ -0,0 +1,260 @@
// SPDX-License-Identifier: GPL-2.0
//! Memory-mapped IO.
//!
//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
use crate::error::{code::EINVAL, Result};
use crate::{bindings, build_assert};
/// Raw representation of an MMIO region.
///
/// By itself, the existence of an instance of this structure does not provide any guarantees that
/// the represented MMIO region does exist or is properly mapped.
///
/// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io`
/// instance providing the actual memory accessors. Only by the conversion into an `Io` structure
/// any guarantees are given.
pub struct IoRaw<const SIZE: usize = 0> {
addr: usize,
maxsize: usize,
}
impl<const SIZE: usize> IoRaw<SIZE> {
/// Returns a new `IoRaw` instance on success, an error otherwise.
pub fn new(addr: usize, maxsize: usize) -> Result<Self> {
if maxsize < SIZE {
return Err(EINVAL);
}
Ok(Self { addr, maxsize })
}
/// Returns the base address of the MMIO region.
#[inline]
pub fn addr(&self) -> usize {
self.addr
}
/// Returns the maximum size of the MMIO region.
#[inline]
pub fn maxsize(&self) -> usize {
self.maxsize
}
}
/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes.
///
/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the
/// mapping, performing an additional region request etc.
///
/// # Invariant
///
/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size
/// `maxsize`.
///
/// # Examples
///
/// ```no_run
/// # use kernel::{bindings, io::{Io, IoRaw}};
/// # use core::ops::Deref;
///
/// // See also [`pci::Bar`] for a real example.
/// struct IoMem<const SIZE: usize>(IoRaw<SIZE>);
///
/// impl<const SIZE: usize> IoMem<SIZE> {
/// /// # Safety
/// ///
/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
/// /// virtual address space.
/// unsafe fn new(paddr: usize) -> Result<Self>{
/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
/// // valid for `ioremap`.
/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE as _) };
/// if addr.is_null() {
/// return Err(ENOMEM);
/// }
///
/// Ok(IoMem(IoRaw::new(addr as _, SIZE)?))
/// }
/// }
///
/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
/// fn drop(&mut self) {
/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
/// unsafe { bindings::iounmap(self.0.addr() as _); };
/// }
/// }
///
/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
/// type Target = Io<SIZE>;
///
/// fn deref(&self) -> &Self::Target {
/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
/// unsafe { Io::from_raw(&self.0) }
/// }
/// }
///
///# fn no_run() -> Result<(), Error> {
/// // SAFETY: Invalid usage for example purposes.
/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
/// iomem.writel(0x42, 0x0);
/// assert!(iomem.try_writel(0x42, 0x0).is_ok());
/// assert!(iomem.try_writel(0x42, 0x4).is_err());
/// # Ok(())
/// # }
/// ```
#[repr(transparent)]
pub struct Io<const SIZE: usize = 0>(IoRaw<SIZE>);
macro_rules! define_read {
($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => {
/// Read IO data from a given offset known at compile time.
///
/// Bound checks are performed on compile time, hence if the offset is not known at compile
/// time, the build will fail.
$(#[$attr])*
#[inline]
pub fn $name(&self, offset: usize) -> $type_name {
let addr = self.io_addr_assert::<$type_name>(offset);
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
unsafe { bindings::$name(addr as _) }
}
/// Read IO data from a given offset.
///
/// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
/// out of bounds.
$(#[$attr])*
pub fn $try_name(&self, offset: usize) -> Result<$type_name> {
let addr = self.io_addr::<$type_name>(offset)?;
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
Ok(unsafe { bindings::$name(addr as _) })
}
};
}
macro_rules! define_write {
($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => {
/// Write IO data from a given offset known at compile time.
///
/// Bound checks are performed on compile time, hence if the offset is not known at compile
/// time, the build will fail.
$(#[$attr])*
#[inline]
pub fn $name(&self, value: $type_name, offset: usize) {
let addr = self.io_addr_assert::<$type_name>(offset);
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
unsafe { bindings::$name(value, addr as _, ) }
}
/// Write IO data from a given offset.
///
/// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
/// out of bounds.
$(#[$attr])*
pub fn $try_name(&self, value: $type_name, offset: usize) -> Result {
let addr = self.io_addr::<$type_name>(offset)?;
// SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
unsafe { bindings::$name(value, addr as _) }
Ok(())
}
};
}
impl<const SIZE: usize> Io<SIZE> {
/// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping.
///
/// # Safety
///
/// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
/// `maxsize`.
pub unsafe fn from_raw(raw: &IoRaw<SIZE>) -> &Self {
// SAFETY: `Io` is a transparent wrapper around `IoRaw`.
unsafe { &*core::ptr::from_ref(raw).cast() }
}
/// Returns the base address of this mapping.
#[inline]
pub fn addr(&self) -> usize {
self.0.addr()
}
/// Returns the maximum size of this mapping.
#[inline]
pub fn maxsize(&self) -> usize {
self.0.maxsize()
}
#[inline]
const fn offset_valid<U>(offset: usize, size: usize) -> bool {
let type_size = core::mem::size_of::<U>();
if let Some(end) = offset.checked_add(type_size) {
end <= size && offset % type_size == 0
} else {
false
}
}
#[inline]
fn io_addr<U>(&self, offset: usize) -> Result<usize> {
if !Self::offset_valid::<U>(offset, self.maxsize()) {
return Err(EINVAL);
}
// Probably no need to check, since the safety requirements of `Self::new` guarantee that
// this can't overflow.
self.addr().checked_add(offset).ok_or(EINVAL)
}
#[inline]
fn io_addr_assert<U>(&self, offset: usize) -> usize {
build_assert!(Self::offset_valid::<U>(offset, SIZE));
self.addr() + offset
}
define_read!(readb, try_readb, u8);
define_read!(readw, try_readw, u16);
define_read!(readl, try_readl, u32);
define_read!(
#[cfg(CONFIG_64BIT)]
readq,
try_readq,
u64
);
define_read!(readb_relaxed, try_readb_relaxed, u8);
define_read!(readw_relaxed, try_readw_relaxed, u16);
define_read!(readl_relaxed, try_readl_relaxed, u32);
define_read!(
#[cfg(CONFIG_64BIT)]
readq_relaxed,
try_readq_relaxed,
u64
);
define_write!(writeb, try_writeb, u8);
define_write!(writew, try_writew, u16);
define_write!(writel, try_writel, u32);
define_write!(
#[cfg(CONFIG_64BIT)]
writeq,
try_writeq,
u64
);
define_write!(writeb_relaxed, try_writeb_relaxed, u8);
define_write!(writew_relaxed, try_writew_relaxed, u16);
define_write!(writel_relaxed, try_writel_relaxed, u32);
define_write!(
#[cfg(CONFIG_64BIT)]
writeq_relaxed,
try_writeq_relaxed,
u64
);
}

View File

@ -18,6 +18,11 @@
#![feature(inline_const)]
#![feature(lint_reasons)]
#![feature(unsize)]
// Stable in Rust 1.83
#![feature(const_maybe_uninit_as_mut_ptr)]
#![feature(const_mut_refs)]
#![feature(const_ptr_write)]
#![feature(const_refs_to_cell)]
// Ensure conditional compilation based on the kernel configuration works;
// otherwise we may silently break things like initcall handling.
@ -35,11 +40,15 @@ pub mod block;
mod build_assert;
pub mod cred;
pub mod device;
pub mod device_id;
pub mod devres;
pub mod driver;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
pub mod firmware;
pub mod fs;
pub mod init;
pub mod io;
pub mod ioctl;
pub mod jump_label;
#[cfg(CONFIG_KUNIT)]
@ -48,11 +57,16 @@ pub mod list;
pub mod miscdevice;
#[cfg(CONFIG_NET)]
pub mod net;
pub mod of;
pub mod page;
#[cfg(CONFIG_PCI)]
pub mod pci;
pub mod pid_namespace;
pub mod platform;
pub mod prelude;
pub mod print;
pub mod rbtree;
pub mod revocable;
pub mod security;
pub mod seq_file;
pub mod sizes;
@ -116,6 +130,12 @@ impl<T: Module> InPlaceModule for T {
}
}
/// Metadata attached to a [`Module`] or [`InPlaceModule`].
pub trait ModuleMetadata {
/// The name of the module as specified in the `module!` macro.
const NAME: &'static crate::str::CStr;
}
/// Equivalent to `THIS_MODULE` in the C API.
///
/// C header: [`include/linux/init.h`](srctree/include/linux/init.h)

View File

@ -10,8 +10,11 @@
use crate::{
bindings,
device::Device,
error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR},
fs::File,
prelude::*,
seq_file::SeqFile,
str::CStr,
types::{ForeignOwnable, Opaque},
};
@ -84,6 +87,16 @@ impl<T: MiscDevice> MiscDeviceRegistration<T> {
pub fn as_raw(&self) -> *mut bindings::miscdevice {
self.inner.get()
}
/// Access the `this_device` field.
pub fn device(&self) -> &Device {
// SAFETY: This can only be called after a successful register(), which always
// initialises `this_device` with a valid device. Furthermore, the signature of this
// function tells the borrow-checker that the `&Device` reference must not outlive the
// `&MiscDeviceRegistration<T>` used to obtain it, so the last use of the reference must be
// before the underlying `struct miscdevice` is destroyed.
unsafe { Device::as_ref((*self.as_raw()).this_device) }
}
}
#[pinned_drop]
@ -96,17 +109,17 @@ impl<T> PinnedDrop for MiscDeviceRegistration<T> {
/// Trait implemented by the private data of an open misc device.
#[vtable]
pub trait MiscDevice {
pub trait MiscDevice: Sized {
/// What kind of pointer should `Self` be wrapped in.
type Ptr: ForeignOwnable + Send + Sync;
/// Called when the misc device is opened.
///
/// The returned pointer will be stored as the private data for the file.
fn open() -> Result<Self::Ptr>;
fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>;
/// Called when the misc device is released.
fn release(device: Self::Ptr) {
fn release(device: Self::Ptr, _file: &File) {
drop(device);
}
@ -117,10 +130,11 @@ pub trait MiscDevice {
/// [`kernel::ioctl`]: mod@crate::ioctl
fn ioctl(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
) -> Result<isize> {
kernel::build_error(VTABLE_DEFAULT_ERROR)
kernel::build_error!(VTABLE_DEFAULT_ERROR)
}
/// Handler for ioctls.
@ -133,10 +147,20 @@ pub trait MiscDevice {
#[cfg(CONFIG_COMPAT)]
fn compat_ioctl(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
) -> Result<isize> {
kernel::build_error(VTABLE_DEFAULT_ERROR)
kernel::build_error!(VTABLE_DEFAULT_ERROR)
}
/// Show info for this fd.
fn show_fdinfo(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_m: &SeqFile,
_file: &File,
) {
kernel::build_error!(VTABLE_DEFAULT_ERROR)
}
}
@ -165,6 +189,7 @@ const fn create_vtable<T: MiscDevice>() -> &'static bindings::file_operations {
} else {
None
},
show_fdinfo: maybe_fn(T::HAS_SHOW_FDINFO, fops_show_fdinfo::<T>),
// SAFETY: All zeros is a valid value for `bindings::file_operations`.
..unsafe { MaybeUninit::zeroed().assume_init() }
};
@ -179,21 +204,38 @@ const fn create_vtable<T: MiscDevice>() -> &'static bindings::file_operations {
/// The file must be associated with a `MiscDeviceRegistration<T>`.
unsafe extern "C" fn fops_open<T: MiscDevice>(
inode: *mut bindings::inode,
file: *mut bindings::file,
raw_file: *mut bindings::file,
) -> c_int {
// SAFETY: The pointers are valid and for a file being opened.
let ret = unsafe { bindings::generic_file_open(inode, file) };
let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
if ret != 0 {
return ret;
}
let ptr = match T::open() {
// SAFETY: The open call of a file can access the private data.
let misc_ptr = unsafe { (*raw_file).private_data };
// SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
// associated `struct miscdevice` before calling into this method. Furthermore, `misc_open()`
// ensures that the miscdevice can't be unregistered and freed during this call to `fops_open`.
let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
// SAFETY:
// * This underlying file is valid for (much longer than) the duration of `T::open`.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(raw_file) };
let ptr = match T::open(file, misc) {
Ok(ptr) => ptr,
Err(err) => return err.to_errno(),
};
// SAFETY: The open call of a file owns the private data.
unsafe { (*file).private_data = ptr.into_foreign().cast_mut() };
// This overwrites the private data with the value specified by the user, changing the type of
// this file's private data. All future accesses to the private data is performed by other
// fops_* methods in this file, which all correctly cast the private data to the new type.
//
// SAFETY: The open call of a file can access the private data.
unsafe { (*raw_file).private_data = ptr.into_foreign().cast_mut() };
0
}
@ -211,7 +253,10 @@ unsafe extern "C" fn fops_release<T: MiscDevice>(
// SAFETY: The release call of a file owns the private data.
let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
T::release(ptr);
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
T::release(ptr, unsafe { File::from_raw_file(file) });
0
}
@ -229,7 +274,12 @@ unsafe extern "C" fn fops_ioctl<T: MiscDevice>(
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
match T::ioctl(device, cmd, arg as usize) {
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
match T::ioctl(device, file, cmd, arg as usize) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
@ -249,8 +299,36 @@ unsafe extern "C" fn fops_compat_ioctl<T: MiscDevice>(
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
match T::compat_ioctl(device, cmd, arg as usize) {
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
match T::compat_ioctl(device, file, cmd, arg as usize) {
Ok(ret) => ret as c_long,
Err(err) => err.to_errno() as c_long,
}
}
/// # Safety
///
/// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
/// - `seq_file` must be a valid `struct seq_file` that we can write to.
unsafe extern "C" fn fops_show_fdinfo<T: MiscDevice>(
seq_file: *mut bindings::seq_file,
file: *mut bindings::file,
) {
// SAFETY: The release call of a file owns the private data.
let private = unsafe { (*file).private_data };
// SAFETY: Ioctl calls can borrow the private data of the file.
let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
// SAFETY:
// * The file is valid for the duration of this call.
// * There is no active fdget_pos region on the file on this thread.
let file = unsafe { File::from_raw_file(file) };
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
// this method is called.
let m = unsafe { SeqFile::from_raw(seq_file) };
T::show_fdinfo(device, m, file);
}

60
rust/kernel/of.rs Normal file
View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0
//! Device Tree / Open Firmware abstractions.
use crate::{bindings, device_id::RawDeviceId, prelude::*};
/// IdTable type for OF drivers.
pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
/// An open firmware device id.
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct DeviceId(bindings::of_device_id);
// SAFETY:
// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add
// additional invariants, so it's safe to transmute to `RawType`.
// * `DRIVER_DATA_OFFSET` is the offset to the `data` field.
unsafe impl RawDeviceId for DeviceId {
type RawType = bindings::of_device_id;
const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data);
fn index(&self) -> usize {
self.0.data as _
}
}
impl DeviceId {
/// Create a new device id from an OF 'compatible' string.
pub const fn new(compatible: &'static CStr) -> Self {
let src = compatible.as_bytes_with_nul();
// Replace with `bindings::of_device_id::default()` once stabilized for `const`.
// SAFETY: FFI type is valid to be zero-initialized.
let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() };
// TODO: Use `clone_from_slice` once the corresponding types do match.
let mut i = 0;
while i < src.len() {
of.compatible[i] = src[i] as _;
i += 1;
}
Self(of)
}
}
/// Create an OF `IdTable` with an "alias" for modpost.
#[macro_export]
macro_rules! of_device_table {
($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
const $table_name: $crate::device_id::IdArray<
$crate::of::DeviceId,
$id_info_type,
{ $table_data.len() },
> = $crate::device_id::IdArray::new($table_data);
$crate::module_device_table!("of", $module_table_name, $table_name);
};
}

434
rust/kernel/pci.rs Normal file
View File

@ -0,0 +1,434 @@
// SPDX-License-Identifier: GPL-2.0
//! Abstractions for the PCI bus.
//!
//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h)
use crate::{
alloc::flags::*,
bindings, container_of, device,
device_id::RawDeviceId,
devres::Devres,
driver,
error::{to_result, Result},
io::Io,
io::IoRaw,
str::CStr,
types::{ARef, ForeignOwnable, Opaque},
ThisModule,
};
use core::{ops::Deref, ptr::addr_of_mut};
use kernel::prelude::*;
/// An adapter for the registration of PCI 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::pci_driver;
unsafe fn register(
pdrv: &Opaque<Self::RegType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
// SAFETY: It's safe to set the fields of `struct pci_driver` on initialization.
unsafe {
(*pdrv.get()).name = name.as_char_ptr();
(*pdrv.get()).probe = Some(Self::probe_callback);
(*pdrv.get()).remove = Some(Self::remove_callback);
(*pdrv.get()).id_table = T::ID_TABLE.as_ptr();
}
// SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
to_result(unsafe {
bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr())
})
}
unsafe fn unregister(pdrv: &Opaque<Self::RegType>) {
// SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
unsafe { bindings::pci_unregister_driver(pdrv.get()) }
}
}
impl<T: Driver + 'static> Adapter<T> {
extern "C" fn probe_callback(
pdev: *mut bindings::pci_dev,
id: *const bindings::pci_device_id,
) -> kernel::ffi::c_int {
// SAFETY: The PCI bus only ever calls the probe callback with a valid pointer to a
// `struct pci_dev`.
let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) };
// SAFETY: `dev` is guaranteed to be embedded in a valid `struct pci_dev` by the call
// above.
let mut pdev = unsafe { Device::from_dev(dev) };
// SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_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(&mut pdev, info) {
Ok(data) => {
// Let the `struct pci_dev` own a reference of the driver's private data.
// SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
// `struct pci_dev`.
unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
}
Err(err) => return Error::to_errno(err),
}
0
}
extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
// SAFETY: The PCI bus only ever calls the remove callback with a valid pointer to a
// `struct pci_dev`.
let ptr = unsafe { bindings::pci_get_drvdata(pdev) };
// 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`.
let _ = unsafe { KBox::<T>::from_foreign(ptr) };
}
}
/// Declares a kernel module that exposes a single PCI driver.
///
/// # Example
///
///```ignore
/// kernel::module_pci_driver! {
/// type: MyDriver,
/// name: "Module name",
/// author: "Author name",
/// description: "Description",
/// license: "GPL v2",
/// }
///```
#[macro_export]
macro_rules! module_pci_driver {
($($f:tt)*) => {
$crate::module_driver!(<T>, $crate::pci::Adapter<T>, { $($f)* });
};
}
/// Abstraction for bindings::pci_device_id.
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct DeviceId(bindings::pci_device_id);
impl DeviceId {
const PCI_ANY_ID: u32 = !0;
/// Equivalent to C's `PCI_DEVICE` macro.
///
/// Create a new `pci::DeviceId` from a vendor and device ID number.
pub const fn from_id(vendor: u32, device: u32) -> Self {
Self(bindings::pci_device_id {
vendor,
device,
subvendor: DeviceId::PCI_ANY_ID,
subdevice: DeviceId::PCI_ANY_ID,
class: 0,
class_mask: 0,
driver_data: 0,
override_only: 0,
})
}
/// Equivalent to C's `PCI_DEVICE_CLASS` macro.
///
/// Create a new `pci::DeviceId` from a class number and mask.
pub const fn from_class(class: u32, class_mask: u32) -> Self {
Self(bindings::pci_device_id {
vendor: DeviceId::PCI_ANY_ID,
device: DeviceId::PCI_ANY_ID,
subvendor: DeviceId::PCI_ANY_ID,
subdevice: DeviceId::PCI_ANY_ID,
class,
class_mask,
driver_data: 0,
override_only: 0,
})
}
}
// SAFETY:
// * `DeviceId` is a `#[repr(transparent)` wrapper of `pci_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::pci_device_id;
const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::pci_device_id, driver_data);
fn index(&self) -> usize {
self.0.driver_data as _
}
}
/// IdTable type for PCI
pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
/// Create a PCI `IdTable` with its alias for modpost.
#[macro_export]
macro_rules! pci_device_table {
($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
const $table_name: $crate::device_id::IdArray<
$crate::pci::DeviceId,
$id_info_type,
{ $table_data.len() },
> = $crate::device_id::IdArray::new($table_data);
$crate::module_device_table!("pci", $module_table_name, $table_name);
};
}
/// The PCI driver trait.
///
/// # Example
///
///```
/// # use kernel::{bindings, pci};
///
/// struct MyDriver;
///
/// kernel::pci_device_table!(
/// PCI_TABLE,
/// MODULE_PCI_TABLE,
/// <MyDriver as pci::Driver>::IdInfo,
/// [
/// (pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, bindings::PCI_ANY_ID as _), ())
/// ]
/// );
///
/// impl pci::Driver for MyDriver {
/// type IdInfo = ();
/// const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
///
/// fn probe(
/// _pdev: &mut pci::Device,
/// _id_info: &Self::IdInfo,
/// ) -> Result<Pin<KBox<Self>>> {
/// Err(ENODEV)
/// }
/// }
///```
/// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the
/// `Adapter` documentation for an example.
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>;
/// PCI driver probe.
///
/// Called when a new platform device is added or discovered.
/// Implementers should attempt to initialize the device here.
fn probe(dev: &mut Device, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
}
/// The PCI device representation.
///
/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
/// device, hence, also increments the base device' reference count.
///
/// # Invariants
///
/// `Device` hold a valid reference of `ARef<device::Device>` whose underlying `struct device` is a
/// member of a `struct pci_dev`.
#[derive(Clone)]
pub struct Device(ARef<device::Device>);
/// A PCI BAR to perform I/O-Operations on.
///
/// # Invariants
///
/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O
/// memory mapped PCI bar and its size.
pub struct Bar<const SIZE: usize = 0> {
pdev: Device,
io: IoRaw<SIZE>,
num: i32,
}
impl<const SIZE: usize> Bar<SIZE> {
fn new(pdev: Device, num: u32, name: &CStr) -> Result<Self> {
let len = pdev.resource_len(num)?;
if len == 0 {
return Err(ENOMEM);
}
// Convert to `i32`, since that's what all the C bindings use.
let num = i32::try_from(num)?;
// SAFETY:
// `pdev` is valid by the invariants of `Device`.
// `num` is checked for validity by a previous call to `Device::resource_len`.
// `name` is always valid.
let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) };
if ret != 0 {
return Err(EBUSY);
}
// SAFETY:
// `pdev` is valid by the invariants of `Device`.
// `num` is checked for validity by a previous call to `Device::resource_len`.
// `name` is always valid.
let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize;
if ioptr == 0 {
// SAFETY:
// `pdev` valid by the invariants of `Device`.
// `num` is checked for validity by a previous call to `Device::resource_len`.
unsafe { bindings::pci_release_region(pdev.as_raw(), num) };
return Err(ENOMEM);
}
let io = match IoRaw::new(ioptr, len as usize) {
Ok(io) => io,
Err(err) => {
// SAFETY:
// `pdev` is valid by the invariants of `Device`.
// `ioptr` is guaranteed to be the start of a valid I/O mapped memory region.
// `num` is checked for validity by a previous call to `Device::resource_len`.
unsafe { Self::do_release(&pdev, ioptr, num) };
return Err(err);
}
};
Ok(Bar { pdev, io, num })
}
/// # Safety
///
/// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`.
unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) {
// SAFETY:
// `pdev` is valid by the invariants of `Device`.
// `ioptr` is valid by the safety requirements.
// `num` is valid by the safety requirements.
unsafe {
bindings::pci_iounmap(pdev.as_raw(), ioptr as _);
bindings::pci_release_region(pdev.as_raw(), num);
}
}
fn release(&self) {
// SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`.
unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) };
}
}
impl Bar {
fn index_is_valid(index: u32) -> bool {
// A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries.
index < bindings::PCI_NUM_RESOURCES
}
}
impl<const SIZE: usize> Drop for Bar<SIZE> {
fn drop(&mut self) {
self.release();
}
}
impl<const SIZE: usize> Deref for Bar<SIZE> {
type Target = Io<SIZE>;
fn deref(&self) -> &Self::Target {
// SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
unsafe { Io::from_raw(&self.io) }
}
}
impl Device {
/// Create a PCI Device instance from an existing `device::Device`.
///
/// # Safety
///
/// `dev` must be an `ARef<device::Device>` whose underlying `bindings::device` is a member of
/// a `bindings::pci_dev`.
pub unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
Self(dev)
}
fn as_raw(&self) -> *mut bindings::pci_dev {
// SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
// embedded in `struct pci_dev`.
unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ }
}
/// Returns the PCI vendor ID.
pub fn vendor_id(&self) -> u16 {
// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
unsafe { (*self.as_raw()).vendor }
}
/// Returns the PCI device ID.
pub fn device_id(&self) -> u16 {
// SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`.
unsafe { (*self.as_raw()).device }
}
/// Enable memory resources for this device.
pub fn enable_device_mem(&self) -> Result {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) };
if ret != 0 {
Err(Error::from_errno(ret))
} else {
Ok(())
}
}
/// Enable bus-mastering for this device.
pub fn set_master(&self) {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
unsafe { bindings::pci_set_master(self.as_raw()) };
}
/// Returns the size of the given PCI bar resource.
pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
if !Bar::index_is_valid(bar) {
return Err(EINVAL);
}
// SAFETY:
// - `bar` is a valid bar number, as guaranteed by the above call to `Bar::index_is_valid`,
// - 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()?) })
}
/// 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>(
&self,
bar: u32,
name: &CStr,
) -> Result<Devres<Bar<SIZE>>> {
let bar = Bar::<SIZE>::new(self.clone(), bar, name)?;
let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?;
Ok(devres)
}
/// Mapps an entire PCI-BAR after performing a region-request on it.
pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result<Devres<Bar>> {
self.iomap_region_sized::<0>(bar, name)
}
}
impl AsRef<device::Device> for Device {
fn as_ref(&self) -> &device::Device {
&self.0
}
}

200
rust/kernel/platform.rs Normal file
View File

@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0
//! Abstractions for the platform bus.
//!
//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
use crate::{
bindings, container_of, device, driver,
error::{to_result, Result},
of,
prelude::*,
str::CStr,
types::{ARef, ForeignOwnable, Opaque},
ThisModule,
};
use core::ptr::addr_of_mut;
/// An adapter for the registration of platform 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::platform_driver;
unsafe fn register(
pdrv: &Opaque<Self::RegType>,
name: &'static CStr,
module: &'static ThisModule,
) -> Result {
let of_table = match T::OF_ID_TABLE {
Some(table) => table.as_ptr(),
None => core::ptr::null(),
};
// SAFETY: It's safe to set the fields of `struct platform_driver` on initialization.
unsafe {
(*pdrv.get()).driver.name = name.as_char_ptr();
(*pdrv.get()).probe = Some(Self::probe_callback);
(*pdrv.get()).remove = Some(Self::remove_callback);
(*pdrv.get()).driver.of_match_table = of_table;
}
// SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) })
}
unsafe fn unregister(pdrv: &Opaque<Self::RegType>) {
// SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
unsafe { bindings::platform_driver_unregister(pdrv.get()) };
}
}
impl<T: Driver + 'static> Adapter<T> {
extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int {
// SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`.
let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) };
// SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the
// call above.
let mut pdev = unsafe { Device::from_dev(dev) };
let info = <Self as driver::Adapter>::id_info(pdev.as_ref());
match T::probe(&mut pdev, info) {
Ok(data) => {
// Let the `struct platform_device` own a reference of the driver's private data.
// SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
// `struct platform_device`.
unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
}
Err(err) => return Error::to_errno(err),
}
0
}
extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
// SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
// 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`.
let _ = unsafe { KBox::<T>::from_foreign(ptr) };
}
}
impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
type IdInfo = T::IdInfo;
fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
T::OF_ID_TABLE
}
}
/// Declares a kernel module that exposes a single platform driver.
///
/// # Examples
///
/// ```ignore
/// kernel::module_platform_driver! {
/// type: MyDriver,
/// name: "Module name",
/// author: "Author name",
/// description: "Description",
/// license: "GPL v2",
/// }
/// ```
#[macro_export]
macro_rules! module_platform_driver {
($($f:tt)*) => {
$crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
};
}
/// The platform driver trait.
///
/// Drivers must implement this trait in order to get a platform driver registered.
///
/// # Example
///
///```
/// # use kernel::{bindings, c_str, of, platform};
///
/// struct MyDriver;
///
/// kernel::of_device_table!(
/// OF_TABLE,
/// MODULE_OF_TABLE,
/// <MyDriver as platform::Driver>::IdInfo,
/// [
/// (of::DeviceId::new(c_str!("test,device")), ())
/// ]
/// );
///
/// impl platform::Driver for MyDriver {
/// type IdInfo = ();
/// const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
///
/// fn probe(
/// _pdev: &mut platform::Device,
/// _id_info: Option<&Self::IdInfo>,
/// ) -> Result<Pin<KBox<Self>>> {
/// Err(ENODEV)
/// }
/// }
///```
pub trait Driver {
/// The type holding driver private data about each device id supported by the driver.
///
/// TODO: Use associated_type_defaults once stabilized:
///
/// type IdInfo: 'static = ();
type IdInfo: 'static;
/// The table of OF device ids supported by the driver.
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>>;
/// Platform driver probe.
///
/// Called when a new platform device is added or discovered.
/// Implementers should attempt to initialize the device here.
fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
}
/// The platform device representation.
///
/// A platform device is based on an always reference counted `device:Device` instance. Cloning a
/// platform device, hence, also increments the base device' reference count.
///
/// # Invariants
///
/// `Device` holds a valid reference of `ARef<device::Device>` whose underlying `struct device` is a
/// member of a `struct platform_device`.
#[derive(Clone)]
pub struct Device(ARef<device::Device>);
impl Device {
/// Convert a raw kernel device into a `Device`
///
/// # Safety
///
/// `dev` must be an `Aref<device::Device>` whose underlying `bindings::device` is a member of a
/// `bindings::platform_device`.
unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
Self(dev)
}
fn as_raw(&self) -> *mut bindings::platform_device {
// SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
// embedded in `struct platform_device`.
unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
}
}
impl AsRef<device::Device> for Device {
fn as_ref(&self) -> &device::Device {
&self.0
}
}

219
rust/kernel/revocable.rs Normal file
View File

@ -0,0 +1,219 @@
// SPDX-License-Identifier: GPL-2.0
//! Revocable objects.
//!
//! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence
//! of a [`RevocableGuard`] ensures that objects remain valid.
use crate::{bindings, prelude::*, sync::rcu, types::Opaque};
use core::{
marker::PhantomData,
ops::Deref,
ptr::drop_in_place,
sync::atomic::{AtomicBool, Ordering},
};
/// An object that can become inaccessible at runtime.
///
/// Once access is revoked and all concurrent users complete (i.e., all existing instances of
/// [`RevocableGuard`] are dropped), the wrapped object is also dropped.
///
/// # Examples
///
/// ```
/// # use kernel::revocable::Revocable;
///
/// struct Example {
/// a: u32,
/// b: u32,
/// }
///
/// fn add_two(v: &Revocable<Example>) -> Option<u32> {
/// let guard = v.try_access()?;
/// Some(guard.a + guard.b)
/// }
///
/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap();
/// assert_eq!(add_two(&v), Some(30));
/// v.revoke();
/// assert_eq!(add_two(&v), None);
/// ```
///
/// Sample example as above, but explicitly using the rcu read side lock.
///
/// ```
/// # use kernel::revocable::Revocable;
/// use kernel::sync::rcu;
///
/// struct Example {
/// a: u32,
/// b: u32,
/// }
///
/// fn add_two(v: &Revocable<Example>) -> Option<u32> {
/// let guard = rcu::read_lock();
/// let e = v.try_access_with_guard(&guard)?;
/// Some(e.a + e.b)
/// }
///
/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap();
/// assert_eq!(add_two(&v), Some(30));
/// v.revoke();
/// assert_eq!(add_two(&v), None);
/// ```
#[pin_data(PinnedDrop)]
pub struct Revocable<T> {
is_available: AtomicBool,
#[pin]
data: Opaque<T>,
}
// SAFETY: `Revocable` is `Send` if the wrapped object is also `Send`. This is because while the
// functionality exposed by `Revocable` can be accessed from any thread/CPU, it is possible that
// this isn't supported by the wrapped object.
unsafe impl<T: Send> Send for Revocable<T> {}
// SAFETY: `Revocable` is `Sync` if the wrapped object is both `Send` and `Sync`. We require `Send`
// from the wrapped object as well because of `Revocable::revoke`, which can trigger the `Drop`
// implementation of the wrapped object from an arbitrary thread.
unsafe impl<T: Sync + Send> Sync for Revocable<T> {}
impl<T> Revocable<T> {
/// Creates a new revocable instance of the given data.
pub fn new(data: impl PinInit<T>) -> impl PinInit<Self> {
pin_init!(Self {
is_available: AtomicBool::new(true),
data <- Opaque::pin_init(data),
})
}
/// Tries to access the revocable wrapped object.
///
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
///
/// Returns a guard that gives access to the object otherwise; the object is guaranteed to
/// remain accessible while the guard is alive. In such cases, callers are not allowed to sleep
/// because another CPU may be waiting to complete the revocation of this object.
pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
let guard = rcu::read_lock();
if self.is_available.load(Ordering::Relaxed) {
// Since `self.is_available` is true, data is initialised and has to remain valid
// because the RCU read side lock prevents it from being dropped.
Some(RevocableGuard::new(self.data.get(), guard))
} else {
None
}
}
/// Tries to access the revocable wrapped object.
///
/// Returns `None` if the object has been revoked and is therefore no longer accessible.
///
/// Returns a shared reference to the object otherwise; the object is guaranteed to
/// remain accessible while the rcu read side guard is alive. In such cases, callers are not
/// allowed to sleep because another CPU may be waiting to complete the revocation of this
/// object.
pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> Option<&'a T> {
if self.is_available.load(Ordering::Relaxed) {
// SAFETY: Since `self.is_available` is true, data is initialised and has to remain
// valid because the RCU read side lock prevents it from being dropped.
Some(unsafe { &*self.data.get() })
} else {
None
}
}
/// # Safety
///
/// Callers must ensure that there are no more concurrent users of the revocable object.
unsafe fn revoke_internal<const SYNC: bool>(&self) {
if self.is_available.swap(false, Ordering::Relaxed) {
if SYNC {
// SAFETY: Just an FFI call, there are no further requirements.
unsafe { bindings::synchronize_rcu() };
}
// SAFETY: We know `self.data` is valid because only one CPU can succeed the
// `compare_exchange` above that takes `is_available` from `true` to `false`.
unsafe { drop_in_place(self.data.get()) };
}
}
/// Revokes access to and drops the wrapped object.
///
/// Access to the object is revoked immediately to new callers of [`Revocable::try_access`],
/// expecting that there are no concurrent users of the object.
///
/// # Safety
///
/// Callers must ensure that there are no more concurrent users of the revocable object.
pub unsafe fn revoke_nosync(&self) {
// SAFETY: By the safety requirement of this function, the caller ensures that nobody is
// accessing the data anymore and hence we don't have to wait for the grace period to
// finish.
unsafe { self.revoke_internal::<false>() }
}
/// Revokes access to and drops the wrapped object.
///
/// Access to the object is revoked immediately to new callers of [`Revocable::try_access`].
///
/// If there are concurrent users of the object (i.e., ones that called
/// [`Revocable::try_access`] beforehand and still haven't dropped the returned guard), this
/// function waits for the concurrent access to complete before dropping the wrapped object.
pub fn revoke(&self) {
// SAFETY: By passing `true` we ask `revoke_internal` to wait for the grace period to
// finish.
unsafe { self.revoke_internal::<true>() }
}
}
#[pinned_drop]
impl<T> PinnedDrop for Revocable<T> {
fn drop(self: Pin<&mut Self>) {
// Drop only if the data hasn't been revoked yet (in which case it has already been
// dropped).
// SAFETY: We are not moving out of `p`, only dropping in place
let p = unsafe { self.get_unchecked_mut() };
if *p.is_available.get_mut() {
// SAFETY: We know `self.data` is valid because no other CPU has changed
// `is_available` to `false` yet, and no other CPU can do it anymore because this CPU
// holds the only reference (mutable) to `self` now.
unsafe { drop_in_place(p.data.get()) };
}
}
}
/// A guard that allows access to a revocable object and keeps it alive.
///
/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context
/// holding the RCU read-side lock.
///
/// # Invariants
///
/// The RCU read-side lock is held while the guard is alive.
pub struct RevocableGuard<'a, T> {
data_ref: *const T,
_rcu_guard: rcu::Guard,
_p: PhantomData<&'a ()>,
}
impl<T> RevocableGuard<'_, T> {
fn new(data_ref: *const T, rcu_guard: rcu::Guard) -> Self {
Self {
data_ref,
_rcu_guard: rcu_guard,
_p: PhantomData,
}
}
}
impl<T> Deref for RevocableGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// SAFETY: By the type invariants, we hold the rcu read-side lock, so the object is
// guaranteed to remain valid.
unsafe { &*self.data_ref }
}
}

View File

@ -12,6 +12,7 @@ mod condvar;
pub mod lock;
mod locked_by;
pub mod poll;
pub mod rcu;
pub use arc::{Arc, ArcBorrow, UniqueArc};
pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};

47
rust/kernel/sync/rcu.rs Normal file
View File

@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0
//! RCU support.
//!
//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
use crate::{bindings, types::NotThreadSafe};
/// Evidence that the RCU read side lock is held on the current thread/CPU.
///
/// The type is explicitly not `Send` because this property is per-thread/CPU.
///
/// # Invariants
///
/// The RCU read side lock is actually held while instances of this guard exist.
pub struct Guard(NotThreadSafe);
impl Guard {
/// Acquires the RCU read side lock and returns a guard.
pub fn new() -> Self {
// SAFETY: An FFI call with no additional requirements.
unsafe { bindings::rcu_read_lock() };
// INVARIANT: The RCU read side lock was just acquired above.
Self(NotThreadSafe)
}
/// Explicitly releases the RCU read side lock.
pub fn unlock(self) {}
}
impl Default for Guard {
fn default() -> Self {
Self::new()
}
}
impl Drop for Guard {
fn drop(&mut self) {
// SAFETY: By the type invariants, the RCU read side is locked, so it is ok to unlock it.
unsafe { bindings::rcu_read_unlock() };
}
}
/// Acquires the RCU read side lock.
pub fn read_lock() -> Guard {
Guard::new()
}

View File

@ -281,6 +281,17 @@ impl<T> Opaque<T> {
}
}
/// 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| {
// SAFETY:
// - `ptr` is a valid pointer to uninitialized memory,
// - `slot` is not accessed on error; the call is infallible,
// - `slot` is pinned in memory.
let _ = unsafe { init::PinInit::<T>::__pinned_init(slot, ptr) };
})
}
/// Creates a pin-initializer from the given initializer closure.
///
/// The returned initializer calls the given closure with the pointer to the inner `T` of this

View File

@ -228,6 +228,10 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
kernel::ThisModule::from_ptr(core::ptr::null_mut())
}};
impl kernel::ModuleMetadata for {type_} {{
const NAME: &'static kernel::str::CStr = kernel::c_str!(\"{name}\");
}}
// Double nested modules, since then nobody can access the public items inside.
mod __module_init {{
mod __module_init {{

View File

@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL
If unsure, say N.
config SAMPLE_RUST_MISC_DEVICE
tristate "Misc device"
help
This option builds the Rust misc device.
To compile this as a module, choose M here:
the module will be called rust_misc_device.
If unsure, say N.
config SAMPLE_RUST_PRINT
tristate "Printing macros"
help
@ -30,6 +40,27 @@ config SAMPLE_RUST_PRINT
If unsure, say N.
config SAMPLE_RUST_DRIVER_PCI
tristate "PCI Driver"
depends on PCI
help
This option builds the Rust PCI driver sample.
To compile this as a module, choose M here:
the module will be called driver_pci.
If unsure, say N.
config SAMPLE_RUST_DRIVER_PLATFORM
tristate "Platform Driver"
help
This option builds the Rust Platform driver sample.
To compile this as a module, choose M here:
the module will be called rust_driver_platform.
If unsure, say N.
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help

View File

@ -2,7 +2,10 @@
ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
rust_print-y := rust_print_main.o rust_print_events.o

View File

@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0
//! Rust PCI driver sample (based on QEMU's `pci-testdev`).
//!
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
use kernel::{bindings, c_str, devres::Devres, pci, prelude::*};
struct Regs;
impl Regs {
const TEST: usize = 0x0;
const OFFSET: usize = 0x4;
const DATA: usize = 0x8;
const COUNT: usize = 0xC;
const END: usize = 0x10;
}
type Bar0 = pci::Bar<{ Regs::END }>;
#[derive(Debug)]
struct TestIndex(u8);
impl TestIndex {
const NO_EVENTFD: Self = Self(0);
}
struct SampleDriver {
pdev: pci::Device,
bar: Devres<Bar0>,
}
kernel::pci_device_table!(
PCI_TABLE,
MODULE_PCI_TABLE,
<SampleDriver as pci::Driver>::IdInfo,
[(
pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
TestIndex::NO_EVENTFD
)]
);
impl SampleDriver {
fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
// Select the test.
bar.writeb(index.0, Regs::TEST);
let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize;
let data = bar.readb(Regs::DATA);
// Write `data` to `offset` to increase `count` by one.
//
// Note that we need `try_writeb`, since `offset` can't be checked at compile-time.
bar.try_writeb(data, offset)?;
Ok(bar.readl(Regs::COUNT))
}
}
impl pci::Driver for SampleDriver {
type IdInfo = TestIndex;
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
fn probe(pdev: &mut pci::Device, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
dev_dbg!(
pdev.as_ref(),
"Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n",
pdev.vendor_id(),
pdev.device_id()
);
pdev.enable_device_mem()?;
pdev.set_master();
let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?;
let drvdata = KBox::new(
Self {
pdev: pdev.clone(),
bar,
},
GFP_KERNEL,
)?;
let bar = drvdata.bar.try_access().ok_or(ENXIO)?;
dev_info!(
pdev.as_ref(),
"pci-testdev data-match count: {}\n",
Self::testdev(info, &bar)?
);
Ok(drvdata.into())
}
}
impl Drop for SampleDriver {
fn drop(&mut self) {
dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n");
}
}
kernel::module_pci_driver! {
type: SampleDriver,
name: "rust_driver_pci",
author: "Danilo Krummrich",
description: "Rust PCI driver",
license: "GPL v2",
}

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
//! Rust Platform driver sample.
use kernel::{c_str, of, platform, prelude::*};
struct SampleDriver {
pdev: platform::Device,
}
struct Info(u32);
kernel::of_device_table!(
OF_TABLE,
MODULE_OF_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
[(of::DeviceId::new(c_str!("test,rust-device")), Info(42))]
);
impl platform::Driver for SampleDriver {
type IdInfo = Info;
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
if let Some(info) = info {
dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0);
}
let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?;
Ok(drvdata.into())
}
}
impl Drop for SampleDriver {
fn drop(&mut self) {
dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
}
}
kernel::module_platform_driver! {
type: SampleDriver,
name: "rust_driver_platform",
author: "Danilo Krummrich",
description: "Rust Platform driver",
license: "GPL v2",
}

View File

@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2024 Google LLC.
//! Rust misc device sample.
/// Below is an example userspace C program that exercises this sample's functionality.
///
/// ```c
/// #include <stdio.h>
/// #include <stdlib.h>
/// #include <errno.h>
/// #include <fcntl.h>
/// #include <unistd.h>
/// #include <sys/ioctl.h>
///
/// #define RUST_MISC_DEV_FAIL _IO('|', 0)
/// #define RUST_MISC_DEV_HELLO _IO('|', 0x80)
/// #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int)
/// #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int)
///
/// int main() {
/// int value, new_value;
/// int fd, ret;
///
/// // Open the device file
/// printf("Opening /dev/rust-misc-device for reading and writing\n");
/// fd = open("/dev/rust-misc-device", O_RDWR);
/// if (fd < 0) {
/// perror("open");
/// return errno;
/// }
///
/// // Make call into driver to say "hello"
/// printf("Calling Hello\n");
/// ret = ioctl(fd, RUST_MISC_DEV_HELLO, NULL);
/// if (ret < 0) {
/// perror("ioctl: Failed to call into Hello");
/// close(fd);
/// return errno;
/// }
///
/// // Get initial value
/// printf("Fetching initial value\n");
/// ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value);
/// if (ret < 0) {
/// perror("ioctl: Failed to fetch the initial value");
/// close(fd);
/// return errno;
/// }
///
/// value++;
///
/// // Set value to something different
/// printf("Submitting new value (%d)\n", value);
/// ret = ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value);
/// if (ret < 0) {
/// perror("ioctl: Failed to submit new value");
/// close(fd);
/// return errno;
/// }
///
/// // Ensure new value was applied
/// printf("Fetching new value\n");
/// ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value);
/// if (ret < 0) {
/// perror("ioctl: Failed to fetch the new value");
/// close(fd);
/// return errno;
/// }
///
/// if (value != new_value) {
/// printf("Failed: Committed and retrieved values are different (%d - %d)\n", value, new_value);
/// close(fd);
/// return -1;
/// }
///
/// // Call the unsuccessful ioctl
/// printf("Attempting to call in to an non-existent IOCTL\n");
/// ret = ioctl(fd, RUST_MISC_DEV_FAIL, NULL);
/// if (ret < 0) {
/// perror("ioctl: Succeeded to fail - this was expected");
/// } else {
/// printf("ioctl: Failed to fail\n");
/// close(fd);
/// return -1;
/// }
///
/// // Close the device file
/// printf("Closing /dev/rust-misc-device\n");
/// close(fd);
///
/// printf("Success\n");
/// return 0;
/// }
/// ```
use core::pin::Pin;
use kernel::{
c_str,
device::Device,
fs::File,
ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},
miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
new_mutex,
prelude::*,
sync::Mutex,
types::ARef,
uaccess::{UserSlice, UserSliceReader, UserSliceWriter},
};
const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80);
const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81);
const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82);
module! {
type: RustMiscDeviceModule,
name: "rust_misc_device",
author: "Lee Jones",
description: "Rust misc device sample",
license: "GPL",
}
#[pin_data]
struct RustMiscDeviceModule {
#[pin]
_miscdev: MiscDeviceRegistration<RustMiscDevice>,
}
impl kernel::InPlaceModule for RustMiscDeviceModule {
fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
pr_info!("Initialising Rust Misc Device Sample\n");
let options = MiscDeviceOptions {
name: c_str!("rust-misc-device"),
};
try_pin_init!(Self {
_miscdev <- MiscDeviceRegistration::register(options),
})
}
}
struct Inner {
value: i32,
}
#[pin_data(PinnedDrop)]
struct RustMiscDevice {
#[pin]
inner: Mutex<Inner>,
dev: ARef<Device>,
}
#[vtable]
impl MiscDevice for RustMiscDevice {
type Ptr = Pin<KBox<Self>>;
fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {
let dev = ARef::from(misc.device());
dev_info!(dev, "Opening Rust Misc Device Sample\n");
KBox::try_pin_init(
try_pin_init! {
RustMiscDevice {
inner <- new_mutex!( Inner{ value: 0_i32 } ),
dev: dev,
}
},
GFP_KERNEL,
)
}
fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {
dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
let size = _IOC_SIZE(cmd);
match cmd {
RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?,
RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?,
RUST_MISC_DEV_HELLO => me.hello()?,
_ => {
dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);
return Err(ENOTTY);
}
};
Ok(0)
}
}
#[pinned_drop]
impl PinnedDrop for RustMiscDevice {
fn drop(self: Pin<&mut Self>) {
dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");
}
}
impl RustMiscDevice {
fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> {
let new_value = reader.read::<i32>()?;
let mut guard = self.inner.lock();
dev_info!(
self.dev,
"-> Copying data from userspace (value: {})\n",
new_value
);
guard.value = new_value;
Ok(0)
}
fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> {
let guard = self.inner.lock();
let value = guard.value;
// Free-up the lock and use our locally cached instance from here
drop(guard);
dev_info!(
self.dev,
"-> Copying data to userspace (value: {})\n",
&value
);
writer.write::<i32>(&value)?;
Ok(0)
}
fn hello(&self) -> Result<isize> {
dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");
Ok(0)
}
}

View File

@ -725,7 +725,7 @@ static void default_mock_decoder(struct cxl_decoder *cxld)
cxld->reset = mock_decoder_reset;
}
static int first_decoder(struct device *dev, void *data)
static int first_decoder(struct device *dev, const void *data)
{
struct cxl_decoder *cxld;