Files
linux-nv-oot/drivers/platform/tegra/dce/dce-admin.c
Prateek Patel 9b5416fd50 drivers: platform: dce: fix Coverity defects
Dereference after null check for pointers cl and handlep. Add a null
check before referencing cl and handlep.

Check return value of request_firmware for error.

Using uninitialized value event when calling dce_worker_thread_wait.
Add EVENT_ID_DCE_INVALID_EVENT and have a check before using the
value event.

CID 10127898
CID 10127999
CID 10127954
CID 10127811

Bug 3461002

Change-Id: If00ece28fd52e495b3a8d3eec7bdb4825d3c7892
Signed-off-by: Prateek Patel <prpatel@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2661588
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: svc-mobile-cert <svc-mobile-cert@nvidia.com>
Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Mahesh Kumar <mahkumar@nvidia.com>
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
GVS: Gerrit_Virtual_Submit
2023-04-14 19:23:43 +00:00

563 lines
12 KiB
C

/*
* Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <dce.h>
#include <dce-mailbox.h>
#include <dce-util-common.h>
#include <dce-client-ipc-internal.h>
#include <interface/dce-interface.h>
#include <interface/dce-admin-cmds.h>
/**
* dce_admin_ipc_wait - Waits for message from DCE.
*
* @d : Pointer to tegra_dce struct.
*
* Return : 0 if successful
*/
int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type)
{
int ret = 0;
enum dce_worker_event_id_type event = EVENT_ID_DCE_INVALID_EVENT;
struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc;
switch (w_type) {
case DCE_IPC_WAIT_TYPE_SYNC:
event = EVENT_ID_DCE_IPC_MESSAGE_SENT;
break;
case DCE_IPC_WAIT_TYPE_RPC:
event = EVENT_ID_DCE_IPC_MESSAGE_SENT;
break;
default:
dce_err(d, "Invalid wait type [%d]", w_type);
break;
}
if (dce_is_bootstrap_done(d)) {
DCE_COND_WAIT_INTERRUPTIBLE(&admin_rpc->recv_wait,
atomic_read(&admin_rpc->complete) == 1,
0);
atomic_set(&admin_rpc->complete, 0);
} else {
if (event != EVENT_ID_DCE_INVALID_EVENT)
dce_worker_thread_wait(d, event);
else {
dce_err(d, "Invalid event type [%d]", event);
ret = -1;
goto end;
}
}
if (dce_worker_get_state(d)
== STATE_DCE_WORKER_ABORTED)
ret = -1;
end:
return ret;
}
/**
* dce_admin_interface_isr - Isr for the CCPLEX<->DCE admin interface
*
* @d : Pointer to tegra_de struct.
*
* Return : Void
*/
static void dce_admin_wakeup_ipc(struct tegra_dce *d)
{
enum dce_worker_event_id_type event = EVENT_ID_DCE_IPC_SIGNAL_RECEIVED;
dce_worker_thread_wakeup(d, event);
}
/**
* dce_admin_wakeup_rpc_post_boot - Wakeup process waiting for response
* from DCE for Admin rpc interface.
*
* @d : Pointer to tegra_de struct.
*
* Return : Void
*/
static void dce_admin_ipc_post_boot_wakeup(struct tegra_dce *d)
{
struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc;
atomic_set(&admin_rpc->complete, 1);
dce_cond_signal_interruptible(&admin_rpc->recv_wait);
}
/**
* dce_admin_ipc_handle_signal - Isr for the CCPLEX<->DCE admin interface
*
* @d : Pointer to tegra_de struct.
*
* Return : Void
*/
void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type)
{
bool wakeup_needed = false;
if (!dce_ipc_channel_is_synced(d, ch_type)) {
/**
* The ivc channel is not ready yet. Exit
* and wait for another signal from target.
*/
return;
}
/**
* Channel already in sync with remote. Check if data
* is available to read.
*/
wakeup_needed = dce_ipc_is_data_available(d, ch_type);
if (!wakeup_needed) {
dce_info(d, "Spurious signal on channel: [%d]. Ignored...",
ch_type);
return;
}
if (ch_type == DCE_IPC_CH_KMD_TYPE_ADMIN) {
if (dce_is_bootstrap_done(d))
dce_admin_ipc_post_boot_wakeup(d);
else
dce_admin_wakeup_ipc(d);
} else {
dce_client_ipc_wakeup(d, ch_type);
}
}
/**
* dce_admin_ivc_channel_reset - Resets the admin ivc channel
*
* @d : Pointer to tegra_dce struct.
*
* Return : Void.
*/
void dce_admin_ivc_channel_reset(struct tegra_dce *d)
{
dce_ipc_channel_reset(d, DCE_IPC_CH_KMD_TYPE_ADMIN);
}
/**
* dce_admin_channel_deinit - Cleans up the channel resources.
*
* @d : Pointer to tegra_dce struct
*
* Return : Void
*/
static void dce_admin_channel_deinit(struct tegra_dce *d)
{
u32 loop_cnt;
for (loop_cnt = 0; loop_cnt < DCE_IPC_CH_KMD_TYPE_MAX; loop_cnt++)
dce_ipc_channel_deinit(d, loop_cnt);
}
/**
* dce_admin_channel_init - Initializes the admin ivc interface
*
* @d : Pointer to tegra_dce.
*
* Return : 0 if successful.
*/
static int dce_admin_channel_init(struct tegra_dce *d)
{
int ret = 0;
u32 loop_cnt;
for (loop_cnt = 0; loop_cnt < DCE_IPC_CH_KMD_TYPE_MAX; loop_cnt++) {
ret = dce_ipc_channel_init(d, loop_cnt);
if (ret) {
dce_err(d, "Channel init failed for type : [%d]",
loop_cnt);
goto out;
}
}
out:
if (ret)
dce_admin_channel_deinit(d);
return ret;
}
/**
* dce_admin_init - Sets up resources managed by admin.
*
* @d : Pointer to tegra_dce struct.
*
* Return : 0 if successful
*/
int dce_admin_init(struct tegra_dce *d)
{
int ret = 0;
struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc;
d->boot_status |= DCE_EARLY_INIT_START;
ret = dce_ipc_allocate_region(d);
if (ret) {
dce_err(d, "IPC region allocation failed");
goto err_ipc_reg_alloc;
}
ret = dce_admin_channel_init(d);
if (ret) {
dce_err(d, "Channel Initialization Failed");
goto err_channel_init;
}
ret = dce_cond_init(&admin_rpc->recv_wait);
if (ret) {
dce_err(d, "Admin post-bootstrap RPC cond init failed");
goto err_admin_postboot_rpc_init;
}
atomic_set(&admin_rpc->complete, 0);
d->boot_status |= DCE_EARLY_INIT_DONE;
return 0;
err_admin_postboot_rpc_init:
dce_cond_destroy(&admin_rpc->recv_wait);
err_channel_init:
dce_ipc_free_region(d);
err_ipc_reg_alloc:
d->boot_status |= DCE_EARLY_INIT_FAILED;
return ret;
}
/**
* dce_admin_deinit - Releases the resources
* associated with admin interface.
*
* @d : Pointer to tegra_dce struct.
*
* Return : Void
*/
void dce_admin_deinit(struct tegra_dce *d)
{
struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc;
atomic_set(&admin_rpc->complete, 0);
dce_cond_destroy(&admin_rpc->recv_wait);
dce_admin_channel_deinit(d);
dce_ipc_free_region(d);
dce_mailbox_deinit_interface(d,
DCE_MAILBOX_ADMIN_INTERFACE);
}
/**
* dce_admin_allocate_message - Allocates memory for a message
* on admin interface.
* @d : Pointer tegra_dce struct.
*
* Return : Allocated msg if successful.
*/
struct dce_ipc_message *dce_admin_allocate_message(struct tegra_dce *d)
{
struct dce_ipc_message *msg;
msg = dce_kzalloc(d, sizeof(*msg), false);
if (!msg) {
dce_err(d, "Insufficient memory for admin msg");
goto err_alloc_msg;
}
msg->tx.data = dce_kzalloc(d, DCE_ADMIN_CMD_SIZE, false);
if (!msg->tx.data) {
dce_err(d, "Insufficient memory for admin msg");
goto err_alloc_tx;
}
msg->rx.data = dce_kzalloc(d, DCE_ADMIN_RESP_SIZE, false);
if (!msg->rx.data) {
dce_err(d, "Insufficient memory for admin msg");
goto err_alloc_rx;
}
msg->tx.size = DCE_ADMIN_CMD_SIZE;
msg->rx.size = DCE_ADMIN_RESP_SIZE;
return msg;
err_alloc_rx:
dce_kfree(d, msg->tx.data);
err_alloc_tx:
dce_kfree(d, msg);
err_alloc_msg:
return NULL;
}
/**
* dce_admin_free_message - Frees memory allocated for a message
* on admin interface.
*
* @d : Pointer to tegra_dce struct.
* @msg : Pointer to allocated message.
*
* Return : Void.
*/
void dce_admin_free_message(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
if (!msg || !msg->tx.data || !msg->rx.data)
return;
dce_kfree(d, msg->tx.data);
dce_kfree(d, msg->rx.data);
dce_kfree(d, msg);
}
/**
* dce_admin_send_msg - Sends messages on Admin Channel
* synchronously and waits for an ack.
*
* @d : Pointer to tegra_dce struct.
* @msg : Pointer to allocated message.
*
* Return : 0 if successful
*/
int dce_admin_send_msg(struct tegra_dce *d, struct dce_ipc_message *msg)
{
int ret = 0;
ret = dce_ipc_send_message_sync(d, DCE_IPC_CHANNEL_TYPE_ADMIN, msg);
if (ret)
dce_err(d, "Error sending admin message on admin interface");
return ret;
}
/**
* dce_admin_get_ipc_channel_info - Provides channel's
* buff details
*
* @d - Pointer to tegra_dce struct.
* @q_info : Pointer to struct dce_ipc_queue_info
*
* Return - 0 if successful
*/
int dce_admin_get_ipc_channel_info(struct tegra_dce *d,
struct dce_ipc_queue_info *q_info)
{
int ret;
u8 channel_id = DCE_IPC_CHANNEL_TYPE_ADMIN;
ret = dce_ipc_get_channel_info(d, q_info, channel_id);
return ret;
}
/**
* dce_admin_send_cmd_echo - Sends DCE_ADMIN_CMD_ECHO cmd.
*
* @d - Pointer to tegra_dce struct.
* @msg - Pointer to dce_ipc_msg struct.
*
* Return - 0 if successful
*/
int dce_admin_send_cmd_echo(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
int ret = -1;
struct dce_admin_ipc_cmd *req_msg;
struct dce_admin_ipc_resp *resp_msg;
if (!msg || !msg->tx.data || !msg->rx.data)
goto out;
/* return if dce bootstrap not completed */
if (!dce_is_bootstrap_done(d)) {
dce_err(d, "Admin Bootstrap not yet done");
goto out;
}
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_ECHO;
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending echo msg : [%d]", ret);
goto out;
}
out:
return ret;
}
/**
* dce_admin_send_cmd_ver - Sends DCE_ADMIN_CMD_VERSION cmd.
*
* @d - Pointer to tegra_dce struct.
* @msg - Pointer to dce_ipc_msg struct.
*
* Return - 0 if successful
*/
static int dce_admin_send_cmd_ver(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
int ret = 0;
struct dce_admin_ipc_cmd *req_msg;
struct dce_admin_ipc_resp *resp_msg;
struct dce_admin_version_info *ver_info;
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_VERSION;
ver_info = (struct dce_admin_version_info *)(&resp_msg->args.version);
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending get version info : [%d]", ret);
goto out;
}
dce_info(d, "version : [0x%x] err : [0x%x]", ver_info->version,
resp_msg->error);
out:
/**
* TODO : Add more error handling here
*/
return ret;
}
static int dce_admin_setup_clients_ipc(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
uint32_t i;
int ret = 0;
struct dce_admin_ipc_cmd *req_msg;
struct dce_admin_ipc_resp *resp_msg;
struct dce_ipc_queue_info q_info;
struct dce_admin_ipc_create_args *ipc_info;
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_IPC_CREATE;
ipc_info = (struct dce_admin_ipc_create_args *)
(&req_msg->args.ipc_create);
for (i = 0; i < DCE_IPC_CH_KMD_TYPE_MAX; i++) {
if (i == DCE_IPC_CH_KMD_TYPE_ADMIN)
continue;
ret = dce_ipc_get_channel_info(d, &q_info, i);
if (ret) {
dce_info(d, "Get queue info failed for [%u]", i);
ret = 0;
continue;
}
ipc_info->type = dce_ipc_get_ipc_type(d, i);
ipc_info->rd_iova = q_info.tx_iova;
ipc_info->wr_iova = q_info.rx_iova;
ipc_info->fsize = q_info.frame_sz;
ipc_info->n_frames = q_info.nframes;
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending IPC create msg for type [%u]",
i);
goto out;
}
if (resp_msg->error) {
dce_err(d, "IPC create for type [%u] failed", i);
goto out;
}
dce_ipc_channel_reset(d, i);
dce_info(d, "Channel Reset Complete for Type [%u] ...", i);
}
out:
/**
* TODO : Add more error handling here
*/
return ret;
}
static int dce_admin_send_rm_bootstrap(struct tegra_dce *d,
struct dce_ipc_message *msg)
{
int ret = 0;
struct dce_admin_ipc_cmd *req_msg;
struct dce_admin_ipc_resp *resp_msg;
struct dce_admin_version_info *ver_info;
req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data);
resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data);
req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_RM_BOOTSTRAP;
ver_info = (struct dce_admin_version_info *)(&resp_msg->args.version);
ret = dce_admin_send_msg(d, msg);
if (ret) {
dce_err(d, "Error sending rm bootstrap cmd: [%d]", ret);
goto out;
}
if (resp_msg->error) {
dce_err(d, "Error in handling rm bootstrap cmd on dce: [0x%x]",
resp_msg->error);
ret = -EINVAL;
}
out:
/**
* TODO : Add more error handling here
*/
return ret;
}
int dce_start_admin_seq(struct tegra_dce *d)
{
int ret = 0;
struct dce_ipc_message *msg;
msg = dce_admin_allocate_message(d);
if (!msg)
return -1;
d->boot_status |= DCE_FW_ADMIN_SEQ_START;
ret = dce_admin_send_cmd_ver(d, msg);
if (ret) {
dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION");
goto out;
}
ret = dce_admin_setup_clients_ipc(d, msg);
if (ret) {
dce_err(d, "RPC failed for DCE_ADMIN_CMD_IPC_CREATE");
goto out;
}
ret = dce_admin_send_rm_bootstrap(d, msg);
if (ret) {
dce_err(d, "RPC failed for DCE_ADMIN_CMD_RM_BOOTSTRAP");
goto out;
}
d->boot_status |= DCE_FW_ADMIN_SEQ_DONE;
out:
dce_admin_free_message(d, msg);
if (ret)
d->boot_status |= DCE_FW_ADMIN_SEQ_FAILED;
return ret;
}