mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-09-03 02:16:09 +02:00

[ Upstream commit7d0bc6360f
] Commit19b8766459
("firmware: arm_ffa: Fix FFA device names for logical partitions") added an ID to the FFA device using ida_alloc() and append the same to "arm-ffa" to make up a unique device name. However it missed to stash the id value in ffa_dev to help freeing the ID later when the device is destroyed. Due to the missing/unassigned ID in FFA device, we get the following warning when the FF-A device is unregistered. | ida_free called for id=0 which is not allocated. | WARNING: CPU: 7 PID: 1 at lib/idr.c:525 ida_free+0x114/0x164 | CPU: 7 PID: 1 Comm: swapper/0 Not tainted 6.6.0-rc4 #209 | pstate: 61400009 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) | pc : ida_free+0x114/0x164 | lr : ida_free+0x114/0x164 | Call trace: | ida_free+0x114/0x164 | ffa_release_device+0x24/0x3c | device_release+0x34/0x8c | kobject_put+0x94/0xf8 | put_device+0x18/0x24 | klist_devices_put+0x14/0x20 | klist_next+0xc8/0x114 | bus_for_each_dev+0xd8/0x144 | arm_ffa_bus_exit+0x30/0x54 | ffa_init+0x68/0x330 | do_one_initcall+0xdc/0x250 | do_initcall_level+0x8c/0xac | do_initcalls+0x54/0x94 | do_basic_setup+0x1c/0x28 | kernel_init_freeable+0x104/0x170 | kernel_init+0x20/0x1a0 | ret_from_fork+0x10/0x20 Fix the same by actually assigning the ID in the FFA device this time for real. Fixes:19b8766459
("firmware: arm_ffa: Fix FFA device names for logical partitions") Link: https://lore.kernel.org/r/20231003085932.3553985-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla <sudeep.holla@arm.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
233 lines
4.9 KiB
C
233 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2021 ARM Ltd.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/arm_ffa.h>
|
|
#include <linux/device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "common.h"
|
|
|
|
static DEFINE_IDA(ffa_bus_id);
|
|
|
|
static int ffa_device_match(struct device *dev, struct device_driver *drv)
|
|
{
|
|
const struct ffa_device_id *id_table;
|
|
struct ffa_device *ffa_dev;
|
|
|
|
id_table = to_ffa_driver(drv)->id_table;
|
|
ffa_dev = to_ffa_dev(dev);
|
|
|
|
while (!uuid_is_null(&id_table->uuid)) {
|
|
/*
|
|
* FF-A v1.0 doesn't provide discovery of UUIDs, just the
|
|
* partition IDs, so fetch the partitions IDs for this
|
|
* id_table UUID and assign the UUID to the device if the
|
|
* partition ID matches
|
|
*/
|
|
if (uuid_is_null(&ffa_dev->uuid))
|
|
ffa_device_match_uuid(ffa_dev, &id_table->uuid);
|
|
|
|
if (uuid_equal(&ffa_dev->uuid, &id_table->uuid))
|
|
return 1;
|
|
id_table++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ffa_device_probe(struct device *dev)
|
|
{
|
|
struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
|
|
struct ffa_device *ffa_dev = to_ffa_dev(dev);
|
|
|
|
return ffa_drv->probe(ffa_dev);
|
|
}
|
|
|
|
static void ffa_device_remove(struct device *dev)
|
|
{
|
|
struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
|
|
|
|
if (ffa_drv->remove)
|
|
ffa_drv->remove(to_ffa_dev(dev));
|
|
}
|
|
|
|
static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
|
|
{
|
|
const struct ffa_device *ffa_dev = to_ffa_dev(dev);
|
|
|
|
return add_uevent_var(env, "MODALIAS=arm_ffa:%04x:%pUb",
|
|
ffa_dev->vm_id, &ffa_dev->uuid);
|
|
}
|
|
|
|
static ssize_t partition_id_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct ffa_device *ffa_dev = to_ffa_dev(dev);
|
|
|
|
return sprintf(buf, "0x%04x\n", ffa_dev->vm_id);
|
|
}
|
|
static DEVICE_ATTR_RO(partition_id);
|
|
|
|
static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct ffa_device *ffa_dev = to_ffa_dev(dev);
|
|
|
|
return sprintf(buf, "%pUb\n", &ffa_dev->uuid);
|
|
}
|
|
static DEVICE_ATTR_RO(uuid);
|
|
|
|
static struct attribute *ffa_device_attributes_attrs[] = {
|
|
&dev_attr_partition_id.attr,
|
|
&dev_attr_uuid.attr,
|
|
NULL,
|
|
};
|
|
ATTRIBUTE_GROUPS(ffa_device_attributes);
|
|
|
|
struct bus_type ffa_bus_type = {
|
|
.name = "arm_ffa",
|
|
.match = ffa_device_match,
|
|
.probe = ffa_device_probe,
|
|
.remove = ffa_device_remove,
|
|
.uevent = ffa_device_uevent,
|
|
.dev_groups = ffa_device_attributes_groups,
|
|
};
|
|
EXPORT_SYMBOL_GPL(ffa_bus_type);
|
|
|
|
int ffa_driver_register(struct ffa_driver *driver, struct module *owner,
|
|
const char *mod_name)
|
|
{
|
|
int ret;
|
|
|
|
if (!driver->probe)
|
|
return -EINVAL;
|
|
|
|
driver->driver.bus = &ffa_bus_type;
|
|
driver->driver.name = driver->name;
|
|
driver->driver.owner = owner;
|
|
driver->driver.mod_name = mod_name;
|
|
|
|
ret = driver_register(&driver->driver);
|
|
if (!ret)
|
|
pr_debug("registered new ffa driver %s\n", driver->name);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ffa_driver_register);
|
|
|
|
void ffa_driver_unregister(struct ffa_driver *driver)
|
|
{
|
|
driver_unregister(&driver->driver);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ffa_driver_unregister);
|
|
|
|
static void ffa_release_device(struct device *dev)
|
|
{
|
|
struct ffa_device *ffa_dev = to_ffa_dev(dev);
|
|
|
|
ida_free(&ffa_bus_id, ffa_dev->id);
|
|
kfree(ffa_dev);
|
|
}
|
|
|
|
static int __ffa_devices_unregister(struct device *dev, void *data)
|
|
{
|
|
device_unregister(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ffa_devices_unregister(void)
|
|
{
|
|
bus_for_each_dev(&ffa_bus_type, NULL, NULL,
|
|
__ffa_devices_unregister);
|
|
}
|
|
|
|
bool ffa_device_is_valid(struct ffa_device *ffa_dev)
|
|
{
|
|
bool valid = false;
|
|
struct device *dev = NULL;
|
|
struct ffa_device *tmp_dev;
|
|
|
|
do {
|
|
dev = bus_find_next_device(&ffa_bus_type, dev);
|
|
tmp_dev = to_ffa_dev(dev);
|
|
if (tmp_dev == ffa_dev) {
|
|
valid = true;
|
|
break;
|
|
}
|
|
put_device(dev);
|
|
} while (dev);
|
|
|
|
put_device(dev);
|
|
|
|
return valid;
|
|
}
|
|
|
|
struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id,
|
|
const struct ffa_ops *ops)
|
|
{
|
|
int id, ret;
|
|
struct device *dev;
|
|
struct ffa_device *ffa_dev;
|
|
|
|
id = ida_alloc_min(&ffa_bus_id, 1, GFP_KERNEL);
|
|
if (id < 0)
|
|
return NULL;
|
|
|
|
ffa_dev = kzalloc(sizeof(*ffa_dev), GFP_KERNEL);
|
|
if (!ffa_dev) {
|
|
ida_free(&ffa_bus_id, id);
|
|
return NULL;
|
|
}
|
|
|
|
dev = &ffa_dev->dev;
|
|
dev->bus = &ffa_bus_type;
|
|
dev->release = ffa_release_device;
|
|
dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
|
|
|
|
ffa_dev->id = id;
|
|
ffa_dev->vm_id = vm_id;
|
|
ffa_dev->ops = ops;
|
|
uuid_copy(&ffa_dev->uuid, uuid);
|
|
|
|
ret = device_register(&ffa_dev->dev);
|
|
if (ret) {
|
|
dev_err(dev, "unable to register device %s err=%d\n",
|
|
dev_name(dev), ret);
|
|
put_device(dev);
|
|
return NULL;
|
|
}
|
|
|
|
return ffa_dev;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ffa_device_register);
|
|
|
|
void ffa_device_unregister(struct ffa_device *ffa_dev)
|
|
{
|
|
if (!ffa_dev)
|
|
return;
|
|
|
|
device_unregister(&ffa_dev->dev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(ffa_device_unregister);
|
|
|
|
int arm_ffa_bus_init(void)
|
|
{
|
|
return bus_register(&ffa_bus_type);
|
|
}
|
|
|
|
void arm_ffa_bus_exit(void)
|
|
{
|
|
ffa_devices_unregister();
|
|
bus_unregister(&ffa_bus_type);
|
|
ida_destroy(&ffa_bus_id);
|
|
}
|