* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that 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 "pbl/pbl_davinci_api.h"
#include "davinci_interface.h"
#include "urd_define.h"
#include "urd_notifier.h"
#include "urd_feature.h"
#include "urd_kv.h"
#include "securec.h"
#include "urd_init.h"
#include "pbl/pbl_urd.h"
#include "urd_cmd.h"
#include "ka_compiler_pub.h"
#include "ka_ioctl_pub.h"
#include "ka_fs_pub.h"
#include "ka_task_pub.h"
#include "ka_system_pub.h"
#include "ka_memory_pub.h"
#include "ka_kernel_def_pub.h"
#include "ka_base_pub.h"
#include "ka_common_pub.h"
#define COPY_FUNC_NUM 2
#define FILTER_MAX_LEN 128
STATIC int copy_from_user_local(void *to, unsigned long to_len, const void *from, unsigned long from_len)
{
return (int)ka_base_copy_from_user(to, (void __ka_user *)from, from_len);
}
STATIC int copy_to_user_local(void *to, unsigned long to_len, const void *from, unsigned long from_len)
{
return (int)ka_base_copy_to_user((void __ka_user *)to, from, from_len);
}
STATIC int memcpy_s_local(void *to, unsigned long to_len, const void *from, unsigned long from_len)
{
return memcpy_s(to, to_len, from, from_len);
}
int (*copy_func_to[MSG_FROM_TYPE_MAX])(void *to, unsigned long to_len, const void *from, unsigned long from_len) = {
copy_to_user_local,
memcpy_s_local,
copy_to_user_local,
};
int (*copy_func_from[MSG_FROM_TYPE_MAX])(void *to, unsigned long to_len, const void *from, unsigned long from_len) = {
copy_from_user_local,
memcpy_s_local,
copy_from_user_local,
};
STATIC int dms_open(ka_inode_t* inode, ka_file_t* filep)
{
struct urd_file_private_stru* file_private = NULL;
if (filep == NULL) {
return -EINVAL;
}
if (!ka_system_try_module_get(KA_THIS_MODULE)) {
return -EBUSY;
}
file_private =
(struct urd_file_private_stru*)ka_mm_kzalloc(sizeof(struct urd_file_private_stru), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (file_private == NULL) {
dms_err("ka_mm_kzalloc failed. (size=%lu)\n", sizeof(struct urd_file_private_stru));
ka_system_module_put(KA_THIS_MODULE);
return -ENOMEM;
}
file_private->owner_pid = ka_task_get_current_tgid();
filep->private_data = (void *)file_private;
ka_base_atomic_set(&file_private->work_count, 0);
return 0;
}
STATIC int dms_release(ka_inode_t* inode, ka_file_t* filep)
{
struct urd_file_private_stru* file_private = NULL;
if (filep == NULL) {
return -EINVAL;
}
ka_system_module_put(KA_THIS_MODULE);
file_private = filep->private_data;
if (file_private == NULL) {
return -EBADFD;
}
urd_notifier_call(URD_NOTIFIER_RELEASE, file_private);
ka_mm_kfree(file_private);
file_private = NULL;
return 0;
}
STATIC unsigned int dms_msg_poll(ka_file_t* filep, ka_poll_table_struct_t* wait)
{
return KA_POLLERR;
}
STATIC int dms_make_feature_key(struct urd_cmd* cmd, DMS_FEATURE_ARG_S* arg)
{
int ret;
char* filter = NULL;
if ((cmd->filter_len > FILTER_MAX_LEN) || ((cmd->filter_len == 0) && (cmd->filter != NULL))) {
dms_err("filter_len is error. (filter_len=%u)", cmd->filter_len);
return -EINVAL;
}
arg->key = (char*)ka_mm_kzalloc(KV_KEY_MAX_LEN + 1, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (arg->key == NULL) {
dms_err("ka_mm_kzalloc failed. (size=%d)\n", KV_KEY_MAX_LEN);
return -ENOMEM;
}
if (cmd->filter != NULL) {
filter = ka_mm_kzalloc(cmd->filter_len + 1, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (filter == NULL) {
dms_err("ka_mm_kzalloc failed. (size=%u)\n", cmd->filter_len + 1);
ka_mm_kfree(arg->key);
arg->key = NULL;
return -ENOMEM;
}
ret = copy_func_from[arg->msg_source]((void*)filter, (unsigned long)cmd->filter_len + 1,
(void*)cmd->filter, (unsigned long)cmd->filter_len);
if (ret != 0) {
dms_err("copy_func_from failed. (ret=%d; size=%u)\n", ret, cmd->filter_len);
ka_mm_kfree(arg->key);
arg->key = NULL;
ka_mm_kfree(filter);
filter = NULL;
return -EFAULT;
}
}
ret = dms_feature_make_key(cmd->main_cmd, cmd->sub_cmd, filter, arg->key, KV_KEY_MAX_LEN + 1);
if (ret != 0) {
dms_err(
"make feature's key failed. (ret=%d; main_cmd=%u; sub_cmd=%u)\n", ret, cmd->main_cmd, cmd->sub_cmd);
ka_mm_kfree(arg->key);
arg->key = NULL;
}
if (filter != NULL) {
ka_mm_kfree(filter);
filter = NULL;
}
return ret;
}
STATIC int dms_make_feature_input(struct urd_cmd_para* cmd_para, DMS_FEATURE_ARG_S* arg)
{
int ret;
arg->input = NULL;
arg->input_len = 0;
if ((cmd_para->input_len == 0) && (cmd_para->input == NULL)) {
return 0;
}
if ((cmd_para->input_len != 0) && (cmd_para->input != NULL) && (cmd_para->input_len <= DMS_MAX_INPUT_LEN)) {
arg->input = ka_mm_kmalloc(cmd_para->input_len, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (arg->input == NULL) {
dms_err("ka_mm_kzalloc failed. (size=%u)\n", cmd_para->input_len);
return -ENOMEM;
}
arg->input_len = cmd_para->input_len;
ret = copy_func_from[arg->msg_source]((void*)arg->input, (unsigned long)arg->input_len,
(void*)cmd_para->input, (unsigned long)cmd_para->input_len);
if (ret != 0) {
dms_err("copy_func_from failed. (ret=%d; size=%u)\n", ret, cmd_para->input_len);
ka_mm_kfree(arg->input);
arg->input = NULL;
return -EFAULT;
}
return 0;
} else {
dms_err("Input para error. (input_len=%u)\n", cmd_para->input_len);
return -EINVAL;
}
}
#define DMS_OUTPUT_2M_LIMIT (2 * 1024 * 1024)
STATIC int dms_make_feature_max_output(struct urd_cmd_para* cmd_para, DMS_FEATURE_ARG_S* arg)
{
if (cmd_para->output_len > DMS_OUTPUT_2M_LIMIT) {
dms_err("Output len is larger than 2M limit. (output_len=%u)\n", cmd_para->output_len);
return -EINVAL;
}
arg->output = ka_mm_vzalloc(cmd_para->output_len);
if (arg->output == NULL) {
dms_err("ka_mm_vzalloc failed. (size=%u)\n", cmd_para->output_len);
return -ENOMEM;
}
arg->output_len = cmd_para->output_len;
return 0;
}
static bool dms_is_need_high_capacity_memory(struct urd_cmd* cmd)
{
if ((cmd->main_cmd == DMS_MAIN_CMD_BASIC && cmd->sub_cmd == DMS_SUBCMD_GET_HISTORY_FAULT_EVENT) ||
(cmd->main_cmd == DMS_MAIN_CMD_BASIC && cmd->sub_cmd == DMS_SUBCMD_GET_FAULT_EVENT_OBJ) ||
(cmd->main_cmd == DMS_MAIN_CMD_BASIC && cmd->sub_cmd == DMS_SUBCMD_GET_DEVICE_STATE) ||
(cmd->main_cmd == DMS_MAIN_CMD_BASIC && cmd->sub_cmd == DMS_SUBCMD_GET_DEV_PROBE_LIST) ||
(cmd->main_cmd == DMS_MAIN_CMD_BASIC && cmd->sub_cmd == DMS_SUBCMD_GET_ALL_DEV_LIST) ||
(cmd->main_cmd == DMS_MAIN_CMD_BASIC && cmd->sub_cmd == DMS_SUBCMD_GET_DEVICE_FROM_CHIP)) {
return true;
}
return false;
}
STATIC int dms_make_feature_output(struct urd_cmd *cmd, struct urd_cmd_para *cmd_para, DMS_FEATURE_ARG_S* arg)
{
arg->output = NULL;
arg->output_len = 0;
if (cmd_para->output_len == 0) {
return 0;
}
if (dms_is_need_high_capacity_memory(cmd)) {
return dms_make_feature_max_output(cmd_para, arg);
}
if (cmd_para->output_len <= DMS_MAX_OUTPUT_LEN) {
arg->output = ka_mm_vzalloc(cmd_para->output_len);
if (arg->output == NULL) {
dms_err("ka_mm_vzalloc failed. (size=%u)\n", cmd_para->output_len);
return -ENOMEM;
}
arg->output_len = cmd_para->output_len;
return 0;
} else {
dms_err("Output len error. (output_len=%u)\n", cmd_para->output_len);
return -EINVAL;
}
}
STATIC int dms_proc_feature_output(struct urd_cmd_para *cmd_para, DMS_FEATURE_ARG_S* arg)
{
int ret;
if (cmd_para->output != NULL) {
ret = copy_func_to[arg->msg_source]((void*)cmd_para->output, (unsigned long)cmd_para->output_len,
(void*)arg->output, (unsigned long)cmd_para->output_len);
if (ret != 0) {
dms_err("copy_func_to failed. (size=%u)\n", cmd_para->output_len);
return -EFAULT;
}
}
return 0;
}
STATIC void dms_free_msg(DMS_FEATURE_ARG_S* arg)
{
if (arg->key != NULL) {
ka_mm_kfree(arg->key);
arg->key = NULL;
}
if (arg->input != NULL) {
ka_mm_kfree(arg->input);
arg->input = NULL;
}
if (arg->output != NULL) {
ka_mm_vfree(arg->output);
arg->output = NULL;
}
return;
}
STATIC int dms_make_up_msg_arg(struct urd_cmd *cmd, struct urd_cmd_para *cmd_para, DMS_FEATURE_ARG_S* arg)
{
int ret;
ret = dms_make_feature_key(cmd, arg);
if (ret != 0) {
return ret;
}
ret = dms_make_feature_input(cmd_para, arg);
if (ret != 0) {
dms_free_msg(arg);
return ret;
}
ret = dms_make_feature_output(cmd, cmd_para, arg);
if (ret != 0) {
dms_free_msg(arg);
return ret;
}
return 0;
}
STATIC int dms_cmd_process(u32 devid, struct urd_cmd *cmd, struct urd_cmd_para *cmd_para, u32 msg_source)
{
int ret;
DMS_FEATURE_ARG_S arg = {0};
if (msg_source >= MSG_FROM_TYPE_MAX) {
dms_err("msg_source is wrong. (msg_source=%u)\n", msg_source);
return -EINVAL;
}
arg.msg_source = msg_source;
arg.devid = devid;
ret = dms_make_up_msg_arg(cmd, cmd_para, &arg);
if (ret != 0) {
dms_err("Make ka_task_up message arg failed. (ret=%d)\n", ret);
return ret;
}
ret = dms_feature_process(&arg);
if (ret != 0) {
goto out;
}
ret = dms_proc_feature_output(cmd_para, &arg);
if (ret != 0) {
dms_err("Process output failed. (ret=%d; size=%u)\n", ret, cmd_para->output_len);
}
out:
dms_free_msg(&arg);
return ret;
}
#define CMD_MASK (0x0000FFFFU)
STATIC bool check_cmd_arg_valid(unsigned int cmd, struct urd_ioctl_arg ctl_arg)
{
if ((cmd & CMD_MASK) == ((ctl_arg.cmd.main_cmd << _KA_IOC_TYPESHIFT) + (ctl_arg.cmd.sub_cmd << _KA_IOC_NRSHIFT))) {
return true;
} else {
return false;
}
}
int dms_cmd_process_from_kernel(u32 devid, struct urd_cmd *cmd, struct urd_cmd_para *cmd_para)
{
if ((cmd == NULL) || (cmd_para == NULL)) {
dms_err("Invalid file_private_data.\n");
return -EINVAL;
}
return dms_cmd_process(devid, cmd, cmd_para, MSG_FROM_KERNEL);
}
KA_EXPORT_SYMBOL(dms_cmd_process_from_kernel);
STATIC long dms_ioctl(ka_file_t* filep, unsigned int ioctl_cmd, unsigned long arg)
{
int ret;
struct urd_ioctl_arg ctl_arg = {0};
u32 msg_source;
if ((filep == NULL) || (arg == 0)) {
dms_err("Invalid file_private_data.\n");
return -EINVAL;
}
ret = ka_base_copy_from_user(&ctl_arg, (void*)((uintptr_t)arg), sizeof(struct urd_ioctl_arg));
if (ret != 0) {
dms_err("copy_from_user_safe failed.\n");
return ret;
}
if ((ioctl_cmd == URD_IOCTL_CMD) || (check_cmd_arg_valid(ioctl_cmd, ctl_arg))) {
msg_source = (ascend_intf_is_restrict_access(filep) ? MSG_FROM_USER_REST_ACC : MSG_FROM_USER);
ret = dms_cmd_process(ctl_arg.devid, &ctl_arg.cmd, &ctl_arg.cmd_para, msg_source);
} else {
dms_err("Invalid commmad. (command=%u; main_cmd=%u; sub_cmd=%u)\n", ioctl_cmd, ctl_arg.cmd.main_cmd, ctl_arg.cmd.sub_cmd);
ret = -ENODEV;
}
return ret;
}
const ka_file_operations_t g_dms_file_operations = {
ka_fs_init_f_owner(KA_THIS_MODULE)
ka_fs_init_f_open(dms_open)
ka_fs_init_f_release(dms_release)
ka_fs_init_f_poll(dms_msg_poll)
ka_fs_init_f_unlocked_ioctl(dms_ioctl)
};
STATIC int urd_release_prepare(ka_file_t *file_op, unsigned long mode)
{
if (mode != NOTIFY_MODE_RELEASE_PREPARE) {
dms_err("Invalid mode. (mode=%lu)\n", mode);
return -EINVAL;
}
if ((file_op == NULL) || (file_op->private_data == NULL)) {
return -EBADFD;
}
urd_notifier_call(URD_NOTIFIER_RELEASE_PREPARE, file_op->private_data);
return 0;
}
static const struct notifier_operations urd_notifier_ops = {
.owner = KA_THIS_MODULE,
.notifier_call = urd_release_prepare,
};
int urd_init(void)
{
int ret;
dms_info("dms urd driver init start.\n");
ret = drv_davinci_register_sub_module(DAVINCI_INTF_MODULE_URD, &g_dms_file_operations);
if (ret != 0) {
dms_err("Failed to register DMS module. (ret=%d)\n", ret);
return ret;
}
ret = drv_ascend_register_notify(DAVINCI_INTF_MODULE_URD, &urd_notifier_ops);
if (ret != 0) {
dms_err("Failed to register davinci notify. (ret=%d)\n", ret);
goto unregister_sub_module;
}
ret = dms_kv_init();
if (ret) {
dms_err("Failed to init dms kv. (ret=%d)\n", ret);
goto unregister_davinci_notify;
}
dms_info("dms urd driver init success.\n");
return 0;
unregister_davinci_notify:
(void)drv_ascend_unregister_notify(DAVINCI_INTF_MODULE_URD);
unregister_sub_module:
(void)drv_ascend_unregister_sub_module(DAVINCI_INTF_MODULE_URD);
return ret;
}
void urd_exit(void)
{
dms_info("Dms urd exit start.\n");
dms_kv_uninit();
(void)drv_ascend_unregister_notify(DAVINCI_INTF_MODULE_URD);
(void)drv_ascend_unregister_sub_module(DAVINCI_INTF_MODULE_URD);
dms_info("Dms urd driver exit success.\n");
return;
}