diff --git a/README.md b/README.md index 1ee212b32..237717465 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ platforms is presented below: - [x] Arm Fixed Virtual Platforms - [x] Toradex Verdin iMX8M Plus (w/ Dahlia Carrier Board) - [x] NXP S32G3 +- [x] BeagleBone AI-64 **Armv7-A / Armv8-A AArch32** - [x] Arm Fixed Virtual Platforms diff --git a/src/arch/armv8/aarch64/inc/arch/subarch/sysregs.h b/src/arch/armv8/aarch64/inc/arch/subarch/sysregs.h index 91a9e30ec..36da8e377 100644 --- a/src/arch/armv8/aarch64/inc/arch/subarch/sysregs.h +++ b/src/arch/armv8/aarch64/inc/arch/subarch/sysregs.h @@ -76,6 +76,7 @@ SYSREG_GEN_ACCESSORS(cntvoff_el2) SYSREG_GEN_ACCESSORS(sctlr_el1) SYSREG_GEN_ACCESSORS(cntkctl_el1) SYSREG_GEN_ACCESSORS(cntfrq_el0) +SYSREG_GEN_ACCESSORS(cntpct_el0) SYSREG_GEN_ACCESSORS(pmcr_el0) SYSREG_GEN_ACCESSORS(par_el1) SYSREG_GEN_ACCESSORS(tcr_el2) diff --git a/src/platform/beaglebone-ai64/bb-ai64_desc.c b/src/platform/beaglebone-ai64/bb-ai64_desc.c new file mode 100644 index 000000000..a9d49b311 --- /dev/null +++ b/src/platform/beaglebone-ai64/bb-ai64_desc.c @@ -0,0 +1,130 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#include + +struct platform platform = { + .cpu_num = 2, + .region_num = 2, + .regions = + (struct mem_region[]){ + { + .base = 0x80000000, + .size = 0x80000000, + }, + { + .base = 0x880000000, + .size = 0x80000000, + }, + }, + .console = + { + .base = 0x02800000, + }, + .arch = + { + .gic = + { + .gicd_addr = 0x01800000, + .gicr_addr = 0x01900000, + .gicc_addr = 0x6f000000, + .gich_addr = 0x6f010000, + .gicv_addr = 0x6f020000, + .maintenance_id = 25, + }, + }, +}; + +#ifndef GENERATING_DEFS + +static mbox_k3_sec_proxy_thread_desc sec_proxy_thread_desc[] = { + MBOX_K3_SEC_PROXY_THREAD_DESC_ENTRY(J721E_HOST_ID_MAIN_A72_CONTEXT_0_SECURE, 0, 2, 30, 10, 20), + MBOX_K3_SEC_PROXY_THREAD_DESC_ENTRY(J721E_HOST_ID_MAIN_A72_CONTEXT_1_SECURE, 5, 2, 30, 10, 20), + MBOX_K3_SEC_PROXY_THREAD_DESC_ENTRY(J721E_HOST_ID_MAIN_A72_CONTEXT_2_NON_SECURE, 10, 2, 22, 2, + 20), + MBOX_K3_SEC_PROXY_THREAD_DESC_ENTRY(J721E_HOST_ID_MAIN_A72_CONTEXT_3_NON_SECURE, 15, 2, 7, 2, 5), + MBOX_K3_SEC_PROXY_THREAD_DESC_ENTRY(J721E_HOST_ID_MAIN_A72_CONTEXT_4_NON_SECURE, 20, 2, 7, 2, 5), + MBOX_K3_SEC_PROXY_THREAD_DESC_ENTRY(J721E_HOST_ID_MAIN_A72_CONTEXT_5_NON_SECURE, 55, 2, 7, 2, 5), +}; + +static mbox_k3_sec_proxy_desc k3_sec_proxy = { + .thread_inst = + { + .id = 0U, + .rt_base = 0x0032400000U, + .rt_size = 0x100000U, + .scfg_base = 0x0032800000U, + .scfg_size = 0x100000U, + .data_base = 0x0032C00000, + .data_size = 0x100000, + .max_msg_size = TISCI_MSG_MAX_SIZE, + }, + .read_timeout_us = TISCI_MSG_TXN_TIMEOUT, + .sec_proxy_thread_desc = sec_proxy_thread_desc, + .num_threads = sizeof(sec_proxy_thread_desc) / sizeof(sec_proxy_thread_desc[0]), +}; + +static int32_t platform_tisci_send(uint8_t chan_id, void* buffer, size_t len) +{ + return mbox_k3_sec_proxy_write(&k3_sec_proxy, chan_id, buffer, len); +} + +static int32_t platform_tisci_recv(uint8_t chan_id, void* buffer, size_t len) +{ + return mbox_k3_sec_proxy_read(&k3_sec_proxy, chan_id, buffer, len); +} + +static tisci_ctx tisci_context = { + .ops = + { + .send = platform_tisci_send, + .recv = platform_tisci_recv, + }, + .seq_id_lock = {0, 0}, + .seq_id = 0U, +}; + +void platform_default_init(void) +{ + if (cpu_is_master()) { + cpu_sync_init(&cpu_glb_sync, platform.cpu_num); + } + + mbox_k3_sec_proxy_init(&k3_sec_proxy); + cpu_sync_barrier(&cpu_glb_sync); + + uint8_t cur_host_id = cpu_is_master() ? J721E_HOST_ID_MAIN_A72_CONTEXT_2_NON_SECURE : + J721E_HOST_ID_MAIN_A72_CONTEXT_3_NON_SECURE; + + mbox_k3_sec_proxy_thread_desc* host_threads = + mbox_k3_sec_proxy_get_thread_by_host_func(&k3_sec_proxy, cur_host_id, 0U); + if (NULL == host_threads) { + ERROR("platform init failed, no secure proxy threads found"); + return; + } + + /* probe all secure proxy threads owned by this host */ + for (uint8_t func_iterator = 0U; func_iterator < MBOX_K3_SEC_PROXY_NUM_HOST_FUNCTIONS; + func_iterator++) { + int32_t probe_status = + mbox_k3_sec_proxy_probe(&k3_sec_proxy, host_threads[func_iterator].sec_proxy_thread_id); + if (MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR != probe_status) { + ERROR("platform init failed, k3 secure_proxy_thread_%d probe status=%d", + host_threads[func_iterator].sec_proxy_thread_id, probe_status); + return; + } + } + + /* ping test to ensure that host<->sysfw pipeline is clean */ + tisci_msg_version_rsp ping_rsp = { 0 }; + int32_t ping_status = tisci_get_revision(&tisci_context, cur_host_id, + host_threads[MBOX_K3_SEC_PROXY_HOST_FUNCTION_LOW_PRIORITY].sec_proxy_thread_id, + host_threads[MBOX_K3_SEC_PROXY_HOST_FUNCTION_RESPONSE].sec_proxy_thread_id, &ping_rsp); + if (TISCI_STATUS_CODE_NO_ERROR != ping_status) { + ERROR("platform init failed, ping to sysfw failed, ping_status=%d", ping_status); + } +} + +#endif /* ifndef GENERATING_DEFS */ diff --git a/src/platform/beaglebone-ai64/inc/plat/platform.h b/src/platform/beaglebone-ai64/inc/plat/platform.h new file mode 100644 index 000000000..f029794a6 --- /dev/null +++ b/src/platform/beaglebone-ai64/inc/plat/platform.h @@ -0,0 +1,23 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) Bao Project and Contributors. All rights reserved + */ + +#ifndef __PLAT_PLATFORM_H__ +#define __PLAT_PLATFORM_H__ + +#define UART8250_REG_WIDTH (4U) + +#include +#include +#include + +/* j721e host id enumeration */ +#define J721E_HOST_ID_MAIN_A72_CONTEXT_0_SECURE (10) +#define J721E_HOST_ID_MAIN_A72_CONTEXT_1_SECURE (11) +#define J721E_HOST_ID_MAIN_A72_CONTEXT_2_NON_SECURE (12) +#define J721E_HOST_ID_MAIN_A72_CONTEXT_3_NON_SECURE (13) +#define J721E_HOST_ID_MAIN_A72_CONTEXT_4_NON_SECURE (14) +#define J721E_HOST_ID_MAIN_A72_CONTEXT_5_NON_SECURE (30) + +#endif diff --git a/src/platform/beaglebone-ai64/inc/plat/psci.h b/src/platform/beaglebone-ai64/inc/plat/psci.h new file mode 100644 index 000000000..82f96ee16 --- /dev/null +++ b/src/platform/beaglebone-ai64/inc/plat/psci.h @@ -0,0 +1,20 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#ifndef __PLAT_PSCI_H__ +#define __PLAT_PSCI_H__ + +#define PSCI_POWER_STATE_LVL_0 (0x0U) + +#define PSCI_STATE_TYPE_STANDBY (0x0U) +#define PSCI_STATE_TYPE_BIT (1UL << 16) + +#define PSCI_STATE_ID_RUN (0x0U) +#define PSCI_STATE_ID_RETENTION (0x1U) +#define PSCI_STATE_ID_POWERDOWN (0x2U) + +#define PSCI_STATE_TYPE_POWERDOWN PSCI_STATE_TYPE_BIT | PSCI_STATE_ID_POWERDOWN + +#endif // __PLAT_PSCI_H__ diff --git a/src/platform/beaglebone-ai64/objects.mk b/src/platform/beaglebone-ai64/objects.mk new file mode 100644 index 000000000..9f55c3dcf --- /dev/null +++ b/src/platform/beaglebone-ai64/objects.mk @@ -0,0 +1,4 @@ +## SPDX-License-Identifier: Apache-2.0 +## Copyright (c) Bao Project and Contributors. All rights reserved. + +boards-objs-y+=bb-ai64_desc.o diff --git a/src/platform/beaglebone-ai64/platform.mk b/src/platform/beaglebone-ai64/platform.mk new file mode 100644 index 000000000..fbaf62945 --- /dev/null +++ b/src/platform/beaglebone-ai64/platform.mk @@ -0,0 +1,18 @@ +## SPDX-License-Identifier: Apache-2.0 +## Copyright (c) Bao Project and Contributors. All rights reserved. + +# Architecture definition +ARCH:=armv8 +# CPU definition +CPU:=cortex-a72 + +#needs to be checked +GIC_VERSION:=GICV3 + +drivers = 8250_uart mailbox firmware + +platform_description:=bb-ai64_desc.c + +platform-cflags = -mtune=$(CPU) +platform-asflags = +platform-ldflags = diff --git a/src/platform/drivers/firmware/inc/drivers/tisci.h b/src/platform/drivers/firmware/inc/drivers/tisci.h new file mode 100644 index 000000000..8bc6996b3 --- /dev/null +++ b/src/platform/drivers/firmware/inc/drivers/tisci.h @@ -0,0 +1,327 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#ifndef __TISCI_H_ +#define __TISCI_H_ + +#include +#include +#include +#include +#include + +/** + * @brief TISCI Status Codes + */ +typedef enum { + TISCI_STATUS_CODE_NO_ERROR = 0, + TISCI_STATUS_CODE_INVALID_MSG_LEN = -1, + TISCI_STATUS_CODE_MSG_RCV_FAILED = -2, + TISCI_STATUS_CODE_MSG_SEND_FAILED = -3, + TISCI_STATUS_CODE_MSG_RX_TIMEOUT = -4, + TISCI_STATUS_CODE_NO_ACK_RCVD = -5, +} TISCI_STATUS_CODES; + +#define TISCI_HOST_DMSC (0U) + +/* msg request flags */ +#define TISCI_MSG_REQ_FLAG_RESERVED0_IDX (0) +#define TISCI_MSG_REQ_FLAG_AOP_IDX (1) +#define TISCI_MSG_REQ_FLAG_REQ_NOTFWD2DM_IDX (3) + +#define TISCI_MSG_REQ_FLAG_RESERVED0 (1 << TISCI_MSG_REQ_FLAG_RESERVED0_IDX) +#define TISCI_MSG_REQ_FLAG_AOP \ + (1 << TISCI_MSG_REQ_FLAG_AOP_IDX) /* ACK On Processed (AOP) - set if msg \ + requires ACK from DMSC */ +#define TISCI_MSG_REQ_FLAG_REQ_NOTFWD2DM (1 << TISCI_MSG_REQ_FLAG_REQ_NOTFWD2DM_IDX) + +/* msg response flags */ +#define TISCI_MSG_RSP_FLAG_ACK_IDX (1) + +#define TISCI_MSG_RSP_FLAG_ACK (1 << TISCI_MSG_RSP_FLAG_ACK_IDX) +#define TISCI_MSG_RSP_FLAG_NAK (0 << TISCI_MSG_RSP_FLAG_ACK_IDX) + +#define TISCI_MSG_MAX_SIZE (56U) +#define TISCI_MSG_TXN_TIMEOUT (1000) /* 1000ms */ +#define TISCI_MSG_NUM_RETRIES (5) + +#define TISCI_RM_IRQ_VALID_DEST_ID_IDX (0U) +#define TISCI_RM_IRQ_VALID_DEST_ID (1U << TISCI_RM_IRQ_VALID_DEST_ID_IDX) + +#define TISCI_RM_IRQ_VALID_DEST_HOST_IRQ_IDX (1U) +#define TISCI_RM_IRQ_VALID_DEST_HOST_IRQ (1U << TISCI_RM_IRQ_VALID_DEST_HOST_IRQ_IDX) + +#define TISCI_RM_IRQ_VALID_IA_ID_IDX (2U) +#define TISCI_RM_IRQ_VALID_IA_ID (1U << TISCI_RM_IRQ_VALID_IA_ID_IDX) + +#define TISCI_RM_IRQ_VALID_VIRT_INT_IDX (3U) +#define TISCI_RM_IRQ_VALID_VIRT_INT (1U << TISCI_RM_IRQ_VALID_VIRT_INT_IDX) + +#define TISCI_RM_IRQ_VALID_GLOBAL_EVENT_IDX (4U) +#define TISCI_RM_IRQ_VALID_GLOBAL_EVENT (1U << TISCI_RM_IRQ_VALID_GLOBAL_EVENT_IDX) + +#define TISCI_RM_IRQ_VALID_VIRT_INT_STATUS_BIT_INDEX_IDX (5U) +#define TISCI_RM_IRQ_VALID_VIRT_INT_STATUS_BIT_INDEX \ + (1U << TISCI_RM_IRQ_VALID_VIRT_INT_STATUS_BIT_INDEX_IDX) + +#define TISCI_RM_IRQ_VALID_SEC_HOST_IDX (31U) +#define TISCI_RM_IRQ_VALID_SEC_HOST (1U << TISCI_RM_IRQ_VALID_SEC_HOST_IDX) + +/** + * review the version of the currently running sysfw + * secure thread msg => NO + */ +#define TISCI_MSG_VERSION (0x0002U) + +/** + * configure peripherals within the SoC interrupt architecture + * secure thread msg => NO + */ +#define TISCI_MSG_RM_IRQ_SET (0x1000U) + +/** + * release an IRQ route between a device peripheral and a host processor + * secure thread msg => NO + */ +#define TISCI_MSG_RM_IRQ_RELEASE (0x1001U) + +/* + * tisci msg format + * + * ^ +-----------------------+ +----------------------+ + * | | secure header (4B) |------>| integrity check (2B) | + * | +-----------------------+ +----------------------+ + * min: 12B | | | | reserved (2B) | + * | | TISCI header (8B) | +----------------------+ + * | | | + * + +-----------------------+ + * | | | + * | | | + * max: 56B | | payload (max 44B) | + * | | | + * | | | + * v +-----------------------+ + * + */ + +/** + * @brief TISCI generic msg header for ALL msg and rsp + */ +typedef struct __attribute__((packed)) { + /** Message type ID */ + uint16_t type; + + /** + * logically distinct high level software entity identifier, used to select + * the set of channels to communicate with the DMSC + */ + uint8_t host_id; + + /** + * sequence identifier that will be returned back to the hypervisor for the + * response corresponding to the message + */ + uint8_t seq_id; + + /** Message flags */ + uint32_t msg_flags; +} tisci_msg_header; + +/** + * @brief Prefix header for all TISCI msg sent over secure transport. + */ +typedef struct __attribute__((packed)) { + /** msg integrity checksum, calculated for total msg in the request path */ + uint16_t integ_check; + + /** reserved for future use */ + uint16_t reserved; +} tisci_secure_msg_header; + +/** + * @brief TISCI Transport Operations + * + * Platform specific transport operations for sending and receiving messages. + */ +typedef struct { + /** + * @brief Send a message over a specific channel. + * + * @param chan_id The channel ID to send the message on. + * @param buffer Pointer to the data to send. + * @param len Length of the data in bytes. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, error code otherwise. + */ + int32_t (*send)(uint8_t chan_id, void* buffer, size_t len); + + /** + * @brief Receive a message from a specific channel. + * + * @param chan_id The channel ID to receive the message from. + * @param buffer Pointer to the buffer to store received data. + * @param len Maximum length of the buffer. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, error code otherwise. + */ + int32_t (*recv)(uint8_t chan_id, void* buffer, size_t len); +} tisci_ops; + +/** + * @brief TISCI Driver Context + * + * Holds the state and configuration for the TISCI driver instance. + */ +typedef struct { + /** Transport operations for sending/receiving messages */ + tisci_ops ops; + /** Lock to protect sequence ID generation */ + spinlock_t seq_id_lock; + /** Current sequence ID counter */ + uint8_t seq_id; +} tisci_ctx; + +/** + * @brief Response structure for TISCI_MSG_VERSION + * + * Returns version information of the currently running System Firmware (SYSFW). + */ +typedef struct __attribute__((packed)) { + /** Standard TISCI message header */ + tisci_msg_header msg_header; + /** Description string of the SYSFW */ + char sysfw_desc[32]; + /** SYSFW version number */ + uint16_t sysfw_version; + /** SYSFW ABI major version */ + uint8_t sysfw_abi_major; + /** SYSFW ABI minor version */ + uint8_t sysfw_abi_minor; + /** SYSFW sub-version number */ + uint8_t sysfw_sub_version; + /** SYSFW patch version number */ + uint8_t sysfw_patch_version; +} tisci_msg_version_rsp; + +/** + * @brief Configures peripheral to processor interrupt route according to the valid configuration + * provided. + */ +typedef struct __attribute__((packed)) { + /** Standard TISCI message header */ + tisci_msg_header msg_header; + /** Bitfield defining validity of interrupt route set parameters */ + uint32_t valid_params; + /** ID of interrupt source peripheral */ + uint16_t src_id; + /** Interrupt source index within source peripheral */ + uint16_t src_idx; + /** SoC IR device ID */ + uint16_t dest_id; + /** SoC IR output index */ + uint16_t dest_host_irq; + /** Device ID of interrupt aggregator in which the virtual interrupt resides */ + uint16_t intr_aggr_id; + /** Virtual interrupt number when configuring an interrupt aggregator */ + uint16_t virt_int; + /** Global event mapped to interrupt aggregator virtual interrupt status bit */ + uint16_t global_event; + /** Virtual interrupt status bit to set if the interrupt route utilizes an IA */ + uint8_t virt_int_status_bit_idx; + /** Secondary host value to replace the host value for the message */ + uint8_t sec_host; +} tisci_msg_rm_irq_set_req; + +/** + * @brief Response to setting a peripheral to processor interrupt route. + */ +typedef struct __attribute__((packed)) { + /** Standard TISCI message header */ + tisci_msg_header msg_header; +} tisci_msg_rm_irq_set_rsp; + +/** + * @brief Releases peripheral to processor interrupt route according to the valid configuration + * provided. + */ +typedef struct __attribute__((packed)) { + /** Standard TISCI message header */ + tisci_msg_header msg_header; + /** Bitfield defining validity of interrupt route release parameters */ + uint32_t valid_params; + /** ID of interrupt source peripheral */ + uint16_t src_id; + /** Interrupt source index within source peripheral */ + uint16_t src_idx; + /** SoC IR device ID */ + uint16_t dest_id; + /** SoC IR output index */ + uint16_t dest_host_irq; + /** Device ID of interrupt aggregator in which the virtual interrupt resides */ + uint16_t intr_aggr_id; + /** Virtual interrupt number if the interrupt route is through an interrupt aggregator */ + uint16_t virt_int; + /** Global event mapped to interrupt aggregator virtual interrupt status bit */ + uint16_t global_event; + /** Virtual interrupt status bit to release if the interrupt route utilizes an IA */ + uint8_t virt_int_status_bit_idx; + /** Secondary host value to replace the host value for the message */ + uint8_t sec_host; +} tisci_msg_rm_irq_release_req; + +/** + * @brief Response to releasing a peripheral to processor interrupt route. + */ +typedef struct __attribute__((packed)) { + /** Standard TISCI message header */ + tisci_msg_header msg_header; +} tisci_msg_rm_irq_release_rsp; + +/** + * @brief Retrieves the revision of the System Firmware. + * + * @param ctx Pointer to the TISCI driver context. + * @param host_id The Host ID to be used in the message header. + * @param tx_chan_id The channel ID to use for the send transaction. + * @param rx_chan_id The channel ID to use for the recv transaction. + * @param ver_rsp Pointer to the structure where the version response will be stored. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, or a negative error code on failure. + */ +int32_t tisci_get_revision(tisci_ctx* ctx, uint8_t host_id, uint8_t tx_chan_id, uint8_t rx_chan_id, + tisci_msg_version_rsp* ver_rsp); + +/** + * @brief Configures peripheral to processor interrupt route. + * + * @param ctx Pointer to the TISCI driver context. + * @param host_id The Host ID to be used in the message header. + * @param tx_chan_id The channel ID to use for the send transaction. + * @param rx_chan_id The channel ID to use for the recv transaction. + * @param irq_set_req Pointer to the interrupt route set request. + * @param irq_set_rsp Pointer to the interrupt route set response. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, or a negative error code on failure. + */ +int32_t tisci_rm_irq_set(tisci_ctx* ctx, uint8_t host_id, uint8_t tx_chan_id, uint8_t rx_chan_id, + tisci_msg_rm_irq_set_req* irq_set_req, tisci_msg_rm_irq_set_rsp* irq_set_rsp); + +/** + * @brief Releases peripheral to processor interrupt route. + * + * @param ctx Pointer to the TISCI driver context. + * @param host_id The Host ID to be used in the message header. + * @param tx_chan_id The channel ID to use for the send transaction. + * @param rx_chan_id The channel ID to use for the recv transaction. + * @param irq_release_req Pointer to the interrupt route release request. + * @param irq_release_rsp Pointer to the interrupt route release response. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, or a negative error code on failure. + */ +int32_t tisci_rm_irq_release(tisci_ctx* ctx, uint8_t host_id, uint8_t tx_chan_id, + uint8_t rx_chan_id, tisci_msg_rm_irq_release_req* irq_release_req, + tisci_msg_rm_irq_release_rsp* irq_release_rsp); + +#endif /* __TISCI_H_ */ diff --git a/src/platform/drivers/firmware/objects.mk b/src/platform/drivers/firmware/objects.mk new file mode 100644 index 000000000..86824188b --- /dev/null +++ b/src/platform/drivers/firmware/objects.mk @@ -0,0 +1,4 @@ +## SPDX-License-Identifier: Apache-2.0 +## Copyright (c) Bao Project and Contributors. All rights reserved. + +drivers-objs-y+=firmware/tisci.o diff --git a/src/platform/drivers/firmware/tisci.c b/src/platform/drivers/firmware/tisci.c new file mode 100644 index 000000000..e5d27f1f1 --- /dev/null +++ b/src/platform/drivers/firmware/tisci.c @@ -0,0 +1,276 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#include +#include +#include +#include + +/** + * @brief Prepares the TISCI message header. + * + * Assigns a new sequence ID to the message and sets the 'ACK On Processed' (AOP) flag + * to ensure the DMSC sends a response. + * + * @param ctx The driver context containing the sequence ID counter. + * @param tx_buf Pointer to the transmit buffer containing the message header. + * @param tx_len Length of the transmit buffer. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, error code on failure. + */ +static int32_t _setup_hdr(tisci_ctx* ctx, void* tx_buf, size_t tx_len) +{ + tisci_msg_header* msg_hdr; + + if (tx_len > TISCI_MSG_MAX_SIZE || tx_len < sizeof(tisci_msg_header)) { + return TISCI_STATUS_CODE_INVALID_MSG_LEN; + } + + msg_hdr = (tisci_msg_header*)tx_buf; + + /* increment and assign sequence ID for tracking this specific transaction */ + msg_hdr->seq_id = ++ctx->seq_id; + msg_hdr->msg_flags |= TISCI_MSG_REQ_FLAG_AOP; + + return TISCI_STATUS_CODE_NO_ERROR; +} + +/** + * @brief Receives a response from the TISCI channel. + * + * This function polls the receive channel for a response. It verifies: + * 1. The message was received successfully. + * 2. The received sequence ID matches the expected one (from the context). + * 3. The response has the ACK flag set. + * + * It checks up to TISCI_MSG_NUM_RETRIES times to handle mismatched messages (e.g. stale responses). + * The actual timeout is handled by the underlying recv operation. + * + * @param ctx The driver context. + * @param chan_id The channel ID to receive from. + * @param rx_buf Pointer to the receive buffer. + * @param rx_len Length of the receive buffer. + * @param expected_seq_id Expected Sequence ID for the received message. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, error code on failure. + */ +static int32_t _get_rsp(tisci_ctx* ctx, uint8_t chan_id, void* rx_buf, size_t rx_len, + uint8_t expected_seq_id) +{ + tisci_msg_header* msg_hdr; + + /* Validate buffer size before receiving to prevent overflow */ + if (rx_len > TISCI_MSG_MAX_SIZE) { + ERROR("channel_%d expected msg len too long", chan_id); + return TISCI_STATUS_CODE_INVALID_MSG_LEN; + } + + unsigned int retry_iterator = TISCI_MSG_NUM_RETRIES; + for (; retry_iterator > 0; retry_iterator--) { + int32_t recv_status = ctx->ops.recv(chan_id, rx_buf, rx_len); + if (TISCI_STATUS_CODE_NO_ERROR != recv_status) { + ERROR("channel_%d tisci msg rcv failed", chan_id); + return TISCI_STATUS_CODE_MSG_RCV_FAILED; + } + + msg_hdr = (tisci_msg_header*)rx_buf; + /* Verify that the response corresponds to the request we just sent */ + if (msg_hdr->seq_id == expected_seq_id) { + break; + } else { + WARNING("channel_%d unexpected seq_id (rcvd=%d, expected=%d)", chan_id, msg_hdr->seq_id, + expected_seq_id); + } + } + + if (0 == retry_iterator) { + ERROR("channel_%d timed out waiting for msg", chan_id); + return TISCI_STATUS_CODE_MSG_RX_TIMEOUT; + } + + /* Check if the DMSC acknowledged the request */ + if (0 == (msg_hdr->msg_flags & TISCI_MSG_RSP_FLAG_ACK)) { + ERROR("channel_%d no ack rcvd", chan_id); + return TISCI_STATUS_CODE_NO_ACK_RCVD; + } + + return TISCI_STATUS_CODE_NO_ERROR; +} + +/** + * @brief Transaction context for sending and receiving TISCI messages. + * + * Holds the channel ID, buffer, and buffer length required for a single tx or rx operation during a + * TISCI transaction. + */ +typedef struct { + /** Channel ID for the transaction */ + uint8_t chan_id; + /** Pointer to the buffer containing the message data */ + void* buff; + /** Length of the message buffer in bytes */ + size_t buff_len; +} txn_ctx; + +/** + * @brief Performs a full TISCI transaction (Send + Receive). + * + * Wraps the send and receive operations. It first sends the request, + * then immediately waits for the corresponding response. + * + * @param ctx The driver context. + * @param tx_ctx Pointer to the tx transaction context. + * @param rx_ctx Pointer to the rx transaction context. + * @param seq_id Expected Sequence ID for the received message. + * + * @return TISCI_STATUS_CODE_NO_ERROR on success, error code on failure. + */ +static int32_t _perform_txn(tisci_ctx* ctx, txn_ctx* tx_ctx, txn_ctx* rx_ctx, uint8_t seq_id) +{ + int32_t txn_status = ctx->ops.send(tx_ctx->chan_id, tx_ctx->buff, tx_ctx->buff_len); + if (TISCI_STATUS_CODE_NO_ERROR != txn_status) { + ERROR("channel_%d txn - write failed", tx_ctx->chan_id); + return TISCI_STATUS_CODE_MSG_SEND_FAILED; + } + + txn_status = _get_rsp(ctx, rx_ctx->chan_id, rx_ctx->buff, rx_ctx->buff_len, seq_id); + if (TISCI_STATUS_CODE_NO_ERROR != txn_status) { + ERROR("channel_%d txn - read failed", rx_ctx->chan_id); + return txn_status; + } + + return TISCI_STATUS_CODE_NO_ERROR; +} + +int32_t tisci_get_revision(tisci_ctx* ctx, uint8_t host_id, uint8_t tx_chan_id, uint8_t rx_chan_id, + tisci_msg_version_rsp* ver_rsp) +{ + tisci_msg_header msg_header = { + .host_id = host_id, + .type = TISCI_MSG_VERSION, + }; + + /* critical section - aquire a spinlock on hdr setup to ensure atomic updates to seq_id and + * store them for the current txn */ + spin_lock(&(ctx->seq_id_lock)); + + int32_t rev_status = _setup_hdr(ctx, &msg_header, sizeof(msg_header)); + if (TISCI_STATUS_CODE_NO_ERROR != rev_status) { + ERROR("channel_%d msg hdr setup failed", tx_chan_id); + spin_unlock(&(ctx->seq_id_lock)); + return rev_status; + } + + uint8_t cur_seq_id = ctx->seq_id; + spin_unlock(&(ctx->seq_id_lock)); + + txn_ctx tx_ctx = { + .chan_id = tx_chan_id, + .buff = &msg_header, + .buff_len = sizeof(msg_header), + }; + txn_ctx rx_ctx = { + .chan_id = rx_chan_id, + .buff = ver_rsp, + .buff_len = sizeof(*ver_rsp), + }; + rev_status = _perform_txn(ctx, &tx_ctx, &rx_ctx, cur_seq_id); + + if (TISCI_STATUS_CODE_NO_ERROR != rev_status) { + ERROR("channel_%d tisci txn failed", tx_chan_id); + return rev_status; + } + + return TISCI_STATUS_CODE_NO_ERROR; +} + +int32_t tisci_rm_irq_set(tisci_ctx* ctx, uint8_t host_id, uint8_t tx_chan_id, uint8_t rx_chan_id, + tisci_msg_rm_irq_set_req* irq_set_req, tisci_msg_rm_irq_set_rsp* irq_set_rsp) +{ + tisci_msg_header msg_header = { + .host_id = host_id, + .type = TISCI_MSG_RM_IRQ_SET, + }; + + /* critical section - aquire a spinlock on hdr setup to ensure atomic updates to seq_id and + * store them for the current txn */ + spin_lock(&(ctx->seq_id_lock)); + + int32_t rev_status = _setup_hdr(ctx, &msg_header, sizeof(msg_header)); + if (TISCI_STATUS_CODE_NO_ERROR != rev_status) { + ERROR("channel_%d msg hdr setup failed", tx_chan_id); + spin_unlock(&(ctx->seq_id_lock)); + return rev_status; + } + + uint8_t cur_seq_id = ctx->seq_id; + spin_unlock(&(ctx->seq_id_lock)); + + memcpy(&(irq_set_req->msg_header), &msg_header, sizeof(msg_header)); + + txn_ctx tx_ctx = { + .chan_id = tx_chan_id, + .buff = irq_set_req, + .buff_len = sizeof(tisci_msg_rm_irq_set_req), + }; + txn_ctx rx_ctx = { + .chan_id = rx_chan_id, + .buff = irq_set_rsp, + .buff_len = sizeof(tisci_msg_rm_irq_set_rsp), + }; + rev_status = _perform_txn(ctx, &tx_ctx, &rx_ctx, cur_seq_id); + + if (TISCI_STATUS_CODE_NO_ERROR != rev_status) { + ERROR("channel_%d tisci txn failed", tx_chan_id); + return rev_status; + } + + return TISCI_STATUS_CODE_NO_ERROR; +} + +int32_t tisci_rm_irq_release(tisci_ctx* ctx, uint8_t host_id, uint8_t tx_chan_id, + uint8_t rx_chan_id, tisci_msg_rm_irq_release_req* irq_release_req, + tisci_msg_rm_irq_release_rsp* irq_release_rsp) +{ + tisci_msg_header msg_header = { + .host_id = host_id, + .type = TISCI_MSG_RM_IRQ_RELEASE, + }; + + /* critical section - aquire a spinlock on hdr setup to ensure atomic updates to seq_id and + * store them for the current txn */ + spin_lock(&(ctx->seq_id_lock)); + + int32_t rev_status = _setup_hdr(ctx, &msg_header, sizeof(msg_header)); + if (TISCI_STATUS_CODE_NO_ERROR != rev_status) { + ERROR("channel_%d msg hdr setup failed", tx_chan_id); + spin_unlock(&(ctx->seq_id_lock)); + return rev_status; + } + + uint8_t cur_seq_id = ctx->seq_id; + spin_unlock(&(ctx->seq_id_lock)); + + memcpy(&(irq_release_req->msg_header), &msg_header, sizeof(msg_header)); + + txn_ctx tx_ctx = { + .chan_id = tx_chan_id, + .buff = irq_release_req, + .buff_len = sizeof(tisci_msg_rm_irq_release_req), + }; + txn_ctx rx_ctx = { + .chan_id = rx_chan_id, + .buff = irq_release_rsp, + .buff_len = sizeof(tisci_msg_rm_irq_release_rsp), + }; + rev_status = _perform_txn(ctx, &tx_ctx, &rx_ctx, cur_seq_id); + + if (TISCI_STATUS_CODE_NO_ERROR != rev_status) { + ERROR("channel_%d tisci txn failed", tx_chan_id); + return rev_status; + } + + return TISCI_STATUS_CODE_NO_ERROR; +} diff --git a/src/platform/drivers/mailbox/inc/drivers/k3_sec_proxy.h b/src/platform/drivers/mailbox/inc/drivers/k3_sec_proxy.h new file mode 100644 index 000000000..2f531dd77 --- /dev/null +++ b/src/platform/drivers/mailbox/inc/drivers/k3_sec_proxy.h @@ -0,0 +1,260 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#ifndef __MBOX_K3_SEC_PROXY_H_ +#define __MBOX_K3_SEC_PROXY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Defines the specific host function associated with a Secure Proxy + * thread. + */ +typedef enum { + MBOX_K3_SEC_PROXY_HOST_FUNCTION_NOTIFY, + MBOX_K3_SEC_PROXY_HOST_FUNCTION_RESPONSE, + MBOX_K3_SEC_PROXY_HOST_FUNCTION_HIGH_PRIORITY, + MBOX_K3_SEC_PROXY_HOST_FUNCTION_LOW_PRIORITY, + MBOX_K3_SEC_PROXY_HOST_FUNCTION_NOTIFY_RESP, + MBOX_K3_SEC_PROXY_NUM_HOST_FUNCTIONS, +} MBOX_K3_SEC_PROXY_HOST_FUNCTION; + +/** + * @brief Specifies the direction of data flow for a Secure Proxy thread. + */ +typedef enum { + MBOX_K3_SEC_PROXY_MSG_DRXN_WRITE = 0, + MBOX_K3_SEC_PROXY_MSG_DRXN_READ = 1, +} MBOX_K3_SEC_PROXY_MSG_DRXN; + +/** + * @brief Status codes returned by Secure Proxy APIs. + */ +typedef enum { + MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR = 0, + MBOX_K3_SEC_PROXY_STATUS_CODE_THREAD_CORRUPTED = -1, + MBOX_K3_SEC_PROXY_STATUS_CODE_INCORRECT_DRXN = -2, + MBOX_K3_SEC_PROXY_STATUS_CODE_NO_DATA = -4, + MBOX_K3_SEC_PROXY_STATUS_CODE_INVALID_MSG_LEN = -5, + MBOX_K3_SEC_PROXY_STATUS_CODE_THREAD_CLEAR_FAILED = -6, + MBOX_K3_SEC_PROXY_STATUS_CODE_DIRTY_HANDOFF = -7, +} MBOX_K3_SEC_PROXY_STATUS_CODES; + +/* bit indices */ +#define MBOX_K3_SEC_PROXY_RT_THREAD_STATUS_OFFSET (0x0U) +#define MBOX_K3_SEC_PROXY_RT_THREAD_THRESHOLD_OFFSET (0x4U) + +#define MBOX_K3_SEC_PROXY_RT_STATUS_ERROR_IDX (31) +#define MBOX_K3_SEC_PROXY_RT_STATUS_ERROR_MASK (1U << MBOX_K3_SEC_PROXY_RT_STATUS_ERROR_IDX) + +#define MBOX_K3_SEC_PROXY_RT_STATUS_CUR_CNT_IDX (0) +#define MBOX_K3_SEC_PROXY_RT_STATUS_CUR_CNT_MASK (0xFFU << MBOX_K3_SEC_PROXY_RT_STATUS_CUR_CNT_IDX) + +#define MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_OFFSET (0x1000U) + +#define MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_DIR_IDX (31) +#define MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_DIR_MASK \ + (1U << MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_DIR_IDX) + +#define MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id) (0x1000U * (thread_id)) + +#define MBOX_K3_SEC_PROXY_DATA_START_OFFSET (0x4U) +#define MBOX_K3_SEC_PROXY_DATA_END_OFFSET (0x3CU) /* completion trigger offset */ + +/** + * @brief Hardware configuration for a Secure Proxy instance. + */ +typedef struct { + /* note: declared as a 32-bit val because it is kept as such in u-boot + * implementation. However, in J721E, there are only 2 supported sec_proxy + * IDs: + * 0 => NAVSS0_SEC_PROXY_0 + * 1 => MCU_NAVSS0_SEC_PROXY0 + * can modify type based on requirements... + */ + uint32_t id; /**< Instance ID (e.g., NAVSS0_SEC_PROXY_0) */ + + paddr_t rt_base; /**< Base address of Real-Time (RT) region */ + size_t rt_size; /**< Size of RT region */ + + paddr_t scfg_base; /**< Base address of Secure Configuration (SCFG) region */ + size_t scfg_size; /**< Size of SCFG region */ + + paddr_t data_base; /**< Base address of Data region */ + size_t data_size; /**< Size of Data region */ + + size_t max_msg_size; +} mbox_k3_sec_proxy_instance; + +/** + * @brief Configuration descriptor for a single Secure Proxy thread. + */ +typedef struct { + uint8_t sec_proxy_thread_id; /**< Secure Proxy thread ID */ + MBOX_K3_SEC_PROXY_MSG_DRXN msg_drxn; /**< Message direction (Read/Write) */ + uint8_t mbox_queue_depth; /**< Depth of the mailbox queue */ + uint8_t host_id; /**< ID of the host associated with this thread */ + MBOX_K3_SEC_PROXY_HOST_FUNCTION host_function; /**< Function/Role of the thread */ + uint16_t irq_line_threshold; /**< IRQ threshold configuration */ + uint16_t irq_line_error; /**< IRQ error configuration */ +} mbox_k3_sec_proxy_thread_desc; + +/** + * @brief Top-level descriptor aggregating the Secure Proxy instance and its + * thread configurations. + */ +typedef struct { + mbox_k3_sec_proxy_instance thread_inst; /**< Secure Proxy instance configuration */ + uint32_t read_timeout_us; /**< timeout before read operation fails due to no data in the mbox + queue */ + mbox_k3_sec_proxy_thread_desc* sec_proxy_thread_desc; /**< Array of thread descriptors */ + size_t num_threads; /**< Number of entries in the sec_proxy_thread_desc array */ +} mbox_k3_sec_proxy_desc; + +// clang-format off +#define MBOX_K3_SEC_PROXY_THREAD_DESC_ENTRY( \ + host, base_thread_id, notify_queue_depth, resp_queue_depth, \ + high_priority_queue_depth, low_priority_queue_depth) \ + [base_thread_id] = \ + { \ + .sec_proxy_thread_id = base_thread_id, \ + .msg_drxn = MBOX_K3_SEC_PROXY_MSG_DRXN_READ, \ + .mbox_queue_depth = notify_queue_depth, \ + .host_id = host, \ + .host_function = MBOX_K3_SEC_PROXY_HOST_FUNCTION_NOTIFY}, \ + [base_thread_id + 1] = \ + { \ + .sec_proxy_thread_id = (base_thread_id + 1), \ + .msg_drxn = MBOX_K3_SEC_PROXY_MSG_DRXN_READ, \ + .mbox_queue_depth = resp_queue_depth, \ + .host_id = host, \ + .host_function = MBOX_K3_SEC_PROXY_HOST_FUNCTION_RESPONSE}, \ + [base_thread_id + 2] = \ + { \ + .sec_proxy_thread_id = (base_thread_id + 2), \ + .msg_drxn = MBOX_K3_SEC_PROXY_MSG_DRXN_WRITE, \ + .mbox_queue_depth = high_priority_queue_depth, \ + .host_id = host, \ + .host_function = MBOX_K3_SEC_PROXY_HOST_FUNCTION_HIGH_PRIORITY}, \ + [base_thread_id + 3] = \ + { \ + .sec_proxy_thread_id = (base_thread_id + 3), \ + .msg_drxn = MBOX_K3_SEC_PROXY_MSG_DRXN_WRITE, \ + .mbox_queue_depth = low_priority_queue_depth, \ + .host_id = host, \ + .host_function = MBOX_K3_SEC_PROXY_HOST_FUNCTION_LOW_PRIORITY}, \ + [base_thread_id + 4] = \ + { \ + .sec_proxy_thread_id = (base_thread_id + 4), \ + .msg_drxn = MBOX_K3_SEC_PROXY_MSG_DRXN_WRITE, \ + .mbox_queue_depth = 2, \ + .host_id = host, \ + .host_function = MBOX_K3_SEC_PROXY_HOST_FUNCTION_NOTIFY_RESP} +// clang-format on + +/** + * @brief Verifies the status and configuration of a Secure Proxy thread before + * a transaction. + * + * @desc This function checks for thread corruption, validates the thread's + * configured direction (read/write) against its intended usage, and ensures the + * message queue is not empty if reading. + * + * @param sec_proxy_desc Pointer to the Secure Proxy descriptor. + * @param thread_id The ID of the thread to verify. + * @param msg_drxn Expected message direction (MSG_DRXN_READ or MSG_DRXN_WRITE). + * + * @return int32_t STATUS_CODE_NO_ERROR if the thread is valid and ready, + * otherwise respective error codes. + */ +int32_t mbox_k3_sec_proxy_verify_thread(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id, + uint8_t msg_drxn); + +/** + * @brief Reads a message from a specific Secure Proxy thread. + * + * @param sec_proxy_desc Pointer to the Secure Proxy descriptor. + * @param thread_id The ID of the thread to read from. + * @param buffer Pointer to the buffer where the read message will be stored. + * @param len Length of the message to read in bytes. + * + * @return int32_t STATUS_CODE_NO_ERROR on success, or respective error codes + * on failure. + * + * @notes + * - does not support big-endian systems, current implementation only is + * for little-endian. + * - byte-ordering logic for trailing bytes assumes LSB-first memory layout. + */ +int32_t mbox_k3_sec_proxy_read(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id, + void* buffer, size_t len); + +/** + * @brief Writes a message to a specific Secure Proxy thread. + * + * @param sec_proxy_desc Pointer to the Secure Proxy descriptor. + * @param thread_id The ID of the thread to write to. + * @param buffer Pointer to the buffer containing the message to send. + * @param len Length of the message to send in bytes. + * + * @return int32_t STATUS_CODE_NO_ERROR on success, or an error code on + * failure. + * + * @notes + * - does not support big-endian systems, current implementation only is + * for little-endian. + * - byte-ordering logic for trailing bytes assumes LSB-first memory layout. + */ +int32_t mbox_k3_sec_proxy_write(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id, + void* buffer, size_t len); + +/** + * @brief Clears all pending messages from a Secure Proxy thread. + * + * @param sec_proxy_desc Pointer to the Secure Proxy descriptor. + * @param thread_id The ID of the thread to clear. + * + * @return int32_t STATUS_CODE_NO_ERROR on success, or an error code on failure. + */ +int32_t mbox_k3_sec_proxy_clear(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id); + +/** + * @brief Performs a health check on a Secure Proxy thread and reports status. + * + * @param sec_proxy_desc Pointer to the Secure Proxy descriptor. + * @param thread_id The ID of the thread to probe. + * + * @return int32_t STATUS_CODE_NO_ERROR on success, or respective error codes + * on failure. + */ +int32_t mbox_k3_sec_proxy_probe(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id); + +/** + * @brief Finds a thread descriptor based on Host Function. + * + * Searches the descriptor's thread array for an entry matching both the + * provided host identifier and the specific function role. + * + * @param sec_proxy_desc Pointer to the Secure Proxy descriptor. + * @param host_id The hardware Host ID to search for. + * @param host_function The specific role (e.g., RESPONSE, HIGH_PRIORITY). + * + * @return mbox_k3_sec_proxy_thread_desc* Pointer to the matching thread + * descriptor, or NULL if no match is found. + */ +mbox_k3_sec_proxy_thread_desc* +mbox_k3_sec_proxy_get_thread_by_host_func(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t host_id, + MBOX_K3_SEC_PROXY_HOST_FUNCTION host_function); + +void mbox_k3_sec_proxy_init(mbox_k3_sec_proxy_desc* sec_proxy_desc); + +#endif /* __MBOX_K3_SEC_PROXY_H_ */ diff --git a/src/platform/drivers/mailbox/k3_sec_proxy.c b/src/platform/drivers/mailbox/k3_sec_proxy.c new file mode 100644 index 000000000..4ffa0e9ae --- /dev/null +++ b/src/platform/drivers/mailbox/k3_sec_proxy.c @@ -0,0 +1,337 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) Bao Project and Contributors. All rights reserved. + */ + +#include + +/** + * @brief Reads a 32-bit value from a memory-mapped register. + * + * @param addr Base address of the peripheral or register block. + * @param offset Offset from the base address to the specific register. + * + * @return The 32-bit value read from the register. + */ +static inline uint32_t read_reg(paddr_t addr, paddr_t offset) +{ + return *((volatile uint32_t*)((paddr_t)(addr + offset))); +} + +/** + * @brief Writes a 32-bit value to a memory-mapped register. + * + * @param addr Base address of the peripheral or register block. + * @param offset Offset from the base address to the specific register. + * @param value The 32-bit value to write. + */ +static inline void write_reg(paddr_t addr, paddr_t offset, uint32_t value) +{ + *((volatile uint32_t*)((paddr_t)(addr + offset))) = value; +} + +int32_t mbox_k3_sec_proxy_verify_thread(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id, + uint8_t msg_drxn) +{ + paddr_t thread_rt_base = + sec_proxy_desc->thread_inst.rt_base + MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id); + paddr_t thread_scfg_base = + sec_proxy_desc->thread_inst.scfg_base + MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id); + + /* check for existing errors */ + if (read_reg(thread_rt_base, MBOX_K3_SEC_PROXY_RT_THREAD_STATUS_OFFSET) & + MBOX_K3_SEC_PROXY_RT_STATUS_ERROR_MASK) { + ERROR("secure_proxy_thread_%d corrupted", thread_id); + return MBOX_K3_SEC_PROXY_STATUS_CODE_THREAD_CORRUPTED; + } + + /* validate thread drxn config */ + if ((read_reg(thread_scfg_base, MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_OFFSET) & + MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_DIR_MASK) >> + MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_DIR_IDX != + msg_drxn) { + if (MBOX_K3_SEC_PROXY_MSG_DRXN_WRITE == msg_drxn) { + ERROR("secure_proxy_thread_%d cannot READ on WRITE thread", thread_id); + return MBOX_K3_SEC_PROXY_STATUS_CODE_INCORRECT_DRXN; + } else { + ERROR("secure_proxy_thread_%d cannot WRITE on READ thread", thread_id); + return MBOX_K3_SEC_PROXY_STATUS_CODE_INCORRECT_DRXN; + } + } + + /* check if msg queue has entries before txn attempt */ + if (MBOX_K3_SEC_PROXY_MSG_DRXN_READ == msg_drxn) { + uint64_t tick_start = sysreg_cntpct_el0_read(); + uint64_t ticks_per_us = sysreg_cntfrq_el0_read() / 1000000U; + while (0 == + (read_reg(thread_rt_base, MBOX_K3_SEC_PROXY_RT_THREAD_STATUS_OFFSET) & + MBOX_K3_SEC_PROXY_RT_STATUS_CUR_CNT_MASK)) { + if ((sysreg_cntpct_el0_read() - tick_start) > + (sec_proxy_desc->read_timeout_us * ticks_per_us)) { + ERROR("secure_proxy_thread_%d no entries in message queue", thread_id); + return MBOX_K3_SEC_PROXY_STATUS_CODE_NO_DATA; + } + } + } + + return MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR; +} + +/* + * read transaction: + * + * +-------------------------+ + * | 1. verify thread status | + * +-----------+-------------+ + * | + * v + * +-----------+-------------+ +------------------------------------+ + * | 2. read whole words | <---- | regs: base + DATA_START_OFFSET ... | + * +-----------+-------------+ +------------------------------------+ + * | + * v + * +-----------+-------------+ +------------------------------------+ + * | 3. read & unpack trail | <---- | reg: base + N | + * | bytes (little endian)| | (extract from LSB) | + * +-----------+-------------+ +------------------------------------+ + * | + * v + * +-----------+-------------+ +------------------------------------+ + * | 4. read trigger reg if | <---- | trigger: base + DATA_END_OFFSET | + * | not reached yet | +------------------------------------+ + * +-------------------------+ + */ +int32_t mbox_k3_sec_proxy_read(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id, + void* buffer, size_t len) +{ + /* verify thread status */ + int32_t read_status = + mbox_k3_sec_proxy_verify_thread(sec_proxy_desc, thread_id, MBOX_K3_SEC_PROXY_MSG_DRXN_READ); + if (MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR != read_status) { + ERROR("secure_proxy_thread_%d thread verif failed", thread_id); + return read_status; + } + + /* perform read transaction */ + paddr_t data_reg = sec_proxy_desc->thread_inst.data_base + + MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id) + MBOX_K3_SEC_PROXY_DATA_START_OFFSET; + + /* read whole words first */ + uint32_t word_iterator; + size_t num_words = len / sizeof(uint32_t); + for (word_iterator = 0; word_iterator < num_words; word_iterator++) { + ((uint32_t*)buffer)[word_iterator] = read_reg(data_reg, word_iterator * sizeof(uint32_t)); + } + + /* read remaining bytes */ + uint32_t trail_bytes = len % sizeof(uint32_t); + if (0 != trail_bytes) { + uint32_t data_trail = read_reg(data_reg, word_iterator++ * sizeof(uint32_t)); + + size_t trail_iterator = len - trail_bytes; + while (trail_bytes--) { + ((uint8_t*)buffer)[trail_iterator++] = (uint8_t)(data_trail & 0xFFU); + data_trail >>= 8; + } + } + + /* in case the completion trigger register is not accessed during the read, + * the following access is performed, to mark the completion of the + * transaction. */ + if ((MBOX_K3_SEC_PROXY_DATA_START_OFFSET + (word_iterator * sizeof(uint32_t))) <= + MBOX_K3_SEC_PROXY_DATA_END_OFFSET) { + read_reg(data_reg, MBOX_K3_SEC_PROXY_DATA_END_OFFSET - MBOX_K3_SEC_PROXY_DATA_START_OFFSET); + } + + INFO("secure_proxy_thread_%d data READ success", thread_id); + + return read_status; +} + +/* + * write transaction: + * + * +-------------------------+ + * | 1. verify thread status | + * +-----------+-------------+ + * | + * v + * +-----------+-------------+ + * | 2. check message length | + * +-----------+-------------+ + * | + * v + * +-----------+-------------+ +------------------------------------+ + * | 3. write whole words | ----> | regs: base + DATA_START_OFFSET ... | + * +-----------+-------------+ +------------------------------------+ + * | + * v + * +-----------+-------------+ +------------------------------------+ + * | 4. pack & write trail | ----> | reg: base + N | + * | bytes (little endian)| | (right-aligned/LSB) | + * +-----------+-------------+ +------------------------------------+ + * | + * v + * +-----------+-------------+ +------------------------------------+ + * | 5. pad with zeros until | ----> | trigger: base + DATA_END_OFFSET | + * | end offset (trigger) | +------------------------------------+ + * +-------------------------+ + */ +int32_t mbox_k3_sec_proxy_write(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id, + void* buffer, size_t len) +{ + /* verify thread status */ + int32_t write_status = mbox_k3_sec_proxy_verify_thread(sec_proxy_desc, thread_id, + MBOX_K3_SEC_PROXY_MSG_DRXN_WRITE); + if (MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR != write_status) { + ERROR("secure_proxy_thread_%d thread verif failed", thread_id); + return write_status; + } + + /* msg len check */ + if (len > sec_proxy_desc->thread_inst.max_msg_size) { + ERROR("secure_proxy_thread_%d msg len exceeds limit", thread_id); + return MBOX_K3_SEC_PROXY_STATUS_CODE_INVALID_MSG_LEN; + } + + /* perform write transaction */ + paddr_t data_reg = sec_proxy_desc->thread_inst.data_base + + MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id) + MBOX_K3_SEC_PROXY_DATA_START_OFFSET; + + /* write whole words first */ + uint32_t word_iterator; + size_t num_words = len / sizeof(uint32_t); + for (word_iterator = 0; word_iterator < num_words; word_iterator++) { + write_reg(data_reg, word_iterator * sizeof(uint32_t), ((uint32_t*)buffer)[word_iterator]); + } + + /* write remaining bytes */ + uint32_t trail_bytes = len % sizeof(uint32_t); + if (0 != trail_bytes) { + uint32_t data_trail = 0; + + size_t trail_iterator = len - trail_bytes; + for (uint32_t i = 0; i < trail_bytes; i++) { + data_trail |= (uint32_t)((uint8_t*)buffer)[trail_iterator++] << (i * 8); + } + + write_reg(data_reg, word_iterator++ * sizeof(uint32_t), data_trail); + } + + /* + * Pad the remaining registers with zeros up to the completion trigger offset. + * The Secure Proxy hardware triggers the message transmission ONLY when + * the last register at MBOX_K3_SEC_PROXY_DATA_END_OFFSET is written. + */ + while ((MBOX_K3_SEC_PROXY_DATA_START_OFFSET + (word_iterator * sizeof(uint32_t))) <= + MBOX_K3_SEC_PROXY_DATA_END_OFFSET) { + write_reg(data_reg, word_iterator++ * sizeof(uint32_t), 0U); + } + + INFO("secure_proxy_thread_%d data WRITE success", thread_id); + + return write_status; +} + +int32_t mbox_k3_sec_proxy_clear(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id) +{ + int32_t clear_status = + mbox_k3_sec_proxy_verify_thread(sec_proxy_desc, thread_id, MBOX_K3_SEC_PROXY_MSG_DRXN_READ); + if (MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR != clear_status) { + ERROR("secure_proxy_thread_%d thread verif failed", thread_id); + return clear_status; + } + + paddr_t thread_rt_base = + sec_proxy_desc->thread_inst.rt_base + MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id); + paddr_t data_reg = + sec_proxy_desc->thread_inst.data_base + MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id); + + uint32_t try_count = 10; + while (0 != + (read_reg(thread_rt_base, MBOX_K3_SEC_PROXY_RT_THREAD_STATUS_OFFSET) & + MBOX_K3_SEC_PROXY_RT_STATUS_CUR_CNT_MASK)) { + if (0 == (try_count--)) { + ERROR("secure_proxy_thread_%d mailbox clear failed", thread_id); + return MBOX_K3_SEC_PROXY_STATUS_CODE_THREAD_CLEAR_FAILED; + } + + WARNING("secure_proxy_thread_%d mailbox clear in progress", thread_id); + read_reg(data_reg, MBOX_K3_SEC_PROXY_DATA_END_OFFSET); + } + + return MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR; +} + +int32_t mbox_k3_sec_proxy_probe(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t thread_id) +{ + paddr_t thread_scfg_base = + sec_proxy_desc->thread_inst.scfg_base + MBOX_K3_SEC_PROXY_THREAD_OFFSET(thread_id); + uint32_t config = read_reg(thread_scfg_base, MBOX_K3_SEC_PROXY_SCFG_THREAD_CTRL_OFFSET); + + uint8_t hw_host = (config >> 8) & 0xFF; + uint8_t expected_host = sec_proxy_desc->sec_proxy_thread_desc[thread_id].host_id; + + /* [step-1] verify thread access/host ownership */ + if (hw_host != expected_host) { + ERROR("sec_proxy_thread_%d probe failed (hw_host=%d, expected=%d)", thread_id, hw_host, + expected_host); + return MBOX_K3_SEC_PROXY_STATUS_CODE_THREAD_CORRUPTED; + } + + /* [step-2] verify if thread is clean */ + int32_t probe_status = mbox_k3_sec_proxy_verify_thread(sec_proxy_desc, thread_id, + sec_proxy_desc->sec_proxy_thread_desc[thread_id].msg_drxn); + + if (MBOX_K3_SEC_PROXY_MSG_DRXN_READ == + sec_proxy_desc->sec_proxy_thread_desc[thread_id].msg_drxn && + MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR == probe_status) { + ERROR("secure_proxy_thread_%d probe failed (message queue not clean)", thread_id); + return MBOX_K3_SEC_PROXY_STATUS_CODE_DIRTY_HANDOFF; + } + + if (MBOX_K3_SEC_PROXY_MSG_DRXN_READ == + sec_proxy_desc->sec_proxy_thread_desc[thread_id].msg_drxn && + MBOX_K3_SEC_PROXY_STATUS_CODE_NO_DATA == probe_status) { + probe_status = MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR; + } + + if (MBOX_K3_SEC_PROXY_STATUS_CODE_NO_ERROR != probe_status) { + INFO("sec_proxy_thread_%d probe failed (error_id=%d)", thread_id, probe_status); + return probe_status; + } + + INFO("sec_proxy_thread_%d probe success", thread_id); + return probe_status; +} + +mbox_k3_sec_proxy_thread_desc* +mbox_k3_sec_proxy_get_thread_by_host_func(mbox_k3_sec_proxy_desc* sec_proxy_desc, uint8_t host_id, + MBOX_K3_SEC_PROXY_HOST_FUNCTION host_function) +{ + for (size_t thread_iterator = 0; thread_iterator < sec_proxy_desc->num_threads; + thread_iterator++) { + if (sec_proxy_desc->sec_proxy_thread_desc[thread_iterator].host_id == host_id && + sec_proxy_desc->sec_proxy_thread_desc[thread_iterator].host_function == host_function) { + return &sec_proxy_desc->sec_proxy_thread_desc[thread_iterator]; + } + } + return NULL; +} + +void mbox_k3_sec_proxy_init(mbox_k3_sec_proxy_desc* sec_proxy_desc) +{ + if (cpu_is_master()) { + sec_proxy_desc->thread_inst.rt_base = (vaddr_t)mem_alloc_map_dev(&cpu()->as, SEC_HYP_GLOBAL, + INVALID_VA, sec_proxy_desc->thread_inst.rt_base, + NUM_PAGES(sec_proxy_desc->thread_inst.rt_size)); + + sec_proxy_desc->thread_inst.scfg_base = (vaddr_t)mem_alloc_map_dev(&cpu()->as, + SEC_HYP_GLOBAL, INVALID_VA, sec_proxy_desc->thread_inst.scfg_base, + NUM_PAGES(sec_proxy_desc->thread_inst.scfg_size)); + + sec_proxy_desc->thread_inst.data_base = (vaddr_t)mem_alloc_map_dev(&cpu()->as, + SEC_HYP_GLOBAL, INVALID_VA, sec_proxy_desc->thread_inst.data_base, + NUM_PAGES(sec_proxy_desc->thread_inst.data_size)); + } +} diff --git a/src/platform/drivers/mailbox/objects.mk b/src/platform/drivers/mailbox/objects.mk new file mode 100644 index 000000000..e23dea275 --- /dev/null +++ b/src/platform/drivers/mailbox/objects.mk @@ -0,0 +1,4 @@ +## SPDX-License-Identifier: Apache-2.0 +## Copyright (c) Bao Project and Contributors. All rights reserved. + +drivers-objs-y+=mailbox/k3_sec_proxy.o