// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2019 Broadcom. */ #include #include #include #include #include #include #include #define MAX_SHM_MEM_SZ SZ_4M #define MAX_TEE_PARAM_ARRY_MEMB 4 enum ta_cmd { /* * TA_CMD_BNXT_FASTBOOT - boot bnxt device by copying f/w into sram * * param[0] unused * param[1] unused * param[2] unused * param[3] unused * * Result: * TEE_SUCCESS - Invoke command success * TEE_ERROR_ITEM_NOT_FOUND - Corrupt f/w image found on memory */ TA_CMD_BNXT_FASTBOOT = 0, /* * TA_CMD_BNXT_COPY_COREDUMP - copy the core dump into shm * * param[0] (inout memref) - Coredump buffer memory reference * param[1] (in value) - value.a: offset, data to be copied from * value.b: size of data to be copied * param[2] unused * param[3] unused * * Result: * TEE_SUCCESS - Invoke command success * TEE_ERROR_BAD_PARAMETERS - Incorrect input param * TEE_ERROR_ITEM_NOT_FOUND - Corrupt core dump */ TA_CMD_BNXT_COPY_COREDUMP = 3, }; /** * struct tee_bnxt_fw_private - OP-TEE bnxt private data * @dev: OP-TEE based bnxt device. * @ctx: OP-TEE context handler. * @session_id: TA session identifier. */ struct tee_bnxt_fw_private { struct device *dev; struct tee_context *ctx; u32 session_id; struct tee_shm *fw_shm_pool; }; static struct tee_bnxt_fw_private pvt_data; static void prepare_args(int cmd, struct tee_ioctl_invoke_arg *arg, struct tee_param *param) { memset(arg, 0, sizeof(*arg)); memset(param, 0, MAX_TEE_PARAM_ARRY_MEMB * sizeof(*param)); arg->func = cmd; arg->session = pvt_data.session_id; arg->num_params = MAX_TEE_PARAM_ARRY_MEMB; /* Fill invoke cmd params */ switch (cmd) { case TA_CMD_BNXT_COPY_COREDUMP: param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; param[0].u.memref.shm = pvt_data.fw_shm_pool; param[0].u.memref.size = MAX_SHM_MEM_SZ; param[0].u.memref.shm_offs = 0; param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; break; case TA_CMD_BNXT_FASTBOOT: default: /* Nothing to do */ break; } } /** * tee_bnxt_fw_load() - Load the bnxt firmware * Uses an OP-TEE call to start a secure * boot process. * Returns 0 on success, negative errno otherwise. */ int tee_bnxt_fw_load(void) { int ret = 0; struct tee_ioctl_invoke_arg arg; struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB]; if (!pvt_data.ctx) return -ENODEV; prepare_args(TA_CMD_BNXT_FASTBOOT, &arg, param); ret = tee_client_invoke_func(pvt_data.ctx, &arg, param); if (ret < 0 || arg.ret != 0) { dev_err(pvt_data.dev, "TA_CMD_BNXT_FASTBOOT invoke failed TEE err: %x, ret:%x\n", arg.ret, ret); return -EINVAL; } return 0; } EXPORT_SYMBOL(tee_bnxt_fw_load); /** * tee_bnxt_copy_coredump() - Copy coredump from the allocated memory * Uses an OP-TEE call to copy coredump * @buf: destination buffer where core dump is copied into * @offset: offset from the base address of core dump area * @size: size of the dump * * Returns 0 on success, negative errno otherwise. */ int tee_bnxt_copy_coredump(void *buf, u32 offset, u32 size) { struct tee_ioctl_invoke_arg arg; struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB]; void *core_data; u32 rbytes = size; u32 nbytes = 0; int ret = 0; if (!pvt_data.ctx) return -ENODEV; prepare_args(TA_CMD_BNXT_COPY_COREDUMP, &arg, param); while (rbytes) { nbytes = rbytes; nbytes = min_t(u32, rbytes, param[0].u.memref.size); /* Fill additional invoke cmd params */ param[1].u.value.a = offset; param[1].u.value.b = nbytes; ret = tee_client_invoke_func(pvt_data.ctx, &arg, param); if (ret < 0 || arg.ret != 0) { dev_err(pvt_data.dev, "TA_CMD_BNXT_COPY_COREDUMP invoke failed TEE err: %x, ret:%x\n", arg.ret, ret); return -EINVAL; } core_data = tee_shm_get_va(pvt_data.fw_shm_pool, 0); if (IS_ERR(core_data)) { dev_err(pvt_data.dev, "tee_shm_get_va failed\n"); return PTR_ERR(core_data); } memcpy(buf, core_data, nbytes); rbytes -= nbytes; buf += nbytes; offset += nbytes; } return 0; } EXPORT_SYMBOL(tee_bnxt_copy_coredump); static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) { return (ver->impl_id == TEE_IMPL_ID_OPTEE); } static int tee_bnxt_fw_probe(struct device *dev) { struct tee_client_device *bnxt_device = to_tee_client_device(dev); int ret, err = -ENODEV; struct tee_ioctl_open_session_arg sess_arg; struct tee_shm *fw_shm_pool; memset(&sess_arg, 0, sizeof(sess_arg)); /* Open context with TEE driver */ pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); if (IS_ERR(pvt_data.ctx)) return -ENODEV; /* Open session with Bnxt load Trusted App */ memcpy(sess_arg.uuid, bnxt_device->id.uuid.b, TEE_IOCTL_UUID_LEN); sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; sess_arg.num_params = 0; ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); if (ret < 0 || sess_arg.ret != 0) { dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret); err = -EINVAL; goto out_ctx; } pvt_data.session_id = sess_arg.session; pvt_data.dev = dev; fw_shm_pool = tee_shm_alloc(pvt_data.ctx, MAX_SHM_MEM_SZ, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); if (IS_ERR(fw_shm_pool)) { tee_client_close_context(pvt_data.ctx); dev_err(pvt_data.dev, "tee_shm_alloc failed\n"); err = PTR_ERR(fw_shm_pool); goto out_sess; } pvt_data.fw_shm_pool = fw_shm_pool; return 0; out_sess: tee_client_close_session(pvt_data.ctx, pvt_data.session_id); out_ctx: tee_client_close_context(pvt_data.ctx); return err; } static int tee_bnxt_fw_remove(struct device *dev) { tee_shm_free(pvt_data.fw_shm_pool); tee_client_close_session(pvt_data.ctx, pvt_data.session_id); tee_client_close_context(pvt_data.ctx); pvt_data.ctx = NULL; return 0; } static const struct tee_client_device_id tee_bnxt_fw_id_table[] = { {UUID_INIT(0x6272636D, 0x2019, 0x0716, 0x42, 0x43, 0x4D, 0x5F, 0x53, 0x43, 0x48, 0x49)}, {} }; MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table); static struct tee_client_driver tee_bnxt_fw_driver = { .id_table = tee_bnxt_fw_id_table, .driver = { .name = KBUILD_MODNAME, .bus = &tee_bus_type, .probe = tee_bnxt_fw_probe, .remove = tee_bnxt_fw_remove, }, }; static int __init tee_bnxt_fw_mod_init(void) { return driver_register(&tee_bnxt_fw_driver.driver); } static void __exit tee_bnxt_fw_mod_exit(void) { driver_unregister(&tee_bnxt_fw_driver.driver); } module_init(tee_bnxt_fw_mod_init); module_exit(tee_bnxt_fw_mod_exit); MODULE_AUTHOR("Vikas Gupta "); MODULE_DESCRIPTION("Broadcom bnxt firmware manager"); MODULE_LICENSE("GPL v2");