From edad30e9fa6ed1341865e3b7e6f57ea1aa9e9c2b Mon Sep 17 00:00:00 2001 From: Dwitam Ghosh Date: Wed, 26 Aug 2026 21:39:16 +0530 Subject: [PATCH] drivers: qcom: add SMEM driver Add Qualcomm SMEM driver for OP-TEE. Structure the implementation as a platform-independent common layer (qti_smem.c/h), a TEE-specific platform abstraction layer (qti_smem_plat.c/h), and a clean public API. qti_smem_init() - initialize driver, validate TOC and map partitions qti_smem_host_id() - construct a SMEM host identifier qti_smem_lookup() - look up an existing SMEM item by host and item ID AI Usage: This contribution was developed, designed, implemented, and validated by the authors. AI-assisted tools were used only to help with code review, documentation drafting, commit message refinement, and general development productivity. No code was automatically generated and submitted without author review and verification. Change-Id: I103398bc7380fe21d846e98fa44dc4df997c087c Signed-off-by: Dwitam Ghosh Signed-off-by: Rameshwar Varaganti --- .../arch/arm/plat-qcom/wildcat/nord/target.mk | 3 + .../plat-qcom/wildcat/nord/target_config.h | 4 + core/drivers/qcom/smem/smem.c | 579 ++++++++++++++++++ core/drivers/qcom/smem/smem_internal.h | 248 ++++++++ core/drivers/qcom/smem/smem_plat.c | 89 +++ core/drivers/qcom/smem/smem_plat.h | 99 +++ core/drivers/qcom/smem/sub.mk | 7 + core/drivers/qcom/sub.mk | 1 + core/include/drivers/qcom/smem/smem.h | 147 +++++ 9 files changed, 1177 insertions(+) create mode 100644 core/drivers/qcom/smem/smem.c create mode 100644 core/drivers/qcom/smem/smem_internal.h create mode 100644 core/drivers/qcom/smem/smem_plat.c create mode 100644 core/drivers/qcom/smem/smem_plat.h create mode 100644 core/drivers/qcom/smem/sub.mk create mode 100644 core/include/drivers/qcom/smem/smem.h diff --git a/core/arch/arm/plat-qcom/wildcat/nord/target.mk b/core/arch/arm/plat-qcom/wildcat/nord/target.mk index c774f510f..b8f8c08eb 100644 --- a/core/arch/arm/plat-qcom/wildcat/nord/target.mk +++ b/core/arch/arm/plat-qcom/wildcat/nord/target.mk @@ -11,3 +11,6 @@ $(call force,CFG_TEE_CORE_NB_CORE,18) CFG_TZDRAM_START ?= 0xBC280000 CFG_TEE_RAM_VA_SIZE ?= 0x00200000 CFG_TA_RAM_VA_SIZE ?= 0x07B80000 + +# Enabling SMEM Support +CFG_QCOM_SMEM ?= y \ No newline at end of file diff --git a/core/arch/arm/plat-qcom/wildcat/nord/target_config.h b/core/arch/arm/plat-qcom/wildcat/nord/target_config.h index 3d7065ff7..1369f2310 100644 --- a/core/arch/arm/plat-qcom/wildcat/nord/target_config.h +++ b/core/arch/arm/plat-qcom/wildcat/nord/target_config.h @@ -18,4 +18,8 @@ #define DRAM2_BASE ULL(0x8800000000) #define DRAM2_SIZE ULL(0x3800000000) +/* SMEM base address */ +#define SMEM_BASE 0x89B00000 +#define SMEM_SIZE 0x00400000 + #endif /* TARGET_CONFIG_H */ diff --git a/core/drivers/qcom/smem/smem.c b/core/drivers/qcom/smem/smem.c new file mode 100644 index 000000000..199fa3a85 --- /dev/null +++ b/core/drivers/qcom/smem/smem.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* + * Qualcomm Shared Memory (SMEM) - Common Core + * + */ + +#include +#include +#include +#include + +#include "smem_internal.h" +#include "smem_plat.h" + +/* ----------------------------------------------------------------------- + * Driver state instance + * ----------------------------------------------------------------------- + */ + +/* Single static instance */ +static struct qti_smem_info qti_smem_info; + +/* ----------------------------------------------------------------------- + * Internal helpers + * ----------------------------------------------------------------------- + */ + +static inline uint16_t smem_rd16(const void *ptr) +{ + uint16_t val; + + memcpy(&val, ptr, sizeof(val)); + return val; +} + +static inline uint32_t smem_rd32(const void *ptr) +{ + uint32_t val; + + memcpy(&val, ptr, sizeof(val)); + return val; +} + +static TEE_Result smem_validate_toc_entry(const struct qti_smem_toc_entry *e) +{ + uint32_t off = smem_rd32(&e->offset); + uint32_t sz = smem_rd32(&e->size); + uint64_t end; + + /* Partition must be large enough to hold the partition header. */ + if (sz < (uint32_t)sizeof(struct qti_smem_partition_header)) + return TEE_ERROR_BAD_FORMAT; + /* offset + size must not overflow and must lie within SMEM. */ + end = (uint64_t)off + (uint64_t)sz; + if (off < QTI_SMEM_BOOT_INFO_SIZE || + end > (uint64_t)qti_smem_info.toc_offset) + return TEE_ERROR_BAD_FORMAT; + return TEE_SUCCESS; +} + +static int smem_part_involves_local(const struct qti_smem_toc_entry *e) +{ + uint16_t lh = (uint16_t)qti_smem_info.local_host; + uint16_t h0 = smem_rd16(&e->host0); + uint16_t h1 = smem_rd16(&e->host1); + + /* Common partition - accessible to all hosts. */ + if (h0 == (uint16_t)QTI_SMEM_HOST_COMMON && + h1 == (uint16_t)QTI_SMEM_HOST_COMMON) { + return 1; + } + + /* Edge-pair partition: local host is one endpoint. */ + return ((h0 == lh) || (h1 == lh)) ? 1 : 0; +} + +static int smem_part_matches(const struct qti_smem_toc_entry *e, uint16_t host) +{ + uint16_t lh = (uint16_t)qti_smem_info.local_host; + uint16_t rh = host; + uint16_t h0 = smem_rd16(&e->host0); + uint16_t h1 = smem_rd16(&e->host1); + + /* Common partition lookup. */ + if (host == QTI_SMEM_HOST_COMMON) { + return ((h0 == (uint16_t)QTI_SMEM_HOST_COMMON) && + (h1 == (uint16_t)QTI_SMEM_HOST_COMMON)) ? + 1 : + 0; + } + + /* Edge-pair: one endpoint must be local, the other must be remote. */ + return (((h0 == lh) && (h1 == rh)) || ((h0 == rh) && (h1 == lh))) ? 1 : + 0; +} + +static TEE_Result smem_scan_uncached(const uint8_t *base, uint32_t scan_limit, + uint16_t item_id, void **addr, + size_t *size) +{ + const uint8_t *limit; + const uint8_t *ptr; + const struct qti_smem_item_header *ihdr; + uint32_t item_size; + uint32_t step = 0U; + + if (scan_limit < (uint32_t)sizeof(struct qti_smem_partition_header)) + return TEE_ERROR_BAD_FORMAT; + + limit = base + scan_limit; + + for (ptr = base + sizeof(struct qti_smem_partition_header); ptr < limit; + ptr += (size_t)step) { + if ((uintptr_t)limit - (uintptr_t)ptr < + sizeof(struct qti_smem_item_header)) + return TEE_ERROR_BAD_FORMAT; + + ihdr = (const struct qti_smem_item_header *)(const void *)ptr; + + if (smem_rd16(&ihdr->canary) != (uint16_t)QTI_SMEM_ITEM_CANARY) + return TEE_ERROR_BAD_FORMAT; + + item_size = smem_rd32(&ihdr->size); + + if (item_size == 0U) + return TEE_ERROR_BAD_FORMAT; + if ((uint32_t)smem_rd16(&ihdr->padding_data) > item_size) + return TEE_ERROR_BAD_FORMAT; + + step = (uint32_t)sizeof(struct qti_smem_item_header) + + (uint32_t)smem_rd16(&ihdr->padding_header) + item_size; + if (step < item_size) + return TEE_ERROR_BAD_FORMAT; + if ((uintptr_t)limit - (uintptr_t)ptr < (uintptr_t)step) + return TEE_ERROR_BAD_FORMAT; + + if (smem_rd16(&ihdr->item) != item_id) + continue; + + *addr = (void *)(ptr + sizeof(struct qti_smem_item_header) + + (size_t)smem_rd16(&ihdr->padding_header)); + if (size) { + size_t pad = (size_t)smem_rd16(&ihdr->padding_data); + + *size = (size_t)(item_size - pad); + } + goto out; + } + return TEE_ERROR_ITEM_NOT_FOUND; +out: + return TEE_SUCCESS; +} + +static TEE_Result smem_search_partition(uint32_t part_offset, + uint32_t part_size, uint16_t item_id, + void **addr, size_t *size) +{ + const struct qti_smem_partition_header *phdr; + void *va; + TEE_Result rc; + uint32_t offset_free_uncached; + uint32_t offset_free_cached; + uint32_t min_uncached; + + /* Validate partition range before any memory access. */ + if (part_size < (uint32_t)sizeof(struct qti_smem_partition_header)) + return TEE_ERROR_BAD_FORMAT; + if ((uint64_t)part_offset + (uint64_t)part_size > + (uint64_t)qti_smem_info.smem_size) { + return TEE_ERROR_BAD_FORMAT; + } + + va = qti_smem_plat_get_addr(part_offset); + if (!va) + return TEE_ERROR_ACCESS_DENIED; + + phdr = (const struct qti_smem_partition_header *)va; + + /* + * Validate static partition fields. + * magic and size are written once at partition creation time and + * never change, so they can be read without the HW lock. + */ + if (smem_rd32(&phdr->magic) != QTI_SMEM_PART_MAGIC) { + EMSG("smem: bad partition magic 0x%08x\n", + (unsigned int)smem_rd32(&phdr->magic)); + return TEE_ERROR_BAD_FORMAT; + } + if (smem_rd32(&phdr->size) != part_size) { + EMSG("smem: partition size mismatch (header=%u toc=%u)\n", + (unsigned int)smem_rd32(&phdr->size), + (unsigned int)part_size); + return TEE_ERROR_BAD_FORMAT; + } + + /* Read mutable heap pointers. */ + offset_free_uncached = smem_rd32(&phdr->offset_free_uncached); + offset_free_cached = smem_rd32(&phdr->offset_free_cached); + + /* + * Validate heap pointers: + * offset_free_uncached >= sizeof(partition_header) + * offset_free_uncached <= offset_free_cached + * offset_free_cached <= part_size + */ + min_uncached = (uint32_t)sizeof(struct qti_smem_partition_header); + if (offset_free_uncached < min_uncached || + offset_free_uncached > offset_free_cached || + offset_free_cached > part_size) { + EMSG("smem: bad heap pointers uncached=%u cached=%u size=%u\n", + (unsigned int)offset_free_uncached, + (unsigned int)offset_free_cached, (unsigned int)part_size); + return TEE_ERROR_BAD_FORMAT; + } + + rc = smem_scan_uncached((const uint8_t *)va, offset_free_uncached, + item_id, addr, size); + + return rc; +} + +static TEE_Result +smem_validate_boot_version(const struct qti_smem_static_header *static_hdr) +{ + uint32_t boot_version; + uint32_t boot_major; + uint32_t local_major; + + boot_version = + smem_rd32(&static_hdr->ver[QTI_SMEM_VERSION_BOOT_OFFSET]); + boot_major = boot_version & QTI_SMEM_MAJOR_VERSION_MASK; + local_major = QTI_SMEM_VERSION_ID & QTI_SMEM_MAJOR_VERSION_MASK; + + if (boot_major != local_major) { + EMSG("smem: BOOT version mismatch: 0x%08x vs 0x%08x\n", + (unsigned int)boot_version, (unsigned int)local_major); + return TEE_ERROR_NOT_SUPPORTED; + } + + return TEE_SUCCESS; +} + +static TEE_Result +smem_validate_toc_header(const struct qti_smem_toc_header *toc, + uint32_t *num_entries_out) +{ + uint32_t num_entries; + + if (smem_rd32(&toc->magic) != QTI_SMEM_TOC_MAGIC) { + EMSG("smem: bad TOC magic 0x%08x\n", + (unsigned int)smem_rd32(&toc->magic)); + return TEE_ERROR_BAD_FORMAT; + } + + if (smem_rd32(&toc->version) != QTI_SMEM_TOC_VERSION) { + EMSG("smem: unsupported TOC version %u\n", + (unsigned int)smem_rd32(&toc->version)); + return TEE_ERROR_NOT_SUPPORTED; + } + + num_entries = smem_rd32(&toc->num_entries); + if (num_entries == 0U || num_entries > QTI_SMEM_TOC_MAX_ENTRIES) { + EMSG("smem: invalid TOC num_entries %u\n", + (unsigned int)num_entries); + return TEE_ERROR_BAD_FORMAT; + } + + /* Entries array must fit within the TOC page. */ + if ((uint32_t)sizeof(struct qti_smem_toc_header) + + num_entries * (uint32_t)sizeof(struct qti_smem_toc_entry) > + QTI_SMEM_TOC_SIZE) { + EMSG("smem: TOC entries overflow TOC page\n"); + return TEE_ERROR_BAD_FORMAT; + } + + *num_entries_out = num_entries; + return TEE_SUCCESS; +} + +static TEE_Result smem_map_partitions(const struct qti_smem_toc_entry *entries, + uint32_t num_entries) +{ + uint32_t i; + TEE_Result ret; + + for (i = 0U; i < num_entries; i++) { + const struct qti_smem_toc_entry *e = &entries[i]; + + if (smem_validate_toc_entry(e) != TEE_SUCCESS) + continue; /* skip malformed entries silently */ + + if (smem_part_involves_local(e) == 0) + continue; /* not relevant partition - do not map */ + + ret = qti_smem_plat_map(smem_rd32(&e->offset), + (size_t)smem_rd32(&e->size), + QTI_SMEM_PLAT_MAP_RW); + if (ret != TEE_SUCCESS) { + EMSG("smem: failed to map partition %u/%u: %#x\n", + (unsigned int)smem_rd16(&e->host0), + (unsigned int)smem_rd16(&e->host1), + (unsigned int)ret); + return ret; + } + + if (smem_rd16(&e->host0) == (uint16_t)QTI_SMEM_HOST_COMMON && + smem_rd16(&e->host1) == (uint16_t)QTI_SMEM_HOST_COMMON && + qti_smem_info.common_part_offset == 0U) { + qti_smem_info.common_part_offset = + smem_rd32(&e->offset); + qti_smem_info.common_part_size = smem_rd32(&e->size); + } + } + + return TEE_SUCCESS; +} + +/* ----------------------------------------------------------------------- + * Public API + * ----------------------------------------------------------------------- + */ + +TEE_Result qti_smem_host_id(uint16_t proc_id, uint16_t proc_num, + uint16_t pd_num, uint16_t chiplet, uint16_t *host) +{ + uint16_t encoded; + + if (!host) + return TEE_ERROR_BAD_PARAMETERS; + + /* Range checks: each field must fit in its allocated bit-width. */ + if ((uint32_t)proc_id > HOST_PROC_ID_MASK) + return TEE_ERROR_BAD_PARAMETERS; + if ((uint32_t)proc_num > HOST_PROC_NUM_MASK) + return TEE_ERROR_BAD_PARAMETERS; + if ((uint32_t)pd_num > HOST_PD_NUM_MASK) + return TEE_ERROR_BAD_PARAMETERS; + if ((uint32_t)chiplet > HOST_CHIPLET_MASK) + return TEE_ERROR_BAD_PARAMETERS; + + encoded = (uint16_t)(((uint16_t)proc_id << HOST_PROC_ID_SHIFT) | + ((uint16_t)proc_num << HOST_PROC_NUM_SHIFT) | + ((uint16_t)pd_num << HOST_PD_NUM_SHIFT) | + ((uint16_t)chiplet << HOST_CHIPLET_SHIFT)); + + /* + * Reject any encoding that collides with a reserved/special host. + * These checks protect against accidental construction of reserved + * values even when the individual field ranges permit it. + */ + if (encoded == (uint16_t)QTI_SMEM_HOST_COMMON) + return TEE_ERROR_BAD_PARAMETERS; + if (encoded == (uint16_t)QTI_SMEM_HOST_INVALID) + return TEE_ERROR_BAD_PARAMETERS; + if (encoded == (uint16_t)QTI_SMEM_HOST_MULTIHOST) + return TEE_ERROR_BAD_PARAMETERS; + + *host = encoded; + return TEE_SUCCESS; +} + +/* + * qti_smem_init() - Initialize the SMEM common core. + * + * Return: TEE_SUCCESS on success, TEE_ERROR_BAD_STATE if already initialized, + * or another TEE_ERROR_* value on failure. + */ +TEE_Result qti_smem_init(void) +{ + struct qti_smem_plat_info plat_info; + const struct qti_smem_static_header *static_hdr; + const struct qti_smem_toc_header *toc; + const struct qti_smem_toc_entry *entries; + void *va; + uint32_t smem_size; + uint32_t toc_offset; + uint32_t num_entries; + TEE_Result ret; + + if (qti_smem_info.initialized != 0) + return TEE_ERROR_BAD_STATE; + + ret = qti_smem_plat_init(&plat_info); + if (ret != TEE_SUCCESS) + return ret; + + if (plat_info.local_host == (uint16_t)QTI_SMEM_HOST_INVALID) { + EMSG("smem: invalid local_host\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + /* + * smem_size must hold at least the BOOT info page and the TOC page + * without overlap. The constant sum cannot overflow size_t. + */ + if (plat_info.smem_size < + (size_t)(QTI_SMEM_BOOT_INFO_SIZE + QTI_SMEM_TOC_SIZE)) { + EMSG("smem: smem_size too small\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + /* smem_size must fit in uint32_t (offsets are uint32_t). */ + if (plat_info.smem_size > (size_t)UINT32_MAX) { + EMSG("smem: smem_size exceeds uint32_t\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + if (plat_info.max_items == 0U) { + EMSG("smem: max_items is zero\n"); + return TEE_ERROR_BAD_PARAMETERS; + } + + smem_size = (uint32_t)plat_info.smem_size; + toc_offset = smem_size - QTI_SMEM_TOC_SIZE; + + ret = qti_smem_plat_map(0U, QTI_SMEM_BOOT_INFO_SIZE, + QTI_SMEM_PLAT_MAP_RO); + if (ret != TEE_SUCCESS) { + EMSG("smem: failed to map BOOT info: %#x\n", (unsigned int)ret); + return ret; + } + + va = qti_smem_plat_get_addr(0U); + if (!va) { + EMSG("smem: BOOT info not mapped\n"); + return TEE_ERROR_BAD_FORMAT; + } + + static_hdr = (const struct qti_smem_static_header *)va; + + ret = smem_validate_boot_version(static_hdr); + if (ret != TEE_SUCCESS) + return ret; + + ret = qti_smem_plat_map(toc_offset, QTI_SMEM_TOC_SIZE, + QTI_SMEM_PLAT_MAP_RO); + if (ret != TEE_SUCCESS) { + EMSG("smem: failed to map TOC: %#x\n", (unsigned int)ret); + return ret; + } + + va = qti_smem_plat_get_addr(toc_offset); + if (!va) { + EMSG("smem: TOC not mapped\n"); + return TEE_ERROR_BAD_FORMAT; + } + + toc = (const struct qti_smem_toc_header *)va; + entries = (const struct qti_smem_toc_entry + *)((const uint8_t *)va + + sizeof(struct qti_smem_toc_header)); + + ret = smem_validate_toc_header(toc, &num_entries); + if (ret != TEE_SUCCESS) + return ret; + + /* + * Populate qti_smem_info fields needed by smem_validate_toc_entry(), + * smem_part_involves_local(), and smem_map_partitions() before + * calling them. + */ + qti_smem_info.local_host = plat_info.local_host; + qti_smem_info.max_items = plat_info.max_items; + qti_smem_info.smem_size = smem_size; + qti_smem_info.toc_offset = toc_offset; + qti_smem_info.num_toc_entries = num_entries; + + ret = smem_map_partitions(entries, num_entries); + if (ret != TEE_SUCCESS) { + /* Roll back partially populated state. */ + memset(&qti_smem_info, 0, sizeof(qti_smem_info)); + return ret; + } + + qti_smem_info.initialized = 1; + + return TEE_SUCCESS; +} + +/* + * qti_smem_lookup() - Look up an existing SMEM item. + * + * Fast path (QTI_SMEM_HOST_COMMON): + * Uses the cached common_part_offset / common_part_size from + * qti_smem_info to call smem_search_partition() directly, bypassing + * the TOC walk entirely. + * + * Slow path (edge-pair host): + * Walks the TOC to find a partition matching the requested host pair, + * then calls smem_search_partition() for the first matching entry. + */ +TEE_Result qti_smem_lookup(uint16_t remote_host, uint16_t item, uint32_t flags, + void **item_ptr, size_t *item_size) +{ + const struct qti_smem_toc_entry *entries; + void *toc_va; + uint32_t i; + TEE_Result rc; + + /* Validate arguments. */ + if (!item_ptr) + return TEE_ERROR_BAD_PARAMETERS; + if (remote_host == QTI_SMEM_HOST_INVALID) + return TEE_ERROR_BAD_PARAMETERS; + if (flags != 0U) + return TEE_ERROR_BAD_PARAMETERS; + + if (qti_smem_info.initialized == 0) + return TEE_ERROR_BAD_STATE; + + if ((uint32_t)item >= (uint32_t)qti_smem_info.max_items) + return TEE_ERROR_BAD_PARAMETERS; + + /* + * Fast path: common partition lookup. + * + * The common partition offset and size were cached during + * qti_smem_init() by smem_map_partitions(). Use them directly + * to avoid walking the TOC on every common-partition lookup. + * + * If no common partition was found during init (common_part_offset + * == 0 and common_part_size == 0), continue below to the TOC walk + * which will also return TEE_ERROR_ITEM_NOT_FOUND. + */ + if (remote_host == QTI_SMEM_HOST_COMMON) { + if (qti_smem_info.common_part_offset != 0U || + qti_smem_info.common_part_size != 0U) { + uint32_t common_offset = + qti_smem_info.common_part_offset; + uint32_t common_size = + qti_smem_info.common_part_size; + + return smem_search_partition(common_offset, + common_size, + item, item_ptr, + item_size); + } + /* No common partition mapped - item cannot exist. */ + return TEE_ERROR_ITEM_NOT_FOUND; + } + + /* + * Slow path: edge-pair partition lookup. + * + * Walk the TOC looking for a partition matching {local_host, host}. + * First matching valid partition wins. + */ + toc_va = qti_smem_plat_get_addr(qti_smem_info.toc_offset); + if (!toc_va) + return TEE_ERROR_BAD_FORMAT; + + entries = (const struct qti_smem_toc_entry + *)((const uint8_t *)toc_va + + sizeof(struct qti_smem_toc_header)); + + for (i = 0U; i < qti_smem_info.num_toc_entries; i++) { + const struct qti_smem_toc_entry *e = &entries[i]; + + if (smem_validate_toc_entry(e) != TEE_SUCCESS) + continue; + + if (smem_part_matches(e, remote_host) == 0) + continue; + + rc = smem_search_partition(smem_rd32(&e->offset), + smem_rd32(&e->size), item, item_ptr, + item_size); + if (rc != TEE_ERROR_ITEM_NOT_FOUND) + return rc; /* found, or hard error */ + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +early_init(qti_smem_init); diff --git a/core/drivers/qcom/smem/smem_internal.h b/core/drivers/qcom/smem/smem_internal.h new file mode 100644 index 000000000..1ee1d214d --- /dev/null +++ b/core/drivers/qcom/smem/smem_internal.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* + * Qualcomm Shared Memory (SMEM) - Internal definitions + * + * This header is private to the SMEM driver implementation. + * It must NOT be included by any code outside of the qti_smem driver. + */ + +#ifndef SMEM_INTERNAL_H +#define SMEM_INTERNAL_H + +#include +#include +#include +#include +#include +#include "drivers/qcom/smem/smem.h" + +/* ----------------------------------------------------------------------- + * Protocol constants + * ----------------------------------------------------------------------- + */ + +/* + * BOOT info page: the first 4096 bytes of SMEM contain BOOT/static + * version metadata. This page is mapped read-only during init. + * + * Layout of the first 4096 bytes: + * [0, 64): proc_comm[16] - legacy IPC mechanism (16 x uint32_t) + * [64, 192): ver[32] - version array (32 x uint32_t) + * [192, 4096): ... - other static metadata + * + * The BOOT SMEM version is at ver[QTI_SMEM_VERSION_BOOT_OFFSET]. + */ +#define QTI_SMEM_BOOT_INFO_SIZE 4096U + +/* TOC page size: the TOC occupies the last 4096 bytes of SMEM. */ +#define QTI_SMEM_TOC_SIZE 4096U + +/* Supported TOC version. */ +#define QTI_SMEM_TOC_VERSION 1U + +/* Maximum number of TOC entries processed (bounds the TOC walk). */ +#define QTI_SMEM_TOC_MAX_ENTRIES 40U + +/* + * TOC magic: "$TOC" stored as a little-endian uint32_t. + * '$' = 0x24, 'T' = 0x54, 'O' = 0x4F, 'C' = 0x43 + */ +#define QTI_SMEM_TOC_MAGIC 0x434F5424U + +/* + * Partition magic: "$PRT" stored as a little-endian uint32_t. + * '$' = 0x24, 'P' = 0x50, 'R' = 0x52, 'T' = 0x54 + */ +#define QTI_SMEM_PART_MAGIC 0x54525024U + +/** Canary value stored in every item header. */ +#define QTI_SMEM_ITEM_CANARY 0xa5a5U + +/* + * BOOT SMEM version constants. + * + * The BOOT SMEM version word is stored at index QTI_SMEM_VERSION_BOOT_OFFSET + * within the ver[] array of struct qti_smem_static_header. + * + * Version word format: + * bits [31:16] = major version + * bits [15:0] = minor version + * + * This driver supports major version 0x000C (12). + * Minor version differences are accepted if the layout is compatible. + */ +#define QTI_SMEM_VERSION_ID 0x000C0001U +#define QTI_SMEM_MAJOR_VERSION_MASK 0xffff0000U +#define QTI_SMEM_MINOR_VERSION_MASK 0x0000ffffU +#define QTI_SMEM_VERSION_BOOT_OFFSET 7U + +/* + * Internal multi-host partition marker. + * A TOC entry with host0 == host1 == QTI_SMEM_HOST_MULTIHOST describes + * a multi-host partition whose membership is encoded in hosts_bitmap. + * Not exposed publicly. + */ +#define QTI_SMEM_HOST_MULTIHOST 0xfffcU + +/* ----------------------------------------------------------------------- + * Host-ID encoding + * + * A regular host ID is a 16-bit value encoded as: + * + * bits [5:0] proc_id (6 bits, values 0-63) + * bits [9:6] proc_num (4 bits, values 0-15) + * bits [12:10] pd_num (3 bits, values 0-7) + * bits [15:13] chiplet (3 bits, values 0-7) + * + * Special values that must never be produced by qti_smem_host_id(): + * QTI_SMEM_HOST_COMMON = 0xfffe + * QTI_SMEM_HOST_INVALID = 0xffff + * QTI_SMEM_HOST_MULTIHOST = 0xfffc + * ----------------------------------------------------------------------- + */ + +#define HOST_PROC_ID_BITS 6U +#define HOST_PROC_NUM_BITS 4U +#define HOST_PD_NUM_BITS 3U +#define HOST_CHIPLET_BITS 3U + +#define HOST_PROC_ID_SHIFT 0U +#define HOST_PROC_NUM_SHIFT 6U +#define HOST_PD_NUM_SHIFT 10U +#define HOST_CHIPLET_SHIFT 13U + +#define HOST_PROC_ID_MASK ((1U << HOST_PROC_ID_BITS) - 1U) +#define HOST_PROC_NUM_MASK ((1U << HOST_PROC_NUM_BITS) - 1U) +#define HOST_PD_NUM_MASK ((1U << HOST_PD_NUM_BITS) - 1U) +#define HOST_CHIPLET_MASK ((1U << HOST_CHIPLET_BITS) - 1U) + +/* ----------------------------------------------------------------------- + * Shared-memory ABI structures + * + * These structures represent the fixed binary layout of the SMEM protocol. + * They are internal to the SMEM driver and must NOT be exposed in any + * public header. + * ----------------------------------------------------------------------- + */ + +/* + * struct qti_smem_static_header - layout of the first 4096 bytes of SMEM. + * + * The ver[] array at offset 64 contains version words for each SMEM + * subsystem. Index QTI_SMEM_VERSION_BOOT_OFFSET (7) holds the BOOT + * SMEM version that this driver validates during init. + */ +struct qti_smem_static_header { + uint32_t proc_comm[16]; /* legacy IPC: 16 x uint32_t = 64 bytes */ + uint32_t ver[32]; /* version array: 32 x uint32_t = 128 bytes */ +} __packed; + +/* + * struct qti_smem_toc_header - SMEM partition table (TOC) header. + * + * Located at: smem_base + smem_size - QTI_SMEM_TOC_SIZE + * Immediately followed by an array of qti_smem_toc_entry records. + */ +struct qti_smem_toc_header { + uint32_t magic; /* must equal QTI_SMEM_TOC_MAGIC */ + uint32_t version; /* must equal QTI_SMEM_TOC_VERSION */ + uint32_t num_entries; /* number of valid qti_smem_toc_entry records */ + uint32_t minor_version; /* informational minor version */ + uint32_t reserved[4]; /* reserved; not validated */ +} __packed; + +/* + * struct qti_smem_toc_entry - one entry in the SMEM partition table. + * + * Entries follow the qti_smem_toc_header immediately in memory. + * host0 and host1 use uint16_t to match the SMEM wire format. + * The ABI layout must remain stable. + */ +struct qti_smem_toc_entry { + uint32_t offset; /* byte offset of partition from SMEM base */ + uint32_t size; /* partition size in bytes */ + uint32_t flags; /* reserved flags */ + uint16_t host0; /* first host identifier (wire uint16) */ + uint16_t host1; /* second host identifier (wire uint16) */ + uint32_t size_cacheline; /* cached-item alignment (0 = default) */ + uint32_t reserved[3]; /* reserved; not validated */ + uint32_t exclusion_sizes[4]; /* per-host exclusion sizes */ +} __packed; + +/* + * struct qti_smem_partition_header - header at the start of each partition. + * + * host0 and host1 use uint16_t to match the SMEM wire format. + * The ABI layout must remain stable. + * + * offset_free_uncached: end of the allocated uncached (upward) region. + * Grows upward from sizeof(partition_header). + * offset_free_cached: start of the allocated cached (downward) region. + * Grows downward from partition size. + */ +struct qti_smem_partition_header { + uint32_t magic; /* must equal QTI_SMEM_PART_MAGIC */ + uint16_t host0; /* first host identifier (wire uint16) */ + uint16_t host1; /* second host identifier (wire uint16) */ + uint32_t size; /* total partition size in bytes */ + uint32_t offset_free_uncached; /* end of uncached region */ + uint32_t offset_free_cached; /* start of allocated cached region */ + uint32_t reserved[3]; /* reserved; not validated */ +} __packed; + +/* + * struct qti_smem_item_header - header preceding each allocated SMEM item. + * + * Uncached item layout (growing upward from partition header): + * [qti_smem_item_header][padding_header bytes][data][padding_data bytes] + * + * Data address: ptr + sizeof(header) + padding_header + * Data size: size - padding_data + * Next header: ptr + sizeof(header) + padding_header + size + */ +struct qti_smem_item_header { + uint16_t canary; /* must equal QTI_SMEM_ITEM_CANARY (0xa5a5) */ + uint16_t item; /* SMEM item identifier */ + uint32_t size; /* total rounded size including padding_data */ + uint16_t padding_data; /* unused bytes at end of data region */ + uint16_t padding_header; /* alignment gap before data */ + uint32_t reserved; /* reserved; not validated */ +} __packed; + +/* Compile-time size assertions - catch layout regressions immediately. */ +static_assert(sizeof(struct qti_smem_static_header) == 192U, + "qti_smem_static_header size mismatch"); +static_assert(sizeof(struct qti_smem_toc_header) == 32U, + "qti_smem_toc_header size mismatch"); +static_assert(sizeof(struct qti_smem_toc_entry) == 48U, + "qti_smem_toc_entry size mismatch"); +static_assert(sizeof(struct qti_smem_partition_header) == 32U, + "qti_smem_partition_header size mismatch"); +static_assert(sizeof(struct qti_smem_item_header) == 16U, + "qti_smem_item_header size mismatch"); + +/* ----------------------------------------------------------------------- + * Driver state + * ----------------------------------------------------------------------- + */ + +/* + * struct qti_smem_info - driver state. + * + */ +struct qti_smem_info { + int initialized; /* 0 = uninitialized, 1 = initialized */ + uint16_t local_host; /* local host ID */ + uint16_t max_items; /* maximum item ID (exclusive) */ + uint32_t smem_size; /* total SMEM size in bytes */ + uint32_t toc_offset; /* byte offset of TOC from SMEM base */ + uint32_t num_toc_entries; /* validated TOC entry count */ + uint32_t common_part_offset; /* common partition offset */ + uint32_t common_part_size; /* common partition size */ +}; + +#endif /* SMEM_INTERNAL_H */ diff --git a/core/drivers/qcom/smem/smem_plat.c b/core/drivers/qcom/smem/smem_plat.c new file mode 100644 index 000000000..979046d33 --- /dev/null +++ b/core/drivers/qcom/smem/smem_plat.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* + * Qualcomm Shared Memory (SMEM) - TFA platform integration + * + */ + +#include +#include +#include + +#include + +#include "drivers/qcom/smem/smem.h" +#include "smem_plat.h" + +struct qti_smem_part_map { + SLIST_ENTRY(qti_smem_part_map) link; + uint32_t offset; + void *va; + size_t size; +}; + +SLIST_HEAD(qti_smem_part_map_list, qti_smem_part_map); + +static struct qti_smem_part_map_list qti_smem_plat_map_list = + SLIST_HEAD_INITIALIZER(qti_smem_plat_map_list); + +TEE_Result qti_smem_plat_map(uint32_t offset, size_t size, uint32_t flags) +{ + struct qti_smem_part_map *part_map; + uintptr_t pa; + void *va; + + (void)flags; + + if ((uint64_t)offset + (uint64_t)size > (uint64_t)SMEM_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + part_map = malloc(sizeof(*part_map)); + if (!part_map) + return TEE_ERROR_OUT_OF_MEMORY; + + pa = (uintptr_t)SMEM_BASE + (uintptr_t)offset; + va = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, pa, size); + if (!va) + return TEE_ERROR_OUT_OF_MEMORY; + + part_map->offset = offset; + part_map->va = va; + part_map->size = size; + SLIST_INSERT_HEAD(&qti_smem_plat_map_list, part_map, link); + + return TEE_SUCCESS; +} + +void *qti_smem_plat_get_addr(uint32_t offset) +{ + struct qti_smem_part_map *part_map; + + SLIST_FOREACH(part_map, &qti_smem_plat_map_list, link) { + if (part_map->offset == offset) + return part_map->va; + } + + return NULL; +} + +TEE_Result qti_smem_plat_init(struct qti_smem_plat_info *plat_info) +{ + TEE_Result ret; + + if (!plat_info) + return TEE_ERROR_BAD_PARAMETERS; + + ret = qti_smem_host_id(QTI_SMEM_PROC_TZ, 0, 0, 0, + &plat_info->local_host); + + if (ret != TEE_SUCCESS) + return ret; + + plat_info->max_items = 0xFFFF; + plat_info->smem_size = SMEM_SIZE; + + return TEE_SUCCESS; +} diff --git a/core/drivers/qcom/smem/smem_plat.h b/core/drivers/qcom/smem/smem_plat.h new file mode 100644 index 000000000..bfbf99e47 --- /dev/null +++ b/core/drivers/qcom/smem/smem_plat.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* + * Qualcomm Shared Memory (SMEM) - Internal platform abstraction interface + * + */ + +#ifndef SMEM_PLAT_H +#define SMEM_PLAT_H + +#include +#include +#include +#include +#include "drivers/qcom/smem/smem.h" +#include "target_config.h" + +/* ----------------------------------------------------------------------- + * Mapping attribute flags passed to qti_smem_plat_map(). + * ----------------------------------------------------------------------- + */ + +/* QTI_SMEM_PLAT_MAP_RO - install a read-only mapping. */ +#define QTI_SMEM_PLAT_MAP_RO 0x1U + +/* QTI_SMEM_PLAT_MAP_RW - install a read-write mapping. */ +#define QTI_SMEM_PLAT_MAP_RW 0x2U + +/* ----------------------------------------------------------------------- + * Platform target information + * ----------------------------------------------------------------------- + */ + +/* + * struct qti_smem_plat_info - platform-supplied SMEM target parameters. + * @local_host: SMEM host ID of the processor running this driver instance. + * Must be constructed with qti_smem_host_id(). + * @max_items: Maximum item index the driver will accept (exclusive upper + * bound for the @item argument to qti_smem_lookup()). + * @smem_size: Total size of the SMEM region in bytes. + * + */ +struct qti_smem_plat_info { + uint16_t local_host; + uint16_t max_items; + size_t smem_size; +}; + +/* ----------------------------------------------------------------------- + * Platform operation interface + * + * Implemented once per platform in qti_smem_plat_xxx.c. + * The common core calls these; it does not know PA/VA/MMU details. + * ----------------------------------------------------------------------- + */ + +/* + * qti_smem_plat_init() - Platform entry point for SMEM initialization. + * @plat_info: caller-allocated struct to fill with platform parameters. + * + * Discovers SMEM target parameters (size, max_items, local host) + * from WONCE registers or device tree and writes them into @plat_info. + * + * PA base and VA base are stored in platform-private state only. + * + * Return: TEE_SUCCESS on success, TEE_ERROR_BAD_PARAMETERS if @plat_info is + * NULL, TEE_ERROR_NOT_SUPPORTED if target info is unavailable, + * TEE_ERROR_BAD_FORMAT if target info is corrupted. + */ +TEE_Result qti_smem_plat_init(struct qti_smem_plat_info *plat_info); + +/* + * qti_smem_plat_map() - Map a region of SMEM into the virtual address space. + * @offset: byte offset from the SMEM physical base (not PA, not VA). + * @size: number of bytes to map. + * @flags: QTI_SMEM_PLAT_MAP_RO or QTI_SMEM_PLAT_MAP_RW. + * + * Must validate that offset + size does not exceed smem_size. + * Mapping is ignored because SMEM is statically mapped. + * + * Return: TEE_SUCCESS on success, TEE_ERROR_BAD_PARAMETERS on failure. + */ +TEE_Result qti_smem_plat_map(uint32_t offset, size_t size, uint32_t flags); + +/* + * qti_smem_plat_get_addr() - Translate an SMEM offset to a virtual address. + * @offset: byte offset from the SMEM physical base. + * + * Returns the virtual address corresponding to the given offset within + * the statically mapped SMEM region (1:1 PA/VA mapping). + * + * Return: virtual address on success, NULL if the offset is out of range. + */ +void *qti_smem_plat_get_addr(uint32_t offset); + +#endif /* SMEM_PLAT_H */ diff --git a/core/drivers/qcom/smem/sub.mk b/core/drivers/qcom/smem/sub.mk new file mode 100644 index 000000000..5c0c37496 --- /dev/null +++ b/core/drivers/qcom/smem/sub.mk @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-$(CFG_QCOM_SMEM) += smem.c +srcs-$(CFG_QCOM_SMEM) += smem_plat.c \ No newline at end of file diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index 56e7d0625..21ac5c26b 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -12,3 +12,4 @@ subdirs-$(CFG_QCOM_CMD_DB) += cmd_db subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh subdirs-$(CFG_QCOM_QFPROM) += qfprom subdirs-$(CFG_QCOM_XPUV4) += xpu +subdirs-$(CFG_QCOM_SMEM) += smem diff --git a/core/include/drivers/qcom/smem/smem.h b/core/include/drivers/qcom/smem/smem.h new file mode 100644 index 000000000..6e73c9af8 --- /dev/null +++ b/core/include/drivers/qcom/smem/smem.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* + * Qualcomm Shared Memory (SMEM) - Public API + * + */ + +/* + * ========================================================================= + * Qualcomm Shared Memory (SMEM) Framework Overview + * ========================================================================= + * + * SMEM is a fixed-size, physically contiguous shared memory region used as + * the primary inter-processor communication (IPC) substrate on Qualcomm SoCs. + * It is initialized at boot time by the boot firmware and remains accessible + * to all participating processors for the lifetime of the system. Each + * processor (host) that participates in SMEM has a unique host identifier + * and may read from or write to the portions of SMEM that it is permitted + * to access. + * + * Partition Model + * --------------- + * SMEM data is organised into partitions of two kinds: + * + * Common partition: accessible to all hosts; holds globally shared items. + * Edge-pair partition: shared between exactly two hosts; holds items + * private to that host pair. + * + * Within each partition, items are stored in two heap regions that grow + * toward each other: an uncached/upward region and a cached/downward region. + * + * NOTE: This driver supports lookup of uncached/upward items only. + * Cached/downward item allocation and lookup are not implemented; items + * that exist only in the cached region are not visible to this driver. + * + * Item Model + * ---------- + * An SMEM item is a data blob identified by a 16-bit item ID. + * + * Host Identifier Model + * --------------------- + * Each processor is assigned a 16-bit host identifier that encodes + * the processor type, instance, protection domain, and chiplet. + * Two special values are reserved: QTI_SMEM_HOST_COMMON (common partition + * access) and QTI_SMEM_HOST_INVALID (unset/error sentinel). Host + * identifiers must be constructed with qti_smem_host_id(), never directly. + * + * ========================================================================= + */ + +#ifndef SMEM_H +#define SMEM_H + +#include +#include +#include + +/* + * QTI_SMEM_HOST_COMMON - pseudo-host for the common SMEM partition. + * + * Items in the common partition are accessible to all hosts. + */ +#define QTI_SMEM_HOST_COMMON ((uint16_t)0xfffeU) + +/* + * QTI_SMEM_HOST_INVALID - sentinel value for an invalid or unset host. + * + */ +#define QTI_SMEM_HOST_INVALID ((uint16_t)0xffffU) + +/* ----------------------------------------------------------------------- + * Processor identifier + * + * Pass this as the proc_id argument to qti_smem_host_id(). + * Currently only QTI_SMEM_PROC_TZ is supported. + * ----------------------------------------------------------------------- + */ +#define QTI_SMEM_PROC_TZ 7U + +/* Lookup flags - currently only 0 is valid */ +#define QTI_SMEM_FLAG_NONE 0U + +/* + * qti_smem_init() - Initialize the SMEM driver. + * + * Initializes platform-specific SMEM resources and prepares the driver. + * + * This function must be called once before using other SMEM APIs. Calling it + * again after successful initialization returns TEE_ERROR_BAD_STATE without + * modifying driver state. + * + * Return: TEE_SUCCESS on success, TEE_ERROR_BAD_STATE if already initialized, + * or another TEE_ERROR_* value on failure. + */ +TEE_Result qti_smem_init(void); + +/* + * qti_smem_host_id() - Construct a SMEM host identifier. + * @proc_id: [in] processor ID (use QTI_SMEM_PROC_* macro) + * @proc_num: [in] processor instance number + * @pd_num: [in] protection-domain number + * @chiplet: [in] chiplet identifier + * @host: [out] output host identifier (must not be NULL) + * + * Constructs a valid SMEM host identifier from the given parameters. + * The internal bit encoding is an implementation detail; callers must + * use this function rather than constructing host values directly. + * + * Return: + * TEE_SUCCESS success + * TEE_ERROR_BAD_PARAMETERS @host is NULL, any parameter is out of range, + * or the encoded value would collide with a + * reserved host identifier + */ +TEE_Result qti_smem_host_id(uint16_t proc_id, uint16_t proc_num, + uint16_t pd_num, uint16_t chiplet, uint16_t *host); + +/* + * qti_smem_lookup() - Look up an existing SMEM item. + * @remote_host: [in] remote host for a host-pair partition, or + * QTI_SMEM_HOST_COMMON for the common partition + * @item: [in] SMEM item ID + * @flags: [in] must be QTI_SMEM_FLAG_NONE + * @item_ptr: [out] pointer to item payload (must not be NULL) + * @item_size: [out] size of item payload in bytes (may be NULL) + * + * Searches the SMEM partition selected by @remote_host for an item with the + * given item ID. + * + * Return: + * TEE_SUCCESS success; *item_ptr points to item payload + * TEE_ERROR_BAD_PARAMETERS invalid argument (@item_ptr is NULL, + * @remote_host is QTI_SMEM_HOST_INVALID, + * @flags is not QTI_SMEM_FLAG_NONE, or + * @item >= max_items) + * TEE_ERROR_BAD_STATE driver not initialized + * TEE_ERROR_ITEM_NOT_FOUND item or partition not found + * TEE_ERROR_ACCESS_DENIED partition not mapped or access denied + * TEE_ERROR_BAD_FORMAT corrupted shared-memory metadata + */ +TEE_Result qti_smem_lookup(uint16_t remote_host, uint16_t item, uint32_t flags, + void **item_ptr, size_t *item_size); + +#endif /* SMEM_H */