qcom_pil_info.c (3097B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 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 regions 13 * 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 the 16 * region followed by a 64 bit base address and 32 bit size, both little 17 * endian. 18 */ 19#define PIL_RELOC_NAME_LEN 8 20#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 IMEM 66 * @image: name of the image 67 * @base: base address of the loaded image 68 * @size: size of the loaded image 69 * 70 * Return: 0 on success, negative errno on failure 71 */ 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 the 93 * 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");