Files
linux-stable-mirror/drivers/media/platform/qcom/iris/iris_firmware.c
T
Stephan Gerhold 57429b0fdd media: iris: Fix firmware reference leak and unmap memory after load
When we succeed loading the firmware, we don't want to hold on to the
firmware pointer anymore, since it won't be freed anywhere else. The same
applies for the mapped memory. Unmapping the memory is particularly
important since the memory will be protected after the Iris firmware is
started, so we need to make sure there will be no accidental access to this
region (even if just a speculative one from the CPU).

Almost the same firmware loading code also exists in venus/firmware.c,
there it is implemented correctly.

Fix this by dropping the early "return ret" and move the call of
qcom_scm_pas_auth_and_reset() out of iris_load_fw_to_memory(). We should
unmap the memory before bringing the firmware out of reset.

Cc: stable@vger.kernel.org
Fixes: d19b163356 ("media: iris: implement video firmware load/unload")
Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Reviewed-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
Signed-off-by: Bryan O'Donoghue <bod@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
2025-09-09 15:59:20 +02:00

116 lines
2.6 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/firmware.h>
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "iris_core.h"
#include "iris_firmware.h"
#define MAX_FIRMWARE_NAME_SIZE 128
static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
{
u32 pas_id = core->iris_platform_data->pas_id;
const struct firmware *firmware = NULL;
struct device *dev = core->dev;
struct reserved_mem *rmem;
struct device_node *node;
phys_addr_t mem_phys;
size_t res_size;
ssize_t fw_size;
void *mem_virt;
int ret;
if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4)
return -EINVAL;
node = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!node)
return -EINVAL;
rmem = of_reserved_mem_lookup(node);
of_node_put(node);
if (!rmem)
return -EINVAL;
mem_phys = rmem->base;
res_size = rmem->size;
ret = request_firmware(&firmware, fw_name, dev);
if (ret)
return ret;
fw_size = qcom_mdt_get_size(firmware);
if (fw_size < 0 || res_size < (size_t)fw_size) {
ret = -EINVAL;
goto err_release_fw;
}
mem_virt = memremap(mem_phys, res_size, MEMREMAP_WC);
if (!mem_virt) {
ret = -ENOMEM;
goto err_release_fw;
}
ret = qcom_mdt_load(dev, firmware, fw_name,
pas_id, mem_virt, mem_phys, res_size, NULL);
memunmap(mem_virt);
err_release_fw:
release_firmware(firmware);
return ret;
}
int iris_fw_load(struct iris_core *core)
{
struct tz_cp_config *cp_config = core->iris_platform_data->tz_cp_config_data;
const char *fwpath = NULL;
int ret;
ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0,
&fwpath);
if (ret)
fwpath = core->iris_platform_data->fwname;
ret = iris_load_fw_to_memory(core, fwpath);
if (ret) {
dev_err(core->dev, "firmware download failed\n");
return -ENOMEM;
}
ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id);
if (ret) {
dev_err(core->dev, "auth and reset failed: %d\n", ret);
return ret;
}
ret = qcom_scm_mem_protect_video_var(cp_config->cp_start,
cp_config->cp_size,
cp_config->cp_nonpixel_start,
cp_config->cp_nonpixel_size);
if (ret) {
dev_err(core->dev, "protect memory failed\n");
qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
return ret;
}
return ret;
}
int iris_fw_unload(struct iris_core *core)
{
return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
}
int iris_set_hw_state(struct iris_core *core, bool resume)
{
return qcom_scm_set_remote_state(resume, 0);
}