// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2022-2023 Arm Limited and/or its affiliates * * Authors: * Abdellatif El Khlifi */ #include #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* The partitions (SPs) table */ static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = { { .info = { .id = SANDBOX_SP1_ID, .exec_ctxt = 0x5687, .properties = 0x89325621 }, .sp_uuid = { .a1 = SANDBOX_SERVICE1_UUID_A1, .a2 = SANDBOX_SERVICE1_UUID_A2, .a3 = SANDBOX_SERVICE1_UUID_A3, .a4 = SANDBOX_SERVICE1_UUID_A4, } }, { .info = { .id = SANDBOX_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, .sp_uuid = { .a1 = SANDBOX_SERVICE2_UUID_A1, .a2 = SANDBOX_SERVICE2_UUID_A2, .a3 = SANDBOX_SERVICE2_UUID_A3, .a4 = SANDBOX_SERVICE2_UUID_A4, } }, { .info = { .id = SANDBOX_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, .sp_uuid = { .a1 = SANDBOX_SERVICE1_UUID_A1, .a2 = SANDBOX_SERVICE1_UUID_A2, .a3 = SANDBOX_SERVICE1_UUID_A3, .a4 = SANDBOX_SERVICE1_UUID_A4, } }, { .info = { .id = SANDBOX_SP4_ID, .exec_ctxt = 0x1487, .properties = 0x70325621 }, .sp_uuid = { .a1 = SANDBOX_SERVICE2_UUID_A1, .a2 = SANDBOX_SERVICE2_UUID_A2, .a3 = SANDBOX_SERVICE2_UUID_A3, .a4 = SANDBOX_SERVICE2_UUID_A4, } } }; /* The emulator functions */ /** * sandbox_ffa_version() - Emulated FFA_VERSION handler function * @emul: The sandbox FF-A emulator device * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_VERSION FF-A function. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_version(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) { struct sandbox_ffa_emul *priv = dev_get_priv(emul); priv->fwk_version = FFA_VERSION_1_0; res->a0 = priv->fwk_version; /* x1-x7 MBZ */ memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(ulong)); return 0; } /** * sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function * @emul: The sandbox FF-A emulator device * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_ID_GET FF-A function. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_id_get(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) { struct sandbox_ffa_emul *priv = dev_get_priv(emul); res->a0 = FFA_SMC_32(FFA_SUCCESS); res->a1 = 0; priv->id = NS_PHYS_ENDPOINT_ID; res->a2 = priv->id; /* x3-x7 MBZ */ memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); return 0; } /** * sandbox_ffa_features() - Emulated FFA_FEATURES handler function * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_FEATURES FF-A function. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_features(ffa_value_t *pargs, ffa_value_t *res) { res->a1 = 0; if (pargs->a1 == FFA_SMC_64(FFA_RXTX_MAP)) { res->a0 = FFA_SMC_32(FFA_SUCCESS); res->a2 = RXTX_BUFFERS_MIN_SIZE; res->a3 = 0; /* x4-x7 MBZ */ memset(FFA_X4X7_MBZ_REG_START, 0, FFA_X4X7_MBZ_CNT * sizeof(ulong)); return 0; } res->a0 = FFA_SMC_32(FFA_ERROR); res->a2 = -NOT_SUPPORTED; /* x3-x7 MBZ */ memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); log_err("FF-A interface %lx not implemented\n", pargs->a1); return ffa_to_std_errmap[NOT_SUPPORTED]; } /** * sandbox_ffa_partition_info_get() - Emulated FFA_PARTITION_INFO_GET handler * @emul: The sandbox FF-A emulator device * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_PARTITION_INFO_GET FF-A function. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_partition_info_get(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) { struct ffa_partition_info *rxbuf_desc_info = NULL; u32 descs_cnt; u32 descs_size_bytes; int ret; struct sandbox_ffa_emul *priv = dev_get_priv(emul); res->a0 = FFA_SMC_32(FFA_ERROR); if (!priv->pair.rxbuf) { res->a2 = -DENIED; ret = ffa_to_std_errmap[DENIED]; goto cleanup; } if (priv->pair_info.rxbuf_owned) { res->a2 = -BUSY; ret = ffa_to_std_errmap[BUSY]; goto cleanup; } if (!priv->partitions.descs) { priv->partitions.descs = sandbox_partitions; priv->partitions.count = SANDBOX_PARTITIONS_CNT; } descs_size_bytes = SANDBOX_PARTITIONS_CNT * sizeof(struct ffa_partition_desc); /* Abort if the RX buffer size is smaller than the descs buffer size */ if ((priv->pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { res->a2 = -NO_MEMORY; ret = ffa_to_std_errmap[NO_MEMORY]; goto cleanup; } rxbuf_desc_info = priv->pair.rxbuf; /* No UUID specified. Return the information of all partitions */ if (!pargs->a1 && !pargs->a2 && !pargs->a3 && !pargs->a4) { for (descs_cnt = 0; descs_cnt < SANDBOX_PARTITIONS_CNT; descs_cnt++) *(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info; res->a0 = FFA_SMC_32(FFA_SUCCESS); res->a2 = SANDBOX_PARTITIONS_CNT; /* Transfer ownership to the consumer: the non secure world */ priv->pair_info.rxbuf_owned = 1; ret = 0; goto cleanup; } /* A UUID specified. Return the info of all SPs matching the UUID */ for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) if (pargs->a1 == priv->partitions.descs[descs_cnt].sp_uuid.a1 && pargs->a2 == priv->partitions.descs[descs_cnt].sp_uuid.a2 && pargs->a3 == priv->partitions.descs[descs_cnt].sp_uuid.a3 && pargs->a4 == priv->partitions.descs[descs_cnt].sp_uuid.a4) { *(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info; } if (rxbuf_desc_info != priv->pair.rxbuf) { res->a0 = FFA_SMC_32(FFA_SUCCESS); /* Store the partitions count */ res->a2 = (ulong) (rxbuf_desc_info - (struct ffa_partition_info *) priv->pair.rxbuf); ret = 0; /* Transfer ownership to the consumer: the non secure world */ priv->pair_info.rxbuf_owned = 1; } else { /* Unrecognized UUID */ res->a2 = -INVALID_PARAMETERS; ret = ffa_to_std_errmap[INVALID_PARAMETERS]; } cleanup: log_err("FFA_PARTITION_INFO_GET (%ld)\n", res->a2); res->a1 = 0; /* x3-x7 MBZ */ memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); return ret; } /** * sandbox_ffa_rxtx_map() - Emulated FFA_RXTX_MAP handler * @emul: The sandbox FF-A emulator device * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_RXTX_MAP FF-A function. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_rxtx_map(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) { int ret; struct sandbox_ffa_emul *priv = dev_get_priv(emul); res->a0 = FFA_SMC_32(FFA_ERROR); if (priv->pair.txbuf && priv->pair.rxbuf) { res->a2 = -DENIED; ret = ffa_to_std_errmap[DENIED]; goto feedback; } if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { priv->pair.txbuf = map_sysmem(pargs->a1, 0); priv->pair.rxbuf = map_sysmem(pargs->a2, 0); priv->pair_info.rxtx_buf_size = pargs->a3; priv->pair_info.rxbuf_mapped = 1; res->a0 = FFA_SMC_32(FFA_SUCCESS); res->a2 = 0; ret = 0; goto feedback; } if (!pargs->a1 || !pargs->a2) { res->a2 = -INVALID_PARAMETERS; ret = ffa_to_std_errmap[INVALID_PARAMETERS]; } else { res->a2 = -NO_MEMORY; ret = ffa_to_std_errmap[NO_MEMORY]; } log_err("Error in FFA_RXTX_MAP arguments (%d)\n", (int)res->a2); feedback: res->a1 = 0; /* x3-x7 MBZ */ memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); return ret; } /** * sandbox_ffa_rxtx_unmap() - Emulated FFA_RXTX_UNMAP handler * @emul: The sandbox FF-A emulator device * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_RXTX_UNMAP FF-A function. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_rxtx_unmap(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) { int ret; struct sandbox_ffa_emul *priv = dev_get_priv(emul); res->a0 = FFA_SMC_32(FFA_ERROR); res->a2 = -INVALID_PARAMETERS; ret = ffa_to_std_errmap[INVALID_PARAMETERS]; if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) goto feedback; if (priv->pair.txbuf && priv->pair.rxbuf) { priv->pair.txbuf = 0; priv->pair.rxbuf = 0; priv->pair_info.rxtx_buf_size = 0; priv->pair_info.rxbuf_mapped = 0; res->a0 = FFA_SMC_32(FFA_SUCCESS); res->a2 = 0; ret = 0; goto feedback; } log_err("No buffer pair registered on behalf of the caller\n"); feedback: res->a1 = 0; /* x3-x7 MBZ */ memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); return ret; } /** * sandbox_ffa_rx_release() - Emulated FFA_RX_RELEASE handler * @emul: The sandbox FF-A emulator device * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_RX_RELEASE FF-A function. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_rx_release(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) { int ret; struct sandbox_ffa_emul *priv = dev_get_priv(emul); if (!priv->pair_info.rxbuf_owned) { res->a0 = FFA_SMC_32(FFA_ERROR); res->a2 = -DENIED; ret = ffa_to_std_errmap[DENIED]; } else { priv->pair_info.rxbuf_owned = 0; res->a0 = FFA_SMC_32(FFA_SUCCESS); res->a2 = 0; ret = 0; } res->a1 = 0; /* x3-x7 MBZ */ memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); return ret; } /** * sandbox_ffa_sp_valid() - Check SP validity * @emul: The sandbox FF-A emulator device * @part_id: partition ID to check * * Search the input ID in the descriptors table. * * Return: * * 1 on success (Partition found). Otherwise, failure */ static int sandbox_ffa_sp_valid(struct udevice *emul, u16 part_id) { u32 descs_cnt; struct sandbox_ffa_emul *priv = dev_get_priv(emul); for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) if (priv->partitions.descs[descs_cnt].info.id == part_id) return 1; return 0; } /** * sandbox_ffa_msg_send_direct_req() - Emulated FFA_MSG_SEND_DIRECT_{REQ,RESP} handler * @emul: The sandbox FF-A emulator device * @pargs: The SMC call input arguments a0-a7 * @res: The SMC return data * * Emulate FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A ABIs. * Only SMC 64-bit is supported in Sandbox. * * Emulating interrupts is not supported. So, FFA_RUN and FFA_INTERRUPT are not * supported. In case of success FFA_MSG_SEND_DIRECT_RESP is returned with * default pattern data (0xff). * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_msg_send_direct_req(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) { u16 part_id; struct sandbox_ffa_emul *priv = dev_get_priv(emul); part_id = GET_DST_SP_ID(pargs->a1); if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id || !sandbox_ffa_sp_valid(emul, part_id) || pargs->a2) { res->a0 = FFA_SMC_32(FFA_ERROR); res->a1 = 0; res->a2 = -INVALID_PARAMETERS; /* x3-x7 MBZ */ memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); return ffa_to_std_errmap[INVALID_PARAMETERS]; } res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); res->a1 = PREP_SRC_SP_ID(part_id) | PREP_NS_PHYS_ENDPOINT_ID(priv->id); res->a2 = 0; /* Return 0xff bytes as a response */ res->a3 = -1UL; res->a4 = -1UL; res->a5 = -1UL; res->a6 = -1UL; res->a7 = -1UL; return 0; } /** * sandbox_ffa_get_rxbuf_flags() - Read the mapping/ownership flags * @emul: The sandbox FF-A emulator device * @queried_func_id: The FF-A function to be queried * @func_data: Pointer to the FF-A function arguments container structure * * Query the status flags of the following emulated * ABIs: FFA_RXTX_MAP, FFA_RXTX_UNMAP, FFA_RX_RELEASE. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_get_rxbuf_flags(struct udevice *emul, u32 queried_func_id, struct ffa_sandbox_data *func_data) { struct sandbox_ffa_emul *priv = dev_get_priv(emul); if (!func_data) return -EINVAL; if (!func_data->data0 || func_data->data0_size != sizeof(u8)) return -EINVAL; switch (queried_func_id) { case FFA_RXTX_MAP: case FFA_RXTX_UNMAP: *((u8 *)func_data->data0) = priv->pair_info.rxbuf_mapped; return 0; case FFA_RX_RELEASE: *((u8 *)func_data->data0) = priv->pair_info.rxbuf_owned; return 0; default: log_err("The querried FF-A interface flag (%d) undefined\n", queried_func_id); return -EINVAL; } } /** * sandbox_ffa_get_fwk_version() - Return the FFA framework version * @emul: The sandbox FF-A emulator device * @func_data: Pointer to the FF-A function arguments container structure * * Return the FFA framework version read from the FF-A emulator data. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_get_fwk_version(struct udevice *emul, struct ffa_sandbox_data *func_data) { struct sandbox_ffa_emul *priv = dev_get_priv(emul); if (!func_data) return -EINVAL; if (!func_data->data0 || func_data->data0_size != sizeof(priv->fwk_version)) return -EINVAL; *((u32 *)func_data->data0) = priv->fwk_version; return 0; } /** * sandbox_ffa_get_parts() - Return the address of partitions data * @emul: The sandbox FF-A emulator device * @func_data: Pointer to the FF-A function arguments container structure * * Return the address of partitions data read from the FF-A emulator data. * * Return: * * 0 on success. Otherwise, failure */ static int sandbox_ffa_get_parts(struct udevice *emul, struct ffa_sandbox_data *func_data) { struct sandbox_ffa_emul *priv = dev_get_priv(emul); if (!func_data) return -EINVAL; if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_partitions *)) return -EINVAL; *((struct ffa_partitions **)func_data->data0) = &priv->partitions; return 0; } /** * sandbox_query_ffa_emul_state() - Inspect the FF-A ABIs * @queried_func_id: The FF-A function to be queried * @func_data: Pointer to the FF-A function arguments container structure * * Query the status of FF-A ABI specified in the input argument. * * Return: * * 0 on success. Otherwise, failure */ int sandbox_query_ffa_emul_state(u32 queried_func_id, struct ffa_sandbox_data *func_data) { struct udevice *emul; int ret; ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul); if (ret) { log_err("Cannot find FF-A emulator during querying state\n"); return ret; } switch (queried_func_id) { case FFA_RXTX_MAP: case FFA_RXTX_UNMAP: case FFA_RX_RELEASE: return sandbox_ffa_get_rxbuf_flags(emul, queried_func_id, func_data); case FFA_VERSION: return sandbox_ffa_get_fwk_version(emul, func_data); case FFA_PARTITION_INFO_GET: return sandbox_ffa_get_parts(emul, func_data); default: log_err("Undefined FF-A interface (%d)\n", queried_func_id); return -EINVAL; } } /** * sandbox_arm_ffa_smccc_smc() - FF-A SMC call emulation * @args: the SMC call arguments * @res: the SMC call returned data * * Emulate the FF-A ABIs SMC call. * The emulated FF-A ABI is identified and invoked. * FF-A emulation is based on the FF-A specification 1.0 * * Return: * * 0 on success. Otherwise, failure. * FF-A protocol error codes are returned using the registers arguments as * described by the specification */ void sandbox_arm_ffa_smccc_smc(ffa_value_t *args, ffa_value_t *res) { int ret = 0; struct udevice *emul; ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul); if (ret) { log_err("Cannot find FF-A emulator during SMC emulation\n"); return; } switch (args->a0) { case FFA_SMC_32(FFA_VERSION): ret = sandbox_ffa_version(emul, args, res); break; case FFA_SMC_32(FFA_PARTITION_INFO_GET): ret = sandbox_ffa_partition_info_get(emul, args, res); break; case FFA_SMC_32(FFA_RXTX_UNMAP): ret = sandbox_ffa_rxtx_unmap(emul, args, res); break; case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): ret = sandbox_ffa_msg_send_direct_req(emul, args, res); break; case FFA_SMC_32(FFA_ID_GET): ret = sandbox_ffa_id_get(emul, args, res); break; case FFA_SMC_32(FFA_FEATURES): ret = sandbox_ffa_features(args, res); break; case FFA_SMC_64(FFA_RXTX_MAP): ret = sandbox_ffa_rxtx_map(emul, args, res); break; case FFA_SMC_32(FFA_RX_RELEASE): ret = sandbox_ffa_rx_release(emul, args, res); break; default: log_err("Undefined FF-A interface (%lx)\n", args->a0); } if (ret != 0) log_err("FF-A ABI internal failure (%d)\n", ret); } /** * invoke_ffa_fn() - SMC wrapper * @args: FF-A ABI arguments to be copied to Xn registers * @res: FF-A ABI return data to be copied from Xn registers * * Calls the emulated SMC call. */ void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) { sandbox_arm_ffa_smccc_smc(&args, res); } /** * ffa_emul_find() - Find the FF-A emulator * @dev: the sandbox FF-A device (sandbox-arm-ffa) * @emulp: the FF-A emulator device (sandbox-ffa-emul) * * Search for the FF-A emulator and returns its device pointer. * * Return: * 0 on success. Otherwise, failure */ int ffa_emul_find(struct udevice *dev, struct udevice **emulp) { int ret; ret = uclass_first_device_err(UCLASS_FFA_EMUL, emulp); if (ret) { log_err("Cannot find FF-A emulator\n"); return ret; } log_debug("FF-A emulator ready to use\n"); return 0; } UCLASS_DRIVER(ffa_emul) = { .name = "ffa_emul", .id = UCLASS_FFA_EMUL, .post_bind = dm_scan_fdt_dev, }; static const struct udevice_id sandbox_ffa_emul_ids[] = { { .compatible = "sandbox,arm-ffa-emul" }, { } }; /* Declaring the sandbox FF-A emulator under UCLASS_FFA_EMUL */ U_BOOT_DRIVER(sandbox_ffa_emul) = { .name = "sandbox_ffa_emul", .id = UCLASS_FFA_EMUL, .of_match = sandbox_ffa_emul_ids, .priv_auto = sizeof(struct sandbox_ffa_emul), };