/*

 * 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_spod_info.h"

#include "pbl/pbl_uda.h"

#include "pbl/pbl_feature_loader.h"

#include "dms_template.h"

#include "dms/dms_cmd_def.h"

#include "urd_acc_ctrl.h"

#include "devdrv_common.h"

#include "ascend_kernel_hal.h"

#include "pbl_mem_alloc_interface.h"

#include "ka_base_pub.h"

#include "ka_task_pub.h"

#include "ka_memory_pub.h"

#include "dms/dms_notifier.h"

#include "ascend_dev_num.h"

#include "ka_dfx_pub.h"

#include "ka_common_pub.h"



#ifdef CFG_HOST_ENV

#include "devdrv_manager_container.h"

#include "comm_kernel_interface.h"

#endif



int dms_get_spod_info(void *feature, char *in, u32 in_len, char *out, u32 out_len);

#ifdef CFG_FEATURE_SUPPORT_GET_SPOD_PING_INFO

int dms_get_spod_ping_info(void *feature, char *in, u32 in_len, char *out, u32 out_len);

int dms_get_spod_node_status(void *feature, char *in, u32 in_len, char *out, u32 out_len);

int dms_set_spod_node_status(void *feature, char *in, u32 in_len, char *out, u32 out_len);

#endif



#define DMS_MODULE_SPOD_INFO "dms_spod_info"

BEGIN_DMS_MODULE_DECLARATION(DMS_MODULE_SPOD_INFO)

BEGIN_FEATURE_COMMAND()

ADD_FEATURE_COMMAND(DMS_MODULE_SPOD_INFO, DMS_GET_GET_DEVICE_INFO_CMD, ZERO_CMD,

    "main_cmd=0xc,sub_cmd=0x1", NULL, DMS_SUPPORT_ALL_USER, dms_get_spod_info)

#ifdef CFG_FEATURE_SUPPORT_GET_SPOD_PING_INFO

ADD_FEATURE_COMMAND(DMS_MODULE_SPOD_INFO, DMS_GET_GET_DEVICE_INFO_CMD, ZERO_CMD,

    "main_cmd=0xc,sub_cmd=0x2", NULL,

    DMS_ACC_ALL | DMS_ENV_ALL | DMS_VDEV_NOTSUPPORT,

    dms_get_spod_node_status)

ADD_FEATURE_COMMAND(DMS_MODULE_SPOD_INFO, DMS_GET_SET_DEVICE_INFO_CMD, ZERO_CMD,

    "main_cmd=0xc,sub_cmd=0x2", NULL,

    DMS_ACC_ROOT_ONLY | DMS_ENV_NOT_NORMAL_DOCKER | DMS_VDEV_NOTSUPPORT,

    dms_set_spod_node_status)

ADD_FEATURE_COMMAND(DMS_MODULE_SPOD_INFO, DMS_GET_GET_DEVICE_INFO_CMD, ZERO_CMD,

    "main_cmd=0x10,sub_cmd=0x2", NULL, DMS_SUPPORT_ALL, dms_get_spod_ping_info)

#endif

END_FEATURE_COMMAND()

END_MODULE_DECLARATION();



struct sdid_status {

    unsigned int sdid;

    HAL_KERNEL_SPOD_NODE_STATUS status;

};



typedef int (*dms_spod_notifier)(unsigned int udevid, unsigned int soc_type);



STATIC int dms_dev_info_args_check(const struct dms_get_device_info_in *input, const u32 in_len,

    const struct dms_get_device_info_out *output, const u32 out_len, const u32 min_buff)

{

    if (input == NULL || in_len != sizeof(struct dms_get_device_info_in)) {

        dms_err("Invalid parameter. (in_is_null=%d; in_len_invalid=%u; in_len_valid=%zu)\n",

            input == NULL, in_len, sizeof(struct dms_get_device_info_in));

        return -EINVAL;

    }



    if (input->buff == NULL || input->buff_size < min_buff) {

        dms_err("Invalid parameter. (devid=%u; buff_is_null=%d; buff_size_invalid=%u; min_size=%u)\n",

            input->dev_id, input->buff == NULL, input->buff_size, min_buff);

        return -EINVAL;

    }



    if (output == NULL || out_len != sizeof(struct dms_get_device_info_out)) {

        dms_err("Invalid parameter. (out_is_null=%d; out_len_invalid=%u; out_len_valid=%zu)\n",

            output == NULL, out_len, sizeof(struct dms_get_device_info_out));

        return -EINVAL;

    }



    return 0;

}



#ifdef CFG_FEATURE_SUPPORT_GET_SPOD_PING_INFO

#ifdef CFG_HOST_ENV

static int dms_root_physical_check(u32 udevid)

{

    int ret;

    u32 host_flag;



#ifndef DEVDRV_MANAGER_HOST_UT_TEST

    /* user check and keep errno same with the dms. */

    if ((u32)(ka_task_get_current_cred_euid()) != 0) {

        dms_err("Operation not permitted. (udevid=%u)\n", udevid);

        return -EPERM;

    }

#endif



    if (devdrv_manager_is_pf_device(udevid) == false || devdrv_manager_container_is_in_container()) {

        return -EOPNOTSUPP;

    }



    ret = devdrv_get_host_phy_mach_flag(udevid, &host_flag);

    if (ret != 0) {

        dms_err("Failed to invoke devdrv_get_host_phy_mach_flag for physical-machine check. (udevid=%u)\n", udevid);

        return ret;

    }



    if (host_flag != DEVDRV_HOST_PHY_MACH_FLAG) {

        return -EOPNOTSUPP;

    }

    return 0;

}

#endif

#endif



int dms_get_spod_info(void *feature, char *in, u32 in_len, char *out, u32 out_len)

{

    int ret;

    unsigned int udevid = 0;

    struct dms_get_device_info_in *input = (struct dms_get_device_info_in *)in;

    struct dms_get_device_info_out *output = (struct dms_get_device_info_out *)out;

    struct spod_info spod_stru = {0};



    ret = dms_dev_info_args_check(input, in_len, output, out_len, sizeof(struct spod_info));

    if (ret != 0) {

        return ret;

    }



    ret = uda_devid_to_udevid(input->dev_id, &udevid);

    if (ret != 0) {

        dms_err("Failed to convert the logical ID to the physical ID. (dev_id=%u; ret=%d)\n", input->dev_id, ret);

        return ret;

    }



    if (!devdrv_manager_is_pf_device(udevid)) {

        return -EOPNOTSUPP;

    }



    ret = dbl_get_spod_info(udevid, &spod_stru);

    if (ret != 0) {

        dms_err("Failed to get spod info. (dev_id=%u; ret=%d)\n", input->dev_id, ret);

        return ret;

    }

    ret = ka_base_copy_to_user(input->buff, &spod_stru, sizeof(spod_stru));

    if (ret != 0) {

        dms_err("ka_base_copy_to_user failed. (dev_id=%u; ret=%d)\n", input->dev_id, ret);

        return ret;

    }

    output->out_size = sizeof(spod_stru);



    return 0;

}

#ifdef CFG_FEATURE_SUPPORT_GET_SPOD_PING_INFO

int dms_get_spod_ping_info(void *feature, char *in, u32 in_len, char *out, u32 out_len)

{

#ifdef CFG_HOST_ENV

    int ret;

    u32 udevid = 0, sdid = 0;

    struct dms_get_device_info_in *input = (struct dms_get_device_info_in *)in;

    struct dms_get_device_info_out *output = (struct dms_get_device_info_out *)out;



    ret = dms_dev_info_args_check(input, in_len, output, out_len, sizeof(u32));

    if (ret != 0) {

        return ret;

    }



    ret = uda_devid_to_udevid(input->dev_id, &udevid);

    if (ret != 0) {

        dms_err("Failed to convert the logical ID to the physical ID. (dev_id=%u; ret=%d)\n", input->dev_id, ret);

        return ret;

    }



    ret = dms_root_physical_check(udevid);

    if (ret != 0) {

        return ret;

    }



    ret = ka_base_copy_from_user(&sdid, (void *)((uintptr_t)input->buff), sizeof(u32));

    if (ret != 0) {

        dms_err("Failed to invoke ka_base_copy_from_user for sdid. (devid=%u; udevid=%u; ret=%d)\n",

            input->dev_id, udevid, ret);

        return ret;

    }



    ret = devdrv_s2s_npu_link_check(udevid, sdid);

    if (ret != 0) {

        dms_ex_notsupport_err(ret, "Failed to invoke devdrv_s2s_npu_link_check for hccs link status. (devid=%u)\n",

            udevid);

        return ret;

    }

    output->out_size = 0;



    return 0;

#else

    return -EOPNOTSUPP;

#endif

}

#endif



#ifdef CFG_HOST_ENV

STATIC int dms_spod_node_status_check_soc_type(unsigned int udevid)

{

    int ret;

    unsigned int soc_type = SOC_TYPE_MAX;



    ret = hal_kernel_get_soc_type(udevid, &soc_type);

    if (ret != 0) {

        return ret;

    }



    if (soc_type != SOC_TYPE_CLOUD_V3) {

        return -EOPNOTSUPP;

    }



    return 0;

}

#endif



STATIC int dms_spod_set_node_status(unsigned int local_udevid, unsigned int remote_sdid, unsigned int status)

{

#ifdef CFG_HOST_ENV

    int ret;

    struct sdid_parse_info sdid_info = {0};

    unsigned int index = 0;



    if ((local_udevid >= ASCEND_PDEV_MAX_NUM) || (status >= DMS_SPOD_NODE_STATUS_MAX)) {

        dms_err("Invalid parameter. (local_udevid=%u; status=%d)\n", local_udevid, status);

        return -EINVAL;

    }



    ret = dbl_parse_sdid(remote_sdid, &sdid_info);

    if (ret != 0) {

        dms_err("Failed to parse remote_sdid. (remote_sdid=0x%x; ret=%d)\n", remote_sdid, ret);

        return ret;

    }



    DBL_SPOD_CHECK_SDID(sdid_info, ret);

    if (ret != 0) {

        dms_err("Invalid sdid. (sdid_server_id=%u; sdid_chip_id=%u; sdid_die_id=%u; sdid_udevid=%u)\n",

                    sdid_info.server_id, sdid_info.chip_id, sdid_info.die_id, sdid_info.udevid);

        return ret;

    }



    ret = dms_spod_node_status_check_soc_type(local_udevid);

    if (ret != 0) {

        dms_ex_notsupport_err(ret, "check soc type failed. (local_udevid=%u; ret=%d)\n", local_udevid, ret);

        return ret;

    }



    index = sdid_info.server_id * DBL_SPOD_MAX_UDEVID_NUM + sdid_info.udevid;

    return dbl_set_spod_node_status(local_udevid, index, status);

#else

    (void)local_udevid;

    (void)remote_sdid;

    (void)status;

    return -EOPNOTSUPP;

#endif

}



int dms_get_spod_node_status(void *feature, char *in, u32 in_len, char *out, u32 out_len)

{

    int ret;

    unsigned int udevid = 0;

    unsigned int sdid = 0;

    unsigned int status = 0;

    struct dms_get_device_info_in *input = (struct dms_get_device_info_in *)in;

    struct dms_get_device_info_out *output = (struct dms_get_device_info_out *)out;

    (void)feature;



    ret = dms_dev_info_args_check(input, in_len, output, out_len, sizeof(unsigned int));

    if (ret != 0) {

        return ret;

    }



    ret = uda_devid_to_udevid(input->dev_id, &udevid);

    if (ret != 0) {

        dms_err("Failed to convert the logical ID to the physical ID. (dev_id=%u; ret=%d)\n", input->dev_id, ret);

        return ret;

    }



    ret = ka_base_copy_from_user(&sdid, (void *)((uintptr_t)input->buff), sizeof(u32));

    if (ret != 0) {

        dms_err("Failed to invoke ka_base_copy_from_user for sdid. (devid=%u; udevid=%u; ret=%d)\n",

            input->dev_id, udevid, ret);

        return ret;

    }



    ret = hal_kernel_get_spod_node_status(udevid, sdid, &status);

    if (ret != 0) {

        dms_ex_notsupport_err(ret, "Failed to get sdid's status. (udevid=%u; sdid=0x%x; ret=%d)\n",

            udevid, sdid, ret);

         return (ret != -ERANGE ? ret : -EINVAL);

    }



    ret = ka_base_copy_to_user((void *)((uintptr_t)input->buff), &status, sizeof(u32));

    if (ret != 0) {

        dms_err("Failed to invoke ka_base_copy_to_user for status. (devid=%u; udevid=%u; ret=%d)\n",

            input->dev_id, udevid, ret);

        return ret;

    }

    output->out_size = sizeof(u32);

    return 0;

}



int dms_set_spod_node_status(void *feature, char *in, u32 in_len, char *out, u32 out_len)

{

    int ret;

    unsigned int udevid = 0;

    struct sdid_status para = {0};

    struct dms_set_device_info_in *input = (struct dms_set_device_info_in *)in;

    (void)feature;

    (void)out;

    (void)out_len;



    if ((input == NULL) || (in_len != sizeof(struct dms_set_device_info_in))) {

        dms_err("Invalid parameter. (in_is_null=%d; in_len_invalid=%u; in_len_valid=%zu)\n",

            input == NULL, in_len, sizeof(struct dms_set_device_info_in));

        return -EINVAL;

    }



    if ((input->buff == NULL) || (input->buff_size < sizeof(struct sdid_status))) {

        dms_err("Invalid parameter. (devid=%u; buff_is_null=%d; buff_size_invalid=%u; min_size=%zu)\n",

            input->dev_id, input->buff == NULL, input->buff_size, sizeof(struct sdid_status));

        return -EINVAL;

    }



    ret = uda_devid_to_udevid(input->dev_id, &udevid);

    if (ret != 0) {

        dms_err("Failed to convert the logical ID to the physical ID. (dev_id=%u; ret=%d)\n", input->dev_id, ret);

        return ret;

    }



    ret = ka_base_copy_from_user(&para, (void *)((uintptr_t)input->buff), sizeof(struct sdid_status));

    if (ret != 0) {

        dms_err("Failed to invoke ka_base_copy_from_user for sdid and status. (devid=%u; udevid=%u; ret=%d)\n",

            input->dev_id, udevid, ret);

        return ret;

    }



    ret = dms_spod_set_node_status(udevid, para.sdid, para.status);

    if (ret != 0) {

        dms_ex_notsupport_err(ret, "Failed to set sdid's status. (udevid=%u; sdid=0x%x; ret=%d)\n",

            udevid, para.sdid, ret);

    }

    return ret;

}



STATIC int dms_spod_node_status_init(unsigned int udevid, unsigned int soc_type)

{

#ifdef CFG_HOST_ENV

    if ((soc_type != SOC_TYPE_CLOUD_V3) || (!devdrv_manager_is_pf_device(udevid))) {

        return 0;

    }



    return dbl_spod_node_status_init(udevid);

#endif

    return 0;

}



STATIC void dms_spod_node_status_uninit(unsigned int udevid, unsigned int soc_type)

{

#ifdef CFG_HOST_ENV

    if ((soc_type != SOC_TYPE_CLOUD_V3) || (!devdrv_manager_is_pf_device(udevid))) {

        return;

    }



    dbl_spod_node_status_uninit(udevid);

#endif

    return;

}



STATIC int dms_spod_device_up_notify(unsigned int udevid, unsigned int soc_type)

{

    int ret = 0;



    if (udevid >= ASCEND_DEV_MAX_NUM) {

        dms_err("Invalid udevid. (udevid=%u)\n", udevid);

        return -EINVAL;

    }



    ret = dms_spod_node_status_init(udevid, soc_type);

    if (ret != 0) {

        dms_err("Call dms spod node status init failed. (udevid=%u; soc_type=%u; ret=%d)\n",

            udevid, soc_type, ret);

    }



    return ret;

}



STATIC int dms_spod_device_down_notify(unsigned int udevid, unsigned int soc_type)

{

    int ret = 0;



    if (udevid >= ASCEND_DEV_MAX_NUM) {

        dms_err("Invalid udevid. (udevid=%u)\n", udevid);

        return -EINVAL;

    }



    dms_spod_node_status_uninit(udevid, soc_type);

    return ret;

}



STATIC dms_spod_notifier dms_spod_notifier_handle_func[DMS_DEVICE_NOTIFIER_MAX] = {

    [DMS_DEVICE_UP3] = dms_spod_device_up_notify,

    [DMS_DEVICE_DOWN3] = dms_spod_device_down_notify,

};



STATIC int dms_spod_notifier_handle(ka_notifier_block_t *nb, unsigned long mode, void *data)

{

    int ret;

    unsigned int soc_type = SOC_TYPE_MAX;

    struct devdrv_info *dev_info = (struct devdrv_info *)data;



    if ((data == NULL) || (mode == DMS_DEVICE_NOTIFIER_MIN) ||

        (mode >= DMS_DEVICE_NOTIFIER_MAX)) {

        dms_err("Invalid parameter. (mode=0x%lx; data=\"%s\")\n",

                mode, data == NULL ? "NULL" : "OK");

        return -EINVAL;

    }



    if (mode != DMS_DEVICE_UP3 && mode != DMS_DEVICE_DOWN3) {

        return 0;

    }



    soc_type = (dev_info->multi_die == 1) ? SOC_TYPE_CLOUD_V3 : CHIP_CLOUD_V2;



    ret = dms_spod_notifier_handle_func[mode](dev_info->dev_id, soc_type);

    if (ret != 0) {

        dms_err("Call dms spod notifier handle func failed. (dev_id=%u; mode=%ld; ret=%d)\n",

            dev_info->dev_id, mode, ret);

        return ret;

    }



    return 0;

}



STATIC ka_notifier_block_t g_dms_spod_notifier = {

    .notifier_call = dms_spod_notifier_handle,

};



STATIC int dms_spod_status_module_init(void)

{

    int ret = 0;



    ret = dms_register_notifier(&g_dms_spod_notifier);

    if (ret != 0) {

        dms_err("register dms spod notifier failed. (ret=%d)\n", ret);

    }



    return ret;

}



STATIC void dms_spod_status_module_exit(void)

{

    (void)dms_unregister_notifier(&g_dms_spod_notifier);

}



int dms_spod_init(void)

{

    int ret;



    dms_info("dms spod info init.\n");

    CALL_INIT_MODULE(DMS_MODULE_SPOD_INFO);

    ret = dms_spod_status_module_init();

    if (ret != 0) {

        dms_err("call dms_spod_module_init failed.  (ret=%d)\n", ret);

    }

    return ret;

}

DECLAER_FEATURE_AUTO_INIT(dms_spod_init, FEATURE_LOADER_STAGE_5);



void dms_spod_exit(void)

{

    dms_info("dms spod info exit.\n");

    dms_spod_status_module_exit();

    CALL_EXIT_MODULE(DMS_MODULE_SPOD_INFO);

}

DECLAER_FEATURE_AUTO_UNINIT(dms_spod_exit, FEATURE_LOADER_STAGE_5);