diff --git a/core/arch/riscv/include/sbi_mpxy.h b/core/arch/riscv/include/sbi_mpxy.h index ecf62a04c53..50289837f73 100644 --- a/core/arch/riscv/include/sbi_mpxy.h +++ b/core/arch/riscv/include/sbi_mpxy.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* - * Copyright 2025 NXP + * Copyright 2025-2026 NXP */ #ifndef __SBI_MPXY_H @@ -18,6 +18,11 @@ #define SBI_EXT_MPXY_SEND_MSG_WITHOUT_RESP 0x6 #define SBI_EXT_MPXY_GET_NOTIFICATION_EVENTS 0x7 +/* SET_SHMEM arguments */ +#define SBI_MPXY_SHMEM_DISABLE ULONG_MAX +#define SBI_MPXY_SHMEM_FLAG_OVERWRITE 0 +#define SBI_MPXY_SHMEM_FLAG_OVERWRITE_RETURN 1 + /* Capabilities available through CHANNEL_CAPABILITY attribute */ #define SBI_MPXY_CHAN_CAP_MSI BIT(0) #define SBI_MPXY_CHAN_CAP_SSE BIT(1) @@ -30,8 +35,10 @@ #include #include +#include #include #include +#include #include #include @@ -141,8 +148,11 @@ struct sbi_mpxy_notification_data { }; /* SBI MPXY */ +int sbi_mpxy_init(void); +bool sbi_mpxy_is_available(void); int sbi_mpxy_get_shmem_size(unsigned long *shmem_size); int sbi_mpxy_set_shmem(void); +int sbi_mpxy_disable_shmem(void); int sbi_mpxy_get_channel_ids(uint32_t channel_count, uint32_t *channel_ids); int sbi_mpxy_read_attributes(uint32_t channel_id, uint32_t base_attribute_id, uint32_t attribute_count, void *attribute_buf); @@ -162,8 +172,27 @@ int sbi_mpxy_get_channel_count(uint32_t *channel_count); int sbi_mpxy_get_notification_events(uint32_t channel_id, struct sbi_mpxy_notification_data *notif_data, + unsigned long max_events_data_len, unsigned long *events_data_len); +TEE_Result sbi_mpxy_to_tee_result(int sbi_err); +#endif /*__ASSEMBLER__*/ +#else /*!defined(CFG_RISCV_SBI_MPXY)*/ + +#ifndef __ASSEMBLER__ +#include + +/* sbi.h may not be available without CFG_RISCV_SBI, use the raw value */ +static inline int sbi_mpxy_init(void) +{ + return -2; /* SBI_ERR_NOT_SUPPORTED */ +} + +static inline bool sbi_mpxy_is_available(void) +{ + return false; +} #endif /*__ASSEMBLER__*/ + #endif /*defined(CFG_RISCV_SBI_MPXY)*/ #endif /*__SBI_MPXY_H*/ diff --git a/core/arch/riscv/include/sbi_mpxy_rpmi.h b/core/arch/riscv/include/sbi_mpxy_rpmi.h index 0f960951a7e..d181e960c82 100644 --- a/core/arch/riscv/include/sbi_mpxy_rpmi.h +++ b/core/arch/riscv/include/sbi_mpxy_rpmi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* - * Copyright 2025 NXP + * Copyright 2025-2026 NXP */ #ifndef __SBI_MPXY_RPMI_H @@ -87,6 +87,12 @@ struct sbi_mpxy_rpmi_channel { struct sbi_mpxy_channel_attrs attrs; struct sbi_mpxy_rpmi_channel_attrs rpmi_attrs; struct sbi_mpxy_notification_data *notif; +#if defined(CFG_RISCV_SBI_MPXY_RPMI_MSI) + /* Notification state, see sbi_mpxy_rpmi_msi.c */ + bool have_events_state; + bool started; + uint32_t msi_irq; +#endif }; /* An instance of RPMI-over-MPXY channel group */ @@ -157,5 +163,14 @@ int sbi_mpxy_rpmi_read_attributes(struct sbi_mpxy_rpmi_channel *channel); int sbi_mpxy_rpmi_send_data(struct sbi_mpxy_rpmi_channel *channel, void *data); #endif /*__ASSEMBLER__*/ + +#else /*!defined(CFG_RISCV_SBI_MPXY_RPMI)*/ + +#ifndef __ASSEMBLER__ +static inline void sbi_mpxy_rpmi_probe_channels(void) +{ +} +#endif /*__ASSEMBLER__*/ + #endif /*defined(CFG_RISCV_SBI_MPXY_RPMI)*/ #endif /*__SBI_MPXY_RPMI_H*/ diff --git a/core/arch/riscv/kernel/boot.c b/core/arch/riscv/kernel/boot.c index 82d9a7b93e2..3cf78f3acbc 100644 --- a/core/arch/riscv/kernel/boot.c +++ b/core/arch/riscv/kernel/boot.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-2-Clause /* * Copyright (c) 2023 Andes Technology Corporation - * Copyright 2022-2023 NXP + * Copyright 2022-2023, 2026 NXP */ #include @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -245,6 +247,9 @@ void __weak boot_init_primary_runtime(void) boot_primary_init_core_ids(); init_tee_runtime(); boot_mem_release_tmp_alloc(); + + if (!sbi_mpxy_init()) + sbi_mpxy_rpmi_probe_channels(); } void __weak boot_init_primary_final(void) @@ -273,6 +278,7 @@ static void init_secondary_helper(void) thread_init_per_cpu(); boot_secondary_init_intc(); + sbi_mpxy_init(); IMSG("Secondary CPU%zu (hart%"PRIu32") initialized", pos, thread_get_hartid()); diff --git a/core/arch/riscv/kernel/sbi_mpxy.c b/core/arch/riscv/kernel/sbi_mpxy.c index 62b62abaf30..e7a78154d60 100644 --- a/core/arch/riscv/kernel/sbi_mpxy.c +++ b/core/arch/riscv/kernel/sbi_mpxy.c @@ -1,18 +1,22 @@ // SPDX-License-Identifier: BSD-2-Clause /* - * Copyright 2025 NXP + * Copyright 2025-2026 NXP */ +#include #include #include #include #include #include +#include +#include /* * struct mpxy_core_local - MPXY per-hart local context * @shmem: Virtual base address of MPXY shared memory * @shmem_pa: Physical base address of MPXY shared memory + * @shmem_size: Size in bytes of MPXY shared memory * @shmem_active:Indicates whether shared memory is active for this hart * * Holds MPXY-related per-hart data required for message exchange via @@ -21,10 +25,12 @@ struct mpxy_core_local { void *shmem; paddr_t shmem_pa; + unsigned long shmem_size; bool shmem_active; }; static struct mpxy_core_local mpxy_core_local_array[CFG_TEE_CORE_NB_CORE]; +static bool mpxy_available; static struct mpxy_core_local *mpxy_get_core_local(void) { @@ -40,6 +46,50 @@ static struct mpxy_core_local *mpxy_get_core_local(void) return mpxy; } +/** + * sbi_mpxy_is_available - Check whether the SBI MPXY extension was probed + * + * Return: true if sbi_mpxy_init() found the extension, false otherwise. + */ +bool sbi_mpxy_is_available(void) +{ + return mpxy_available; +} + +/** + * sbi_mpxy_init - Probe the MPXY extension and set up shared memory + * + * Must be called once on every hart, after the heap is usable. The first + * caller probes the SBI implementation for the MPXY extension; every caller + * then registers per-hart shared memory. Safe to call again on a hart that + * already has shared memory registered. + * + * Return: SBI_SUCCESS on success, SBI_ERR_NOT_SUPPORTED if the extension is + * absent, other negative SBI error code on failure. + */ +int sbi_mpxy_init(void) +{ + static bool probed; + int ret = SBI_SUCCESS; + + if (!probed) { + mpxy_available = sbi_probe_extension(SBI_EXT_MPXY) > 0; + probed = true; + if (!mpxy_available) + IMSG("SBI MPXY extension not available"); + } + + if (!mpxy_available) + return SBI_ERR_NOT_SUPPORTED; + + ret = sbi_mpxy_set_shmem(); + if (ret) + EMSG("MPXY shared memory setup failed on core %zu: %d", + get_core_pos(), ret); + + return ret; +} + /** * sbi_mpxy_get_shmem_size - Retrieve the MPXY shared memory size * @shmem_size: Pointer to store the shared memory size in bytes @@ -53,6 +103,9 @@ int sbi_mpxy_get_shmem_size(unsigned long *shmem_size) { struct sbiret sbiret = {}; + if (!mpxy_available) + return SBI_ERR_NOT_SUPPORTED; + sbiret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_GET_SHMEM_SIZE, 0, 0, 0, 0, 0, 0); if (sbiret.error) { @@ -70,9 +123,10 @@ int sbi_mpxy_get_shmem_size(unsigned long *shmem_size) /** * sbi_mpxy_set_shmem - Set up MPXY shared memory on the current hart * - * Allocates and registers a 4 KiB shared memory region, aligned to 4 KiB, - * as required by the MPXY extension. This memory is used for sending and - * receiving messages. Registers the shared memory with the SBI MPXY extension. + * Queries the shared memory size required by the SBI implementation, + * allocates a region of that size aligned to its own size, and registers + * it with the SBI MPXY extension for the calling hart. This memory is used + * for sending and receiving messages. * * Return: SBI_SUCCESS on success, negative SBI error code on failure. */ @@ -80,28 +134,63 @@ int sbi_mpxy_set_shmem(void) { struct mpxy_core_local *mpxy = NULL; struct sbiret sbiret = {}; + unsigned long shmem_size = 0; + unsigned long shmem_phys_lo = 0; + unsigned long shmem_phys_hi = 0; void *shmem = NULL; uint32_t exceptions = 0; int ret = SBI_ERR_FAILURE; + ret = sbi_mpxy_get_shmem_size(&shmem_size); + if (ret) + return ret; + + /* + * The SBI spec requires shmem_size to be a multiple of 4 KiB and + * the region to be aligned to shmem_size. + */ + if (!shmem_size || shmem_size < SMALL_PAGE_SIZE || + !IS_POWER_OF_TWO(shmem_size)) + return SBI_ERR_INVALID_PARAM; + exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); mpxy = mpxy_get_core_local(); - if (mpxy->shmem_active) + + if (mpxy->shmem_active) { + ret = SBI_SUCCESS; goto out; + } - shmem = memalign(SMALL_PAGE_SIZE, SMALL_PAGE_SIZE); - if (!shmem) + shmem = memalign(shmem_size, shmem_size); + if (!shmem) { + ret = SBI_ERR_FAILURE; goto out; + } + memset(shmem, 0, shmem_size); mpxy->shmem = shmem; mpxy->shmem_pa = virt_to_phys(shmem); + mpxy->shmem_size = shmem_size; + + /* + * On RV64 the whole physical address goes in shmem_phys_lo and + * shmem_phys_hi is unused. On RV32 the address is split. + */ + if (IS_ENABLED(CFG_RV32_core)) { + shmem_phys_lo = low32_from_64(mpxy->shmem_pa); + shmem_phys_hi = high32_from_64(mpxy->shmem_pa); + } else { + shmem_phys_lo = mpxy->shmem_pa; + } - sbiret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SET_SHMEM, mpxy->shmem_pa, - 0, 0); + sbiret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SET_SHMEM, + shmem_phys_lo, shmem_phys_hi, + SBI_MPXY_SHMEM_FLAG_OVERWRITE); if (sbiret.error) { - EMSG("MPXY SBI call failed: error=%ld", sbiret.error); + EMSG("MPXY SET_SHMEM failed: error=%ld", sbiret.error); free(shmem); + memset(mpxy, 0, sizeof(*mpxy)); ret = sbiret.error; goto out; } @@ -115,6 +204,45 @@ int sbi_mpxy_set_shmem(void) return ret; } +/** + * sbi_mpxy_disable_shmem - Disable MPXY shared memory on the current hart + * + * Tells the SBI implementation to stop using the shared memory registered + * for this hart and releases the memory. + * + * Return: SBI_SUCCESS on success, negative SBI error code on failure. + */ +int sbi_mpxy_disable_shmem(void) +{ + struct mpxy_core_local *mpxy = NULL; + struct sbiret sbiret = {}; + uint32_t exceptions = 0; + int ret = SBI_SUCCESS; + + exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); + + mpxy = mpxy_get_core_local(); + if (!mpxy->shmem_active) + goto out; + + sbiret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SET_SHMEM, + SBI_MPXY_SHMEM_DISABLE, SBI_MPXY_SHMEM_DISABLE, + SBI_MPXY_SHMEM_FLAG_OVERWRITE); + if (sbiret.error) { + EMSG("MPXY SET_SHMEM (disable) failed: error=%ld", + sbiret.error); + ret = sbiret.error; + goto out; + } + + free(mpxy->shmem); + memset(mpxy, 0, sizeof(*mpxy)); + +out: + thread_unmask_exceptions(exceptions); + return ret; +} + /** * sbi_mpxy_get_channel_ids - Retrieve MPXY channel IDs * @channel_count: Number of channels expected @@ -206,6 +334,11 @@ int sbi_mpxy_read_attributes(uint32_t channel_id, uint32_t base_attribute_id, goto out; } + if (attribute_count > mpxy->shmem_size / sizeof(uint32_t)) { + ret = SBI_ERR_INVALID_PARAM; + goto out; + } + sbiret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_READ_ATTRS, channel_id, base_attribute_id, attribute_count, 0, 0, 0); if (!sbiret.error) @@ -253,6 +386,11 @@ int sbi_mpxy_write_attributes(uint32_t channel_id, uint32_t base_attribute_id, goto out; } + if (attribute_count > mpxy->shmem_size / sizeof(uint32_t)) { + ret = SBI_ERR_INVALID_PARAM; + goto out; + } + memcpy(mpxy->shmem, attributes_buf, attribute_count * sizeof(uint32_t)); sbiret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_WRITE_ATTRS, channel_id, @@ -299,6 +437,10 @@ int sbi_mpxy_send_message_with_response(uint32_t channel_id, if (!message && message_len) return SBI_ERR_INVALID_PARAM; + if (!response && max_response_len) + return SBI_ERR_INVALID_PARAM; + if (response_len) + *response_len = 0; exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); @@ -309,13 +451,30 @@ int sbi_mpxy_send_message_with_response(uint32_t channel_id, goto out; } + if (message_len > mpxy->shmem_size) { + ret = SBI_ERR_INVALID_PARAM; + goto out; + } + if (message_len) memcpy(mpxy->shmem, message, message_len); sbiret = sbi_ecall(SBI_EXT_MPXY, SBI_EXT_MPXY_SEND_MSG_WITH_RESP, channel_id, message_id, message_len, 0, 0, 0); - if (response && !sbiret.error) { - response_bytes = sbiret.value; + if (sbiret.error) { + EMSG("MPXY SBI call failed: error=%ld", sbiret.error); + ret = sbiret.error; + goto out; + } + + /* Never trust a returned length larger than our own shared memory */ + response_bytes = sbiret.value; + if (response_bytes > mpxy->shmem_size) { + ret = SBI_ERR_FAILURE; + goto out; + } + + if (response) { if (response_bytes > max_response_len) { ret = SBI_ERR_INVALID_PARAM; goto out; @@ -326,10 +485,8 @@ int sbi_mpxy_send_message_with_response(uint32_t channel_id, *response_len = response_bytes; } - if (sbiret.error) - EMSG("MPXY SBI call failed: error=%ld", sbiret.error); + ret = SBI_SUCCESS; - ret = sbiret.error; out: thread_unmask_exceptions(exceptions); return ret; @@ -369,6 +526,11 @@ int sbi_mpxy_send_message_without_response(uint32_t channel_id, goto out; } + if (message_len > mpxy->shmem_size) { + ret = SBI_ERR_INVALID_PARAM; + goto out; + } + if (message_len) memcpy(mpxy->shmem, message, message_len); @@ -440,19 +602,24 @@ int sbi_mpxy_get_channel_count(uint32_t *channel_count) * MPXY channel * @channel_id: ID of the channel * @notif_data: Pointer to buffer to store notification data + * @max_events_data_len: Size in bytes of the events_data area of @notif_data * @events_data_len: Pointer to store length of events data in bytes * * Makes an SBI call to fetch notification events from the specified channel - * and copies them from shared memory into the provided buffer. + * and copies the notification header and events data from shared memory + * into the provided buffer. @notif_data must have room for the header plus + * @max_events_data_len bytes. * * Return: SBI_SUCCESS on success, negative SBI error code on failure. */ int sbi_mpxy_get_notification_events(uint32_t channel_id, struct sbi_mpxy_notification_data *notif_data, + unsigned long max_events_data_len, unsigned long *events_data_len) { struct mpxy_core_local *mpxy = NULL; + unsigned long events_bytes = 0; struct sbiret sbiret = {}; uint32_t exceptions = 0; int ret = SBI_ERR_FAILURE; @@ -460,6 +627,8 @@ sbi_mpxy_get_notification_events(uint32_t channel_id, if (!notif_data || !events_data_len) return SBI_ERR_INVALID_PARAM; + *events_data_len = 0; + exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); mpxy = mpxy_get_core_local(); @@ -477,12 +646,53 @@ sbi_mpxy_get_notification_events(uint32_t channel_id, goto out; } - memcpy(notif_data, mpxy->shmem, sbiret.value + 16); - *events_data_len = sbiret.value; + events_bytes = sbiret.value; + if (events_bytes > max_events_data_len || + events_bytes > mpxy->shmem_size - sizeof(*notif_data)) { + ret = SBI_ERR_INVALID_PARAM; + goto out; + } - ret = sbiret.error; + memcpy(notif_data, mpxy->shmem, sizeof(*notif_data) + events_bytes); + *events_data_len = events_bytes; + ret = SBI_SUCCESS; out: thread_unmask_exceptions(exceptions); return ret; } + +/** + * sbi_mpxy_to_tee_result - Convert an SBI error code to a TEE_Result + * @sbi_err: Return value from one of the sbi_mpxy_* functions + * + * Return: The closest matching TEE_Result. + */ +TEE_Result sbi_mpxy_to_tee_result(int sbi_err) +{ + switch (sbi_err) { + case SBI_SUCCESS: + return TEE_SUCCESS; + case SBI_ERR_NOT_SUPPORTED: + return TEE_ERROR_NOT_SUPPORTED; + case SBI_ERR_INVALID_PARAM: + case SBI_ERR_INVALID_ADDRESS: + case SBI_ERR_BAD_RANGE: + return TEE_ERROR_BAD_PARAMETERS; + case SBI_ERR_DENIED: + case SBI_ERR_DENIED_LOCKED: + return TEE_ERROR_ACCESS_DENIED; + case SBI_ERR_NO_SHMEM: + case SBI_ERR_INVALID_STATE: + case SBI_ERR_ALREADY_AVAILABLE: + case SBI_ERR_ALREADY_STARTED: + case SBI_ERR_ALREADY_STOPPED: + return TEE_ERROR_BAD_STATE; + case SBI_ERR_TIMEOUT: + return TEE_ERROR_TIMEOUT; + case SBI_ERR_IO: + return TEE_ERROR_COMMUNICATION; + default: + return TEE_ERROR_GENERIC; + } +}