/** * SVC_NUM_DATA_IN_FIFO - number of struct stratix10_svc_data in the FIFO * * SVC_NUM_CHANNEL - number of channel supported by service layer driver * * FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS - claim back the submitted buffer(s) * from the secure world for FPGA manager to reuse, or to free the buffer(s) * when all bit-stream data had be send. * * FPGA_CONFIG_STATUS_TIMEOUT_SEC - poll the FPGA configuration status, * service layer will return error to FPGA manager when timeout occurs, * timeout is set to 30 seconds (30 * 1000) at Intel Stratix10 SoC.
*/ #define SVC_NUM_DATA_IN_FIFO 32 #define SVC_NUM_CHANNEL 3 #define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS 200 #define FPGA_CONFIG_STATUS_TIMEOUT_SEC 30 #define BYTE_TO_WORD_SIZE 4
/** * struct stratix10_svc_sh_memory - service shared memory structure * @sync_complete: state for a completion * @addr: physical address of shared memory block * @size: size of shared memory block * @invoke_fn: function to issue secure monitor or hypervisor call * * This struct is used to save physical address and size of shared memory * block. The shared memory blocked is allocated by secure monitor software * at secure world. * * Service layer driver uses the physical address and size to create a memory * pool, then allocates data buffer from that memory pool for service client.
*/ struct stratix10_svc_sh_memory { struct completion sync_complete; unsignedlong addr; unsignedlong size;
svc_invoke_fn *invoke_fn;
};
/** * struct stratix10_svc_data_mem - service memory structure * @vaddr: virtual address * @paddr: physical address * @size: size of memory * @node: link list head node * * This struct is used in a list that keeps track of buffers which have * been allocated or freed from the memory pool. Service layer driver also * uses this struct to transfer physical address to virtual address.
*/ struct stratix10_svc_data_mem { void *vaddr;
phys_addr_t paddr;
size_t size; struct list_head node;
};
/** * struct stratix10_svc_data - service data structure * @chan: service channel * @paddr: physical address of to be processed payload * @size: to be processed playload size * @paddr_output: physical address of processed payload * @size_output: processed payload size * @command: service command requested by client * @flag: configuration type (full or partial) * @arg: args to be passed via registers and not physically mapped buffers * * This struct is used in service FIFO for inter-process communication.
*/ struct stratix10_svc_data { struct stratix10_svc_chan *chan;
phys_addr_t paddr;
size_t size;
phys_addr_t paddr_output;
size_t size_output;
u32 command;
u32 flag;
u64 arg[3];
};
/** * struct stratix10_svc_controller - service controller * @dev: device * @chans: array of service channels * @num_chans: number of channels in 'chans' array * @num_active_client: number of active service client * @node: list management * @genpool: memory pool pointing to the memory region * @task: pointer to the thread task which handles SMC or HVC call * @svc_fifo: a queue for storing service message data * @complete_status: state for completion * @svc_fifo_lock: protect access to service message data queue * @invoke_fn: function to issue secure monitor call or hypervisor call * * This struct is used to create communication channels for service clients, to * handle secure monitor or hypervisor call.
*/ struct stratix10_svc_controller { struct device *dev; struct stratix10_svc_chan *chans; int num_chans; int num_active_client; struct list_head node; struct gen_pool *genpool; struct task_struct *task; struct kfifo svc_fifo; struct completion complete_status;
spinlock_t svc_fifo_lock;
svc_invoke_fn *invoke_fn;
};
/** * struct stratix10_svc_chan - service communication channel * @ctrl: pointer to service controller which is the provider of this channel * @scl: pointer to service client which owns the channel * @name: service client name associated with the channel * @lock: protect access to the channel * * This struct is used by service client to communicate with service layer, each * service client has its own channel created by service controller.
*/ struct stratix10_svc_chan { struct stratix10_svc_controller *ctrl; struct stratix10_svc_client *scl; char *name;
spinlock_t lock;
};
/** * svc_pa_to_va() - translate physical address to virtual address * @addr: to be translated physical address * * Return: valid virtual address or NULL if the provided physical * address doesn't exist.
*/ staticvoid *svc_pa_to_va(unsignedlong addr)
{ struct stratix10_svc_data_mem *pmem;
pr_debug("claim back P-addr=0x%016x\n", (unsignedint)addr);
list_for_each_entry(pmem, &svc_data_mem, node) if (pmem->paddr == addr) return pmem->vaddr;
/* physical address is not found */ return NULL;
}
/** * svc_thread_cmd_data_claim() - claim back buffer from the secure world * @ctrl: pointer to service layer controller * @p_data: pointer to service data structure * @cb_data: pointer to callback data structure to service client * * Claim back the submitted buffers from the secure world and pass buffer * back to service client (FPGA manager, etc) for reuse.
*/ staticvoid svc_thread_cmd_data_claim(struct stratix10_svc_controller *ctrl, struct stratix10_svc_data *p_data, struct stratix10_svc_cb_data *cb_data)
{ struct arm_smccc_res res; unsignedlong timeout;
/** * svc_thread_cmd_config_status() - check configuration status * @ctrl: pointer to service layer controller * @p_data: pointer to service data structure * @cb_data: pointer to callback data structure to service client * * Check whether the secure firmware at secure world has finished the FPGA * configuration, and then inform FPGA manager the configuration status.
*/ staticvoid svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl, struct stratix10_svc_data *p_data, struct stratix10_svc_cb_data *cb_data)
{ struct arm_smccc_res res; int count_in_sec; unsignedlong a0, a1, a2;
/** * svc_thread_recv_status_ok() - handle the successful status * @p_data: pointer to service data structure * @cb_data: pointer to callback data structure to service client * @res: result from SMC or HVC call * * Send back the correspond status to the service clients.
*/ staticvoid svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, struct stratix10_svc_cb_data *cb_data, struct arm_smccc_res res)
{
cb_data->kaddr1 = NULL;
cb_data->kaddr2 = NULL;
cb_data->kaddr3 = NULL;
switch (p_data->command) { case COMMAND_RECONFIG: case COMMAND_RSU_UPDATE: case COMMAND_RSU_NOTIFY: case COMMAND_FCS_REQUEST_SERVICE: case COMMAND_FCS_SEND_CERTIFICATE: case COMMAND_FCS_DATA_ENCRYPTION: case COMMAND_FCS_DATA_DECRYPTION:
cb_data->status = BIT(SVC_STATUS_OK); break; case COMMAND_RECONFIG_DATA_SUBMIT:
cb_data->status = BIT(SVC_STATUS_BUFFER_SUBMITTED); break; case COMMAND_RECONFIG_STATUS:
cb_data->status = BIT(SVC_STATUS_COMPLETED); break; case COMMAND_RSU_RETRY: case COMMAND_RSU_MAX_RETRY: case COMMAND_RSU_DCMF_STATUS: case COMMAND_FIRMWARE_VERSION:
cb_data->status = BIT(SVC_STATUS_OK);
cb_data->kaddr1 = &res.a1; break; case COMMAND_SMC_SVC_VERSION:
cb_data->status = BIT(SVC_STATUS_OK);
cb_data->kaddr1 = &res.a1;
cb_data->kaddr2 = &res.a2; break; case COMMAND_RSU_DCMF_VERSION:
cb_data->status = BIT(SVC_STATUS_OK);
cb_data->kaddr1 = &res.a1;
cb_data->kaddr2 = &res.a2; break; case COMMAND_FCS_RANDOM_NUMBER_GEN: case COMMAND_FCS_GET_PROVISION_DATA: case COMMAND_POLL_SERVICE_STATUS:
cb_data->status = BIT(SVC_STATUS_OK);
cb_data->kaddr1 = &res.a1;
cb_data->kaddr2 = svc_pa_to_va(res.a2);
cb_data->kaddr3 = &res.a3; break; case COMMAND_MBOX_SEND_CMD:
cb_data->status = BIT(SVC_STATUS_OK);
cb_data->kaddr1 = &res.a1; /* SDM return size in u8. Convert size to u32 word */
res.a2 = res.a2 * BYTE_TO_WORD_SIZE;
cb_data->kaddr2 = &res.a2; break; default:
pr_warn("it shouldn't happen\n"); break;
}
/** * svc_normal_to_secure_thread() - the function to run in the kthread * @data: data pointer for kthread function * * Service layer driver creates stratix10_svc_smc_hvc_call kthread on CPU * node 0, its function stratix10_svc_secure_call_thread is used to handle * SMC or HVC calls between kernel driver and secure monitor software. * * Return: 0 for success or -ENOMEM on error.
*/ staticint svc_normal_to_secure_thread(void *data)
{ struct stratix10_svc_controller
*ctrl = (struct stratix10_svc_controller *)data; struct stratix10_svc_data *pdata; struct stratix10_svc_cb_data *cbdata; struct arm_smccc_res res; unsignedlong a0, a1, a2, a3, a4, a5, a6, a7; int ret_fifo = 0;
pdata = kmalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM;
switch (res.a0) { case INTEL_SIP_SMC_STATUS_OK:
svc_thread_recv_status_ok(pdata, cbdata, res); break; case INTEL_SIP_SMC_STATUS_BUSY: switch (pdata->command) { case COMMAND_RECONFIG_DATA_SUBMIT:
svc_thread_cmd_data_claim(ctrl,
pdata, cbdata); break; case COMMAND_RECONFIG_STATUS: case COMMAND_POLL_SERVICE_STATUS:
svc_thread_cmd_config_status(ctrl,
pdata, cbdata); break; default:
pr_warn("it shouldn't happen\n"); break;
} break; case INTEL_SIP_SMC_STATUS_REJECTED:
pr_debug("%s: STATUS_REJECTED\n", __func__); /* for FCS */ switch (pdata->command) { case COMMAND_FCS_REQUEST_SERVICE: case COMMAND_FCS_SEND_CERTIFICATE: case COMMAND_FCS_GET_PROVISION_DATA: case COMMAND_FCS_DATA_ENCRYPTION: case COMMAND_FCS_DATA_DECRYPTION: case COMMAND_FCS_RANDOM_NUMBER_GEN: case COMMAND_MBOX_SEND_CMD:
cbdata->status = BIT(SVC_STATUS_INVALID_PARAM);
cbdata->kaddr1 = NULL;
cbdata->kaddr2 = NULL;
cbdata->kaddr3 = NULL;
pdata->chan->scl->receive_cb(pdata->chan->scl,
cbdata); break;
} break; case INTEL_SIP_SMC_STATUS_ERROR: case INTEL_SIP_SMC_RSU_ERROR:
pr_err("%s: STATUS_ERROR\n", __func__);
cbdata->status = BIT(SVC_STATUS_ERROR);
cbdata->kaddr1 = &res.a1;
cbdata->kaddr2 = (res.a2) ?
svc_pa_to_va(res.a2) : NULL;
cbdata->kaddr3 = (res.a3) ? &res.a3 : NULL;
pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); break; default:
pr_warn("Secure firmware doesn't support...\n");
/* * be compatible with older version firmware which * doesn't support newer RSU commands
*/ if ((pdata->command != COMMAND_RSU_UPDATE) &&
(pdata->command != COMMAND_RSU_STATUS)) {
cbdata->status =
BIT(SVC_STATUS_NO_SUPPORT);
cbdata->kaddr1 = NULL;
cbdata->kaddr2 = NULL;
cbdata->kaddr3 = NULL;
pdata->chan->scl->receive_cb(
pdata->chan->scl, cbdata);
} break;
}
}
kfree(cbdata);
kfree(pdata);
return 0;
}
/** * svc_normal_to_secure_shm_thread() - the function to run in the kthread * @data: data pointer for kthread function * * Service layer driver creates stratix10_svc_smc_hvc_shm kthread on CPU * node 0, its function stratix10_svc_secure_shm_thread is used to query the * physical address of memory block reserved by secure monitor software at * secure world. * * svc_normal_to_secure_shm_thread() terminates directly since it is a * standlone thread for which no one will call kthread_stop() or return when * 'kthread_should_stop()' is true.
*/ staticint svc_normal_to_secure_shm_thread(void *data)
{ struct stratix10_svc_sh_memory
*sh_mem = (struct stratix10_svc_sh_memory *)data; struct arm_smccc_res res;
/* SMC or HVC call to get shared memory info from secure world */
sh_mem->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM,
0, 0, 0, 0, 0, 0, 0, &res); if (res.a0 == INTEL_SIP_SMC_STATUS_OK) {
sh_mem->addr = res.a1;
sh_mem->size = res.a2;
} else {
pr_err("%s: after SMC call -- res.a0=0x%016x", __func__,
(unsignedint)res.a0);
sh_mem->addr = 0;
sh_mem->size = 0;
}
complete(&sh_mem->sync_complete); return 0;
}
/** * svc_get_sh_memory() - get memory block reserved by secure monitor SW * @pdev: pointer to service layer device * @sh_memory: pointer to service shared memory structure * * Return: zero for successfully getting the physical address of memory block * reserved by secure monitor software, or negative value on error.
*/ staticint svc_get_sh_memory(struct platform_device *pdev, struct stratix10_svc_sh_memory *sh_memory)
{ struct device *dev = &pdev->dev; struct task_struct *sh_memory_task; unsignedint cpu = 0;
init_completion(&sh_memory->sync_complete);
/* smc or hvc call happens on cpu 0 bound kthread */
sh_memory_task = kthread_create_on_node(svc_normal_to_secure_shm_thread,
(void *)sh_memory,
cpu_to_node(cpu), "svc_smc_hvc_shm_thread"); if (IS_ERR(sh_memory_task)) {
dev_err(dev, "fail to create stratix10_svc_smc_shm_thread\n"); return -EINVAL;
}
wake_up_process(sh_memory_task);
if (!wait_for_completion_timeout(&sh_memory->sync_complete, 10 * HZ)) {
dev_err(dev, "timeout to get sh-memory paras from secure world\n"); return -ETIMEDOUT;
}
if (!sh_memory->addr || !sh_memory->size) {
dev_err(dev, "failed to get shared memory info from secure world\n"); return -ENOMEM;
}
/** * stratix10_svc_request_channel_byname() - request a service channel * @client: pointer to service client * @name: service client name * * This function is used by service client to request a service channel. * * Return: a pointer to channel assigned to the client on success, * or ERR_PTR() on error.
*/ struct stratix10_svc_chan *stratix10_svc_request_channel_byname( struct stratix10_svc_client *client, constchar *name)
{ struct device *dev = client->dev; struct stratix10_svc_controller *controller; struct stratix10_svc_chan *chan = NULL; unsignedlong flag; int i;
/* if probe was called after client's, or error on probe */ if (list_empty(&svc_ctrl)) return ERR_PTR(-EPROBE_DEFER);
controller = list_first_entry(&svc_ctrl, struct stratix10_svc_controller, node); for (i = 0; i < SVC_NUM_CHANNEL; i++) { if (!strcmp(controller->chans[i].name, name)) {
chan = &controller->chans[i]; break;
}
}
/* if there was no channel match */ if (i == SVC_NUM_CHANNEL) {
dev_err(dev, "%s: channel not allocated\n", __func__); return ERR_PTR(-EINVAL);
}
if (chan->scl || !try_module_get(controller->dev->driver->owner)) {
dev_dbg(dev, "%s: svc not free\n", __func__); return ERR_PTR(-EBUSY);
}
/** * stratix10_svc_free_channel() - free service channel * @chan: service channel to be freed * * This function is used by service client to free a service channel.
*/ void stratix10_svc_free_channel(struct stratix10_svc_chan *chan)
{ unsignedlong flag;
/** * stratix10_svc_send() - send a message data to the remote * @chan: service channel assigned to the client * @msg: message data to be sent, in the format of * "struct stratix10_svc_client_msg" * * This function is used by service client to add a message to the service * layer driver's queue for being sent to the secure world. * * Return: 0 for success, -ENOMEM or -ENOBUFS on error.
*/ int stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg)
{ struct stratix10_svc_client_msg
*p_msg = (struct stratix10_svc_client_msg *)msg; struct stratix10_svc_data_mem *p_mem; struct stratix10_svc_data *p_data; int ret = 0; unsignedint cpu = 0;
p_data = kzalloc(sizeof(*p_data), GFP_KERNEL); if (!p_data) return -ENOMEM;
/* first client will create kernel thread */ if (!chan->ctrl->task) {
chan->ctrl->task =
kthread_run_on_cpu(svc_normal_to_secure_thread,
(void *)chan->ctrl,
cpu, "svc_smc_hvc_thread"); if (IS_ERR(chan->ctrl->task)) {
dev_err(chan->ctrl->dev, "failed to create svc_smc_hvc_thread\n");
kfree(p_data); return -EINVAL;
}
}
pr_debug("%s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__,
p_msg->payload, p_msg->command,
(unsignedint)p_msg->payload_length);
/** * stratix10_svc_done() - complete service request transactions * @chan: service channel assigned to the client * * This function should be called when client has finished its request * or there is an error in the request process. It allows the service layer * to stop the running thread to have maximize savings in kernel resources.
*/ void stratix10_svc_done(struct stratix10_svc_chan *chan)
{ /* stop thread when thread is running AND only one active client */ if (chan->ctrl->task && chan->ctrl->num_active_client <= 1) {
pr_debug("svc_smc_hvc_shm_thread is stopped\n");
kthread_stop(chan->ctrl->task);
chan->ctrl->task = NULL;
}
}
EXPORT_SYMBOL_GPL(stratix10_svc_done);
/** * stratix10_svc_allocate_memory() - allocate memory * @chan: service channel assigned to the client * @size: memory size requested by a specific service client * * Service layer allocates the requested number of bytes buffer from the * memory pool, service client uses this function to get allocated buffers. * * Return: address of allocated memory on success, or ERR_PTR() on error.
*/ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan,
size_t size)
{ struct stratix10_svc_data_mem *pmem; unsignedlong va;
phys_addr_t pa; struct gen_pool *genpool = chan->ctrl->genpool;
size_t s = roundup(size, 1 << genpool->min_alloc_order);
pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL); if (!pmem) return ERR_PTR(-ENOMEM);
va = gen_pool_alloc(genpool, s); if (!va) return ERR_PTR(-ENOMEM);
memset((void *)va, 0, s);
pa = gen_pool_virt_to_phys(genpool, va);
/** * stratix10_svc_free_memory() - free allocated memory * @chan: service channel assigned to the client * @kaddr: memory to be freed * * This function is used by service client to free allocated buffers.
*/ void stratix10_svc_free_memory(struct stratix10_svc_chan *chan, void *kaddr)
{ struct stratix10_svc_data_mem *pmem;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.