mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 13:14:02 +02:00
firmware: arm_scmi: Fix transport device teardown lookup
SCMI transport devices are deliberately excluded from normal SCMI bus
matching so protocol drivers cannot bind to the internal transport
children. However, scmi_device_destroy() uses the same protocol/name
lookup to find devices that must be unregistered during channel teardown.
Split the match helper so driver matching still skips transport devices,
while explicit child lookup can find them for teardown. Use a shared
transport-device name prefix macro for both matching and name generation.
Since transport-device names are derived from direction and protocol ID,
reject duplicate protocol channel setup before creating or finding a
transport device. This prevents malformed firmware with duplicate
protocol child nodes from reusing an existing transport device and then
destroying it when the duplicate IDR insertion fails.
Fixes: 9593804c44 ("firmware: arm_scmi: Exclude transport devices from bus matching")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-16-3afe499d46e3@kernel.org
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
This commit is contained in:
parent
6abe8fe36b
commit
a14dd8fe0a
|
|
@ -200,21 +200,33 @@ scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
|
|||
scmi_protocol_device_unrequest(entry);
|
||||
}
|
||||
|
||||
static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev,
|
||||
const struct scmi_device_id *id_table)
|
||||
static bool scmi_device_is_transport(const struct scmi_device *scmi_dev)
|
||||
{
|
||||
return !strncmp(scmi_dev->name, SCMI_TRANSPORT_DEVNAME_PREFIX,
|
||||
strlen(SCMI_TRANSPORT_DEVNAME_PREFIX));
|
||||
}
|
||||
|
||||
static int __scmi_dev_match_by_id_table(struct scmi_device *scmi_dev,
|
||||
const struct scmi_device_id *id_table,
|
||||
bool skip_transport)
|
||||
{
|
||||
if (!id_table || !id_table->name)
|
||||
return 0;
|
||||
|
||||
/* Always skip transport devices from matching */
|
||||
for (; id_table->protocol_id && id_table->name; id_table++)
|
||||
if (id_table->protocol_id == scmi_dev->protocol_id &&
|
||||
strncmp(scmi_dev->name, "__scmi_transport_device", 23) &&
|
||||
!(skip_transport && scmi_device_is_transport(scmi_dev)) &&
|
||||
!strcmp(id_table->name, scmi_dev->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev,
|
||||
const struct scmi_device_id *id_table)
|
||||
{
|
||||
return __scmi_dev_match_by_id_table(scmi_dev, id_table, true);
|
||||
}
|
||||
|
||||
static int scmi_dev_match_id(struct scmi_device *scmi_dev,
|
||||
const struct scmi_driver *scmi_drv)
|
||||
{
|
||||
|
|
@ -234,7 +246,7 @@ static int scmi_match_by_id_table(struct device *dev, const void *data)
|
|||
struct scmi_device *scmi_dev = to_scmi_dev(dev);
|
||||
const struct scmi_device_id *id_table = data;
|
||||
|
||||
return scmi_dev_match_by_id_table(scmi_dev, id_table);
|
||||
return __scmi_dev_match_by_id_table(scmi_dev, id_table, false);
|
||||
}
|
||||
|
||||
/* Returns a device_find_child() reference which must be dropped by caller. */
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
#define SCMI_SHMEM_MAX_PAYLOAD_SIZE 104
|
||||
|
||||
#define SCMI_TRANSPORT_DEVNAME_PREFIX "__scmi_transport_device"
|
||||
|
||||
enum scmi_error_codes {
|
||||
SCMI_SUCCESS = 0, /* Success */
|
||||
SCMI_ERR_SUPPORT = -1, /* Not supported */
|
||||
|
|
|
|||
|
|
@ -2762,6 +2762,9 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
|
|||
idx = tx ? 0 : 1;
|
||||
idr = tx ? &info->tx_idr : &info->rx_idr;
|
||||
|
||||
if (idr_find(idr, prot_id))
|
||||
return -EEXIST;
|
||||
|
||||
if (!info->desc->ops->chan_available(of_node, idx)) {
|
||||
cinfo = idr_find(idr, SCMI_PROTOCOL_BASE);
|
||||
if (unlikely(!cinfo)) /* Possible only if platform has no Rx */
|
||||
|
|
@ -2779,7 +2782,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
|
|||
cinfo->no_completion_irq = info->desc->no_completion_irq;
|
||||
|
||||
/* Create a unique name for this transport device */
|
||||
snprintf(name, 32, "__scmi_transport_device_%s_%02X",
|
||||
snprintf(name, sizeof(name), SCMI_TRANSPORT_DEVNAME_PREFIX "_%s_%02X",
|
||||
idx ? "rx" : "tx", prot_id);
|
||||
/* Create a uniquely named, dedicated transport device for this chan */
|
||||
tdev = scmi_device_create(of_node, info->dev, prot_id, name);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user