linux/drivers/remoteproc/qcom_pil_info.c

130 lines
3.0 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2020 Linaro Ltd.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_address.h>
#include "qcom_pil_info.h"
/*
* The PIL relocation information region is used to communicate memory regions
* occupied by co-processor firmware for post mortem crash analysis.
*
* It consists of an array of entries with an 8 byte textual identifier of the
* region followed by a 64 bit base address and 32 bit size, both little
* endian.
*/
#define PIL_RELOC_NAME_LEN 8
#define PIL_RELOC_ENTRY_SIZE (PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32))
struct pil_reloc {
void __iomem *base;
size_t num_entries;
};
static struct pil_reloc _reloc __read_mostly;
static DEFINE_MUTEX(pil_reloc_lock);
static int qcom_pil_info_init(void)
{
struct device_node *np;
struct resource imem;
void __iomem *base;
int ret;
/* Already initialized? */
if (_reloc.base)
return 0;
np = of_find_compatible_node(NULL, NULL, "qcom,pil-reloc-info");
if (!np)
return -ENOENT;
ret = of_address_to_resource(np, 0, &imem);
of_node_put(np);
if (ret < 0)
return ret;
base = ioremap(imem.start, resource_size(&imem));
if (!base) {
pr_err("failed to map PIL relocation info region\n");
return -ENOMEM;
}
memset_io(base, 0, resource_size(&imem));
_reloc.base = base;
_reloc.num_entries = (u32)resource_size(&imem) / PIL_RELOC_ENTRY_SIZE;
return 0;
}
/**
* qcom_pil_info_store() - store PIL information of image in IMEM
* @image: name of the image
* @base: base address of the loaded image
* @size: size of the loaded image
*
* Return: 0 on success, negative errno on failure
*/
int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size)
{
char buf[PIL_RELOC_NAME_LEN];
void __iomem *entry;
int ret;
int i;
mutex_lock(&pil_reloc_lock);
ret = qcom_pil_info_init();
if (ret < 0) {
mutex_unlock(&pil_reloc_lock);
return ret;
}
for (i = 0; i < _reloc.num_entries; i++) {
entry = _reloc.base + i * PIL_RELOC_ENTRY_SIZE;
memcpy_fromio(buf, entry, PIL_RELOC_NAME_LEN);
/*
* An empty record means we didn't find it, given that the
* records are packed.
*/
if (!buf[0])
goto found_unused;
if (!strncmp(buf, image, PIL_RELOC_NAME_LEN))
goto found_existing;
}
pr_warn("insufficient PIL info slots\n");
mutex_unlock(&pil_reloc_lock);
return -ENOMEM;
found_unused:
remoteproc: qcom: pil_info: Don't memcpy_toio more than is provided If the string passed into qcom_pil_info_store() isn't as long as PIL_RELOC_NAME_LEN we'll try to copy the string assuming the length is PIL_RELOC_NAME_LEN to the io space and go beyond the bounds of the string. Let's only copy as many byes as the string is long, ignoring the NUL terminator. This fixes the following KASAN error: BUG: KASAN: global-out-of-bounds in __memcpy_toio+0x124/0x140 Read of size 1 at addr ffffffd35086e386 by task rmtfs/2392 CPU: 2 PID: 2392 Comm: rmtfs Tainted: G W 5.16.0-rc1-lockdep+ #10 Hardware name: Google Lazor (rev3+) with KB Backlight (DT) Call trace: dump_backtrace+0x0/0x410 show_stack+0x24/0x30 dump_stack_lvl+0x7c/0xa0 print_address_description+0x78/0x2bc kasan_report+0x160/0x1a0 __asan_report_load1_noabort+0x44/0x50 __memcpy_toio+0x124/0x140 qcom_pil_info_store+0x298/0x358 [qcom_pil_info] q6v5_start+0xdf0/0x12e0 [qcom_q6v5_mss] rproc_start+0x178/0x3a0 rproc_boot+0x5f0/0xb90 state_store+0x78/0x1bc dev_attr_store+0x70/0x90 sysfs_kf_write+0xf4/0x118 kernfs_fop_write_iter+0x208/0x300 vfs_write+0x55c/0x804 ksys_pwrite64+0xc8/0x134 __arm64_compat_sys_aarch32_pwrite64+0xc4/0xdc invoke_syscall+0x78/0x20c el0_svc_common+0x11c/0x1f0 do_el0_svc_compat+0x50/0x60 el0_svc_compat+0x5c/0xec el0t_32_sync_handler+0xc0/0xf0 el0t_32_sync+0x1a4/0x1a8 The buggy address belongs to the variable: .str.59+0x6/0xffffffffffffec80 [qcom_q6v5_mss] Memory state around the buggy address: ffffffd35086e280: 00 00 00 00 02 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 ffffffd35086e300: 00 02 f9 f9 f9 f9 f9 f9 00 00 00 06 f9 f9 f9 f9 >ffffffd35086e380: 06 f9 f9 f9 05 f9 f9 f9 00 00 00 00 00 06 f9 f9 ^ ffffffd35086e400: f9 f9 f9 f9 01 f9 f9 f9 04 f9 f9 f9 00 00 01 f9 ffffffd35086e480: f9 f9 f9 f9 00 00 00 00 00 00 00 01 f9 f9 f9 f9 Fixes: 549b67da660d ("remoteproc: qcom: Introduce helper to store pil info in IMEM") Signed-off-by: Stephen Boyd <swboyd@chromium.org> Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> Link: https://lore.kernel.org/r/20211117065454.4142936-1-swboyd@chromium.org
2021-11-17 06:54:54 +00:00
memcpy_toio(entry, image, strnlen(image, PIL_RELOC_NAME_LEN));
found_existing:
/* Use two writel() as base is only aligned to 4 bytes on odd entries */
writel(base, entry + PIL_RELOC_NAME_LEN);
writel((u64)base >> 32, entry + PIL_RELOC_NAME_LEN + 4);
writel(size, entry + PIL_RELOC_NAME_LEN + sizeof(__le64));
mutex_unlock(&pil_reloc_lock);
return 0;
}
EXPORT_SYMBOL_GPL(qcom_pil_info_store);
static void __exit pil_reloc_exit(void)
{
mutex_lock(&pil_reloc_lock);
iounmap(_reloc.base);
_reloc.base = NULL;
mutex_unlock(&pil_reloc_lock);
}
module_exit(pil_reloc_exit);
MODULE_DESCRIPTION("Qualcomm PIL relocation info");
MODULE_LICENSE("GPL v2");