Commit d3010aae authored by Mahesh Rao's avatar Mahesh Rao Committed by Anas Nashif
Browse files

subsystem: sip_svc: Add SDM mailbox specific shell



Add shell based on sip_svc subsystem to access the SDM mailbox in
EL3 layer using ASYNC sip_svc request.

Signed-off-by: default avatarMahesh Rao <mahesh.rao@intel.com>
parent 23df7d2d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -9,4 +9,5 @@ zephyr_linker_sources(DATA_SECTIONS sip_svc_data.ld)

if(CONFIG_ARM_SIP_SVC_SUBSYS_SHELL)
  zephyr_library_sources(sip_svc_shell.c)
  zephyr_library_sources_ifdef(CONFIG_ARM_SIP_SVC_HAS_INTEL_SDM_MAILBOX_FIFO sip_svc_agilex_mailbox_shell.c)
endif()
+347 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2022-2023, Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * A mailbox client command shell on sip_svc service to communicate with SDM.
 */

#include <zephyr/kernel.h>
#include <zephyr/sip_svc/sip_svc.h>
#include <zephyr/drivers/sip_svc/sip_svc_agilex_mailbox.h>
#include <zephyr/drivers/sip_svc/sip_svc_agilex_smc.h>
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MAX_TIMEOUT_SECS (10 * 60UL)

struct private_data {
	struct k_sem semaphore;
	const struct shell *sh;
};

static void *mb_smc_ctrl;
static uint32_t mb_c_token = SIP_SVC_ID_INVALID;

static int cmd_reg(const struct shell *sh, size_t argc, char **argv)
{
	int err;

	if (mb_smc_ctrl) {
		shell_print(sh, "Mailbox client already registered");
		return 0;
	}

	mb_smc_ctrl = sip_svc_get_controller(argv[1]);
	if (!mb_smc_ctrl) {
		shell_error(sh, "Arm SiP service %s not found", argv[1]);
		return -ENODEV;
	}

	mb_c_token = sip_svc_register(mb_smc_ctrl, NULL);
	if (mb_c_token == SIP_SVC_ID_INVALID) {
		mb_smc_ctrl = NULL;
		shell_error(sh, "Mailbox client register fail");
		err = -1;
	} else {
		shell_print(sh, "Mailbox client register success (token %08x)", mb_c_token);
		err = 0;
	}

	return err;
}

static int cmd_unreg(const struct shell *sh, size_t argc, char **argv)
{
	int err;

	if (!mb_smc_ctrl) {
		shell_print(sh, "Mailbox client is not registered");
		return 0;
	}

	err = sip_svc_unregister(mb_smc_ctrl, mb_c_token);
	if (err) {
		shell_error(sh, "Mailbox client unregister fail (%d)", err);
	} else {
		shell_print(sh, "Mailbox client unregister success");
		mb_c_token = SIP_SVC_ID_INVALID;
		mb_smc_ctrl = NULL;
	}

	return err;
}

static int cmd_open(const struct shell *sh, size_t argc, char **argv)
{
	unsigned long seconds = 0;
	int err;
	char *endptr;
	k_timeout_t timeout = K_FOREVER;

	if (!mb_smc_ctrl) {
		shell_print(sh, "Mailbox client is not registered");
		return 0;
	}

	if (argc > 1) {
		errno = 0;
		seconds = strtoul(argv[1], &endptr, 10);
		if (errno == ERANGE) {
			shell_error(sh, "out of range value");
			return -ERANGE;
		} else if (errno || endptr == argv[1] || *endptr) {
			return -errno;
		} else if (seconds <= MAX_TIMEOUT_SECS) {
			timeout = K_SECONDS(seconds);
		} else {
			timeout = K_SECONDS(MAX_TIMEOUT_SECS);
			shell_error(sh, "Setting timeout value to %lu seconds",
					MAX_TIMEOUT_SECS);
		}
	}

	err = sip_svc_open(mb_smc_ctrl, mb_c_token, timeout);
	if (err) {
		shell_error(sh, "Mailbox client open fail (%d)", err);
	} else {
		shell_print(sh, "Mailbox client open success");
	}

	return err;
}

static int cmd_close(const struct shell *sh, size_t argc, char **argv)
{
	int err;
	uint32_t cmd_size = sizeof(uint32_t);
	struct sip_svc_request request;

	if (!mb_smc_ctrl) {
		shell_print(sh, "Mailbox client is not registered");
		return 0;
	}

	uint32_t *cmd_addr = k_malloc(cmd_size);

	if (!cmd_addr) {
		return -ENOMEM;
	}

	*cmd_addr = MAILBOX_CANCEL_COMMAND;

	request.header = SIP_SVC_PROTO_HEADER(SIP_SVC_PROTO_CMD_ASYNC, 0);
	request.a0 = SMC_FUNC_ID_MAILBOX_SEND_COMMAND;
	request.a1 = 0;
	request.a2 = (uint64_t)cmd_addr;
	request.a3 = (uint64_t)cmd_size;
	request.a4 = 0;
	request.a5 = 0;
	request.a6 = 0;
	request.a7 = 0;
	request.resp_data_addr = (uint64_t)NULL;
	request.resp_data_size = 0;
	request.priv_data = NULL;

	err = sip_svc_close(mb_smc_ctrl, mb_c_token, &request);
	if (err) {
		k_free(cmd_addr);
		shell_error(sh, "Mailbox client close fail (%d)", err);
	} else {
		shell_print(sh, "Mailbox client close success");
	}

	return err;
}

static void cmd_send_callback(uint32_t c_token, struct sip_svc_response *response)
{
	if (response == NULL) {
		return;
	}

	struct private_data *priv = (struct private_data *)response->priv_data;
	const struct shell *sh = priv->sh;

	uint32_t *resp_data;
	uint32_t resp_len;
	uint32_t i;

	shell_print(sh, "\n\rsip_svc send command callback\n");
	shell_print(sh, "\theader=%08x\n", response->header);
	shell_print(sh, "\ta0=%016lx\n", response->a0);
	shell_print(sh, "\ta1=%016lx\n", response->a1);
	shell_print(sh, "\ta2=%016lx\n", response->a2);
	shell_print(sh, "\ta3=%016lx\n", response->a3);
	shell_print(sh, "\tresponse data=\n");

	resp_data = (uint32_t *)response->resp_data_addr;
	resp_len = response->resp_data_size / 4;
	if (resp_data && resp_len) {
		for (i = 0; i < resp_len; i++) {
			shell_print(sh, "\t\t[%4d] %08x\n", i, resp_data[i]);
		}
	} else {
		shell_error(sh, "\t\tInvalid addr (%p) or len (%d)\n", resp_data, resp_len);
	}

	/* Client only responsible to free the response data memory space,
	 * the command data memory space had been freed by sip_svc service.
	 */
	if (resp_data) {
		shell_print(sh, "response data %p is freed\n", resp_data);
		k_free((char *)resp_data);
	}

	k_sem_give(&(priv->semaphore));
}

static int parse_mb_data(const struct shell *sh, char *hex_list, char **cmd_addr,
			 uint32_t *cmd_size)
{
	char *hex_str = hex_list;
	uint32_t hex_val;
	uint32_t *buffer;
	uint32_t i = 0;
	char *state;
	char *endptr;

	if (!hex_list || !cmd_addr || !cmd_size) {
		return -EINVAL;
	}

	*cmd_addr = k_malloc(SIP_SVP_MB_MAX_WORD_SIZE * 4);
	if (!*cmd_addr) {
		shell_error(sh, "Fail to allocate command memory");
		return -ENOMEM;
	}

	buffer = (uint32_t *)*cmd_addr;
	hex_str = strtok_r(hex_str, " ", &state);

	while (hex_str) {
		if (i >= SIP_SVP_MB_MAX_WORD_SIZE) {
			k_free(*cmd_addr);
			shell_error(sh, "Mailbox length too long");
			return -EOVERFLOW;
		}
		errno = 0;
		hex_val = strtoul(hex_str, &endptr, 16);
		if (errno == ERANGE) {
			shell_error(sh, " Value is out of range value");
			k_free(*cmd_addr);
			return -ERANGE;
		} else if (errno || endptr == hex_str || *endptr) {
			k_free(*cmd_addr);
			shell_error(sh, " Invalid argument");
			return -EINVAL;
		}

		buffer[i] = hex_val;
		i++;

		hex_str = strtok_r(NULL, " ", &state);
	}

	*cmd_size = i * 4;

	return 0;
}

static int cmd_send(const struct shell *sh, size_t argc, char **argv)
{
	struct sip_svc_request request;
	int trans_id;
	uint32_t cmd_size = 0;
	struct private_data priv;
	char *cmd_addr;
	char *resp_addr, *endptr;
	int err;
	k_timeout_t timeout = K_FOREVER;
	unsigned long msecond = 0;

	if (!mb_smc_ctrl) {
		shell_print(sh, "Mailbox client is not registered");
		return 0;
	}

	err = parse_mb_data(sh, argv[1], &cmd_addr, &cmd_size);
	if (err < 0) {
		return err;
	}

	if (argc > 2) {
		errno = 0;
		msecond = strtoul(argv[2], &endptr, 10);
		if (errno == ERANGE) {
			shell_error(sh, "Out of range value");
			return -ERANGE;
		} else if (errno || endptr == argv[2] || *endptr) {
			shell_error(sh, "Invalid argument");
			return -EINVAL;
		} else if (msecond <= (MSEC_PER_SEC * MAX_TIMEOUT_SECS)) {
			timeout = K_MSEC(msecond);
		} else {
			timeout = K_SECONDS(MAX_TIMEOUT_SECS);
			shell_error(sh, "Setting timeout value to %lu seconds", MAX_TIMEOUT_SECS);
		}
	}

	resp_addr = k_malloc(SIP_SVP_MB_MAX_WORD_SIZE * 4);
	if (!resp_addr) {
		k_free(cmd_addr);
		shell_error(sh, "Fail to allocate response memory");
		return -ENOMEM;
	}
	shell_print(sh, "\tResponse memory %p\n", (char *)resp_addr);

	k_sem_init(&(priv.semaphore), 0, 1);
	priv.sh = sh;

	request.header = SIP_SVC_PROTO_HEADER(SIP_SVC_PROTO_CMD_ASYNC, 0);
	request.a0 = SMC_FUNC_ID_MAILBOX_SEND_COMMAND;
	request.a1 = 0;
	request.a2 = (uint64_t)cmd_addr;
	request.a3 = (uint64_t)cmd_size;
	request.a4 = 0;
	request.a5 = 0;
	request.a6 = 0;
	request.a7 = 0;
	request.resp_data_addr = (uint64_t)resp_addr;
	request.resp_data_size = SIP_SVP_MB_MAX_WORD_SIZE * 4;
	request.priv_data = (void *)&priv;

	trans_id = sip_svc_send(mb_smc_ctrl, mb_c_token, &request, cmd_send_callback);

	if (trans_id < 0) {
		shell_error(sh, "Mailbox send fail (no open or no free trans_id)");
		k_free(cmd_addr);
		k_free(resp_addr);
		err = -EBUSY;
	} else {
		/*wait for callback*/
		if (!k_sem_take(&(priv.semaphore), timeout)) {
			shell_print(sh, "Mailbox send success: trans_id %d", trans_id);
			err = 0;
		} else {
			shell_error(sh, "Mailbox send timeout: trans_id %d", trans_id);
			cmd_close(sh, 0, NULL);
			err = -ETIMEDOUT;
		}
	}
	return err;
}

SHELL_STATIC_SUBCMD_SET_CREATE(
	sub_mailbox, SHELL_CMD_ARG(reg, NULL, "<service>", cmd_reg, 2, 0),
	SHELL_CMD_ARG(unreg, NULL, NULL, cmd_unreg, 1, 0),
	SHELL_CMD_ARG(open, NULL, "[<timeout_sec>]", cmd_open, 1, 1),
	SHELL_CMD_ARG(close, NULL, NULL, cmd_close, 1, 0),
	SHELL_CMD_ARG(send, NULL,
		      "<hex list, example (SYNC): \"2001 11223344 aabbccdd\"> [<timeout_msec>]",
		      cmd_send, 2, 1),
	SHELL_SUBCMD_SET_END);

SHELL_CMD_REGISTER(mailbox, &sub_mailbox, "Intel SoC FPGA SDM mailbox client commands", NULL);