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 */