-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local bios_enum = require 'bios.types.enums'
local log = require 'mc.logging'
local singleton = require 'mc.singleton'
local bios_object = require 'pojo.bios_object_mutihost'
local msg = require 'bios.ipmi.ipmi_message'
local ipmi = require 'ipmi'
local json = require 'cjson'
local bs_util = require 'util.base_util'
local bs = require 'mc.bitstring'
local utils = require 'mc.utils'
local s_pack = string.pack
local s_unpack = string.unpack
local file_op_strategy = require 'service.file.file_operation_strategy'
local prop_method = require 'macros.property_method_app'
local prop_def = require 'macros.property_def'
local obj_def = require 'macros.object_def'
local prop_global = require 'macros.property_global'
local file_object = require 'pojo.file_object'
local bios_factory = require 'factory.bios_factory'
local boot_def = require 'macros.boot_def'
local vos = require 'utils.vos'
local prop_hander = require 'handler.bios_prop_handler'
local skynet = require 'skynet'
local file_sec = require 'utils.file'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local file_service = require 'service.file_service'
local generate_file = require 'service.file.generate_file'
local client = require 'bios.client'
local signal = require 'mc.signal'
local alarms = require 'domain.alarm.alarm'
local utils_core = require 'utils.core'
local defs = require 'domain.alarm.defs'
local skynet_queue = require 'skynet.queue'
local tasks = require 'mc.orm.tasks'
local bios_defs = require 'domain.bios_firmware.defs'
local ipmi_req = require 'bios.ipmi.ipmi'
local context = require 'mc.context'
local prop_method_app = require 'macros.property_method_app'
local clsmgmt = require 'mc.class_mgnt'
local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local table_cache = require 'mc.table_cache'
local cert_core = require 'certificate.core'
local tbl_cache = table_cache.new()

local class = require 'mc.class'

local comp_code = ipmi.types.Cc
local match_rule = org_freedesktop_dbus.MatchRule

local bios_service = class()

local E_OK = 1
local E_FAILED = -1
local MAX_BIOS_VERSION_LEN<const> = 21
local DEFAULT_PORT_ID<const> = 255
local MANUFACTURE_ID<const> = 0x0007db
local SECBOX_USER_UID<const> = 104 -- secbox用户id
local OPERATOR_USER_GID<const> = 200 -- operator用户组id
local ADMIN_USER_GID<const> = 204 -- admin用户组id

local PROP_START<const> = 2
local VALUE_LENGTH_START<const> = 3
local MIN_PROP_LENGTH<const> = 4
local VALUE_START<const> = 4
local BIT_NUMBER<const> = 0x100
local CONFIG_STR<const> = 0
local CONFIG_NUM<const> = 1
local TEEOS_TYPE<const> = 0
local CCA_TYPE<const> = 1

function bios_service:ctor(bus, db)
    self.bus = bus
    self.from_csr = false
    self.dft_mode = false
    self.db = db
    self.spi_channel = false
    self.bios_object_collection = {}
    self.upg_fw_data_opt = file_object.new()
    self.bios_wr_data_opt = {}
    self.bios_rd_data_opt = {}
    self.task_status = {}
    self.on_version_changed = signal.new()
    self.queue = skynet_queue()
    self.multihost_card_config = {}
    self.cpu_die = {}
    self.vrd_type_map = {}
    self.mem_cache_collection = {}
    self.before_power_on_solt = {}
    self.signal_slot = {}
end

function bios_service:init()
    self.upg_fw_data_opt:set_flag(prop_def.BIOS_FLAG_IDLE)
    self.upg_fw_data_opt:set_file_size_limit(prop_def.MAX_FILE_SIZE_90M)
    self.upg_fw_file = nil
end

function bios_service:set_spi_channel(obj)
    self.spi_channel = obj
end

function bios_service:set_cpu_die(obj)
    self.cpu_die = obj
end

function bios_service:set_vrd_type_map(obj)
    self.vrd_type_map = obj
end

function bios_service:set_os_boot(obj)
    self.mdb_os_boot = obj
end

function bios_service:delete_os_boot()
    self.mdb_os_boot = nil
end

function bios_service:enable_multihost()
    self.multihost = true
end

function bios_service:is_multihost()
    return self.multihost
end

function bios_service:set_multihost_card_config(obj)
    log:notice('set_multihost_card_config')
    log:notice(obj)
    self.multihost_card_config = obj
end

function bios_service:get_file_change(sysId)
    local obj = self:get_obj(sysId)
    if not obj then
        log:error('[bios]get file change sysId(%s) invalid', sysId)
        return 0
    end
    return obj:get_file_change() or 0
end

function bios_service:set_ineffective(system_id)
    self:set_prop('SettingsEffectiveStatus', 'Ineffective', system_id)
    log:notice('[bios]set setting effective status to Ineffective system is %s', system_id)
end

function bios_service:set_effective(system_id)
    self:set_prop('SettingsEffectiveStatus', 'Effective', system_id)
    log:notice('[bios]set setting effective status to Effective system is %s', system_id)
end

function bios_service:add_mem_silk_from_cache(system_id)
    local mem_silks = self:get_mem_silk(system_id)
    if not mem_silks or not next(mem_silks) then
        return
    end
    log:notice('memsilk cache is not empty need add silk from cache system id is %s', system_id)
    for _, obj in pairs(mem_silks) do
        self:add_multihost_memsilk_config(system_id, obj)
    end
    self.mem_cache_collection[system_id or 1] = nil
end

function bios_service:delete_object(system_id)
    local bios_obj = self:get_obj(system_id)
    if not bios_obj then
        return
    end
    clsmgmt('Bios'):remove(bios_obj:get_bios_obj())
    self.bios_object_collection[system_id] = nil
end

function bios_service:check_load_obj(obj, position)
    local system_id = obj:get_system_id()
    local bios_obj = self:get_obj(system_id)
    if not bios_obj then
        return false
    end
    if bios_obj.position == position then
        return true
    end
    return false
end

function bios_service:add_object(obj, position)
    self.from_csr = true
    local system_id = obj:get_system_id()
    if self.bios_object_collection[system_id] then
        return
    end
    self.bios_object_collection[system_id] = bios_object.new(obj, self.db, self.bus, position)
    obj:register_mdb_objects()
    --监听预上电信号
    pcall(function()
        local path = string.format('/bmc/kepler/Systems/%s/FruCtrl/:Id', system_id)
        if self.before_power_on_solt[system_id] then
            return
        end
        local sig = match_rule.signal('BeforePowerOnSignal', 'bmc.kepler.Systems.FruCtrl'):with_path(path)
        self.before_power_on_solt[system_id] = true
        self.signal_slot[#self.signal_slot + 1] = self.bus:match(sig, function (msg)
            self:verify_flash(system_id)
        end)
    end)
    self:add_mem_silk_from_cache(system_id)
end

function bios_service:verify_flash(system_id)
    system_id = system_id or bios_defs.SINGLE_HOST_SYSTEM_ID
    log:notice('[bios] system %s bios receive before power on UpgradeSignal.', system_id)
    local flash_channel_ids = self:get_prop(obj_def.FLASH_CHANNEL_IDS, system_id) or {}
    if self:is_multihost() and #flash_channel_ids == 0 then
        log:notice('[bios]multihost no need verify')
        return
    end
    pcall(function()
        local pfr_ser = bios_factory.get_service('pfr_service')
        pfr_ser:start_verify(system_id)
    end)
end

function bios_service:async_fetch_info(system_id)
    pcall(function()
        local obj = self:get_obj(system_id)
        if not obj then
            log:error('[bios]async_fetch_info: system id(%s) invalid', system_id)
            return
        end
        obj:async_fetch_info()
    end)
end

function bios_service:fetch_info(system_id)
    pcall(function()
        local obj = self:get_obj(system_id)
        if not obj then
            log:error('[bios]fetch_info: system id(%s) invalid', system_id)
            return
        end
        obj:fetch_info()
    end)
end

function bios_service:get_obj(system_id)
    return self.bios_object_collection[system_id or 1]
end

function bios_service:get_all_objs()
    return self.bios_object_collection
end

function bios_service:notify_dft_mode()
    self.dft_mode = true
end

function bios_service:update_patch_version(version, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update_patch_version: system id(%s) invalid', system_id)
        return
    end
    obj:set_prop('Version', version)
    obj:set_prop('BackupVersion', version)
    obj:set_firmware_version(version)
    log:notice('[bios]system id(%s) update bios version, patch_version (%s), current_version(%s)',
        system_id, version, self:get_prop('Version', system_id))
end

function bios_service:update_denpendency(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update_denpendency: system id(%s) invalid', system_id)
        return
    end
    obj:update_denpendency()
end

function bios_service:get_bios_version(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]get_bios_version: system id(%s) invalid.', system_id)
        return nil
    end

    return obj:get_bios_version()
end

function bios_service:get_bios_backup_version(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]get_bios_backup_version:system id(%s) invalid.', system_id)
        return nil
    end

    return obj:get_bios_backup_version()
end

function bios_service:update_bios_backup_version(version, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update_bios_backup_version:system id(%s) invalid.', system_id)
        return nil
    end
    obj:update_bios_backup_version(version)
    return E_OK
end

function bios_service:update_bios_startup_state(state, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update_bios_startup_state:system id(%s) invalid.', system_id)
        return E_FAILED
    end

    obj:update_bios_startup_state(state)
    return E_OK
end

function bios_service:save_backup_bios_version(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]save_backup_bios_version:system id(%s) invalid.', system_id)
        return
    end

    local version = obj:get_bios_version()
    if not version or version == '' then
        log:error('save_backup_bios_version: get system %s bios version fail!', system_id)
        return
    end

    local backup_version = obj:get_bios_backup_version()
    if not backup_version then
        log:error('save_backup_bios_version: get system %s bios backup version fail!', system_id)
        return
    end

    if version == backup_version then
        return
    end

    if version == '000' then
        return
    end

    obj:update_bios_backup_version(version)

    log:notice('Set Backup BIOS version to (%s), system id(%s)', version, system_id)
end

function bios_service:update_firmware_date(system_id, smbios_handler)
    if not smbios_handler then
        log:info('[bios]update_firmware_date:smbios_handler is nil, system id(%s).', system_id)
        return
    end

    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update_firmware_date:system id(%s) invalid.', system_id)
        return
    end

    local smbios_bios_info = smbios_handler:get_bios_info_in_smbios()
    if not smbios_bios_info or not smbios_bios_info.Fields or not smbios_bios_info.Fields.firmware_release_date then
        log:error('[bios]update_firmware_date:system id(%s) firmware date is nil.', system_id)
        return
    end

    obj:set_firmware_date(smbios_bios_info.Fields.firmware_release_date)
end

local function get_file_name(selector)
    return prop_global.G_FILE_PROPERTY_NAME[selector]
end

-- 通用： 判断ipmi请求是否为空
function bios_service.judge_req(req)
    if not req then
        log:error('request is null!')
        return E_FAILED
    end
    return E_OK
end

-- 通用： 判断ipmi请求厂商id是否有效
function bios_service.judge_manu_id_valid(req, manu_id)
    if not req.ManufactureId then
        log:error('get_manu_id fail!')
        return E_FAILED
    end
    if req.ManufactureId ~= manu_id then
        log:error('judge_manu_id_valid fail, req is %s, need %s!', req.ManufactureId, manu_id)
        return E_FAILED
    end
    return E_OK
end

-- 判断是否所有的bios版本都和升级包一致
function bios_service:check_all_host_same_version(version, prop)
    for _, bios_obj in pairs(self.bios_object_collection) do
        if not bios_obj:get_prop(prop) or bios_obj:get_prop(prop) ~= version then
            return false
        end
    end
    return true
end

-- 判断传入的bios版本是否有效
function bios_service.judge_version_valid(req)
    if not req.Version or req.Version == '' then
        log:error('request version is null!')
        return E_FAILED
    elseif #req.Version > MAX_BIOS_VERSION_LEN then
        log:error('request version is out of range!')
        return E_FAILED
    end
    return E_OK
end

function bios_service:set_bios_version(version, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]set_bios_version:system id(%s) invalid.', system_id)
        return
    end
    obj:set_bios_version(version)
end

function bios_service:set_bios_prop(prop, value, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]set_bios_prop:(%s)system id(%s) type(%s) invalid.',
            prop, system_id, type(system_id))
        return false
    end
    if prop == nil then
        log:error('bios_service:set_bios_prop system id(%s) prop (%s) is null.', system_id, prop)
        return false
    end

    return obj:set_prop(prop, value)
end


function bios_service:set_prop(prop, value, system_id)
    return self:set_bios_prop(prop, value, system_id)
end

function bios_service:set_spi_channel_direction(system_id, is_bak)
    if self.spi_channel then
        -- 为了兼容单host，上树对象实际使用备host，此处需要减一变成主bios通道
        local direction = self:get_prop('Slot', system_id)
        direction = is_bak and direction + 1 or direction  -- 从片对应instance需加1
        log:notice('system %s switch spi channel direction to %s', system_id, direction)
        self.spi_channel.Switch = direction
    end
end

function bios_service:get_bios_prop(prop, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:notice('bios_service:get bios prop(%s) system id(%s) type(%s) invalid.',
            prop, system_id, type(system_id))
        return nil
    end
    return obj:get_prop(prop)
end


function bios_service:get_prop(prop, system_id)
    return self:get_bios_prop(prop, system_id)
end

function bios_service:get_manu_id()
    -- DB 07 00 -> 00 07 DB
    local manu_id = 0xDB0700
    local bin_id = s_pack('I3', manu_id)
    local res = s_unpack('>I3', bin_id)
    return res
end

function bios_service:get_bios_id()
    return 0
end

function bios_service:get_system_id(ctx)
    if not ctx then
        return 1
    end
    return ctx.HostId or 1
end

function bios_service:update_bios_version(req, ctx)
    local resp = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('bios_service:update_bios_version fail, system id(%s) invalid.', system_id)
        return msg.SetBiosVersionRsp.new(comp_code.InvalidFieldRequest, resp)
    end

    local ret = bios_service.judge_req(req)
    if ret == E_FAILED then
        return msg.SetBiosVersionRsp.new(comp_code.InvalidFieldRequest, resp)
    end
    local manu_id = self:get_manu_id()
    ret = bios_service.judge_manu_id_valid(req, manu_id)
    if ret == E_FAILED then
        return msg.SetBiosVersionRsp.new(comp_code.InvalidCommand, resp)
    end

    ret = bios_service.judge_version_valid(req)
    if ret == E_FAILED then
        return msg.SetBiosVersionRsp.new(comp_code.InvalidCommand, resp)
    end
    obj:set_bios_version(req.Version)
    log:notice('set bios version to %s successfully, system is %s',
        req.Version, system_id)
    ipmi.ipmi_operation_log(ctx, 'BIOS', "Set BIOS version to %s successfully", req.Version)
    pcall(function()
        local upgrade_ser = bios_factory.get_service('upgrade_service')
        upgrade_ser:clear_slave_upgrade(system_id)
    end)
    return msg.SetBiosVersionRsp.new(comp_code.Success, resp)
end

function bios_service:update_teeos_version(req, ctx)
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(self:get_system_id(ctx))
    if not obj then
        log:error('bios_service:update teeos or cca info fail, system id(%s) invalid.', system_id)
        return msg.SetTeeOSVersionRsp.new(comp_code.InvalidFieldRequest, MANUFACTURE_ID)
    end
    if req.Type ~= TEEOS_TYPE and req.Type ~= CCA_TYPE then
        log:error('system %s set teeos or cca info to fail, invalid type(%s)', system_id, req.Type)
        return msg.SetTeeOSVersionRsp.new(comp_code.InvalidFieldRequest, MANUFACTURE_ID)
    end
    if not req.Version or #req.Version ~= req.Length then
        log:error('system %s set teeos or cca info to fail, invalid length(%s)', system_id, req.Length)
        return msg.SetTeeOSVersionRsp.new(comp_code.InvalidFieldRequest, MANUFACTURE_ID)
    end
    -- 当type=1时更新CCA版本号
    if req.Type == CCA_TYPE then
        obj:set_cca_version(req.Version)
        log:notice('system %s set cca info to %s successfully', system_id, req.Version)
        ipmi.ipmi_operation_log(ctx, 'BIOS', "Set CCA version to %s successfully", req.Version)
        return msg.SetTeeOSVersionRsp.new(comp_code.Success, MANUFACTURE_ID)
    end
    -- 当type=0时更新teeos版本号
    obj:set_teeos_version(req.Version)
    log:notice('system %s set teeos version to %s successfully', system_id, req.Version)
    ipmi.ipmi_operation_log(ctx, 'BIOS', "Set TeeOS version to %s successfully", req.Version)
    return msg.SetTeeOSVersionRsp.new(comp_code.Success, MANUFACTURE_ID)
end

function bios_service:get_current_value_setting(system_id, key)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('bios_service:get_current_value_setting fail, system id(%s) invalid.', system_id)
        error(base_messages.InternalError())
    end

    return obj:get_current_value_setting(key)
end

function bios_service:get_rd_buf(system_id)
    if self.bios_rd_data_opt[system_id] then
        return self.bios_rd_data_opt[system_id]
    end
    local obj = file_object.new()
    obj:set_flag(prop_def.BIOS_FLAG_IDLE)
    self.bios_rd_data_opt[system_id] = obj
    return obj
end

function bios_service:get_wr_buf(system_id)
    if self.bios_wr_data_opt[system_id] then
        return self.bios_wr_data_opt[system_id]
    end
    local obj = file_object.new()
    obj:set_flag(prop_def.BIOS_FLAG_IDLE)
    obj:set_file_size_limit(prop_def.BIOS_FILE_MAX_SIZE)
    self.bios_wr_data_opt[system_id] = obj
    return obj
end

function bios_service:get_upgrade_wr_buf()
    return self.upg_fw_data_opt
end

function bios_service:get_data_opt(file_selector, system_id)
    local data_opt, file_size_limit
    if file_selector == prop_def.BIOS_FILE_FIRMWARE then
        data_opt = self:get_upgrade_wr_buf()
        file_size_limit = prop_def.MAX_FILE_SIZE_90M
    else
        data_opt = self:get_wr_buf(system_id)
        file_size_limit = prop_def.BIOS_FILE_MAX_SIZE
    end

    return data_opt, file_size_limit
end

function bios_service:bios_write_prepare(req, ctx, obj)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    if not req then
        log:error('system %s bios_write_prepare:request_msg is null!', system_id)
        local data = msg.WriteFileToBmcRsp.new(
            bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
        return data
    end

    if not obj:check_config() then
        log:error('[bios]system %s bios write prepare:check config fail', system_id)
        return msg.WriteFileToBmcRsp.new(
            bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
    end
    local src_data
    local bios_file_len = 0
    local data_opt, file_size_limit = self:get_data_opt(req.FileSelector, system_id)

    data_opt:set_flag(prop_def.BIOS_FLAG_DOING)
    data_opt:set_offset(0)

    if #req.Srcdata >= prop_def.BIOS_DATA_OFFSET_POS then
        src_data = bs_util.get_bios_wr_data(req.Srcdata)
        bios_file_len = src_data.OffsetToWrite -- prepare阶段 OffsetToWrite 字段表示文件大小
    end

    if bios_file_len > file_size_limit then
        log:error('system %s FileLen:%u(Max:%u) is invalid!', system_id, bios_file_len, file_size_limit)
        data_opt:set_flag(prop_def.BIOS_FLAG_IDLE)
        local data = msg.WriteFileToBmcRsp.new(
            bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
        return data
    end

    data_opt:set_len(bios_file_len)
    data_opt:set_buf('')
    data_opt:init_checksum()

    log:debug('FileLen:%u(Max:%u)!', bios_file_len, prop_def.BIOS_FILE_MAX_SIZE)

    if req.FileSelector == prop_def.BIOS_FILE_FIRMWARE then
        return self:write_prepare_firmware_data_to_bmc(ctx) -- prepare阶段
    end

    local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id,
        '')
    ipmi.ipmi_operation_log(ctx, 'BIOS', 'BIOS write file prepare successfully')
    return data
end

function bios_service:write_data(req, info, data_opt, system_id, ctx)
    local manu_id = self:get_manu_id()

    local bios_offset = info.OffsetToWrite
    local offset = data_opt:get_offset()
    if offset > bios_offset then
        log:notice('write_data: DataOffset:%u(ShouldBe:%u), system id is %s, ignore.',
            bios_offset, offset, system_id)
        local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(),
            manu_id, '')
        return data
    end
    if offset < bios_offset then
        log:error('write_data: DataOffset:%u(ShouldBe:%u) is invalid, system id is %s.',
            bios_offset, offset, system_id)
        local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_WRONG_OFFSET:value(),
            manu_id, tostring(bs_util.resolve_data(4, offset)))
        return data
    end

    local file_data = info.FileData
    local chk_sum = bs_util.calc_checksum(file_data)
    local req_chk_sum = info.DataChecksum
    if req_chk_sum ~= chk_sum then
        log:error('write_data: CheckSum(CalcSum:%d,RxSum:%d) is error, system id is %s.',
            chk_sum, req_chk_sum, system_id)
        local data = msg.WriteFileToBmcRsp.new(
            bios_enum.BiosErrCode.BIOS_ERR_INVALID_CHKSUM:value(), manu_id, '')
        return data
    end

    local data_real_len = #info.FileData
    log:debug('Offset:%d,DataLen:%d!', offset, data_real_len)

    if not data_opt:get_buf() then
        log:error('write_data: The data_addr of GlobalVarible(g_bios_wr_data_cfg) is null! system id is %s', system_id)
        local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_FIELD:value(),
            manu_id, '')
        return data
    end

    if req.FileSelector == prop_def.BIOS_FILE_FIRMWARE then
        return self:write_firmware_data_to_bmc(req, info, data_real_len, ctx)
    end

    data_opt:set_buf(data_opt:get_buf() .. file_data)
    data_opt:set_offset(data_opt:get_offset() + data_real_len)
    data_opt:add_checksum(req_chk_sum)
    local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id,
        '')
    return data
end

function bios_service:bios_write_data(req, ctx)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    if not req then
        log:error('bios_write_data: system %s Param pointer is null!', system_id)
        local data = msg.WriteFileToBmcRsp.new(
            bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
        return data
    end

    local src_data = req.Srcdata
    if (not src_data) or (#src_data < 5) then
        log:error('bios_write_data: system %s src_data is NULL!', system_id)
        local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value(),
            manu_id, '')
        return data
    end

    local data_opt = self:get_data_opt(req.FileSelector, system_id)

    if data_opt:get_flag() ~= prop_def.BIOS_FLAG_DOING then
        log:error('bios_write_data: system %s DataFlag:%u(ShouldBe:%u) is invalid!', system_id, data_opt:get_flag(),
            prop_def.BIOS_FLAG_DOING)
        local data = msg.WriteFileToBmcRsp.new(
            bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
        return data
    end

    local res_data = bs_util.get_bios_wr_data(req.Srcdata)
    local data_real_len = #res_data.FileData

    if data_real_len > prop_def.BIOS_MAXDATA_LEN or data_real_len == 0 or
        (data_opt:get_offset() + data_real_len) > data_opt:get_len() then
        log:error('bios_write_data: system %s DataRealLen:%u(Max:%u,DataOffset:%u,DataLen:%u) is invalid!', system_id,
            data_real_len, prop_def.BIOS_MAXDATA_LEN, data_opt:get_offset(), data_opt:get_len())
        local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value(),
            manu_id, '')
        return data
    end

    return self:write_data(req, res_data, data_opt, system_id, ctx)
end

function bios_service:bios_registry_version_update(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        return
    end
    obj:update_registry_version()
end

function bios_service:bios_result_postprocess(obj)
    if not obj then
        return
    end
    obj:result_postprocess()
end

function bios_service:clear_data(file_selector, data_opt, obj)
    if file_selector == prop_def.BIOS_FILE_CURRENT_VALUE_NUM then
        obj:bios_dynamic_config_sync()
    end

    data_opt:clear_data_operate()
end

function bios_service:deal_selector(file_selector, obj, system_id)
    local change_flag = obj:get_prop('FileChangeFlag')
    if file_selector == prop_def.BIOS_FILE_SMBIOS_RAS then
        return
    elseif file_selector == prop_def.BIOS_FILE_RESULT_NUM then
        obj:result_postprocess()
    elseif file_selector == prop_def.BIOS_FILE_REGISTRY_NUM then
        obj:deal_registry()
    elseif file_selector == prop_def.BIOS_FILE_CURRENT_VALUE_NUM and change_flag ==
        prop_def.BIOS_SETTING_FILE_UNCHANGED then
        obj:set_effective()
        prop_global.G_BIOS_SETTING_FILE_STATE = prop_def.BIOS_SETTING_FILE_EFFECTIVE
    end

    if file_selector == prop_def.BIOS_FILE_CURRENT_VALUE_NUM then
        local boot_ser = bios_factory.get_service('boot_service')
        boot_ser:update_order_from_file(system_id, 'currentvalue.json')
        obj:deal_current()
    end
end

function bios_service:write_empty_buf(valid, req_chk_sum, manu_id, file_selector)
    if req_chk_sum ~= 0 then
        log:error('[bios]bios write empty: checksum is invalid, selector(%s)', file_selector)
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_CHKSUM:value(),
            manu_id, '')
    end
    if not valid then
        log:error('[bios]bios write empty: selector is invalid, selector(%s)', file_selector)
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end

    return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id, '')
end

function bios_service:bios_write_finish(req, ctx, obj)
    local manu_id = self:get_manu_id()
    local file_selector = req.FileSelector
    local system_id = self:get_system_id(ctx)
    local data_opt = self:get_data_opt(file_selector, system_id)

    if not req.Srcdata or #req.Srcdata < 1 then
        self:clear_data(file_selector, data_opt, obj)
        log:error('bios_write_finish: system %s src_data is empty!', system_id)
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value(), manu_id, '')
    end

    -- 处理hpm升级包
    if req.FileSelector == prop_def.BIOS_FILE_FIRMWARE then
        return self:write_finish_firmware_data_to_bmc(req, ctx) -- finish阶段不需要写数据
    end

    local info = bs_util.get_finish_tail(req.Srcdata)
    local req_chk_sum = info.DataChecksum
    local valid = bs_util.judge_write_file_selector_valid(file_selector)

    local data_buf = data_opt:get_buf()
    if not data_buf then
        return self:write_empty_buf(valid, req_chk_sum, manu_id, file_selector)
    end

    local chk_sum = data_opt:get_checksum()
    if req_chk_sum ~= chk_sum then
        log:error('bios_write_finish: system %s write file(%s), CheckSum(CalcSum:%d,RxSum:%d) is error!',
            system_id, get_file_name(file_selector + 1), chk_sum, req_chk_sum)
        self:clear_data(file_selector, data_opt, obj)
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_CHKSUM:value(),
            manu_id, '')
    end

    if valid == false then
        self:clear_data(file_selector, data_opt, obj)
        log:error('bios_write_finish: system %s judge_write_file_selector_valid file_selector:%d fail',
            system_id, file_selector)
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end

    local strategy = file_op_strategy.get_instance()
    local err_code = strategy:write_data(obj, file_selector, data_opt)
    if err_code ~= bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value() then
        self:clear_data(file_selector, data_opt, obj)
        return msg.WriteFileToBmcRsp.new(err_code, manu_id, '')
    end

    skynet.fork_once(function()
        skynet.sleep(10)
        self:deal_selector(file_selector, obj, system_id)
    end)
    self:clear_data(file_selector, data_opt, obj)

    ipmi.ipmi_operation_log(ctx, 'BIOS', 'BIOS write file[resource_id:%x] finish successfully', file_selector)
    log:notice('[bios] system %s bios write file(%s) success', system_id, get_file_name(file_selector + 1))
    return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id, '')
end

function bios_service:default_write()
    local data = msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
        self:get_manu_id(), '')
    return data
end

function bios_service:write_file(req, ctx, obj)
    local operation = req.Operation
    if operation == bios_enum.BiosWriteStage.BIOS_WRITE_PREPARE:value() then
        return self:bios_write_prepare(req, ctx, obj)
    elseif operation == bios_enum.BiosWriteStage.BIOS_WRITE_DATA:value() then
        return self:bios_write_data(req, ctx)
    elseif operation == bios_enum.BiosWriteStage.BIOS_WRITE_FINISH:value() then
        return self:bios_write_finish(req, ctx, obj)
    else
        return self:default_write()
    end
end

function bios_service:bios_write_file_to_bmc(req, ctx)
    local manu_id = self:get_manu_id()

    local obj = self:get_obj(self:get_system_id(ctx))
    if not obj then
        log:error('[bios]bios_write_file_to_bmc sysId(%s) invalid', self:get_system_id(ctx))
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end

    local bios_id = self:get_bios_id()

    local err_code = bs_util.validate(req, bios_id, manu_id, self.multihost)
    if err_code then
        local data = msg.WriteFileToBmcRsp.new(err_code, manu_id, '')
        return data
    end

    return self:write_file(req, ctx, obj)
end

local function get_silk_config_header()
    local header_obj = {}
    header_obj[prop_def.BIOS_SILK_JSON_HEADER_TYPE] = prop_def.BIOS_SILK_JSON_HEADER_TYPE_STR
    header_obj[prop_def.BIOS_SILK_JSON_HEADER_VERSION] = prop_def.BIOS_SILK_JSON_HEADER_VER_VALUE
    return header_obj
end

local function convert_device_type_to_string(device_type)
    if device_type == prop_def.COMPONENT_TYPE_HARDDISK then
        return prop_def.COMPONENT_NVME_STR -- "NVMe"
    elseif device_type == prop_def.COMPONENT_TYPE_PCIE_CARD then
        return prop_def.COMPONENT_PCIE_STR -- "PCIe"
    elseif device_type == prop_def.COMPONENT_TYPE_OCP then
        return prop_def.COMPONENT_OCP_STR -- "OCP"
    else
        return
    end
end

local function get_pcie_silk_node(obj, pcie_obj_list, nic_obj_list)
    if not obj then
        return
    end
    local node_obj = {}
    if obj[obj_def.PCIE_ADDR_INFO_COMPONENT_TYPE]:value() == prop_def.COMPONENT_TYPE_NIC_CARD then
        node_obj[prop_def.BIOS_SILK_JSON_SOCKET_ID] = obj[obj_def.PCIE_ADDR_INFO_SOCKET_ID]:value()
        node_obj[prop_def.BIOS_SILK_JSON_SLOT_ID] = obj[obj_def.PCIE_ADDR_INFO_SLOT_ID]:value()
        node_obj[prop_def.BIOS_SILK_JSON_SILK] = ''
        table.insert(nic_obj_list, node_obj)
    else
        local pcie_type_string = convert_device_type_to_string(
            obj[obj_def.PCIE_ADDR_INFO_COMPONENT_TYPE]:value())
        if not pcie_type_string then -- 只生成NVMe、PCIe、OCP设备的丝印信息
            return
        end

        if pcie_type_string == prop_def.COMPONENT_PCIE_STR and obj[obj_def.PCIE_ADDR_INFO_SLOT_TYPE]:value() == 'OEM' then
            return
        end

        -- 如果是默认值，就不放入丝印文件里。
        if obj[obj_def.PCIE_ADDR_INFO_DEVICE_PORT_ID]:value() == DEFAULT_PORT_ID then
            return
        end
        -- 分别获取PcieAddrInfo属性Segment、SocketID、Device、SlotID的值
        node_obj[prop_def.BIOS_SILK_JSON_SEGMENT] = obj[obj_def.PCIE_ADDR_INFO_SEGMENT]:value()
        node_obj[prop_def.BIOS_SILK_JSON_SOCKET_ID] = obj[obj_def.PCIE_ADDR_INFO_SOCKET_ID]:value()
        node_obj[prop_def.BIOS_SILK_JSON_ROOT_DEVICE_ID] =
            obj[obj_def.PCIE_ADDR_INFO_DEVICE_PORT_ID]:value()
        node_obj[prop_def.BIOS_SILK_JSON_SLOT_ID] = obj[obj_def.PCIE_ADDR_INFO_SLOT_ID]:value()
        node_obj[prop_def.BIOS_SILK_JSON_DEVICE_TYPE] = pcie_type_string
        node_obj[prop_def.BIOS_SILK_JSON_SILK] = ''
        table.insert(pcie_obj_list, node_obj)
        log:notice('PCIeCard PCIeAddrInfo: segment=%s, port_id=%s, socket_id=%s, slot=%s',
            obj[obj_def.PCIE_ADDR_INFO_SEGMENT]:value(), obj[obj_def.PCIE_ADDR_INFO_DEVICE_PORT_ID]:value(),
            obj[obj_def.PCIE_ADDR_INFO_SOCKET_ID]:value(),
            obj[obj_def.PCIE_ADDR_INFO_SLOT_ID]:value())
    end
end

-- 兼容multihost场景，判断是否是当前system_id的卡
local function check_pcie_card_silk(obj, system_id, multihost_card_config)
    if not multihost_card_config or not next(multihost_card_config) then
        return true
    end
    local pcie_type_string = convert_device_type_to_string(
        obj[obj_def.PCIE_ADDR_INFO_COMPONENT_TYPE]:value())
    if not pcie_type_string then -- 只生成NVMe、PCIe、OCP设备的丝印信息
        return
    end

    if pcie_type_string == prop_def.COMPONENT_PCIE_STR and obj[obj_def.PCIE_ADDR_INFO_SLOT_TYPE]:value() == 'OEM' then
        return
    end

    -- 如果是默认值，就不放入丝印文件里。
    if obj[obj_def.PCIE_ADDR_INFO_DEVICE_PORT_ID]:value() == DEFAULT_PORT_ID then
        return
    end
    -- 分别获取PcieAddrInfo属性 Bus、Device、Function、SlotID的值
    local bus_num = obj[obj_def.PCIE_ADDR_INFO_BUS]:value()
    local device_num = obj[obj_def.PCIE_ADDR_INFO_DEVICE]:value()
    local func_num = obj[obj_def.PCIE_ADDR_INFO_FUNCTION]:value()
    local slot_id = obj[obj_def.PCIE_ADDR_INFO_SLOT_ID]:value()
    local str_key = string.format('%s_%s_%s_%s', slot_id, bus_num, device_num, func_num)
    log:notice('PCIeCard PCIeAddrInfo: system %s slot=%s, bus=%s, device=%s, func=%s',
        system_id, slot_id, bus_num, device_num, func_num)
    if multihost_card_config[str_key] and not multihost_card_config[str_key][system_id] then
        return false
    end
    return true
end

local function get_obj_counts(obj_list)
    local cnt = 0
    if not obj_list then
        return cnt
    end
    for _ in pairs(obj_list) do
        cnt = cnt + 1
    end
    return cnt
end

local function update_pcie_nic_obj_array(object_service, system_id, path, multihost_card_config, pcie_obj_array,
    nic_obj_array, obj_list)
    log:notice('get_pcie_silk_config: PCIeAddrInfo count=%s, system id(%s)', get_obj_counts(obj_list), system_id)
    for _, obj in pairs(obj_list) do
        if check_pcie_card_silk(obj, system_id, multihost_card_config) then
            get_pcie_silk_node(obj, pcie_obj_array, nic_obj_array)
        end
    end
end

local function get_pcie_silk_config(system_id, multihost_card_config, obj)
    local pcie_obj_array, nic_obj_array = {}, {}
    local object_service = bios_factory.get_service('object_service')
    if not object_service then
        return pcie_obj_array, nic_obj_array
    end
    local path = string.format('/bmc/kepler/Systems/%s/PCIeDevices/PcieAddrInfo', system_id)
    update_pcie_nic_obj_array(object_service, system_id, path, multihost_card_config, pcie_obj_array,
        nic_obj_array, obj:get_pcie_info())

    if system_id ~= 1 and multihost_card_config and next(multihost_card_config) then
        path = '/bmc/kepler/Systems/1/PCIeDevices/PcieAddrInfo'
        update_pcie_nic_obj_array(object_service, system_id, path, multihost_card_config, pcie_obj_array,
            nic_obj_array, obj:get_sas_info())
    end

    return pcie_obj_array, nic_obj_array
end

function bios_service:get_mem_silk(system_id)
    return self.mem_cache_collection[system_id or 1]
end

function bios_service:cache_mem_silk(system_id, object)
    local mem_silks = self:get_mem_silk(system_id)
    if not mem_silks then
        mem_silks = {}
        self.mem_cache_collection[system_id or 1] = mem_silks
    end
    table.insert(mem_silks, object)
end

function bios_service:add_multihost_memsilk_config(system_id, object)
    local obj = self:get_obj(system_id)
    if obj then
        obj:add_memsilk(object)
    else
        self:cache_mem_silk(system_id, object)
    end
end

function bios_service:get_disk_silk_config(system_id, obj)
    local object_service = bios_factory.get_service('object_service')
    if not object_service then
        return {}
    end

    -- 获取SAS pcieaddrinfo对象
    local obj_list = obj:get_sas_info()
    local disk_obj_list = {} 
    for _, obj in pairs(obj_list) do
        local node_obj = {}
        if obj[obj_def.PCIE_ADDR_INFO_CONTROLLER_TYPE]:value() == obj_def.PCIE_ADDR_INFO_CONTROLLER_TYPE_SAS or 
            obj[obj_def.PCIE_ADDR_INFO_CONTROLLER_TYPE]:value() == obj_def.PCIE_ADDR_INFO_CONTROLLER_TYPE_SATA then
            node_obj[prop_def.BIOS_SILK_JSON_SOCKET_ID] = obj[obj_def.PCIE_ADDR_INFO_SOCKET_ID]:value()
            node_obj[prop_def.BIOS_SIKL_JSON_CONTROL_ID] = 
                obj_def.controller_type_to_control_id[obj[obj_def.PCIE_ADDR_INFO_CONTROLLER_TYPE]:value()]
            node_obj[prop_def.BIOS_SIKL_JSON_PHY_ID] = obj[obj_def.PCIE_ADDR_INFO_DEVICE_PORT_ID]:value()
            node_obj[prop_def.BIOS_SILK_JSON_SLOT_ID] = obj[obj_def.PCIE_ADDR_INFO_SLOT_ID]:value()
            -- 拼接RootBDF
            local ok, val = pcall(string.format, '0000:%02x:%02x.%01x', obj[obj_def.PCIE_ADDR_INFO_BUS]:value(),
                obj[obj_def.PCIE_ADDR_INFO_DEVICE]:value(), obj[obj_def.PCIE_ADDR_INFO_FUNCTION]:value())
            if ok then
                node_obj[prop_def.BIOS_SILK_JSON_ROOT_BDF] = val
            end
            table.insert(disk_obj_list, node_obj)
            log:notice('Disk PCIeAddrInfo: socket_id=%s, control_id=%s, phy_id=%s, slot=%s, root_bdf=%s',
                node_obj[prop_def.BIOS_SILK_JSON_SOCKET_ID], node_obj[prop_def.BIOS_SIKL_JSON_CONTROL_ID],
                node_obj[prop_def.BIOS_SIKL_JSON_PHY_ID], node_obj[prop_def.BIOS_SILK_JSON_SLOT_ID],
                node_obj[prop_def.BIOS_SILK_JSON_ROOT_BDF])
        end
    end
    return disk_obj_list
end

local function prepare_silk_config(data_opt, cached_mem_info, disk_info, system_id, obj, 
        multihost_card_config, is_multihost)
    local silk_path = obj:get_prop('SilkPath')
    local silk_back_path = obj:get_prop('SilkBakPath')
    -- 删除旧的silkconfig.json文件
    utils.remove_file(silk_path)

    local file_obj = {}
    file_obj[prop_def.BIOS_SILK_JSON_FILE_HEADER] = get_silk_config_header()
    file_obj[prop_def.BIOS_SILK_JSON_MEM_TITLE] = cached_mem_info
    file_obj[prop_def.BIOS_SILK_JSON_PCIE_TITLE], file_obj[prop_def.BIOS_SILK_JSON_NIC_TITLE] =
        get_pcie_silk_config(system_id, multihost_card_config, obj)
    file_obj[prop_def.BIOS_SILK_JSON_DISK_TITLE] = disk_info
    file_obj[prop_def.BIOS_SILK_JSON_CPU_TITLE] = obj:get_cpu_info()
    local file_string = json.encode(file_obj)
    local silk_file, err = file_sec.open_s(silk_path, 'w')
    if not silk_file then
        log:error('system %s write silkconfig.json faild. %s, err: %s', system_id, silk_path, err)
        return prop_def.E_ERR
    end

    silk_file:write(file_string)
    silk_file:close()
    utils_core.chmod_s(silk_path, utils.S_IRUSR) -- 修改权限为400
    utils_core.chmod_s(silk_back_path, utils.S_IRUSR) -- 修改权限为400
    -- 更新文件状态记录
    data_opt:set_len(#file_string)
    data_opt:set_buf(file_string)
    data_opt:set_offset(0)
    return E_OK
end

-- 获取read prepare的ipmi命令返回值
local function get_rd_prepare_resp(string_len)
    local format = bs.new('<<Reserved, DataLen:4/unit:8>>')
    local format_data = {Reserved = prop_def.BIOS_FILE_MORE_DATA, DataLen = string_len}
    local resp = format:pack(format_data)
    return resp
end

local function check_import_crl_string(cert_string)
    if string.len(cert_string) > prop_def.CERT_MAX_SIZE then
        return prop_def.SECUREBOOT_METHOD_STRING_TOO_LONG
    end
    local ret = cert_core.verify_pem_crl(cert_string)
    if ret == -1 then
        log:error("invalid crl format")
        return prop_def.SECUREBOOT_METHOD_PEM_FORMAT_ERR
    end
    return prop_def.SECUREBOOT_METHOD_OK
end

local function check_import_cert_string(cert_string)
    if string.len(cert_string) > prop_def.CERT_MAX_SIZE then
        return prop_def.SECUREBOOT_METHOD_STRING_TOO_LONG
    end

    local ok, info = pcall(function()
        return cert_core.get_cert_info_from_mem(cert_string)
    end)
    if not ok then
        log:error("invalid certificate format")
        return prop_def.SECUREBOOT_METHOD_PEM_FORMAT_ERR
    end
    local not_after = info['ValidNotAfter']
    local not_before = info['ValidNotBefore']
    local now = os.time()
    if not_after < now or not_before > now then
        log:error("certificate expired")
        return prop_def.SECUREBOOT_METHOD_EXPIRED_CERT
    end
    return prop_def.SECUREBOOT_METHOD_OK
end

local function check_one_cer(cer_info, check_cert_string_mode)
    if get_obj_counts(cer_info) ~= 4 or cer_info[prop_def.INTERACT_KEY_CERTIFICATE_TYPE] ~= "PEM" or
 	    not cer_info[prop_def.INTERACT_KEY_CERTIFICATE_STRING] or not cer_info[prop_def.INTERACT_KEY_SIZE] then
            return prop_def.SECUREBOOT_METHOD_ERR
    end

    if #cer_info[prop_def.INTERACT_KEY_CERTIFICATE_STRING] ~= cer_info[prop_def.INTERACT_KEY_SIZE] then
        return  prop_def.SECUREBOOT_METHOD_ERR
    end

    if check_cert_string_mode == prop_def.INTERACT_KEY_CERTIFICATES then
        return check_import_cert_string(cer_info[prop_def.INTERACT_KEY_CERTIFICATE_STRING])
    elseif check_cert_string_mode == prop_def.INTERACT_KEY_CRL then
        return check_import_crl_string(cer_info[prop_def.INTERACT_KEY_CERTIFICATE_STRING])
    end
end

local function verify_cer_format(cers_info, obj, check_cert_string_mode)
    if not cers_info or obj.cer_info_recover_flag then
        return
    end
    for _, cer_info in pairs(cers_info) do
        if cer_info[prop_def.INTERACT_KEY_OPERATION] == prop_def.CERT_OPERATION_ADD then
            local ret = check_one_cer(cer_info, check_cert_string_mode)
            if ret == prop_def.SECUREBOOT_METHOD_OK then
                goto continue
            end
        elseif cer_info[prop_def.INTERACT_KEY_OPERATION] == prop_def.CERT_OPERATION_DELETE or
            cer_info[prop_def.INTERACT_KEY_OPERATION] == prop_def.CERT_OPERATION_RESET then
            if get_obj_counts(cer_info) == 1 then
                goto continue
            end
        end
        obj.cer_info_recover_flag = true
        break
        ::continue::
    end
end

local function verify_secure_boot_file(cer_data, obj)
    if #cer_data == 0 or not obj then
        return
    end
    obj.cer_info_recover_flag = false
    local ok = pcall(function ()
        local cer_json = json.decode(cer_data)
        verify_cer_format(cer_json[prop_def.INTERACT_KEY_SECUREBOOT][prop_def.INTERACT_KEY_SECUREBOOT_DATABASES]
            [prop_def.INTERACT_KEY_SECUREBOOT_DB][prop_def.INTERACT_KEY_CERTIFICATES], obj, prop_def.INTERACT_KEY_CERTIFICATES)
        verify_cer_format(cer_json[prop_def.INTERACT_KEY_SECUREBOOT][prop_def.INTERACT_KEY_SECUREBOOT_DATABASES]
            [prop_def.INTERACT_KEY_SECUREBOOT_DBX][prop_def.INTERACT_KEY_CERTIFICATES], obj, prop_def.INTERACT_KEY_CERTIFICATES)
        verify_cer_format(cer_json[prop_def.INTERACT_KEY_BOOT][prop_def.INTERACT_KEY_CERTIFICATES], obj, prop_def.INTERACT_KEY_CERTIFICATES)
        verify_cer_format(cer_json[prop_def.INTERACT_KEY_BOOT][prop_def.INTERACT_KEY_CRL], obj, prop_def.INTERACT_KEY_CRL)
    end)
    if not ok then
        obj.cer_info_recover_flag = true
    end
end

local function read_file_prepare(file_selector, file_path, data_opt)
    if not file_path or file_path == '' then
        log:error('read_file_prepare: file path is invalid.')
        return bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value()
    end
    local file, err = file_sec.open_s(file_path, 'rb')
    if not file then
        log:error('read_file_prepare: open %s file failed. err: %s', file_selector, err)
        return bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value()
    end
    local file_string = file:read('*a')
    file:close()
    if file_string == nil then
        log:error('read_file_prepare: file string is null.')
        return bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value()
    end
    data_opt:set_buf(file_string)
    data_opt:set_len(#file_string)
    data_opt:set_offset(0)
    return bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value()
end

local function get_secure_file(obj)
    local file, err = file_sec.open_s(obj:get_prop('SecureBootNewFile'), 'rb')
    if not file then
        log:error('[bios] system %s read_file_prepare: open new_secure_boot file failed. err: %s', obj.system_id, err)
        return bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value(), nil
    end
    local new_secureboot_data = file:read('*a')
    file:close()
    if new_secureboot_data == nil then
        log:error('[bios] system %s read_file_prepare: file string is null.', obj.system_id)
        return bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value(), nil
    end
    local secure_boot_options_ser = bios_factory.get_service('secure_boot_options_service')
    local new_secureboot_bak_data, sec_options_obj
    if not secure_boot_options_ser then
        log:error('[bios] system %s secure_boot_options_service is nil', obj.system_id)
    else
        sec_options_obj = secure_boot_options_ser:get_obj(obj.system_id)
        if sec_options_obj and sec_options_obj.cert_db_obj then
            new_secureboot_bak_data = sec_options_obj.cert_db_obj[prop_def.INTERACT_KEY_CERTIFICATE_INFO]
        end
    end
    log:notice("[bios] system %s start verify SecureBootNewFile", obj.system_id)
    verify_secure_boot_file(new_secureboot_data, sec_options_obj)

    if sec_options_obj and sec_options_obj.cer_info_recover_flag and new_secureboot_bak_data then
        log:notice("[bios] system %s SecureBootNewFile format is wrong, report backup certinfo to bios", obj.system_id)
        return bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), new_secureboot_bak_data
    end
    if not sec_options_obj or not sec_options_obj.cer_info_recover_flag or not new_secureboot_bak_data then
        log:notice("[bios] system %s SecureBootNewFile is correct or certificate backup is bull, report raw file to bios", obj.system_id)
        return bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), new_secureboot_data
    end
end

function bios_service:prepare_secure_file(data_opt, obj)
    local completion_code, new_secureboot_data = get_secure_file(obj)
    if completion_code ~= bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value() then
        return msg.ReadFileFromBmcRsp.new(completion_code, self:get_manu_id(), '')
    end
    data_opt:set_buf(new_secureboot_data)
    data_opt:set_len(#new_secureboot_data)
    data_opt:set_offset(0)
    local resp = get_rd_prepare_resp(data_opt:get_len())
    log:notice('[bios] system %s start report new secureboot file.', obj.system_id)
    return msg.ReadFileFromBmcRsp.new(completion_code, self:get_manu_id(), resp)
end

function bios_service:prepare_psu_file(data_opt, obj, system_id)
    local manu_id = self:get_manu_id()
    local ok, file_string = pcall(function()
        return generate_file.generate_psu_file(obj:get_prop('PsuInfoFileName'), system_id)
    end)
    if not ok or not file_string then
        log:error('[bios]system %s generate psu_info faild.', system_id)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end
    -- 更新文件状态记录
    data_opt:set_len(#file_string)
    data_opt:set_buf(file_string)
    data_opt:set_offset(0)
    local resp = get_rd_prepare_resp(data_opt:get_len())
    log:info('[bios]generate psu_info success.')
    return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id, resp)
end

local function get_file_selector_valie(req)
    local valid = bs_util.judge_read_file_selector_valid(req.FileSelector)
    if req.FileSelector == prop_def.BIOS_FILE_DISPLAY_NUM or req.FileSelector ==
        prop_def.BIOS_FILE_OPTION_NUM or req.FileSelector == prop_def.BIOS_FILE_CHANGE_NUM or
        req.FileSelector == prop_def.BIOS_FILE_CLP then
        valid = false
    end
    return valid
end

local function running_err(system_id, obj)
    pcall(function()
        log:running(log.RLOG_INFO, "host%s prepare mem silk num %s", system_id, #obj.mem_silk_array)
    end)
end

-- 准备bios所需多帧读取数据
function bios_service:bios_read_prepare(req, ctx, obj)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    if not req then
        log:error('bios_read_prepare: system %s request_msg is null!', system_id)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end

    local buf = self:get_rd_buf(system_id)
    buf:set_flag(prop_def.BIOS_FLAG_DOING)

    local valid = get_file_selector_valie(req)
    if not valid then
        log:error('bios_read_prepare: system %s judge_read_file_selector_valid file_selector:%d fail',
            system_id, req.FileSelector)
        buf:set_flag(prop_def.BIOS_FLAG_IDLE)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end

    -- 0x0C、0x2B处理流程
    if req.FileSelector == prop_def.BIOS_FILE_SETUP_NUM then
        buf:set_flag(prop_def.BIOS_FLAG_IDLE)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end

    if req.FileSelector == prop_def.BIOS_FILE_NEW_SECUREBOOT_NUM then
        return self:prepare_secure_file(buf, obj)
    end

    if req.FileSelector == prop_def.BIOS_FILE_PSUINFO_NUM then
        return self:prepare_psu_file(buf, obj, system_id)
    end

    -- 0x2A 处理流程
    if req.FileSelector == prop_def.BIOS_FILE_SILK_CONFIG_NUM then
        local disk_info = self:get_disk_silk_config(system_id, obj)
        if prepare_silk_config(buf, obj:get_mem_silk_array(), disk_info, system_id, obj,
            self.multihost_card_config, self:is_multihost()) ~= E_OK then
            log:error('system %s prepare pcie silk failed.', system_id)
            buf:set_flag(prop_def.BIOS_FLAG_IDLE) -- 重置标志位
            return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
                manu_id, '')
        end
        running_err(system_id, obj)
        local resp = get_rd_prepare_resp(buf:get_len())
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id,
            resp)
    end

    local completion_code = read_file_prepare(req.FileSelector, obj:get_prop(
        prop_global.G_FILE_PROPERTY_NAME[req.FileSelector + 1]), buf)
    local resp = get_rd_prepare_resp(buf:get_len())
    if completion_code ~= bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value() then
        resp = ''
    end
    return msg.ReadFileFromBmcRsp.new(completion_code, manu_id, resp)
end

function bios_service:bios_read_data_check(req, buf)
    local manu_id = self:get_manu_id()
    if buf:get_flag() ~= prop_def.BIOS_FLAG_DOING then
        log:error('bios_read_data:DataFlag:%d(ShouldBe:%d) is invalid!',
            buf:get_flag(), prop_def.BIOS_FLAG_DOING)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end

    if not req then
        log:error('[bios]bios_read_data:request_msg is null!')
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end
    -- 长度不应小于偏移量(4字节) + 读取长度（1字节）
    if #req.Srcdata < 5 then
        log:error('[bios]bios_read_data:Invalid Srcdata length:%d', #req.Srcdata)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value(), manu_id, '')
    end
end

-- bios多帧读取数据
function bios_service:bios_read_data(req, ctx)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    local buf = self:get_rd_buf(system_id)
    local check = self:bios_read_data_check(req, buf)
    if check then
        return check
    end
    local read_operate = bs_util.get_bios_rd_data(string.sub(req.Srcdata, 1, 5)) -- 解包二进制获取偏移量和读取长度
    if read_operate.OffsetFromRead == 0 and buf:get_len() == 0 then -- 文件为空
        local file_buffer = string.char(prop_def.BIOS_FILE_NO_MORE_DATA) .. string.char(0)
        log:info('[bios]bios_read_data:FileOffset:0 ReadLen:0 CheckSum:0!')
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id,
            file_buffer)
    end
    if (read_operate.OffsetFromRead >= buf:get_len()) or
        ((read_operate.ReadLen + 6) > 248) then
        log:error('[bios]bios_read_data:system %s FileOffset:%d or ReadLen:%d is invalid!',
            system_id, read_operate.OffsetFromRead, (read_operate.ReadLen + 6))
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end
    local reserved = prop_def.BIOS_FILE_MORE_DATA
    -- 读取长度超过文件长度
    if read_operate.OffsetFromRead + read_operate.ReadLen >= buf:get_len() then
        -- 重新计算读取长度
        read_operate.ReadLen = buf:get_len() - read_operate.OffsetFromRead
        reserved = prop_def.BIOS_FILE_NO_MORE_DATA
    end

    if (read_operate.ReadLen + 5) > 512 then
        log:error('system %s Response data buffer length is insufficient : %d %d!',
            system_id, 512, read_operate.ReadLen)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end
    -- ipmi命令中偏移量是从0开始，但是lua中字符串下标从1开始，实际偏移量 = offset + 1
    local read_data = string.sub(buf:get_buf(), read_operate.OffsetFromRead + 1,
        read_operate.OffsetFromRead + read_operate.ReadLen)
    if read_data == '' then
        log:error('system %s get data string failed.', system_id)
    end
    local check_sum = bs_util.calc_checksum(read_data) -- 计算本次传输数据累加和
    -- 读取完成标志位 + 文本内容 + 校验累加合
    local file_buffer = string.char(reserved) .. read_data .. string.char(check_sum)
    log:debug('FileOffset:%d ReadLen:%d CheckSum:%d!', read_operate.OffsetFromRead,
        read_operate.ReadLen, check_sum)
    return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id,
        file_buffer)
end

-- 获取read finish的ipmi命令返回值
local function get_rd_finish_resp(check_sum)
    local format = bs.new('<<Reserved, CheckSum>>')
    local format_data = {Reserved = prop_def.BIOS_FILE_NO_MORE_DATA, CheckSum = check_sum}
    local resp = format:pack(format_data)
    return resp
end

function bios_service:bios_read_finish(req, ctx, obj)
    local manu_id = self:get_manu_id()
    local check_sum = 0
    local system_id = self:get_system_id(ctx)
    local buf = self:get_rd_buf(system_id)
    if buf:get_buf() then
        check_sum = bs_util.calc_checksum(buf:get_buf())
        buf:clear_data_operate()
    end
    if not req then
        log:error('bios_write_prepare:system %s request_msg is null!', system_id)
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            manu_id, '')
    end
    -- 0x2A
    if req.FileSelector == prop_def.BIOS_FILE_SILK_CONFIG_NUM then
        local silk_path = obj:get_prop('SilkPath')
        local silk_back_path = obj:get_prop('SilkBakPath')
        utils.remove_file(silk_back_path)
        file_sec.copy_file_s(silk_path, silk_back_path)
        utils_core.chmod_s(silk_back_path, utils.S_IRUSR) -- 修改权限为400
    end
    log:notice('[bios] system %s read file(%s) success', system_id, req.FileSelector)
    local resp = get_rd_finish_resp(check_sum) -- 将默认返回值和校验和打包为二进制串
    return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id, resp)
end

function bios_service:default_read()
    local data = msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
        self:get_manu_id(), '')
    return data
end

function bios_service:read_file(req, ctx, obj)
    local operation = req.Operation
    if operation == bios_enum.BiosReadStage.BIOS_READ_PREPARE:value() then
        -- read prepare
        return self:bios_read_prepare(req, ctx, obj)
    elseif operation == bios_enum.BiosReadStage.BIOS_READ_DATA:value() then
        -- read data
        return self:bios_read_data(req, ctx)
    elseif operation == bios_enum.BiosReadStage.BIOS_READ_FINISH:value() then
        -- read finish
        return self:bios_read_finish(req, ctx, obj)
    else
        return self:default_read()
    end
end

function bios_service:bios_read_file_from_bmc(req, ctx)
    local obj = self:get_obj(self:get_system_id(ctx))
    if not obj then
        log:error('[bios]bios_read_file_from_bmc: system id(invalid)', self:get_system_id(ctx))
        return msg.ReadFileFromBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(),
            self:get_manu_id(), '')
    end
    local bios_id = self:get_bios_id()
    local manu_id = self:get_manu_id()
    -- 校验制造商ID和BiosID，现在Bios只支持一个，所以默认0x00
    local err_code = bs_util.validate(req, bios_id, manu_id, self.multihost)
    if err_code then
        local data = msg.ReadFileFromBmcRsp.new(err_code, manu_id, '')
        return data
    end
    -- ipmi执行函数
    return self:read_file(req, ctx, obj)
end

function bios_service:bios_get_file_changed(req, ctx)
    local bios_id = self:get_bios_id()
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    -- 校验制造商ID和BiosID，现在Bios只支持一个，所以默认0x00
    local err_code = bs_util.get_file_changed_validate(req, bios_id, manu_id, self.multihost)
    if err_code then
        local data = msg.GetFileChangedRsp.new(err_code, manu_id, '')
        return data
    end
    if req.FileSelector == prop_def.BIOS_FILE_SILK_CONFIG_NUM then
        local changed = string.char(0x01) .. string.char(0x00)
        log:error('[bios] get setting silk file change flag')
        return msg.GetFileChangedRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id,
            changed)
    end
    -- ipmi执行函数
    local setting_change = self:get_file_change(system_id)
    local changed = string.char(setting_change) .. string.char(0x00)
    log:notice('[bios]system %s get setting file(%s) change flag(%s)', system_id, req.FileSelector, setting_change)
    return msg.GetFileChangedRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id,
        changed)
end

-- 单元测试 BEGIN
bios_service.get_pcie_silk_config = get_pcie_silk_config
bios_service.get_rd_prepare_resp = get_rd_prepare_resp
bios_service.get_rd_finish_resp = get_rd_finish_resp
bios_service.prepare_silk_config = prepare_silk_config
bios_service.get_pcie_silk_node = get_pcie_silk_node
bios_service.check_pcie_card_silk = check_pcie_card_silk
-- 单元测试 END

-- 清除CMOS，使BIOS恢复默认配置
function bios_service:clear_cmos(ctx, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]clear_cmos: system id invalid(%s) fail.', system_id)
        bs_util.record_operation(ctx, system_id,
            'Failed to issue the command for restoring BIOS Setup default settings')
        error(base_messages.InternalError())
    end

    local ret = obj:set_prop(obj_def.RESET_CMOS_PROP, boot_def.BMC_ENABLE)
    if not ret then
        log:error('[bios]clear_cmos:system %s set_bios_prop fail.',system_id)
        bs_util.record_operation(ctx, system_id,
            'Failed to issue the command for restoring BIOS Setup default settings')
        error(base_messages.InternalError())
    end

    ret = obj:set_prop(prop_def.RESET_BIOS_TO_DEFAULTS_PENDING, true)
    if not ret then
        log:error('Set prop ResetBiosToDefaultsPending failed')
    end
    bs_util.record_operation(ctx, system_id, 'Restoring BIOS Setup default settings issued successfully ' ..
            'and the BIOS default settings will be restored upon the next startup')
    return prop_def.RESPONSE_OK
end

function bios_service:set_file_change(changed, channel, num, sysId)
    local obj = self:get_obj(sysId)
    if not obj then
        log:error('[bios]set file change sysId(%s) invalid', sysId)
        return
    end

    obj:set_file_change(changed, channel, num)
end

-- 清空配置
function bios_service:reset_bios(ctx, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]reset_bios: system id invalid(%s) fail.', system_id)
        error(base_messages.InternalError())
    end
    local setting_path = obj:get_prop(prop_method.BIOS_FILE_SETTING_NAME)
    if vos.get_file_accessible(setting_path) then
        local res = bs_util.clear_file(setting_path)
        if not res then
            log:error('[bios]reset_bios: system %s clear_file fail.', system_id)
            error(base_messages.InternalError())
        end
        obj:set_file_change(prop_def.BIOS_SETTING_FILE_UNCHANGED, 0)
    end

    return self:clear_cmos(ctx, system_id)
end

local function clear_config_setting(obj)
    local ok, res = pcall(function()
        obj:clear_setting()
    end)
    if not ok then
        log:error('[bios]clear setting fail, err %s', res)
    end
end

-- 清空待生效配置
function bios_service:remove_cache_setting(ctx, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]remove_cache_setting: system id invalid(%s) fail.', system_id)
        error(base_messages.InternalError())
    end
    local setting_path = obj:get_prop(prop_method.BIOS_FILE_SETTING_NAME)
    if not vos.get_file_accessible(setting_path) then
        return
    end

    local res = bs_util.clear_file(setting_path)
    if not res then
        log:error('[bios]remove_cache_setting: system %s clear_file fail.', system_id)
        bs_util.record_operation(ctx, system_id, 'Remove Cached BIOS Setup settings fail')
        error(base_messages.InternalError())
    end
    obj:set_file_change(prop_def.BIOS_SETTING_FILE_UNCHANGED, 0)
    clear_config_setting(obj)
    bs_util.record_operation(ctx, system_id, 'Remove Cached BIOS Setup settings success')
    return prop_def.RESPONSE_OK
end

local function deal_password_error(err_code, type)
    local err_map = {
        [prop_def.RESPONSE_ERROR] = base_messages.InternalError(),
        [prop_def.METHOD_BIOS_CHANGE_PWD_UNSUPPORTED] =
            custom_messages.InvalidValue(type, 'PasswordName')
    }
    if err_map[err_code] then
        error(err_map[err_code])
    end
    error(base_messages.InternalError())
end

-- 是否包含小写字符
local function isconlows(str)
    return string.match(str, '%l')
end

-- 是否包含大写字符
local function isconsups(str)
    return string.match(str, '%u')
end

-- 是否包含数字
local function iscondigs(str)
    return string.match(str, '%d')
end

-- 是否包含特殊字符
local function isconspcs(str)
    return string.match(str, '[^%w]')
end

function bios_service:check_bios_pwd_complexity_suc(pwd)

    -- 长度校验失败
    if #pwd < prop_def.BIOS_PWD_COMPLEXITYLEN_MIN or #pwd > prop_def.BIOS_PWD_COMPLEXITYLEN_MAX then
        log:error('[bios]reset_bios_password: password len is invalid.')
        return false
    end

    -- 没有包含特殊字符直接返回错误
    if not isconspcs(pwd) then
        log:error('[bios]reset_bios_password: The pwd does not contain special characters.')
        return false
    end

    -- 任意包含两种 复杂度校验通过 
    if (isconlows(pwd) and isconsups(pwd)) or (isconsups(pwd) and iscondigs(pwd)) or
        (iscondigs(pwd) and isconlows(pwd)) then
        return true
    end
    log:error('[bios]reset_bios_password: The pwd does not contain two or more types of characters.')
    return false
end

function bios_service:reset_bios_password(ctx, type, old_pwd, new_pwd, system_id)
    local obj = self:get_obj(system_id)
    if not type or not old_pwd or not new_pwd or not obj then
        log:error('[bios]reset_bios_password: get param failed, system id is %s', system_id)
        bs_util.record_operation(ctx, system_id, string.format(
            'Failed to deliver the request for changing BIOS %s password', type))
        return prop_def.RESPONSE_ERROR
    end
    -- 旧密码保持原有逻辑
    if #old_pwd > prop_def.BIOS_PWD_LEN_MAX then
        log:error('[bios]reset_bios_password: password len is invalid, system id is %s', system_id)
        bs_util.record_operation(ctx, system_id, string.format(
            'Failed to deliver the request for changing BIOS %s password', type))
        error(custom_messages.InvalidPasswordLength('0', '32'))
    end

    -- 新密码校验规则
    if not self:check_bios_pwd_complexity_suc(new_pwd) then
        log:error('[bios]reset_bios_password: password complexity is invalid, system id is %s', system_id)
        bs_util.record_operation(ctx, system_id, string.format(
            'Failed to deliver the request for changing BIOS %s password', type))
        error(custom_messages.PasswordComplexityCheckFail())
    end

    local res = obj:save_encrypt_password(type, old_pwd, new_pwd)
    if res == prop_def.RESPONSE_OK then
        bs_util.record_operation(ctx, system_id, string.format(
            'The request for changing BIOS %s password is delivered', type))
    else
        bs_util.record_operation(ctx, system_id, string.format(
            'Failed to deliver the request for changing BIOS %s password', type))
        deal_password_error(res, type)
    end
    return res
end

function bios_service:response_ipmi_status(complete_code)
    local manu_id = self:get_manu_id()
    return msg.UpdateBiosStatusRsp.new(complete_code, manu_id)
end

-- ipmi:密码
local function response_ipmi_password(complete_code, res_data)
    local bin_data = ''
    if res_data ~= '' then
        local bin_manu_id = s_pack('I3', res_data.ManufactureId)
        bin_data = bin_manu_id .. res_data.Information
    end

    return msg.UpdateBiosPasswordRsp.new(complete_code, bin_data)
end

function bios_service:deal_password(req, ctx)
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]deal_password: system id(%s) invalid.', system_id)
        return response_ipmi_password(comp_code.CommandNotAvailable, '')
    end
    local selector = req.ParameterSelector
    if selector == prop_def.BIOS_CHANGE_PWD_SELECTOR_GET then
        return obj:get_setting_password(req, ctx)
    elseif selector == prop_def.BIOS_CHANGE_PWD_SELECTOR_ACK then
        return obj:ack_setting_password(req, ctx)
    else
        log:error('[bios]deal_password: system %s selector[%u] is out of range.', system_id, selector)
        return response_ipmi_password(comp_code.ParmOutOfRange, '')
    end
end

-- ipmi:密码
function bios_service:update_bios_password(req, ctx)
    -- 限制带内通道
    local res = bs_util.filter_msg_by_sys_channel(ctx.chan_num)
    if not res then
        log:error('[bios]update_bios_password: channel num(%s) invalid', ctx.chan_num)
        return response_ipmi_password(comp_code.CommandNotAvailable, '')
    end
    local manu_id = self:get_manu_id()
    local err_code = bs_util.judge_manu_id_valid(req, manu_id)
    if err_code ~= E_OK then
        log:error('[bios]update_bios_password: check request manufacture ID failed.')
        return response_ipmi_password(comp_code.InvalidFieldRequest, '')
    end

    if req.PasswordType > 1 then
        log:error('[bios]update_bios_password: password type[%u] is not supported.',
            req.PasswordType)
        return response_ipmi_password(comp_code.DataNotAvailable, '')
    end

    return self:deal_password(req, ctx)
end

-- ipmi:cmos
function bios_service:update_bios_status(req, ctx)
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update_bios_status: system id(%s) invalid.', system_id)
        return self:response_ipmi_status(comp_code.CommandNotAvailable)
    end

    local manu_id = self:get_manu_id()
    local err_code = bs_util.judge_manu_id_valid(req, manu_id)
    local status = req.Status
    if err_code ~= E_OK then
        log:error('[bios]update_bios_status: system id(%s) check request manufacture ID failed.', system_id)
        return self:response_ipmi_status(comp_code.InvalidFieldRequest)
    end

    if not status then
        log:error('[bios]update_bios_status: system id(%s) not status field.', system_id)
        return self:response_ipmi_status(comp_code.InvalidFieldRequest)
    end

    if status == prop_def.BIOS_LOAD_DEFAULT_CONF then
        local res = obj:set_prop(obj_def.RESET_CMOS_PROP, boot_def.BMC_DISABLE)
        if not res then
            log:error('[bios]update_bios_status: system id(%s) set prop cmos fail.', system_id)
            ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set BIOS status failed')
            return self:response_ipmi_status(comp_code.InvalidFieldRequest)
        end

        res = obj:set_prop(prop_def.RESET_BIOS_TO_DEFAULTS_PENDING, false)
        if not res then
            log:error('[bios]update_bios_status: system id(%s) set prop ResetBiosToDefaultsPending fail', system_id)
        end

        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set BIOS status to (default config) successfully')
    end

    return self:response_ipmi_status(comp_code.Success)
end

-- ipmi:获取bios属性
function bios_service:get_bios_boot_info(req, ctx)
    local manu_id = self:get_manu_id()
    local bios_id = self:get_bios_id()
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]get_bios_boot_info: system id(%s) invalid.', system_id)
        msg.GetBiosBootInfoRsp.new(comp_code.CommandNotAvailable, manu_id, 0, '')
    end

    local err_code = bs_util.judge_manu_id_valid(req, manu_id)
    if err_code ~= E_OK or not req.BiosId or (req.BiosId > bios_id and not self.multihost) then
        log:error('[bios]get_bios_boot_info: system %s param invalid.', system_id)
        return msg.GetBiosBootInfoRsp.new(comp_code.InvalidFieldRequest, manu_id, 0, '')
    end

    local prop = req.PropOption
    if not prop then
        log:error('[bios]get_bios_boot_info: system %s property invalid.', system_id)
        return msg.GetBiosBootInfoRsp.new(comp_code.InvalidFieldRequest, manu_id, 0, '')
    end
    local val = prop_hander.get(prop, self:get_system_id(ctx))
    if val == nil or type(val) ~= 'string' then
        log:error('[bios]get_bios_boot_info: system %s get val fail.', system_id)
        return msg.GetBiosBootInfoRsp.new(comp_code.InvalidFieldRequest, manu_id, 0, '')
    end
    return msg.GetBiosBootInfoRsp.new(comp_code.Success, manu_id, #val, val)
end

-- ipmi:设置bios属性
function bios_service:set_bios_boot_info(req, ctx)
    local manu_id = self:get_manu_id()
    local bios_id = self:get_bios_id()
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]set_bios_boot_info: system id(%s) invalid.', system_id)
        msg.SetBiosBootInfoRsp.new(comp_code.CommandNotAvailable, manu_id)
    end

    local err_code = bs_util.judge_manu_id_valid(req, manu_id)
    if err_code ~= E_OK or not req.BiosId or (req.BiosId > bios_id and not self.multihost) then
        log:error('[bios]set_bios_boot_info: system %s param invalid.', system_id)
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set BIOS info failed')
        return msg.SetBiosBootInfoRsp.new(comp_code.InvalidFieldRequest, manu_id)
    end

    local prop = req.PropOption
    local len = req.PropLen
    local val = req.Information
    if not val or not len or not prop or len ~= #val then
        log:error('[bios]set_bios_boot_info: system %s prop len invalid.', system_id)
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set BIOS info failed')
        return msg.SetBiosBootInfoRsp.new(comp_code.ReqDataLenInvalid, manu_id)
    end

    err_code = prop_hander.set(prop, val, ctx)
    if not err_code then
        log:error('[bios]set_bios_boot_info: system %s set prop failed.', system_id)
        return msg.SetSmBiosInfoRsp.new(comp_code.InvalidFieldRequest, manu_id)
    end

    return msg.SetSmBiosInfoRsp.new(comp_code.Success, manu_id)
end

local function get_system_id(path)
    return tonumber(string.match(path, '/bmc/kepler/Systems/(%d+)'))
end

function bios_service:update_watch_dog_status(system_id)
    -- 写ipmi属性
    local objects = client:GetWatchdog2StatusObjects()
    for _, obj in pairs(objects) do
        if get_system_id(obj.path) == system_id then
            obj.SystemStartupState = prop_def.BIOS_STARTUP_POST_STAGE_FINISH
            break
        end
    end
end

function bios_service:secure_boot_alarm(verify_result)
    if not verify_result then
        log:error('[bios]alert secure boot failed.')
        self:set_prop('RecoverFailed', bios_defs.PfrVerify.Failed)
    else
        log:notice('[bios]secure boot success.')
        self:set_prop('RecoverFailed', bios_defs.PfrVerify.Success)
    end
end

function bios_service:check_secure_boot_verify()
    local secure_boot_enable_completion_code = self:get_prop(obj_def.SECURE_BOOT_ENABLE_COMPLETION_CODE)
    local secure_boot_enable_state = self:get_prop(obj_def.SECURE_BOOT_ENABLE_STATE)
    local secure_boot_verify_completion_code = self:get_prop(obj_def.SECURE_BOOT_VERIFY_COMPLETION_CODE)
    local secure_boot_verify_state = self:get_prop(obj_def.SECURE_BOOT_VERIFY_STATE)
    log:notice('[GoldPackage]check bios secure boot status, secure_boot_enable_completion_code: %s,' ..
        'secure_boot_enable_state: %s, secure_boot_verify_completion_code: %s, secure_boot_verify_state: %s',
        secure_boot_enable_completion_code, secure_boot_enable_state,
        secure_boot_verify_completion_code, secure_boot_verify_state)
    if secure_boot_enable_completion_code == obj_def.BOOT_GUARD['CompletionCode']['UnCompleted'] or
        secure_boot_verify_completion_code == obj_def.BOOT_GUARD['CompletionCode']['UnCompleted'] then
        log:notice('secure boot not to enable')
        return true
    end
    if secure_boot_enable_completion_code == obj_def.BOOT_GUARD['CompletionCode']['Completed'] and
        secure_boot_verify_completion_code == obj_def.BOOT_GUARD['CompletionCode']['Completed'] then
        if secure_boot_enable_state ~= obj_def.BOOT_GUARD['SecureBootEnableState']['Enable'] then
            log:notice('secure boot verify not support or not enable')
            return true
        elseif secure_boot_verify_state == obj_def.BOOT_GUARD['SecureBootVerifyState']['Success'] then
            return true
        elseif secure_boot_verify_state == obj_def.BOOT_GUARD['SecureBootVerifyState']['Failed'] then
            log:notice('secure boot failed, need upgrade from gold')
            return false
        end
    end
    return true
end

function bios_service:update_post_status(req, ctx)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update_bios_status: system id(%s) invalid.', system_id)
        return comp_code.CommandNotAvailable, manu_id
    end

    local ret = bios_service.judge_manu_id_valid(req, manu_id)
    if ret == E_FAILED then
        return comp_code.InvalidFieldRequest, manu_id
    end

    local info = req.Info
    if info == nil or #info == 0 then
        log:error('[bios]update post status: system %s data len invalid!', system_id)
        return comp_code.ReqDataLenInvalid, manu_id
    end
    local status = s_unpack('I1', string.sub(info, 1, 1))
    if status == prop_def.BIOS_REPORT_POST_FINISH_FLAG then
        obj:set_prop(prop_def.SYSTEM_STARTUP_STATE, prop_def.BIOS_STARTUP_POST_STAGE_FINISH)
        log:notice('[bios]update post status: system %s bios already post finish!', system_id)
        pcall(function()
            log:notice('[bios]system %s start fetch component version info', system_id)
            self:async_fetch_info(system_id)
            self:update_watch_dog_status(system_id)
        end)
        return comp_code.Success, manu_id
    else
        log:error('[bios]update post status: system %s bios update post state failed,' ..
                      ' bios post status is %u', system_id, status)
        return comp_code.InvalidCommand, manu_id
    end
end

function bios_service:ipmi_set_base_os_boot_event(req, ctx)
    local manu_id = self:get_manu_id()
    if not self.mdb_os_boot then
        log:error('os boot obj is nil')
        return comp_code.CommandNotAvailable, manu_id
    end
    local asserted = req.EventDir == 0
    if req.DeviceStatus == 6 then
        -- 判断当前事件是否已经assert，如果是需要先deassert才能实现连续上报
        if self.mdb_os_boot.EventData == 6 then
            self.mdb_os_boot.EventData = 0xFFFFFFFF
            skynet.sleep(200)
        end
        self.mdb_os_boot.EventData = asserted and req.DeviceStatus or 0xFFFFFFFF
        log:notice('OS Boot data is changed by ipmi to 0x%08X', self.mdb_os_boot.EventData)
    end

    -- 写入启动信息
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]set_base_os_boot: system id(%s) invalid.', system_id)
        return comp_code.CommandNotAvailable, manu_id
    end
    -- 判断当前状态如果已经是post完成阶段，则不进行设置
    local bios_startup_state = obj:get_prop(prop_def.SYSTEM_STARTUP_STATE)
    if bios_startup_state ~= prop_def.BIOS_STARTUP_POST_STAGE_FINISH then
        obj:set_prop(prop_def.SYSTEM_STARTUP_STATE, prop_def.BIOS_STARTUP_PXE_START)
        log:notice('[bios]update post status: bios start pxe!')
    end
    return ipmi_req.SetBaseOSBootEvent.rsp.new(0x00, 0x0007DB)
end

-- 设置文件改变标记的ipmi命令
function bios_service:bios_set_file_changed(req, ctx)
    local bios_id = self:get_bios_id()
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]bios_set_file_changed: system id(%s) invalid.', system_id)
        return comp_code.CommandNotAvailable, manu_id
    end

    local err = bs_util.judge_manu_id_valid(req, manu_id)
    if err ~= E_OK then
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set file changed flag failed')
        log:error('[bios]bios set file changed: system %s judge manu id(%u) fail.', system_id, req.ManufactureId)
        return comp_code.InvalidCommand, manu_id
    end

    err = bs_util.judge_bios_id(req, bios_id, self.multihost)
    if err then
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set file changed flag failed')
        log:error('[bios]bios set file changed: system %s judge bios id(%u) fail.', system_id, req.BiosId)
        return comp_code.CommandNotAvailable, manu_id
    end

    local file_selector = req.FileSelector
    if file_selector > prop_def.BIOS_FILE_MAX_SELECTOR then
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set file changed flag failed')
        log:error('[bios]bios set file changed: system %s judge file selector(%u) fail.', system_id, file_selector)
        return comp_code.CommandNotAvailable, manu_id
    end

    obj:set_file_change(req.ChangedFlag, req.LastChangedChannel, req.BiosId)
    local change_str = 'no changed'
    if req.ChangedFlag == 1 then
        change_str = 'changed'
    end
    ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set BIOS setting file changed flag to (%s) successfully',
        change_str)
    log:notice('[bios] system %s Set bios setting file changed flag to (%s) successfully', system_id, change_str)
    return comp_code.Success, manu_id
end

function bios_service:get_system_firware_version(req, ctx)
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]bios_set_file_changed: system id(%s) invalid.', system_id)
        error(custom_messages.IPMIDestinationUnavailable())
    end

    local rsp = msg.GetSystemFirmwareVersionRsp.new()
    local version = obj:get_bios_version()
    if not version then
        log:error('system %s get bios version failed', system_id)
        error(custom_messages.IPMIDestinationUnavailable())
    end

    rsp.CompletionCode = comp_code.Success
    rsp.Info = version
    return rsp
end

function bios_service:get_cpu_die(die_id)
    local die = nil
    if self.cpu_die and self.cpu_die.IdRange and self.cpu_die.NameRange and
        #self.cpu_die.IdRange == #self.cpu_die.NameRange then
        for i = 1, #self.cpu_die.IdRange do
            if self.cpu_die.IdRange[i] == die_id then
                die = self.cpu_die.NameRange[i]
                break
            end
        end
    else
        die = defs.PowerDie[die_id]
    end
    if not die then
        error('[bios]notify_alarm: invalid info parameters')
    end
    return die
end

function bios_service:get_power_type(type_id)
    local power_type = nil
    if self.vrd_type_map and self.vrd_type_map.IdRange and self.vrd_type_map.NameRange and 
        #self.vrd_type_map.IdRange == #self.vrd_type_map.NameRange then
        for i = 1, #self.vrd_type_map.IdRange do
            if self.vrd_type_map.IdRange[i] == type_id then
                power_type = self.vrd_type_map.NameRange[i]
                break
            end
        end
    else
        power_type = defs.PowerTypeInfo[type_id]
    end
    if not power_type then
        error('[bios]notify_alarm: invalid info parameters')
    end
    return power_type
end

function bios_service:get_cpu_physical_id(logical_id, system_id)
    local obj = self:get_obj(tonumber(system_id))
    if not obj then
        error('[bios]notify_alarm: invalid system_id')
    end
    local cpu_list = obj:get_cpu_info()
    for _, cpu_obj in pairs(cpu_list) do
        if cpu_obj[prop_def.BIOS_SIKL_JSON_LOGICAL_ID] == logical_id then
            return cpu_obj[prop_def.BIOS_SIKL_JSON_PHYSICAL_ID], cpu_obj[prop_def.CPU_DEVICE_LOCATOR]
        end
    end
    error('[bios]notify_alarm: invalid logical_id')
end

function bios_service:combine_alarm_args(device_id, alarm_type, info, system_id)
    local data = nil
    local args = nil
    local type = nil
    if device_id == defs.DeviceId.PSU then
        if alarm_type == 0 then
            if #info < 4 then
                error('[bios]notify_alarm: invalid info length')
            end
            type = defs.AlarmType.PowerLink
            data = bs_util.binary_to_struct(string.sub(info, 1, 4), '<<CpuId, TypeId, DieId, ChannelId>>')
            args = {
                CPU = self:get_cpu_physical_id(data.CpuId, system_id),
                Type = self:get_power_type(data.TypeId),
                Die = self:get_cpu_die(data.DieId),
                Channel = defs.PowerChannel[data.ChannelId]
            }
        elseif alarm_type == 1 then
            if #info < 3 then
                error('[bios]notify_alarm: invalid info length')
            end
            type = defs.AlarmType.MemoryLink
            data = bs_util.binary_to_struct(string.sub(info, 1, 3), '<<CpuId, ChannelId, DIMMId>>')
            args = {DimmIndex = string.format('DIMM%1x%1x%1x', data.CpuId, data.ChannelId, data.DIMMId)}
        end
    elseif device_id == defs.DeviceId.VR then
        if alarm_type == 0 then
            if #info < 3 then
                error('[bios]notify_alarm: invalid info length')
            end
            type = defs.AlarmType.VRAbnormal
            data = bs_util.binary_to_struct(string.sub(info, 1, 3), '<<CpuId, TypeId, DieId>>')
            local die = self:get_cpu_die(data.DieId)
            local power_type = self:get_power_type(data.TypeId)
            local cpu_id, device_locator = self:get_cpu_physical_id(data.CpuId, system_id)
            args = {
                CPU = cpu_id,
                Die = die,
                Type = power_type,
                DeviceLocator = device_locator
            }
        end
    end
    return type, args
end

function bios_service:notify_alarm(device_id, event_type, alarm_type, info, system_id)
    event_type = event_type & 0x80 ~= 0
    log:info('[bios]notify_alarm: alarm_type(%s) event_type(%s).', type, event_type)
    local ok, type, args = pcall(self.combine_alarm_args, self, device_id, alarm_type, info, system_id)
    if not ok then
        return ok, type
    end
    local msg = {
        type = type,
        state = event_type,
        args = args
    }
    return pcall(function()
        local alarm_instance = alarms.new()
        alarm_instance:alarm(msg, system_id)
    end)
end

-- ipmi: 上报告警信息
function bios_service:report_alarm(device_id, req, ctx)
    local manu_id = self:get_manu_id()
    local ret = bios_service.judge_manu_id_valid(req, manu_id)
    if ret ~= E_OK then
        log:error('[bios]report alarm: judge manu id(%u) fail.', req.ManufactureId)
        return comp_code.InvalidCommand, manu_id
    end

    local event_type = req.EventType
    local alarm_type = req.AlarmType
    if not event_type or not alarm_type then
        log:error('[bios]report alarm: command invalid!')
        return comp_code.CommandNotAvailable, manu_id
    end

    local info = req.Info
    if info == nil or #info == 0 then
        log:error('[bios]report alarm: data invalid!')
        return comp_code.DataNotAvailable, manu_id
    end
    local ok, err
    if self:is_multihost() then
        local system_id = tostring(bs_util.get_system_id(ctx))
        ok, err = self:notify_alarm(device_id, event_type, alarm_type, info, system_id)
    else
        ok, err = self:notify_alarm(device_id, event_type, alarm_type, info)
    end

    if not ok then
        log:info('[bios]report alarm: fail(%s).', err)
        return comp_code.DataNotAvailable, manu_id
    else
        log:info('[bios]report alarm: success')
        return comp_code.Success, manu_id
    end
end

local function get_target_detail(log_info)
    if not log_info then
        return nil, nil
    end

    local log_target, log_detail, event_code = string.match(log_info, '(.+),(.+),(.+)')
    if not log_target or not log_detail or not event_code then
        return nil, nil
    end

    local target_tail = string.match(log_info, ',(.+)')
    local target_len = #log_info - #target_tail
    local target = string.sub(log_info, 1, target_len - 1)
    local detail = string.sub(log_info, target_len + 1)
    return target, detail
end

function bios_service:add_bios_log_entry(req, ctx)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]bios_set_file_changed: system id(%s) invalid.', system_id)
        return comp_code.CommandNotAvailable, manu_id
    end

    local err = bs_util.judge_manu_id_valid(req, manu_id)
    if err ~= E_OK then
        log:error('[bios]add bios log entry: system %s judge manu id(%u) fail.', system_id, req.ManufactureId)
        return comp_code.InvalidCommand, manu_id
    end

    local event_type = req.EventType
    -- 由操作对象，事件详情和事件码构成
    -- 格式:Network, Set Mgnmt Eth IPv4 address (192.168.0.2) success, EvtCode: 0x02120640
    local log_info = req.Log
    local target, details = get_target_detail(log_info)
    if not target or not details then
        log:error('[bios]add bios log entry: system %s log detail format invalid.', system_id)
        return comp_code.InvalidCommand, manu_id
    end

    if event_type == prop_def.IMANA_LOG_OPERATION then
        ipmi.ipmi_operation_log(ctx, 'BIOS', details)
    elseif event_type == prop_def.IMANA_LOG_SECURITY then
        log:security('%s,%s,%s', 'Security', target, details)
    else
        log:error('[bios]add bios log entry: system %s event type(%u) invalid.', system_id, event_type)
        return comp_code.InvalidFieldRequest, manu_id
    end
    return comp_code.Success, manu_id
end

function bios_service:set_systems_state(ctx, state, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]set_systems_state: system id(%s) invalid.', system_id)
        bs_util.record_operation(ctx, system_id, 'Set system startup state failed')
        error(base_messages.InternalError())
    end
    if state == nil then
        log:error('[bios]set systems state: system %s invalid param', system_id)
        bs_util.record_operation(ctx, system_id, 'Set system startup state failed')
        error(base_messages.InternalError())
    end

    local res = obj:set_prop('SystemStartupState', state)
    if not res then
        log:error('[bios]set systems state: system %s set prop fail', system_id)
        bs_util.record_operation(ctx, system_id, 'Set system startup state failed')
        error(base_messages.InternalError())
    end

    bs_util.record_operation(ctx, system_id, string.format('Set system startup state(%d) successfully', state))
end

local print_flag_tbl<const> = {[prop_def.ENABLED] = 'Enabled', [prop_def.DISABLED] = 'Disabled'}

local print_flag_log_tbl<const> = {
    [prop_def.ENABLED] = 'Set BIOS debug info enable ',
    [prop_def.DISABLED] = 'Set BIOS debug info enabled/disabled by BIOS setup menu '
}

function bios_service:set_print_flag(ctx, flag, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]set_print_flag: system id(%s) invalid.', system_id)
        bs_util.record_operation(ctx, system_id, 'Set BIOS debug info failed')
        error(base_messages.InternalError())
    end
    local flag_str = print_flag_tbl[flag]
    if flag == nil or flag_str == nil then
        log:error('[bios]set print flag: system id(%s) invalid param', system_id)
        bs_util.record_operation(ctx, system_id, 'Set BIOS debug info failed')
        error(base_messages.InternalError())
    end

    local file_ser = file_service.new()
    local content = string.format('{"Attributes": {"SYSDBGLevel": "%s"}}', flag_str)
    local ok, _ = pcall(function()
        file_ser:import_setup_json(ctx, 'Setting', content, system_id)
    end)

    if ok then
        bs_util.record_operation(ctx, system_id, print_flag_log_tbl[flag] .. 'successfully')
    else
        log:error('[bios]set print flag: system id(%s) import fail', system_id)
        bs_util.record_operation(ctx, system_id, print_flag_log_tbl[flag] .. 'failed')
        error(base_messages.InternalError())
    end
end

local function check_fileowner(file_path, user_name)
    local get_uid_result, uid = pcall(utils_core.get_uid_gid_by_name, user_name)
    if not get_uid_result then
        log:error('get uid gid by name failed')
        return false
    end

    local get_file_stat_result, file_stat = pcall(utils_core.stat, file_path)
    if not get_file_stat_result then
        log:error('get file stat failed')
        return false
    end

    return file_stat.st_uid == uid
end

-- 固件升级场景 prepare 阶段
function bios_service:write_prepare_firmware_data_to_bmc(ctx)
    local manu_id = self:get_manu_id()
    local user_name
    if ctx and ctx.session and ctx.session.user and ctx.session.user.name then
        user_name = ctx.session.user.name
    else
        user_name = 'secbox'
    end

    -- 非装备模式下，文件上传，应该要限制不能覆盖已有文件（最多允许覆盖用户自己的文件）,从ipmi接口上传的用户为root
    if not self.dft_mode and vos.get_file_accessible(prop_def.UPLOAD_FW_HPM_NAME) and
        not check_fileowner(prop_def.UPLOAD_FW_HPM_NAME, user_name) then
        log:error("The file already exists and not belong to the operator")
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
    end

    -- prepare操作，清除旧文件
    client:PFileFileDelete(context.new(), nil, prop_def.UPLOAD_FW_HPM_NAME)

    if self.upg_fw_file then
        self.upg_fw_file:close()
        self.upg_fw_file = nil
    end
    log:info('[bios] write_prepare_firmware_data_to_bmc.')
    return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id, '')
end

-- 固件升级场景 write 阶段
function bios_service:write_firmware_data_to_bmc(req, src_data, data_real_len, ctx)
    local manu_id = self:get_manu_id()
    local data_opt = self:get_data_opt(req.FileSelector)
    local user_name
    if ctx and ctx.session and ctx.session.user and ctx.session.user.name then
        user_name = ctx.session.user.name
    else
        user_name = 'secbox'
    end

    -- 非装备模式下，文件上传，应该要限制不能覆盖已有文件（最多允许覆盖用户自己的文件）,从ipmi接口上传的用户为root
    if not self.dft_mode and vos.get_file_accessible(prop_def.UPLOAD_FW_HPM_NAME) and
        not check_fileowner(prop_def.UPLOAD_FW_HPM_NAME, user_name) then
        log:error("The file already exists and not belong to the operator")
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
    end

    if data_real_len <= 0 then
        log:error('write_firmware_data_to_bmc data_real_len (%d)', data_real_len)
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_IV_LEN:value(), manu_id, '')
    end

    if not self.upg_fw_file then
        local err
        if data_opt:get_offset() == 0 then
            -- 第一帧创建文件，其他使用追加
            utils.remove_file(prop_def.UPLOAD_INSIDE_FW_HPM_NAME)
            self.upg_fw_file, err = file_sec.open_s(prop_def.UPLOAD_INSIDE_FW_HPM_NAME, 'w+')
            if not self.upg_fw_file then
                log:error('create image hpm fail, err: %s', err)
                return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value(), manu_id, '')
            end
            utils_core.chmod_s(prop_def.UPLOAD_INSIDE_FW_HPM_NAME,
                utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 修改权限为640
            log:info('[bios] write_firmware_data_to_bmc, open w+ mode.')
        else
            self.upg_fw_file, err = file_sec.open_s(prop_def.UPLOAD_INSIDE_FW_HPM_NAME, 'a+')
            if not self.upg_fw_file then
                log:error('open image hpm fail, offset: (%d), err: %s', data_opt:get_offset(), err)
                return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value(), manu_id, '')
            end
            log:info('[bios] write_firmware_data_to_bmc, open a+ mode.')
        end
    end

    local wr_ret = self.upg_fw_file:write(src_data.FileData)
    if not wr_ret then
        log:error('write data to hpm file failed')
        self.upg_fw_file:close()
        self.upg_fw_file = nil
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_FM_FAIL:value(), manu_id, '')
    end

    data_opt:set_offset(data_opt:get_offset() + data_real_len)
    return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id, '')
end

-- 固件升级场景 finish 阶段
function bios_service:write_finish_firmware_data_to_bmc(req, ctx)
    local manu_id = self:get_manu_id()
    local data_opt = self:get_data_opt(req.FileSelector)
    local user_name
    if ctx and ctx.session and ctx.session.user and ctx.session.user.name then
        user_name = ctx.session.user.name
    else
        user_name = 'secbox'
    end

    -- 非装备模式下，文件上传，应该要限制不能覆盖已有文件（最多允许覆盖用户自己的文件）,从ipmi接口上传的用户为root
    if not self.dft_mode and vos.get_file_accessible(prop_def.UPLOAD_FW_HPM_NAME) and
        not check_fileowner(prop_def.UPLOAD_FW_HPM_NAME, user_name) then
        log:error("The file already exists and not belong to the operator")
        return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_INVALID_STATUS:value(), manu_id, '')
    end

    -- finish操作，关闭文件
    self:clear_data(req.FileSelector, data_opt)
    if self.upg_fw_file then
        self.upg_fw_file:close()
        self.upg_fw_file = nil
    end

    local uid
    local gid = OPERATOR_USER_GID
    if ctx and ctx.session and ctx.session.user and ctx.session.user.name then
        uid, _ = utils_core.get_uid_gid_by_name(ctx.session.user.name)
    else
        uid = SECBOX_USER_UID
        gid = ADMIN_USER_GID
    end
    client:PFileFileChown(context.new(), nil, prop_def.UPLOAD_INSIDE_FW_HPM_NAME, uid, gid) --设置文件属主
    client:PFileFileMove(context.new(), nil, prop_def.UPLOAD_INSIDE_FW_HPM_NAME,
        prop_def.UPLOAD_FW_HPM_NAME, uid, gid)

    log:info('[bios] write_finish_firmware_data_to_bmc.')
    return msg.WriteFileToBmcRsp.new(bios_enum.BiosErrCode.BIOS_ERR_NORMALLY:value(), manu_id, '')
end

function bios_service:get_config_data(file_type, system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]export bios config system id(%s) invalid', system_id)
        error(base_messages.InternalError())
    end
    return obj:get_config_data(file_type)
end

function bios_service:get_componet_bitmap_list(system_id, component_names)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]get componet bitmap list system id(%s) invalid', system_id)
        error(base_messages.InternalError())
    end
    return obj.cpt_manager:get_componet_bitmap_list(component_names)
end

function bios_service:update_user_data(system_id, config_data)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]update user data system id(%s) invalid', system_id)
        error(base_messages.InternalError())
    end
    obj.cpt_manager:update_user_data(config_data)
end

function bios_service:is_online_force(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]get online force system id(%s) invalid', system_id)
        return
    end
    return obj.firmware:is_online_force()
end

function bios_service:get_activate_mode(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]get actvate mode system id(%s) invalid', system_id)
        return
    end
    return obj.firmware:get_activate_mode()
end

function bios_service:clear_activate_mode(system_id)
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]clear actvate mode system id(%s) invalid', system_id)
        return
    end
    return obj.firmware:clear_activate_mode()
end

function bios_service:collect_json_file(path, system_id)
    local obj = self:get_obj(system_id)
    if obj then
        obj:collect_json_file(path)
    end
end

function bios_service:get_dump_info(system_id)
    local obj = self:get_obj(system_id)
    if obj then
        return obj:get_dump_info()
    end
end

function bios_service:create_countdown_task(ctx)
    if self:task_running()then
        self:clear_task()
    end

    -- 保存数据
    self.ctx = ctx

    -- 启动倒计时60s任务，时间到则执行清除数据
    self.count_down_task = tasks.get_instance():timeout_ms(60 * 1000, function()
        self:clear_task()
        log:notice('power status not change, revoke clear cmos')
    end)
end

function bios_service:on_power_state_changed()
    if not self:task_running() then
        log:error('bios_service: exist task running.')
        return
    end

    local ok, res = pcall(self.clear_cmos, self, self.ctx, bios_defs.SINGLE_HOST_SYSTEM_ID)
    if not ok or not res then
        log:error('clear cmos fail. res = %s', res)
    end

    self:clear_task()
    log:notice('[bios_service]bios service task detected power state changed, execute success')
end

function bios_service:task_running()
    return self.count_down_task and self.count_down_task:is_running()
end

function bios_service:clear_task()
    self.ctx = nil
    self.clear_cmos_id = nil
    if self.count_down_task then
        self.count_down_task:cancel()
        self.count_down_task = nil
    end
end

local function get_setting_config(system_id, prop)
    local json_current_path = file_service.get_file_path(prop_method_app.BIOS_FILE_CURRENT_VALUE_NAME, system_id)
    local bios_current_conifg_json = bs_util.get_file_json(json_current_path)
    if not bios_current_conifg_json or not bios_current_conifg_json[prop] then
        log:error('get current value %s failed, system: %s', prop, system_id)
        return
    end

    local json_path = file_service.get_file_path(prop_method_app.BIOS_FILE_SETTING_NAME, system_id)
    local bios_setting_conifg_json = bs_util.get_file_json(json_path)
    if not bios_setting_conifg_json then
        return ''
    end
    return bios_setting_conifg_json[prop] or ''
end

local function get_current_config(system_id, prop)
    local json_path = file_service.get_file_path(prop_method_app.BIOS_FILE_CURRENT_VALUE_NAME, system_id)
    local bios_current_conifg_json = bs_util.get_file_json(json_path)
    if not bios_current_conifg_json then
        log:error('get current value %s failed, system: %s', prop, system_id)
        return
    end
    return bios_current_conifg_json[prop]
end

local function get_num_rsp(config_value)
    local result = ''
    local length = 1
    local value = ''
    while config_value // BIT_NUMBER ~= 0 do
        length = length + 1
        value = value .. string.char(config_value % BIT_NUMBER)
        config_value = config_value // BIT_NUMBER
    end
    value = value .. string.char(config_value)
    result = result .. string.char(CONFIG_NUM) .. string.char(length) .. value
    return result
end

local BIOS_CONFIGURATION_TYPE<const> = {
    [0] = get_setting_config,
    [1] = get_current_config
}

function bios_service:get_bios_configuration(req, ctx)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    if system_id == bios_defs.SINGLE_HOST_SYSTEM_ID then
        system_id = req.SystemId == 0 and bios_defs.SINGLE_HOST_SYSTEM_ID or req.SystemId
    end
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]get bios config: system id(%s) invalid.', system_id)
        return comp_code.CommandNotAvailable, manu_id, 0, ''
    end

    local err = bs_util.judge_manu_id_valid(req, manu_id)
    if err ~= E_OK then
        log:error('[bios]get bios config: system %s judge manu id(%s) fail.', system_id, req.ManufactureId)
        return comp_code.InvalidCommand, manu_id, 0, ''
    end

    local config_type = req.Type
    local get_config_fun = BIOS_CONFIGURATION_TYPE[config_type]
    if not get_config_fun then
        log:error('[bios] get bios config failed, type %s invalid, system: %s', config_type, system_id)
        return comp_code.ParmOutOfRange, manu_id, 0, ''
    end

    local info = req.Info

    if not info or #info == 0 then
        log:error('[bios]get bios config failed, info invalid, system: %s', system_id)
        return comp_code.ReqDataLenInvalid, manu_id, 0, ''
    end

    local length = string.sub(info, 1, 1):byte()
    if length + 1 ~= #info then
        log:error('[bios]get bios config failed, prop length %s invalid, system: %s', length, system_id)
        return comp_code.ReqDataLenInvalid, manu_id, 0, ''
    end

    local prop = string.sub(info, 2, length + 1)
    local config_value = get_config_fun(system_id, prop)

    if not config_value then
        return comp_code.ParmOutOfRange, manu_id, 0, ''
    end

    local result

    if type(config_value) == 'string' then
        result = ''
        result = result .. string.char(CONFIG_STR)
        result = result .. string.char(#config_value)
        result = result .. config_value
    elseif type(config_value) == 'number' then
        result = get_num_rsp(config_value)
    end

    return comp_code.Success, manu_id, 0, result
end

local function get_req_value(info, prop_length, type, system_id)
    local value, count
    if type == CONFIG_STR then
        value = string.sub(info, prop_length + VALUE_START, #info)
    elseif type == CONFIG_NUM then
        value = 0
        count = 1
        for i = prop_length + VALUE_START, #info, 1 do
            value = value + string.sub(info, i, i):byte() * count
            count = count * BIT_NUMBER
        end
    else
        error(string.format('[bios]set bios config failed, prop type %s invalid, system: %s', type, system_id))
    end
    return value
end

function bios_service:set_bios_configuration(req, ctx)
    local manu_id = self:get_manu_id()
    local system_id = self:get_system_id(ctx)
    if system_id == bios_defs.SINGLE_HOST_SYSTEM_ID then
        system_id = req.SystemId == 0 and bios_defs.SINGLE_HOST_SYSTEM_ID or req.SystemId
    end
    local obj = self:get_obj(system_id)
    if not obj then
        log:error('[bios]bios_set_file_changed: system id(%s) invalid.', system_id)
        return comp_code.CommandNotAvailable, manu_id
    end

    local err = bs_util.judge_manu_id_valid(req, manu_id)
    if err ~= E_OK then
        log:error('[bios]add bios log entry: system %s judge manu id(%s) fail.', system_id, req.ManufactureId)
        return comp_code.InvalidCommand, manu_id
    end

    local info = req.Info

    if not info or #info == 0 then
        log:error('[bios]set bios config failed, info invalid, system: %s', system_id)
        return comp_code.ReqDataLenInvalid, manu_id
    end

    local prop_length = string.sub(info, 1, 1):byte()
    if #info < prop_length + MIN_PROP_LENGTH then
        log:error('[bios]set bios config failed, prop length %s invalid, system: %s', prop_length, system_id)
        return comp_code.ReqDataLenInvalid, manu_id
    end
    local prop = string.sub(info, PROP_START, prop_length + 1)
    local type = string.sub(info, prop_length + PROP_START, prop_length + PROP_START):byte()
    local value_length = string.sub(info, prop_length + VALUE_LENGTH_START, prop_length + VALUE_LENGTH_START):byte()
    if #info ~= prop_length + value_length + VALUE_LENGTH_START then
        log:error('[bios]set bios config failed, value length %s invalid, system: %s', value_length, system_id)
        return comp_code.ReqDataLenInvalid, manu_id
    end
    local ok, value = pcall(function ()
        return get_req_value(info, prop_length, type, system_id)
    end)
    if not ok then
        log:error('[bios]set bios config failed, error: %s', value)
        return comp_code.ParmOutOfRange, manu_id
    end

    return self:import_configuration(prop, value, ctx, system_id, manu_id)
end

function bios_service:import_configuration(prop, value, ctx, system_id, manu_id)
    local import_json = tbl_cache:allocate()
    import_json["Attributes"] = tbl_cache:allocate()
    import_json["Attributes"][prop] = value

    local file_ser = file_service.new()
    file_ser.redfish_import = true
    local ok, res = pcall(function ()
        file_ser:import_setup_json(ctx, "Setting", json.encode(import_json), system_id)
    end)

    tbl_cache:deallocate(import_json["Attributes"])
    tbl_cache:deallocate(import_json)

    if not ok then
        log:error('set bios config failed, prop or value invalid, system: %s, res: %s', system_id, res)
        if self:is_multihost() then
            ipmi.ipmi_operation_log(ctx, 'BIOS', "Failed to set bios %s configuration", system_id)
        else
            ipmi.ipmi_operation_log(ctx, 'BIOS', "Failed to set bios configuration")
        end
        return comp_code.ParmOutOfRange, manu_id
    end

    if self:is_multihost() then
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set bios %s configuration %s to %s successfully ' ..
        'and will take effect upon the next startup', system_id, prop, value)
    else
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set bios configuration %s to %s successfully ' ..
            'and will take effect upon the next startup', prop, value)
    end
    return comp_code.Success, manu_id
end

return singleton(bios_service)
