Arm SCMI/FF-A fixes for v7.2

Fix two runtime issues in the SCMI framework. Use full 64-bit division
when rounding range-based clock rates, avoiding divisor truncation and
a possible divide-by-zero on 32-bit systems. Rate-limit notification
queue-full warnings emitted from interrupt context to prevent printk
floods and prolonged system stalls during notification bursts. Also
correct a grammar error in the ARM_SCMI_POWER_CONTROL Kconfig help
text.

Fix the FF-A driver RX/TX buffer sizing logic to respect the maximum
buffer size advertised by firmware, while retaining compatibility with
older implementations that may reject PAGE_SIZE-rounded buffers.
Also fix a NULL pointer dereference in ffa_partition_info_get() by
rejecting NULL UUID strings before passing them to uuid_parse().

* tag 'scmi-ffa-fixes-7.2' of https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_scmi: Rate-limit queue-full warnings in IRQ context
  firmware: arm_scmi: Use 64-bit division for clock rate rounding
  firmware: arm_scmi: Grammar s/may needed/may be needed/
  firmware: arm_ffa: Fix NULL dereference in ffa_partition_info_get()
  firmware: arm_ffa: Respect firmware advertised RX/TX buffer size limits

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann
2026-07-15 22:50:24 +02:00
4 changed files with 34 additions and 18 deletions

View File

@@ -32,6 +32,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mutex.h>
@@ -59,7 +60,9 @@
(FIELD_PREP(SENDER_ID_MASK, (s)) | FIELD_PREP(RECEIVER_ID_MASK, (r)))
#define RXTX_MAP_MIN_BUFSZ_MASK GENMASK(1, 0)
#define RXTX_MAP_MIN_BUFSZ(x) ((x) & RXTX_MAP_MIN_BUFSZ_MASK)
#define RXTX_MAP_MAX_BUFSZ_MASK GENMASK(31, 16)
#define RXTX_MAP_MIN_BUFSZ(x) (FIELD_GET(RXTX_MAP_MIN_BUFSZ_MASK, (x)))
#define RXTX_MAP_MAX_BUFSZ(x) (FIELD_GET(RXTX_MAP_MAX_BUFSZ_MASK, (x)))
#define FFA_MAX_NOTIFICATIONS 64
@@ -1139,7 +1142,7 @@ static int ffa_partition_info_get(const char *uuid_str,
uuid_t uuid;
struct ffa_partition_info *pbuf;
if (uuid_parse(uuid_str, &uuid)) {
if (!uuid_str || uuid_parse(uuid_str, &uuid)) {
pr_err("invalid uuid (%s)\n", uuid_str);
return -ENODEV;
}
@@ -2101,7 +2104,7 @@ static int ffa_probe(struct platform_device *pdev)
{
int ret;
u32 buf_sz;
size_t rxtx_bufsz = SZ_4K;
size_t rxtx_min_bufsz = SZ_4K, rxtx_max_bufsz = 0, rxtx_bufsz;
if (IS_BUILTIN(CONFIG_ARM_FFA_TRANSPORT) &&
is_protected_kvm_enabled() && !is_pkvm_initialized())
@@ -2132,15 +2135,18 @@ static int ffa_probe(struct platform_device *pdev)
ret = ffa_features(FFA_FN_NATIVE(RXTX_MAP), 0, &buf_sz, NULL);
if (!ret) {
if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 1)
rxtx_bufsz = SZ_64K;
rxtx_min_bufsz = SZ_64K;
else if (RXTX_MAP_MIN_BUFSZ(buf_sz) == 2)
rxtx_bufsz = SZ_16K;
rxtx_min_bufsz = SZ_16K;
else
rxtx_bufsz = SZ_4K;
rxtx_min_bufsz = SZ_4K;
rxtx_max_bufsz = RXTX_MAP_MAX_BUFSZ(buf_sz) * SZ_4K;
if (rxtx_max_bufsz != 0 && rxtx_max_bufsz < rxtx_min_bufsz)
rxtx_max_bufsz = rxtx_min_bufsz;
}
rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz);
drv_info->rxtx_bufsz = rxtx_bufsz;
rxtx_bufsz = min_not_zero(PAGE_ALIGN(rxtx_min_bufsz), rxtx_max_bufsz);
drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL);
if (!drv_info->rx_buffer) {
ret = -ENOMEM;
@@ -2156,10 +2162,17 @@ static int ffa_probe(struct platform_device *pdev)
ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
virt_to_phys(drv_info->rx_buffer),
rxtx_bufsz / FFA_PAGE_SIZE);
if (ret == -EINVAL && !rxtx_max_bufsz && rxtx_min_bufsz < rxtx_bufsz) {
rxtx_bufsz = rxtx_min_bufsz;
ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer),
virt_to_phys(drv_info->rx_buffer),
rxtx_bufsz / FFA_PAGE_SIZE);
}
if (ret) {
pr_err("failed to register FFA RxTx buffers\n");
goto free_pages;
}
drv_info->rxtx_bufsz = rxtx_bufsz;
mutex_init(&drv_info->rx_lock);
mutex_init(&drv_info->tx_lock);

View File

@@ -96,7 +96,7 @@ config ARM_SCMI_POWER_CONTROL
firmware.
This driver can also be built as a module. If so, the module will be
called scmi_power_control. Note this may needed early in boot to catch
early shutdown/reboot SCMI requests.
called scmi_power_control. Note this may be needed early in boot to
catch early shutdown/reboot SCMI requests.
endmenu

View File

@@ -718,7 +718,7 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
u32 clk_id, unsigned long *rate)
{
u64 fmin, fmax, ftmp;
u64 fmin, fmax, ftmp, step;
struct scmi_clock_info *clk;
struct scmi_clock_desc *clkd;
struct clock_info *ci = ph->get_priv(ph);
@@ -749,11 +749,14 @@ static int scmi_clock_determine_rate(const struct scmi_protocol_handle *ph,
return 0;
}
ftmp = *rate - fmin;
ftmp += clkd->r.rates[RATE_STEP] - 1; /* to round up */
ftmp = div64_ul(ftmp, clkd->r.rates[RATE_STEP]);
step = clkd->r.rates[RATE_STEP];
if (!step)
return -EINVAL;
*rate = ftmp * clkd->r.rates[RATE_STEP] + fmin;
ftmp = *rate - fmin;
ftmp = DIV64_U64_ROUND_UP(ftmp, step);
*rate = ftmp * step + fmin;
return 0;
}

View File

@@ -600,9 +600,9 @@ int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id,
return -EINVAL;
}
if (kfifo_avail(&r_evt->proto->equeue.kfifo) < sizeof(eh) + len) {
dev_warn(handle->dev,
"queue full, dropping proto_id:%d evt_id:%d ts:%lld\n",
proto_id, evt_id, ktime_to_ns(ts));
dev_warn_ratelimited(handle->dev,
"queue full, dropping proto_id:%d evt_id:%d ts:%lld\n",
proto_id, evt_id, ktime_to_ns(ts));
return -ENOMEM;
}