130 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2019-2020 Linaro Ltd.4 */5#include <linux/kernel.h>6#include <linux/module.h>7#include <linux/mutex.h>8#include <linux/of_address.h>9#include "qcom_pil_info.h"10 11/*12 * The PIL relocation information region is used to communicate memory regions13 * occupied by co-processor firmware for post mortem crash analysis.14 *15 * It consists of an array of entries with an 8 byte textual identifier of the16 * region followed by a 64 bit base address and 32 bit size, both little17 * endian.18 */19#define PIL_RELOC_NAME_LEN 820#define PIL_RELOC_ENTRY_SIZE (PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32))21 22struct pil_reloc {23 void __iomem *base;24 size_t num_entries;25};26 27static struct pil_reloc _reloc __read_mostly;28static DEFINE_MUTEX(pil_reloc_lock);29 30static int qcom_pil_info_init(void)31{32 struct device_node *np;33 struct resource imem;34 void __iomem *base;35 int ret;36 37 /* Already initialized? */38 if (_reloc.base)39 return 0;40 41 np = of_find_compatible_node(NULL, NULL, "qcom,pil-reloc-info");42 if (!np)43 return -ENOENT;44 45 ret = of_address_to_resource(np, 0, &imem);46 of_node_put(np);47 if (ret < 0)48 return ret;49 50 base = ioremap(imem.start, resource_size(&imem));51 if (!base) {52 pr_err("failed to map PIL relocation info region\n");53 return -ENOMEM;54 }55 56 memset_io(base, 0, resource_size(&imem));57 58 _reloc.base = base;59 _reloc.num_entries = (u32)resource_size(&imem) / PIL_RELOC_ENTRY_SIZE;60 61 return 0;62}63 64/**65 * qcom_pil_info_store() - store PIL information of image in IMEM66 * @image: name of the image67 * @base: base address of the loaded image68 * @size: size of the loaded image69 *70 * Return: 0 on success, negative errno on failure71 */72int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size)73{74 char buf[PIL_RELOC_NAME_LEN];75 void __iomem *entry;76 int ret;77 int i;78 79 mutex_lock(&pil_reloc_lock);80 ret = qcom_pil_info_init();81 if (ret < 0) {82 mutex_unlock(&pil_reloc_lock);83 return ret;84 }85 86 for (i = 0; i < _reloc.num_entries; i++) {87 entry = _reloc.base + i * PIL_RELOC_ENTRY_SIZE;88 89 memcpy_fromio(buf, entry, PIL_RELOC_NAME_LEN);90 91 /*92 * An empty record means we didn't find it, given that the93 * records are packed.94 */95 if (!buf[0])96 goto found_unused;97 98 if (!strncmp(buf, image, PIL_RELOC_NAME_LEN))99 goto found_existing;100 }101 102 pr_warn("insufficient PIL info slots\n");103 mutex_unlock(&pil_reloc_lock);104 return -ENOMEM;105 106found_unused:107 memcpy_toio(entry, image, strnlen(image, PIL_RELOC_NAME_LEN));108found_existing:109 /* Use two writel() as base is only aligned to 4 bytes on odd entries */110 writel(base, entry + PIL_RELOC_NAME_LEN);111 writel((u64)base >> 32, entry + PIL_RELOC_NAME_LEN + 4);112 writel(size, entry + PIL_RELOC_NAME_LEN + sizeof(__le64));113 mutex_unlock(&pil_reloc_lock);114 115 return 0;116}117EXPORT_SYMBOL_GPL(qcom_pil_info_store);118 119static void __exit pil_reloc_exit(void)120{121 mutex_lock(&pil_reloc_lock);122 iounmap(_reloc.base);123 _reloc.base = NULL;124 mutex_unlock(&pil_reloc_lock);125}126module_exit(pil_reloc_exit);127 128MODULE_DESCRIPTION("Qualcomm PIL relocation info");129MODULE_LICENSE("GPL v2");130