From 5804dba5b0c0fc27aebf622b729ba06210158dac Mon Sep 17 00:00:00 2001 From: Deepak Kumar Singh Date: Tue, 7 Apr 2020 16:58:06 +0530 Subject: [PATCH] soc: qcom: smem: map only partitions used by local HOST SMEM driver is IO mapping complete region and CPU is doing a speculative read into a partition where local HOST does not have permission resulting in a NOC error. Map only those partitions which are accessibly to local HOST. CRs-Fixed: 2647368 Change-Id: If03b21f99642db861ea5d549cac9342b77ad3d43 Signed-off-by: Deepak Kumar Singh Signed-off-by: Sarannya S --- drivers/soc/qcom/smem.c | 276 ++++++++++++++++++++++++++++------------ 1 file changed, 197 insertions(+), 79 deletions(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 78147db94d87..3efb84c0c932 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -201,6 +201,19 @@ struct smem_partition_header { __le32 offset_free_cached; __le32 reserved[3]; }; +/** + * struct smem_partition_desc - descriptor for partition + * @virt_base: starting virtual address of partition + * @phys_base: starting physical address of partition + * @cacheline: alignment for "cached" entries + * @size: size of partition + */ +struct smem_partition_desc { + void __iomem *virt_base; + u32 phys_base; + u32 cacheline; + u32 size; +}; static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 }; @@ -257,9 +270,9 @@ struct smem_region { * struct qcom_smem - device data for the smem device * @dev: device pointer * @hwlock: reference to a hwspinlock - * @global_partition_entry: pointer to global partition entry when in use - * @ptable_entries: list of pointers to partitions table entry of current - * processor/host + * @ptable_base: virtual base of partition table + * @global_partition_desc: descriptor for global partition when in use + * @partition_desc: list of partition descriptor of current processor/host * @item_count: max accepted item number * @num_regions: number of @regions * @regions: list of the memory regions defining the shared memory @@ -269,9 +282,10 @@ struct qcom_smem { struct hwspinlock *hwlock; - struct smem_ptable_entry *global_partition_entry; - struct smem_ptable_entry *ptable_entries[SMEM_HOST_COUNT]; u32 item_count; + struct smem_ptable __iomem *ptable_base; + struct smem_partition_desc global_partition_desc; + struct smem_partition_desc partition_desc[SMEM_HOST_COUNT]; unsigned num_regions; struct smem_region regions[0]; @@ -283,12 +297,6 @@ static struct qcom_smem *__smem; /* Timeout (ms) for the trylock of remote spinlocks */ #define HWSPINLOCK_TIMEOUT 1000 -static struct smem_partition_header * -ptable_entry_to_phdr(struct smem_ptable_entry *entry) -{ - return __smem->regions[0].virt_base + le32_to_cpu(entry->offset); -} - static struct smem_private_entry * phdr_to_last_uncached_entry(struct smem_partition_header *phdr) { @@ -352,18 +360,18 @@ static void *cached_entry_to_item(struct smem_private_entry *e) } static int qcom_smem_alloc_private(struct qcom_smem *smem, - struct smem_ptable_entry *entry, + struct smem_partition_desc *p_desc, unsigned item, size_t size) { struct smem_private_entry *hdr, *end; - struct smem_partition_header *phdr; + struct smem_partition_header __iomem *phdr; size_t alloc_size; void *cached; - void *p_end; + void __iomem *p_end; - phdr = ptable_entry_to_phdr(entry); - p_end = (void *)phdr + le32_to_cpu(entry->size); + phdr = (struct smem_partition_header __iomem *)p_desc->virt_base; + p_end = (void __iomem *)phdr + p_desc->size; hdr = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); @@ -456,7 +464,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, */ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) { - struct smem_ptable_entry *entry; + struct smem_partition_desc *p_desc; unsigned long flags; int ret; @@ -478,12 +486,12 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) if (ret) return ret; - if (host < SMEM_HOST_COUNT && __smem->ptable_entries[host]) { - entry = __smem->ptable_entries[host]; - ret = qcom_smem_alloc_private(__smem, entry, item, size); - } else if (__smem->global_partition_entry) { - entry = __smem->global_partition_entry; - ret = qcom_smem_alloc_private(__smem, entry, item, size); + if (host < SMEM_HOST_COUNT && __smem->partition_desc[host].virt_base) { + p_desc = &__smem->partition_desc[host]; + ret = qcom_smem_alloc_private(__smem, p_desc, item, size); + } else if (__smem->global_partition_desc.virt_base) { + p_desc = &__smem->global_partition_desc; + ret = qcom_smem_alloc_private(__smem, p_desc, item, size); } else { ret = qcom_smem_alloc_global(__smem, item, size); } @@ -534,22 +542,20 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, } static void *qcom_smem_get_private(struct qcom_smem *smem, - struct smem_ptable_entry *entry, + struct smem_partition_desc *p_desc, unsigned item, size_t *size) { struct smem_private_entry *e, *end; - struct smem_partition_header *phdr; - void *item_ptr, *p_end; - u32 partition_size; + struct smem_partition_header __iomem *phdr; + void *item_ptr, __iomem *p_end; size_t cacheline; u32 padding_data; u32 e_size; - phdr = ptable_entry_to_phdr(entry); - partition_size = le32_to_cpu(entry->size); - p_end = (void *)phdr + partition_size; - cacheline = le32_to_cpu(entry->cacheline); + phdr = (struct smem_partition_header __iomem *)p_desc->virt_base; + p_end = (void __iomem *)phdr + p_desc->size; + cacheline = p_desc->cacheline; e = phdr_to_first_uncached_entry(phdr); end = phdr_to_last_uncached_entry(phdr); @@ -566,7 +572,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, e_size = le32_to_cpu(e->size); padding_data = le16_to_cpu(e->padding_data); - if (e_size < partition_size + if (e_size < p_desc->size && padding_data < e_size) *size = e_size - padding_data; else @@ -602,7 +608,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, e_size = le32_to_cpu(e->size); padding_data = le16_to_cpu(e->padding_data); - if (e_size < partition_size + if (e_size < p_desc->size && padding_data < e_size) *size = e_size - padding_data; else @@ -641,7 +647,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, */ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) { - struct smem_ptable_entry *entry; + struct smem_partition_desc *p_desc; unsigned long flags; int ret; void *ptr = ERR_PTR(-EPROBE_DEFER); @@ -658,12 +664,12 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) if (ret) return ERR_PTR(ret); - if (host < SMEM_HOST_COUNT && __smem->ptable_entries[host]) { - entry = __smem->ptable_entries[host]; - ptr = qcom_smem_get_private(__smem, entry, item, size); - } else if (__smem->global_partition_entry) { - entry = __smem->global_partition_entry; - ptr = qcom_smem_get_private(__smem, entry, item, size); + if (host < SMEM_HOST_COUNT && __smem->partition_desc[host].virt_base) { + p_desc = &__smem->partition_desc[host]; + ptr = qcom_smem_get_private(__smem, p_desc, item, size); + } else if (__smem->global_partition_desc.virt_base) { + p_desc = &__smem->global_partition_desc; + ptr = qcom_smem_get_private(__smem, p_desc, item, size); } else { ptr = qcom_smem_get_global(__smem, item, size); } @@ -684,31 +690,33 @@ EXPORT_SYMBOL(qcom_smem_get); */ int qcom_smem_get_free_space(unsigned host) { - struct smem_partition_header *phdr; - struct smem_ptable_entry *entry; + struct smem_partition_header __iomem *phdr; + struct smem_partition_desc *p_desc; struct smem_header *header; unsigned ret; if (!__smem) return -EPROBE_DEFER; - if (host < SMEM_HOST_COUNT && __smem->ptable_entries[host]) { - entry = __smem->ptable_entries[host]; - phdr = ptable_entry_to_phdr(entry); + if (host < SMEM_HOST_COUNT && __smem->partition_desc[host].virt_base) { + p_desc = &__smem->partition_desc[host]; + phdr = (struct smem_partition_header __iomem *) + p_desc->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); - if (ret > le32_to_cpu(entry->size)) + if (ret > p_desc->size) return -EINVAL; - } else if (__smem->global_partition_entry) { - entry = __smem->global_partition_entry; - phdr = ptable_entry_to_phdr(entry); + } else if (__smem->global_partition_desc.virt_base) { + p_desc = &__smem->global_partition_desc; + phdr = (struct smem_partition_header __iomem *) + p_desc->virt_base; ret = le32_to_cpu(phdr->offset_free_cached) - le32_to_cpu(phdr->offset_free_uncached); - if (ret > le32_to_cpu(entry->size)) + if (ret > p_desc->size) return -EINVAL; } else { header = __smem->regions[0].virt_base; @@ -722,6 +730,16 @@ int qcom_smem_get_free_space(unsigned host) } EXPORT_SYMBOL(qcom_smem_get_free_space); +static int addr_in_range(void __iomem *virt_base, unsigned int size, + void *addr) +{ + if (virt_base && addr >= virt_base && + addr < virt_base + size) + return 1; + + return 0; +} + /** * qcom_smem_virt_to_phys() - Convert SMEM address to physical address. * @smem_address Address of SMEM item (returned by qcom_smem_get()) @@ -729,24 +747,42 @@ EXPORT_SYMBOL(qcom_smem_get_free_space); * This function should only be used if an SMEM item needs to be handed off * to a DMA engine. */ -phys_addr_t qcom_smem_virt_to_phys(void *addr) +phys_addr_t qcom_smem_virt_to_phys(void *p) { - phys_addr_t phys_addr = 0; + struct smem_partition_desc *p_desc; struct smem_region *area; - void *end; - int i; + u64 offset; + u32 i; - if (!__smem) - return phys_addr; + for (i = 0; i < SMEM_HOST_COUNT; i++) { + p_desc = &__smem->partition_desc[i]; + + if (addr_in_range(p_desc->virt_base, p_desc->size, p)) { + offset = p - p_desc->virt_base; + + return (phys_addr_t)p_desc->phys_base + offset; + } + } + + p_desc = &__smem->global_partition_desc; + + if (addr_in_range(p_desc->virt_base, p_desc->size, p)) { + offset = p - p_desc->virt_base; + + return (phys_addr_t)p_desc->phys_base + offset; + } for (i = 0; i < __smem->num_regions; i++) { area = &__smem->regions[i]; - end = area->virt_base + area->size; - if (addr >= area->virt_base && addr < end) - phys_addr = addr - area->virt_base + area->aux_base; + + if (addr_in_range(area->virt_base, area->size, p)) { + offset = p - area->virt_base; + + return (phys_addr_t)area->aux_base + offset; + } } - return phys_addr; + return 0; } EXPORT_SYMBOL(qcom_smem_virt_to_phys); @@ -761,12 +797,12 @@ static int qcom_smem_get_sbl_version(struct qcom_smem *smem) return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]); } -static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem) +static struct smem_ptable __iomem *qcom_smem_get_ptable(struct qcom_smem *smem) { - struct smem_ptable *ptable; + struct smem_ptable __iomem *ptable; u32 version; - ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K; + ptable = smem->ptable_base; if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) return NULL; @@ -797,12 +833,19 @@ static u32 qcom_smem_get_dynamic_item(struct qcom_smem *smem) static int qcom_smem_set_global_partition(struct qcom_smem *smem) { - struct smem_partition_header *header; - struct smem_ptable_entry *entry = NULL; - struct smem_ptable *ptable; + struct smem_partition_header __iomem *header; + struct smem_ptable_entry *entry; + struct smem_ptable __iomem *ptable; + u32 phys_addr; u32 host0, host1, size; + bool found = false; int i; + if (smem->global_partition_desc.virt_base) { + dev_err(smem->dev, "Already found the global partition\n"); + return -EINVAL; + } + ptable = qcom_smem_get_ptable(smem); if (IS_ERR_OR_NULL(ptable)) return -EINVAL; @@ -812,8 +855,10 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem) host0 = le16_to_cpu(entry->host0); host1 = le16_to_cpu(entry->host1); - if (host0 == SMEM_GLOBAL_HOST && host0 == host1) + if (host0 == SMEM_GLOBAL_HOST && host0 == host1) { + found = true; break; + } } if (!entry) { @@ -826,12 +871,12 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem) return -EINVAL; } - if (smem->global_partition_entry) { - dev_err(smem->dev, "Already found the global partition\n"); - return -EINVAL; - } + phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset); + header = (struct smem_partition_header __iomem *)devm_ioremap_wc + (smem->dev, phys_addr, le32_to_cpu(entry->size)); + if (!header) + return -ENOMEM; - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); host0 = le16_to_cpu(header->host0); host1 = le16_to_cpu(header->host1); @@ -857,7 +902,10 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem) return -EINVAL; } - smem->global_partition_entry = entry; + smem->global_partition_desc.virt_base = (void __iomem *)header; + smem->global_partition_desc.phys_base = phys_addr; + smem->global_partition_desc.size = le32_to_cpu(entry->size); + smem->global_partition_desc.cacheline = le32_to_cpu(entry->cacheline); return 0; } @@ -865,9 +913,10 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem) static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, unsigned int local_host) { - struct smem_partition_header *header; + struct smem_partition_header __iomem *header; struct smem_ptable_entry *entry; - struct smem_ptable *ptable; + struct smem_ptable __iomem *ptable; + u32 phys_addr; unsigned int remote_host; u32 host0, host1; int i; @@ -902,14 +951,21 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, return -EINVAL; } - if (smem->ptable_entries[remote_host]) { + if (smem->partition_desc[remote_host].virt_base) { dev_err(smem->dev, "Already found a partition for host %d\n", remote_host); return -EINVAL; } - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); + phys_addr = smem->regions[0].aux_base + + le32_to_cpu(entry->offset); + header = (struct smem_partition_header __iomem *)devm_ioremap_wc + (smem->dev, phys_addr, + le32_to_cpu(entry->size)); + if (!header) + return -ENOMEM; + host0 = le16_to_cpu(header->host0); host1 = le16_to_cpu(header->host1); @@ -944,7 +1000,13 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, return -EINVAL; } - smem->ptable_entries[remote_host] = entry; + smem->partition_desc[remote_host].virt_base = + (void __iomem *)header; + smem->partition_desc[remote_host].phys_base = phys_addr; + smem->partition_desc[remote_host].size = + le32_to_cpu(entry->size); + smem->partition_desc[remote_host].cacheline = + le32_to_cpu(entry->cacheline); } return 0; @@ -977,6 +1039,61 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev, return 0; } +static int qcom_smem_map_toc(struct qcom_smem *smem, struct device *dev, + const char *name, int i) +{ + struct device_node *np; + struct resource r; + int ret; + + np = of_parse_phandle(dev->of_node, name, 0); + if (!np) { + dev_err(dev, "No %s specified\n", name); + return -EINVAL; + } + + ret = of_address_to_resource(np, 0, &r); + of_node_put(np); + if (ret) + return ret; + + smem->regions[i].aux_base = (u32)r.start; + smem->regions[i].size = resource_size(&r); + /* map starting 4K for smem header */ + smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, SZ_4K); + /* map last 4k for toc */ + smem->ptable_base = (struct smem_ptable __iomem *)devm_ioremap_wc(dev, + r.start + resource_size(&r) - SZ_4K, SZ_4K); + + if (!smem->regions[i].virt_base || !smem->ptable_base) + return -ENOMEM; + + return 0; +} + +static int qcom_smem_mamp_legacy(struct qcom_smem *smem) +{ + struct smem_header __iomem *header; + u32 phys_addr; + u32 p_size; + + phys_addr = smem->regions[0].aux_base; + header = (struct smem_header __iomem *)smem->regions[0].virt_base; + p_size = readl_relaxed(&header->available); + + /* unmap previously mapped starting 4k for smem header */ + devm_iounmap(smem->dev, smem->regions[0].virt_base); + + smem->regions[0].size = p_size; + smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, + phys_addr, p_size); + + if (!smem->regions[0].virt_base) + return -ENOMEM; + + return 0; +} + static int qcom_smem_probe(struct platform_device *pdev) { struct smem_header *header; @@ -999,7 +1116,7 @@ static int qcom_smem_probe(struct platform_device *pdev) smem->dev = &pdev->dev; smem->num_regions = num_regions; - ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0); + ret = qcom_smem_map_toc(smem, &pdev->dev, "memory-region", 0); if (ret) return ret; @@ -1023,6 +1140,7 @@ static int qcom_smem_probe(struct platform_device *pdev) smem->item_count = qcom_smem_get_dynamic_item(smem); break; case SMEM_GLOBAL_HEAP_VERSION: + qcom_smem_mamp_legacy(smem); smem->item_count = SMEM_ITEM_COUNT; break; default: