// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2023, STMicroelectronics */ #include #include #include #include #ifdef CFG_STM32MP15 #include #endif #include #include #include #include #include #include #include #define TIMEOUT_US_1MS U(1000) /** * struct stm32_rproc_mem - Memory regions used by the remote processor * * @addr: physical base address from the CPU space perspective * @da: device address corresponding to the physical base address * from remote processor space perspective * @size: size of the region */ struct stm32_rproc_mem { paddr_t addr; paddr_t da; size_t size; }; /** * struct stm32_rproc_instance - rproc instance context * * @cdata: pointer to the device compatible data * @link: the node in the rproc_list * @n_regions: number of memory regions * @regions: memory regions used * @mcu_rst: remote processor reset control * @hold_boot: remote processor hold boot control * @ns_loading: True if supports non-secure firmware loading, false otherwise */ struct stm32_rproc_instance { const struct stm32_rproc_compat_data *cdata; SLIST_ENTRY(stm32_rproc_instance) link; size_t n_regions; struct stm32_rproc_mem *regions; struct rstctrl *mcu_rst; struct rstctrl *hold_boot; bool ns_loading; }; /** * struct stm32_rproc_compat_data - rproc associated data for compatible list * * @rproc_id: identifies the remote processor */ struct stm32_rproc_compat_data { uint32_t rproc_id; }; static SLIST_HEAD(, stm32_rproc_instance) rproc_list = SLIST_HEAD_INITIALIZER(rproc_list); void *stm32_rproc_get(uint32_t rproc_id) { struct stm32_rproc_instance *rproc = NULL; SLIST_FOREACH(rproc, &rproc_list, link) if (rproc->cdata->rproc_id == rproc_id) break; return rproc; } bool stm32_rproc_is_secure(uint32_t rproc_id) { struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); if (rproc) return !rproc->cdata->ns_loading; return false; } TEE_Result stm32_rproc_start(uint32_t rproc_id) { struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); TEE_Result res = TEE_ERROR_GENERIC; if (!rproc || !rproc->hold_boot) return TEE_ERROR_GENERIC; /* * The firmware is started by de-asserting the hold boot and * asserting it back to avoid auto restart on a crash. * No need to release the MCU reset as it is automatically released by * the hardware. */ res = rstctrl_deassert_to(rproc->hold_boot, TIMEOUT_US_1MS); if (!res) res = rstctrl_assert_to(rproc->hold_boot, TIMEOUT_US_1MS); return res; } static TEE_Result rproc_stop(struct stm32_rproc_instance *rproc) { TEE_Result res = TEE_ERROR_GENERIC; if (!rproc->hold_boot || !rproc->mcu_rst) return TEE_ERROR_GENERIC; res = rstctrl_assert_to(rproc->hold_boot, TIMEOUT_US_1MS); if (!res) res = rstctrl_assert_to(rproc->mcu_rst, TIMEOUT_US_1MS); return res; } TEE_Result stm32_rproc_stop(uint32_t rproc_id) { struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); if (!rproc) return TEE_ERROR_BAD_PARAMETERS; return rproc_stop(rproc); } TEE_Result stm32_rproc_da_to_pa(uint32_t rproc_id, paddr_t da, size_t size, paddr_t *pa) { struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); struct stm32_rproc_mem *mems = NULL; unsigned int i = 0; if (!rproc) return TEE_ERROR_BAD_PARAMETERS; mems = rproc->regions; for (i = 0; i < rproc->n_regions; i++) { if (core_is_buffer_inside(da, size, mems[i].da, mems[i].size)) { /* * A match between the requested DA memory area and the * registered regions has been found. * The PA is the reserved-memory PA address plus the * delta between the requested DA and the * reserved-memory DA address. */ *pa = mems[i].addr + da - mems[i].da; return TEE_SUCCESS; } } return TEE_ERROR_ACCESS_DENIED; } TEE_Result stm32_rproc_map(uint32_t rproc_id, paddr_t pa, size_t size, void **va) { struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); struct stm32_rproc_mem *mems = NULL; unsigned int i = 0; if (!rproc) return TEE_ERROR_BAD_PARAMETERS; mems = rproc->regions; for (i = 0; i < rproc->n_regions; i++) { if (!core_is_buffer_inside(pa, size, mems[i].addr, mems[i].size)) continue; *va = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, pa, size); if (!*va) { EMSG("Can't map region %#"PRIxPA" size %zu", pa, size); return TEE_ERROR_GENERIC; } return TEE_SUCCESS; } return TEE_ERROR_ACCESS_DENIED; } TEE_Result stm32_rproc_unmap(uint32_t rproc_id, void *va, size_t size) { struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); struct stm32_rproc_mem *mems = NULL; paddr_t pa = virt_to_phys(va); unsigned int i = 0; if (!rproc || !pa) return TEE_ERROR_BAD_PARAMETERS; mems = rproc->regions; for (i = 0; i < rproc->n_regions; i++) { if (!core_is_buffer_inside(pa, size, mems[i].addr, mems[i].size)) continue; /* Flush the cache before unmapping the memory */ dcache_clean_range(va, size); if (core_mmu_remove_mapping(MEM_AREA_RAM_NSEC, va, size)) { EMSG("Can't unmap region %#"PRIxPA" size %zu", pa, size); return TEE_ERROR_GENERIC; } return TEE_SUCCESS; } return TEE_ERROR_ACCESS_DENIED; } static TEE_Result stm32_rproc_get_dma_range(struct stm32_rproc_mem *region, const void *fdt, int node) { const fdt32_t *list = NULL; int ahb_node = 0; int len = 0; int nranges = 0; int i = 0; /* * The match between local and remote processor memory mapping is * described in the dma-ranges defined by the bus parent node. */ ahb_node = fdt_parent_offset(fdt, node); list = fdt_getprop(fdt, ahb_node, "dma-ranges", &len); if (!list) { if (len != -FDT_ERR_NOTFOUND) return TEE_ERROR_GENERIC; /* Same memory mapping */ DMSG("No dma-ranges found in DT"); region->da = region->addr; return TEE_SUCCESS; } if ((len % (sizeof(uint32_t) * 3))) return TEE_ERROR_GENERIC; nranges = len / sizeof(uint32_t); for (i = 0; i < nranges; i += 3) { uint32_t da = fdt32_to_cpu(list[i]); uint32_t pa = fdt32_to_cpu(list[i + 1]); uint32_t size = fdt32_to_cpu(list[i + 2]); if (core_is_buffer_inside(region->addr, region->size, pa, size)) { region->da = da + (region->addr - pa); return TEE_SUCCESS; } } return TEE_ERROR_BAD_PARAMETERS; } /* Get device tree memory regions reserved for the Cortex-M and the IPC */ static TEE_Result stm32_rproc_parse_mems(struct stm32_rproc_instance *rproc, const void *fdt, int node) { const fdt32_t *list = NULL; TEE_Result res = TEE_ERROR_GENERIC; struct stm32_rproc_mem *regions = NULL; int len = 0; int n_regions = 0; int i = 0; list = fdt_getprop(fdt, node, "memory-region", &len); if (!list) { EMSG("No memory regions found in DT"); return TEE_ERROR_GENERIC; } n_regions = len / sizeof(uint32_t); regions = calloc(n_regions, sizeof(*regions)); if (!regions) return TEE_ERROR_OUT_OF_MEMORY; for (i = 0; i < n_regions; i++) { int pnode = 0; pnode = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(list[i])); if (pnode < 0) { res = TEE_ERROR_GENERIC; goto err; } if (fdt_reg_info(fdt, pnode, ®ions[i].addr, ®ions[i].size)) { res = TEE_ERROR_GENERIC; goto err; } #if defined(CFG_STM32MP15) if (stm32mp1_ram_intersect_pager_ram(regions[i].addr, regions[i].size)) { EMSG("Region %#"PRIxPA"..%#"PRIxPA" intersects pager secure memory", regions[i].addr, regions[i].addr + regions[i].size); return TEE_ERROR_GENERIC; } #endif res = stm32_rproc_get_dma_range(®ions[i], fdt, node); if (res) goto err; if (!regions[i].addr || !regions[i].size) { res = TEE_ERROR_BAD_PARAMETERS; goto err; } DMSG("register region %#"PRIxPA" size %#zx", regions[i].addr, regions[i].size); } rproc->n_regions = n_regions; rproc->regions = regions; return TEE_SUCCESS; err: free(regions); return res; } TEE_Result stm32_rproc_clean_up_memories(uint32_t rproc_id) { struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); struct stm32_rproc_mem *mems = NULL; TEE_Result res = TEE_ERROR_GENERIC; unsigned int i = 0; void *va = NULL; size_t size = 0; paddr_t pa = 0; if (!rproc) return TEE_ERROR_BAD_PARAMETERS; mems = rproc->regions; for (i = 0; i < rproc->n_regions; i++) { pa = mems[i].addr; size = mems[i].size; res = stm32_rproc_map(rproc_id, pa, size, &va); if (res) break; memset(va, 0, size); res = stm32_rproc_unmap(rproc_id, va, size); if (res) break; } return res; } static void stm32_rproc_cleanup(struct stm32_rproc_instance *rproc) { free(rproc->regions); free(rproc); } static TEE_Result stm32_rproc_probe(const void *fdt, int node, const void *comp_data) { struct stm32_rproc_instance *rproc = NULL; TEE_Result res = TEE_ERROR_GENERIC; rproc = calloc(1, sizeof(*rproc)); if (!rproc) return TEE_ERROR_OUT_OF_MEMORY; rproc->cdata = comp_data; res = stm32_rproc_parse_mems(rproc, fdt, node); if (res) goto err; res = rstctrl_dt_get_by_name(fdt, node, "mcu_rst", &rproc->mcu_rst); if (res) goto err; res = rstctrl_dt_get_by_name(fdt, node, "hold_boot", &rproc->hold_boot); if (res) goto err; /* Ensure that the MCU is HOLD */ if (rproc->mcu_rst) { res = rproc_stop(rproc); if (res) goto err; } if (!stm32_rcc_is_secure()) { IMSG("WARNING: insecure rproc isolation, RCC is not secure"); if (!IS_ENABLED(CFG_INSECURE)) panic(); } else { stm32_rcc_set_mckprot(true); } /* * The memory management should be enhance with firewall * mechanism to map the memory in secure area for the firmware * loading and then to give exclusive access right to the * coprocessor (except for the shared memory). */ IMSG("Warning: the remoteproc memories are not protected by firewall"); SLIST_INSERT_HEAD(&rproc_list, rproc, link); return TEE_SUCCESS; err: stm32_rproc_cleanup(rproc); return res; } static const struct stm32_rproc_compat_data stm32_rproc_m4_compat = { .rproc_id = STM32_M4_RPROC_ID, .ns_loading = false, }; static const struct dt_device_match stm32_rproc_match_table[] = { { .compatible = "st,stm32mp1-m4-tee", .compat_data = &stm32_rproc_m4_compat, }, { } }; DEFINE_DT_DRIVER(stm32_rproc_dt_driver) = { .name = "stm32-rproc", .match_table = stm32_rproc_match_table, .probe = &stm32_rproc_probe, };