* 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 "securec.h"
#include "ascend_hal_error.h"
#include "urd_define.h"
#include "urd_init.h"
#include "pbl/pbl_davinci_api.h"
#include "urd_acc_ctrl.h"
#include "urd_container.h"
#include "ka_driver_pub.h"
#include "ka_memory_pub.h"
#include "ka_kernel_def_pub.h"
#include "ka_task_pub.h"
#include "ka_base_pub.h"
#ifndef CFG_HOST_ENV
# include "drv_whitelist.h"
#endif
#define CHECK_PERI(user, profile, mask, ret_value) \
do { \
if ((((profile) & (mask)) == 0) && (((user) & (mask)) != 0)) { \
return ret_value; \
} \
} while (0)
static inline s32 vdevice_access_check(u32 user_acc, u32 profile)
{
if ((user_acc & DMS_VDEV_MASK) == 0) {
return 0;
}
CHECK_PERI(user_acc, profile, DMS_VDEV_PHYSICAL, DRV_ERROR_NOT_SUPPORT);
CHECK_PERI(user_acc, profile, DMS_VDEV_VIRTUAL, DRV_ERROR_NOT_SUPPORT);
CHECK_PERI(user_acc, profile, DMS_VDEV_DOCKER, DRV_ERROR_NOT_SUPPORT);
return 0;
}
static inline s32 runtime_env_check(u32 user_acc, u32 profile)
{
if ((user_acc & DMS_RUN_ENV_MASK) == 0) {
return DRV_ERROR_NOT_SUPPORT;
}
CHECK_PERI(user_acc, profile, DMS_ENV_PHYSICAL, DRV_ERROR_NOT_SUPPORT);
CHECK_PERI(user_acc, profile, DMS_ENV_VIRTUAL, DRV_ERROR_NOT_SUPPORT);
CHECK_PERI(user_acc, profile, DMS_ENV_ADMIN_DOCKER, DRV_ERROR_NOT_SUPPORT);
CHECK_PERI(user_acc, profile, DMS_ENV_DOCKER, DRV_ERROR_NOT_SUPPORT);
return 0;
}
static inline s32 user_group_identify(u32 user_acc, u32 profile)
{
if ((user_acc & DMS_ACC_MASK) == 0) {
return DRV_ERROR_OPER_NOT_PERMITTED;
}
Use Drv error code, the user can get accurate error code by DmsIoctlConvertErrno(). */
CHECK_PERI(user_acc, profile, DMS_ACC_LIMIT_USER, (-EPERM));
CHECK_PERI(user_acc, profile, DMS_ACC_USER, DRV_ERROR_OPER_NOT_PERMITTED);
CHECK_PERI(user_acc, profile, DMS_ACC_OPERATE, DRV_ERROR_OPER_NOT_PERMITTED);
CHECK_PERI(user_acc, profile, DMS_ACC_ROOT, DRV_ERROR_OPER_NOT_PERMITTED);
CHECK_PERI(user_acc, profile, DMS_ACC_DM_USER, DRV_ERROR_OPER_NOT_PERMITTED);
return 0;
}
static inline bool dms_check_in_virtual_machine(void)
{
if (ka_driver_dmi_match(DMI_SYS_VENDOR, DMI_VIRTUAL_SYSVENDOR)) {
return true;
}
return false;
}
static inline bool dms_check_in_container(void)
{
if (urd_container_is_in_container() == true) {
return true;
}
return false;
}
static inline bool dms_check_in_admin_container(void)
{
if (urd_container_is_in_admin_container() == true) {
return true;
}
return false;
}
STATIC u32 dms_get_run_env(void)
{
u32 env;
if (dms_check_in_container()) {
env = DMS_ENV_DOCKER;
} else if (dms_check_in_admin_container()) {
env = DMS_ENV_ADMIN_DOCKER;
} else if (dms_check_in_virtual_machine()) {
env = DMS_ENV_VIRTUAL;
} else {
env = DMS_ENV_PHYSICAL;
}
return env;
}
STATIC u32 dms_get_user_role(void)
{
const ka_cred_t *cred = ka_current_cred();
u32 acc;
if ((cred != NULL) && (cred->euid.val == 0)) {
acc = DMS_ACC_ROOT;
} else {
if ((cred != NULL) && davinci_intf_confirm_user()) {
if (davinci_intf_get_manage_group() == cred->egid.val) {
acc = DMS_ACC_DM_USER;
} else {
acc = DMS_ACC_OPERATE;
}
} else {
acc = DMS_ACC_USER;
}
}
return acc;
}
static bool dms_check_in_vdev_docker(void)
{
u32 phyid = 0, vfid = 0;
if (!dms_check_in_container()) {
return false;
}
if (urd_container_logical_id_to_physical_id(0, &phyid, &vfid) != 0) {
return false;
}
if ((vfid != 0) || (!urd_is_pf_device(phyid))) {
return true;
}
return false;
}
STATIC bool dms_check_in_vdev_virtual_machine(void)
{
u32 phyid = 0, vfid = 0;
if (!dms_check_in_virtual_machine()) {
return false;
}
if (urd_container_logical_id_to_physical_id(0, &phyid, &vfid) != 0) {
return false;
}
if ((vfid != 0) || (!urd_is_pf_device(phyid))) {
return true;
}
return false;
}
static inline u32 dms_get_vdev_env(void)
{
u32 env = 0;
if (dms_check_in_vdev_docker()) {
env = DMS_VDEV_DOCKER;
} else if (dms_check_in_vdev_virtual_machine()) {
env = DMS_VDEV_VIRTUAL;
}
return env;
}
static bool dms_role_is_restrict_access(u32 acc, u32 msg_source)
{
if ((acc == DMS_ACC_ROOT) || (acc == DMS_ACC_DM_USER)) {
return false;
}
return ((msg_source == MSG_FROM_USER_REST_ACC) ? true : false);
}
STATIC u32 dms_feature_access_get_acc(u32 msg_source)
{
u32 acc;
acc = dms_get_user_role();
acc = (dms_role_is_restrict_access(acc, msg_source) ? DMS_ACC_LIMIT_USER : acc);
acc |= dms_get_run_env();
acc |= dms_get_vdev_env();
return acc;
}
s32 dms_feature_access_identify(u32 feature_prof, u32 msg_source)
{
int ret;
u32 user_acc;
user_acc = dms_feature_access_get_acc(msg_source);
ret = vdevice_access_check(user_acc, feature_prof);
if (ret != 0) {
return ret;
}
ret = runtime_env_check(user_acc, feature_prof);
if (ret != 0) {
return ret;
}
ret = user_group_identify(user_acc, feature_prof);
if (ret != 0) {
return ret;
}
return 0;
}
KA_EXPORT_SYMBOL_GPL(dms_feature_access_identify);
#ifndef CFG_HOST_ENV
static int dms_parse_string(char **str, const char *delim, char **out,
u32 *out_len)
{
const char *sep = NULL;
char *s = NULL;
char *tok = NULL;
char sc, c;
u32 max_len, count;
if ((str == NULL) || (*str == NULL) || (delim == NULL) || (out == NULL) ||
(out_len == NULL) || (*out_len == 0)) {
return -EINVAL;
}
max_len = *out_len;
*out_len = 0;
count = 0;
s = *str;
for (tok = s; ((tok != NULL) && (count < max_len));) {
c = *s++;
sep = delim;
do {
sc = *sep++;
if (sc != c) {
continue;
}
if (c == '\0') {
s = NULL;
} else {
s[-1] = 0;
}
*out++ = tok;
count++;
tok = s;
break;
} while (sc != 0);
}
*out_len = count;
return 0;
}
STATIC int dms_feature_alloc_and_save_proc_name(char **process_name, int process_num, char ***proc_ctl)
{
int i;
char **tmp_proc = NULL;
tmp_proc = (char **)ka_mm_kzalloc(process_num * sizeof(char *), KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (tmp_proc == NULL) {
dms_err("ka_mm_kzalloc failed. (len=%lu)\n", process_num * sizeof(char *));
return -ENOMEM;
}
*proc_ctl = tmp_proc;
for (i = 0; i < process_num; i++) {
tmp_proc[i] = process_name[i];
}
return 0;
}
#endif
int dms_feature_parse_proc_ctrl(const char *proc_str, char **buf, char ***proc_ctl, u32 *proc_num)
{
#ifndef CFG_HOST_ENV
char *process_name[WL_NAME_NUM_MAX + 1] = {NULL};
int ret;
unsigned int process_num;
size_t len;
if (proc_str == NULL) {
return 0;
}
if ((buf == NULL) || (proc_ctl == NULL) || (proc_num == NULL)) {
return -EINVAL;
}
len = ka_base_strnlen(proc_str, DMS_ACCESS_PROC_STRING_MAX);
if (len >= DMS_ACCESS_PROC_STRING_MAX) {
dms_err("Parse proc failed. (len=%lu)\n", len);
return -EINVAL;
}
*buf = (char *)ka_mm_kzalloc(len + 1, KA_GFP_KERNEL | __KA_GFP_ACCOUNT);
if (*buf == NULL) {
return -ENOMEM;
}
ret = strcpy_s(*buf, len + 1, proc_str);
if (ret != 0) {
dms_err("strcpy_s failed. (len=%lu)\n", len);
goto ERR_OUT;
}
process_num = WL_NAME_NUM_MAX;
ret = dms_parse_string(buf, ",", process_name, &process_num);
if ((ret != 0) || (process_num == 0)) {
dms_err("dms_parse_string failed. (ret=%d)\n", ret);
ret = -ENOEXEC;
goto ERR_OUT;
}
ret = dms_feature_alloc_and_save_proc_name(process_name, process_num, proc_ctl);
if (ret != 0) {
dms_err("dms_feature_alloc_and_save_proc_name failed. (len=%lu)\n", process_num * sizeof(char *));
goto ERR_OUT;
}
*proc_num = process_num;
return 0;
ERR_OUT:
ka_mm_kfree(*buf);
*buf = NULL;
return ret;
#else
return 0;
#endif
}
s32 dms_feature_whitelist_check(const char **proc_ctrl, u32 proc_num)
{
int ret = 0;
#ifndef CFG_HOST_ENV
if (proc_ctrl == NULL) {
return 0;
}
if ((proc_num == 0) || (proc_num > WL_NAME_NUM_MAX)) {
return DRV_ERROR_OPER_NOT_PERMITTED;
}
ret = whitelist_process_handler(proc_ctrl, proc_num);
if (ret != 0) {
dms_err("Permission denied! ret = %d.\n", ret);
return DRV_ERROR_OPER_NOT_PERMITTED;
}
#endif
return ret;
}