/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2020. All rights reserved. * Description: SSM function file for Huanglong SSM * Author: ssm group * Create: 2019/12/11 */ #include "linux/huanglong/securec.h" #include "linux/dma-buf.h" #include "drv_ssm.h" #include "drv_sys_ext.h" #include "osal_ext.h" #ifdef CONFIG_SOCT_TEE_SUPPORT #include "teek_client_api.h" #endif #include "ssm_version.h" #define SSM_TEEC_NAME "tee_ssm_session" #define ssm_check_pointer_return_if_fail(pointer) \ do { \ if ((pointer) == TD_NULL) { \ soc_log_err("pointer %s is null\n", #pointer); \ return TD_FAILURE; \ } \ } while (0) typedef struct { td_handle session_handle; ext_drv_ssm_buffer_id buf_id; td_u64 fd; td_u32 buf_len; td_handle module_handle; } ssm_teek_attach_buf_ctl; #ifdef SSM_TEST_SUPPORT typedef struct { td_handle session_handle; td_handle module_handle; ext_drv_ssm_buffer_id buf_id; td_u64 buf_smmu_fd; td_u64 buf_len; } ssm_teek_check_buf_ctl; #endif typedef struct { struct file *filp; td_handle ssm_handle; } ssm_info; typedef struct { td_u32 ssm_num; ssm_info chan_info[SSM_MAX_SESSION_NUM]; } ssm_dev_handler; #ifdef CONFIG_SOCT_TEE_SUPPORT static TEEC_Context g_teec_context = {0}; TEEC_Session g_teec_session = {0}; TEEC_UUID g_teec_uuid = { 0x90ae48e5, 0xc757, 0x44a7, { 0xb5, 0x13, 0xde, 0x4b, 0x2b, 0x14, 0xa0, 0x7c } }; #endif static ssm_dev_handler g_ssm_dev = {0}; static osal_mutex g_ssm_lock = {0}; /**************************************************************** * * static functions which are defined only in this file * All functions name are started with "ssm_" * ****************************************************************/ static td_bool ssm_check_support(td_void) { ext_chip_name_id chip_name_id; ext_drv_sys_get_chip_name_id(&chip_name_id); if (chip_name_id == CHIP_NAME_RESERVED5 || chip_name_id == CHIP_NAME_RESERVED17 || chip_name_id == CHIP_NAME_RESERVED19 || chip_name_id == CHIP_NAME_HI3751V811) { soc_log_warn("can not support ssm\n"); return TD_FALSE; } return TD_TRUE; } static td_s32 ssm_do_destroy(td_handle ssm_handle) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; td_u32 i; soc_info_func_enter(); soc_log_info("%s %d ssm_handle=0x%x\n", __func__, __LINE__, ssm_handle); operation.started = 1; operation.params[0].value.a = (td_u32)ssm_handle; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_DESTROY, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke destroy fail:%x\n", result); return result; } (td_void)osal_mutex_lock(&g_ssm_lock); for (i = 0; i < SSM_MAX_SESSION_NUM; i++) { if (g_ssm_dev.chan_info[i].ssm_handle != ssm_handle) { continue; } g_ssm_dev.chan_info[i].ssm_handle = 0; g_ssm_dev.chan_info[i].filp = TD_NULL; g_ssm_dev.ssm_num--; break; } (td_void)osal_mutex_unlock(&g_ssm_lock); soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } static td_s32 ssm_do_teec_attach_buffer(ssm_teek_attach_buf_ctl *teek_attach_info, td_u64 *addr) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); ssm_check_pointer_return_if_fail(teek_attach_info); ssm_check_pointer_return_if_fail(addr); soc_log_info("%s %d session_handle=0x%x module_handle=0x%x buf_id=%d buf_len=%d fd=0x%llx\n", __func__, __LINE__, teek_attach_info->session_handle, teek_attach_info->module_handle, teek_attach_info->buf_id, teek_attach_info->buf_len, teek_attach_info->fd); operation.started = 1; operation.params[0].tmpref.buffer = (void *)teek_attach_info; /* the fd in this struct is useless */ operation.params[0].tmpref.size = sizeof(ssm_teek_attach_buf_ctl); operation.params[1].memfd.fd = teek_attach_info->fd; /* fd has to be sent in this way */ operation.params[2].value.a = 0; /* 2 is the 3rd param */ operation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_SECSMMU_HAND_INPUT, TEEC_VALUE_INOUT, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_ATTACH_BUFFER, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke attach buffer fail:%x\n", result); return result; } (*addr) = operation.params[2].value.b; /* 2 is the 3rd param */ soc_log_info("%s %d addr=0x%llx\n", __func__, __LINE__, *addr); soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } static td_s32 ssm_dump_context(td_void) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); operation.started = 1; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_DUMP_CONTEXT, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke dump context fail:%x\n", result); return result; } soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } /**************************************************************** * * exported driver ssm APIs, which are defined in drv_ssm.h * All functions name are started with "drv_ssm_" * ****************************************************************/ td_s32 drv_ssm_teec_init(td_void) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Result teec_ret; TEEC_Operation teec_operation = {0}; uint32_t teec_ret_origion = 0; td_u32 root_id = 0; td_s32 ret; soc_info_func_enter(); ret = osal_mutex_init(&g_ssm_lock); if (ret != TD_SUCCESS) { soc_log_err("cannot init osal lock! : 0x%x\n", ret); return ret; } teec_ret = TEEK_InitializeContext(TD_NULL, &g_teec_context); if (teec_ret != TEEC_SUCCESS) { soc_log_fatal("teec init content fail:%x\n", teec_ret); return teec_ret; } teec_operation.started = 1; teec_operation.cancel_flag = 0; teec_operation.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_INPUT); teec_operation.params[2].tmpref.buffer = (void *)&root_id; /* 2 is offset */ teec_operation.params[2].tmpref.size = sizeof(root_id); /* 2 is offset */ teec_operation.params[3].tmpref.buffer = (void *)SSM_TEEC_NAME; /* 3 is offset */ teec_operation.params[3].tmpref.size = strlen(SSM_TEEC_NAME) + 1; /* 3 is offset */ teec_ret = TEEK_OpenSession(&g_teec_context, &g_teec_session, &g_teec_uuid, TEEC_LOGIN_IDENTIFY, TD_NULL, &teec_operation, &teec_ret_origion); if (teec_ret != TEEC_SUCCESS) { soc_log_fatal("TEEK_OpenSession fail:%x\n", teec_ret); TEEK_FinalizeContext(&g_teec_context); } if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } memset_s(&teec_operation, sizeof(TEEC_Operation), 0, sizeof(TEEC_Operation)); teec_operation.started = 1; teec_operation.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_NONE, TEEC_NONE); teec_ret = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_INIT, &teec_operation, &teec_ret_origion); if (teec_ret != TEEC_SUCCESS) { soc_log_err("TEEC invoke fail:%x\n", teec_ret); TEEK_CloseSession(&g_teec_session); TEEK_FinalizeContext(&g_teec_context); return teec_ret; } soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } td_void drv_ssm_teec_deinit(td_void) { #ifdef CONFIG_SOCT_TEE_SUPPORT soc_info_func_enter(); (td_void)TEEK_CloseSession(&g_teec_session); (td_void)TEEK_FinalizeContext(&g_teec_context); soc_info_func_exit(); #endif return; } td_s32 drv_ssm_teec_create(struct file *ssm_filp, ext_ssm_intent intent, td_handle *handle) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; td_u32 i; soc_info_func_enter(); ssm_check_pointer_return_if_fail(handle); if (ssm_check_support() == TD_FALSE) { *handle = (SOC_ID_SSM << 24) | (SOC_ID_SSM << 16) | (SOC_ID_SSM << 8) | 0; /* 24 & 16 & 8 used to get handle */ return TD_SUCCESS; } soc_log_info("%s %d intent=%d\n", __func__, __LINE__, intent); operation.started = 1; operation.params[0].value.a = (td_u32)(intent); operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_CREATE, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke fail:%x\n", result); return result; } (*handle) = operation.params[0].value.b; (td_void)osal_mutex_lock(&g_ssm_lock); for (i = 0; i < SSM_MAX_SESSION_NUM; i++) { if (g_ssm_dev.chan_info[i].ssm_handle != 0) { continue; } g_ssm_dev.chan_info[i].ssm_handle = operation.params[0].value.b; g_ssm_dev.chan_info[i].filp = ssm_filp; g_ssm_dev.ssm_num++; break; } (td_void)osal_mutex_unlock(&g_ssm_lock); if (i >= SSM_MAX_SESSION_NUM) { soc_log_err("drv_ssm_teec_create fail:%x\n", result); (td_void)ssm_do_destroy(*handle); *handle = TD_INVALID_HANDLE; return TD_FAILURE; } soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } td_s32 drv_ssm_teec_destroy(td_handle ssm_handle) { if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } return ssm_do_destroy(ssm_handle); } td_s32 drv_ssm_teec_add_resource(td_handle ssm_handle, ext_ssm_module_resource res_info) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } soc_log_info("%s %d ssm_handle=0x%x module_handle=0x%x\n", __func__, __LINE__, ssm_handle, res_info.module_handle); operation.started = 1; operation.params[0].value.a = (td_u32)ssm_handle; operation.params[1].tmpref.buffer = (void *)&res_info; operation.params[1].tmpref.size = sizeof(res_info); operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_ADD_RESOUCE, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke add resource fail:%x\n", result); return result; } soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } td_s32 drv_ssm_teec_attach_buffer(ext_ssm_buffer_attach_info attach_info, td_u64 *addr) { #ifdef CONFIG_SOCT_TEE_SUPPORT ssm_teek_attach_buf_ctl teek_attach_info = {0}; struct dma_buf *dma_buf_addr = TD_NULL; td_s32 ret; if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } ssm_check_pointer_return_if_fail(addr); teek_attach_info.session_handle = attach_info.session_handle; teek_attach_info.buf_id = attach_info.buf_id; teek_attach_info.module_handle = attach_info.module_handle; dma_buf_addr = osal_mem_handle_get(attach_info.dma_buf_handle, SOC_ID_SSM); if (dma_buf_addr == TD_NULL) { soc_log_err("get dma buf addr fail\n"); return TD_FAILURE; } teek_attach_info.fd = attach_info.dma_buf_handle; teek_attach_info.buf_len = dma_buf_addr->size; osal_mem_ref_put(dma_buf_addr, SOC_ID_SSM); ret = ssm_do_teec_attach_buffer(&teek_attach_info, addr); if (ret != TD_SUCCESS) { soc_log_err("tec attach buffer\n"); return ret; } return TD_SUCCESS; #else return TD_FAILURE; #endif } td_s32 drv_ssm_teec_get_intent(td_handle ssm_handle, ext_ssm_intent *get_intent) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } ssm_check_pointer_return_if_fail(get_intent); operation.started = 1; operation.params[0].value.a = (td_u32)(ssm_handle); operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_GET_INTENT, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke get intent fail:%x\n", result); return result; } (*get_intent) = operation.params[0].value.b; soc_log_info("%s %d ssm_handle=0x%x get_intent=%d\n", __func__, __LINE__, ssm_handle, *get_intent); soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } #ifdef SSM_TEST_SUPPORT td_s32 drv_ssm_teec_check_buffer(ext_ssm_buffer_check_info check_info) { ssm_teek_check_buf_ctl teek_check_info = {0}; struct dma_buf *dma_buf_addr = TD_NULL; TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } dma_buf_addr = osal_mem_handle_get(check_info.dma_buf_handle, SOC_ID_SSM); if (dma_buf_addr == TD_NULL) { soc_log_err("get dma buf addr fail\n"); return TD_FAILURE; } soc_log_info("%s %d session_handle=0x%x module_handle=0x%x buf_id=%d buf_len=%d dma_buf_handle=0x%llx\n", __func__, __LINE__, check_info.session_handle, check_info.module_handle, check_info.buf_id, dma_buf_addr->size, check_info.dma_buf_handle); teek_check_info.session_handle = check_info.session_handle; teek_check_info.module_handle = check_info.module_handle; teek_check_info.buf_id = check_info.buf_id; teek_check_info.buf_smmu_fd = check_info.dma_buf_handle; teek_check_info.buf_len = dma_buf_addr->size; operation.started = 1; operation.params[0].tmpref.buffer = (void *)&teek_check_info; operation.params[0].tmpref.size = sizeof(ssm_teek_check_buf_ctl); operation.params[1].memfd.fd = teek_check_info.buf_smmu_fd; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_SECSMMU_HAND_INPUT, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_CHECK_BUF, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke get intent fail:%x\n", result); return result; } soc_info_func_exit(); return TD_SUCCESS; } td_s32 drv_ssm_teec_send_tbl(td_handle session_handle, ext_ssm_send_policy_tbl *p) { TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } soc_log_info("%s %d session_handle=0x%x\n", __func__, __LINE__, session_handle); operation.started = 1; operation.params[0].tmpref.buffer = (void *)p; /* the fd in this struct is useless */ operation.params[0].tmpref.size = sizeof(ext_ssm_send_policy_tbl); operation.params[1].value.a = session_handle; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_SEND_POLICY_TBL, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC drv_ssm_teec_send_tblfail:%x\n", result); return result; } soc_info_func_exit(); return TD_SUCCESS; } #endif td_s32 drv_ssm_teec_set_spread_cfg(soc_logic_mod_id module_id, td_u32 val) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_dbg_func_enter(); soc_log_dbg("%s %d module_id=%d, val=0x%x\n", __func__, __LINE__, module_id, val); operation.started = 1; operation.params[0].value.a = (td_u32)(module_id); operation.params[0].value.b = (td_u32)(val); operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_SET_SPREAD_CFG, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke set spread_cfg fail:%x\n", result); return result; } soc_dbg_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } td_s32 drv_ssm_teec_iommu_config(soc_logic_mod_id module_id) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); soc_log_info("%s %d module_id=%d\n", __func__, __LINE__, module_id); operation.started = 1; operation.params[0].value.a = (td_u32)(module_id); operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_IOMMU_CONFIG, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke set iommu tag fail:%x\n", result); return result; } soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } td_void drv_ssm_error_handler(struct file *ssm_filp) { td_u32 i; if (ssm_check_support() == TD_FALSE) { return; } for (i = 0; i < SSM_MAX_SESSION_NUM; i++) { if (g_ssm_dev.chan_info[i].filp != ssm_filp) { continue; } (td_void)ssm_do_destroy(g_ssm_dev.chan_info[i].ssm_handle); } return; } td_s32 drv_ssm_proc_read(td_void *fp) { if (fp == TD_NULL) { soc_log_err("drv_ssm_proc_read args is null\n"); return TD_FAILURE; } osal_seq_printf(fp, "ree_version :%s\n", g_ssm_ree_version); osal_seq_printf(fp, "tee_version :%s\n", g_ssm_tee_version); osal_seq_printf(fp, "sa_version :%s\n", g_ssm_sa_version); ssm_dump_context(); return TD_SUCCESS; } td_s32 drv_ssm_create_proc(td_char *name, fn_ssm_proc_read read_fn, osal_proc_cmd *cmd_list, td_u32 cmd_cnt) { osal_proc_entry *item = TD_NULL; if (name == TD_NULL) { soc_log_err("drv_ssm_create_proc args is null\n"); return TD_FAILURE; } item = osal_proc_add(name, strlen(name)); if (item == TD_NULL) { soc_log_err("drv_ssm_create_proc osal_proc_add failed\n"); return TD_FAILURE; } item->read = read_fn; item->cmd_list = cmd_list; item->cmd_cnt = cmd_cnt; return TD_SUCCESS; } td_void drv_ssm_destroy_proc(td_char *name) { if (name == TD_NULL) { soc_log_err("drv_ssm_destroy_proc args is null\n"); return; } osal_proc_remove(name, strlen(name)); } td_s32 drv_ssm_set_tee_log_level(td_u32 level) { #ifdef CONFIG_SOCT_TEE_SUPPORT TEEC_Operation operation = {0}; TEEC_Result result; td_u32 origin = 0; soc_info_func_enter(); operation.started = 1; operation.params[0].value.a = level; operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); result = TEEK_InvokeCommand(&g_teec_session, TEEC_CMD_SSM_TEE_LOG_LEVEL, &operation, &origin); if (result != TEEC_SUCCESS) { soc_log_err("TEEC invoke log level fail:%x\n", result); return result; } soc_info_func_exit(); return TD_SUCCESS; #else return TD_FAILURE; #endif } /**************************************************************** * * external driver ssm APIs, which can be called by other modules * All functions name are started with "ext_" * ****************************************************************/ td_s32 ext_drv_ssm_iommu_config(soc_logic_mod_id mod_id) { return drv_ssm_teec_iommu_config(mod_id); } td_s32 ext_drv_ssm_set_spread_cfg(soc_logic_mod_id mod_id, td_u32 val) { return drv_ssm_teec_set_spread_cfg(mod_id, val); } td_s32 ext_drv_ssm_attach_buffer(ext_drv_ssm_buf_attach_info attach_info, td_u64 *sec_info_addr) { ssm_teek_attach_buf_ctl attach_ctl = {0}; td_s64 fd; td_s32 ret; if (ssm_check_support() == TD_FALSE) { return TD_SUCCESS; } ssm_check_pointer_return_if_fail(sec_info_addr); attach_ctl.buf_id = attach_info.buf_id; attach_ctl.module_handle = attach_info.module_handle; attach_ctl.session_handle = attach_info.session_handle; fd = osal_mem_create_fd((struct dma_buf *)(uintptr_t)attach_info.dma_buf_addr, OSAL_O_CLOEXEC); if (fd < 0) { soc_log_err("cannot get sec buffer fd\n"); return TD_FAILURE; } attach_ctl.fd = fd; attach_ctl.buf_len = ((struct dma_buf *)(uintptr_t)attach_info.dma_buf_addr)->size; ret = ssm_do_teec_attach_buffer(&attach_ctl, sec_info_addr); if (ret != TD_SUCCESS) { soc_log_err("cannot attach buffer!\n"); } osal_mem_close_fd(fd); return TD_SUCCESS; } EXPORT_SYMBOL(ext_drv_ssm_attach_buffer); EXPORT_SYMBOL(ext_drv_ssm_iommu_config); EXPORT_SYMBOL(ext_drv_ssm_set_spread_cfg);