-- 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.

-- Description: test the external IPMI SDR interfaces of sensor.
local log = require 'mc.logging'
local bs = require 'mc.bitstring'
local ipmi = require 'sensor.ipmi.ipmi'
local cc = require 'sdr.sdr_const'
local common = require 'test_sensor_common'

local ipmi_interface = {}
ipmi_interface.__index = ipmi_interface

function ipmi_interface.test_get_sdr_reserve_id(bus)
    log:info('== Testing ipmi get sdr reserve id ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x22'
    local netfn = 0x0A
    local cmd = 0x22

    local payload = ''
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.GetSDRReserveId.encode):unpack(ret)
    local checks = (rsp.ReserveIdH << 8) | rsp.ReserveIdL
    assert(checks > cc.SDR_RESERVE_ID_INIT, 'actual: ' .. checks)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
end

function ipmi_interface.test_get_sdr(bus)
    log:info('== Testing ipmi get sdr ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x23 <reserve_id> <record_id> <offset> <length>'
    local netfn = 0x0A
    local cmd = 0x23

    local req = ipmi.GetSDR.req.new(0x00, 0x00, 0x01, 0x00, 0x00, 0xFF)
    local payload = bs.new(ipmi.GetSDR.decode):pack(req)
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.GetSDR.encode):unpack(ret)
    assert(rsp.RecordIdL == 2, 'actual: ' .. rsp.RecordIdL)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
end

function ipmi_interface.test_get_device_sdr_info(bus)
    log:info('== Testing get device sdr info ...')

    -- ipmi command: 'ipmitool raw 0x04 0x20'
    local netfn = 0x04
    local cmd = 0x20

    local payload = ''
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.GetDeviceSDRInfo.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
end

function ipmi_interface.test_get_device_sdr(bus)
    log:info('== Testing get device sdr ...')

    -- ipmi command: 'ipmitool raw 0x04 0x21 <reserve_id> <record_id> <offset> <length>'
    local netfn = 0x04
    local cmd = 0x21

    local req = ipmi.GetDeviceSDR.req.new(0x00, 0x00, 0x01, 0x00, 0x00, 0xFF)
    local payload = bs.new(ipmi.GetDeviceSDR.decode):pack(req)
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.GetDeviceSDR.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
    assert(rsp.RecordIdL == 2, 'actual: ' .. rsp.RecordIdL)
    assert(rsp.RecordIdH == 0, 'actual: ' .. rsp.RecordIdH)
end

function ipmi_interface.test_reserve_device_sdr(bus)
    log:info('== Testing reserve device sdr ...')

    -- ipmi command: 'ipmitool raw 0x04 0x22'
    local netfn = 0x04
    local cmd = 0x22

    local payload = ''
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.ReserveDeviceSDR.encode):unpack(ret)
    local checks = (rsp.ReserveIdH << 8) | rsp.ReserveIdL
    assert(checks > cc.SDR_RESERVE_ID_INIT, 'actual: ' .. checks)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
end

function ipmi_interface.test_enter_sdr_repo_update(bus)
    log:info('== Testing enter sdr repository update mode ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x2A'
    local netfn = 0x0A
    local cmd = 0x2A

    local payload = ''
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.EnterSDRRepoUpdate.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
end

function ipmi_interface.test_exit_sdr_repo_update(bus)
    log:info('== Testing exit sdr repository update mode ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x2B'
    local netfn = 0x0A
    local cmd = 0x2B

    local payload = ''
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.ExitSDRRepoUpdate.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
end

function ipmi_interface.test_clear_sdr(bus)
    log:info('== Testing ipmi clear sdr ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x27 <reserve_id> <CLR> <operation>'
    local netfn = 0x0A
    local cmd = 0x27

    local sb = string.byte
    local req = ipmi.ClearSDR.req.new(0x00, 0x00, sb('C'), sb('L'), sb('R'), 0xAA)
    local payload = bs.new(ipmi.ClearSDR.decode):pack(req)
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.ClearSDR.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
    assert(rsp.Status == 1, 'actual: ' .. rsp.Status)
end

function ipmi_interface.test_add_partial_sdr(bus)
    log:info('== Testing ipmi add partial sdr ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x25 <reserve_id> <record_id> <offset> <progress> <datas>'
    -- 添加SDR，完整的SDR记录如下: 前5位是标记，第5位是长度，剩余的是内容，分两次添加
    -- 01 00 51 01 38 20 00 01 ff 00 7f 68 01 01 72 80
    -- 72 80 18 18 80 01 00 00 64 00 00 00 00 e0 01 19
    -- 00 00 7f 80 00 32 2e 00 00 00 02 02 00 00 00 cd
    -- 53 74 75 62 49 6e 6c 65 74 54 65 6d 70
    local netfn = 0x0A
    local cmd = 0x25

    local sc = string.char
    local sdr_parts = {
        {
            sc(0x01),  sc(0x00),  sc(0x51),  sc(0x01),  sc(0x38)
        },
        {
            sc(0x20), sc(0x00), sc(0x01), sc(0xff), sc(0x00), sc(0x7f), sc(0x68),
            sc(0x01), sc(0x01), sc(0x72), sc(0x80), sc(0x72), sc(0x80), sc(0x18),
            sc(0x18), sc(0x80), sc(0x01), sc(0x00), sc(0x00)
        },
        {
            sc(0x64), sc(0x00), sc(0x00), sc(0x00), sc(0x00), sc(0xe0), sc(0x01),
            sc(0x19), sc(0x00), sc(0x00), sc(0x7f), sc(0x80), sc(0x00), sc(0x32),
            sc(0x2e), sc(0x00), sc(0x00), sc(0x00), sc(0x02)
        },
        {
            sc(0x02), sc(0x00), sc(0x00), sc(0x00), sc(0xcd), sc(0x53), sc(0x74),
            sc(0x75), sc(0x62), sc(0x49), sc(0x6e), sc(0x6c), sc(0x65), sc(0x74),
            sc(0x54), sc(0x65), sc(0x6d), sc(0x70)
        }
    }
    local offset_part = {5, 19, 19, 18}
    local offset = 0
    local record_id = 0
    for i = 1, #sdr_parts do
        local tail = i == #sdr_parts and 1 or 0
        local datas = table.concat(sdr_parts[i], '')
        local rid_l = record_id & 0xFF
        local rid_h = (record_id >> 8) & 0xFF
        local req = ipmi.AddPartialSDR.req.new(0x00, 0x00, rid_l, rid_h, offset, tail, 0, datas)
        local payload = bs.new(ipmi.AddPartialSDR.decode):pack(req)
        local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
        assert(ok)
        local rsp = bs.new(ipmi.AddPartialSDR.encode):unpack(ret)
        assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
        record_id = (rsp.RecordIdH << 8) | rsp.RecordIdL
        assert(record_id ~= 0, 'actual: ' .. record_id)
        offset = offset + offset_part[i]
    end
end

function ipmi_interface.test_add_sdr(bus)
    log:info('== Testing ipmi add sdr ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x25 <reserve_id> <record_id> <offset> <progress> <datas>'
    -- 添加SDR，完整的SDR记录如下: 前5位是标记，第5位是长度，剩余的是内容
    -- 01 00 51 01 38 20 00 01 ff 00 7f 68 01 01 72 80
    -- 72 80 18 18 80 01 00 00 64 00 00 00 00 e0 01 19
    -- 00 00 7f 80 00 32 2e 00 00 00 02 02 00 00 00 cd
    -- 53 74 75 62 49 6e 6c 65 74 54 65 6d 70
    local netfn = 0x0A
    local cmd = 0x24

    local sc = string.char
    local sdr_data = {
        sc(0x01),  sc(0x00),  sc(0x51),  sc(0x01),  sc(0x38),
        sc(0x20), sc(0x00), sc(0x01), sc(0xff), sc(0x00), sc(0x7f), sc(0x68),
        sc(0x01), sc(0x01), sc(0x72), sc(0x80), sc(0x72), sc(0x80), sc(0x18),
        sc(0x18), sc(0x80), sc(0x01), sc(0x00), sc(0x00),
        sc(0x64), sc(0x00), sc(0x00), sc(0x00), sc(0x00), sc(0xe0), sc(0x01),
        sc(0x19), sc(0x00), sc(0x00), sc(0x7f), sc(0x80), sc(0x00), sc(0x32),
        sc(0x2e), sc(0x00), sc(0x00), sc(0x00), sc(0x02),
        sc(0x02), sc(0x00), sc(0x00), sc(0x00), sc(0xcd), sc(0x53), sc(0x74),
        sc(0x75), sc(0x62), sc(0x49), sc(0x6e), sc(0x6c), sc(0x65), sc(0x74),
        sc(0x54), sc(0x65), sc(0x6d), sc(0x70)
    }

    local req = ipmi.AddSDR.req.new(table.concat(sdr_data))
    local payload = bs.new(ipmi.AddSDR.decode):pack(req)
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.AddSDR.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
    local record_id = (rsp.RecordIdH << 8) | rsp.RecordIdL
    assert(record_id ~= 0, 'actual: ' .. record_id)
end

function ipmi_interface.test_get_sdr_repo_info(bus)
    log:info('== Testing ipmi get sdr repository info ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x20'
    local netfn = 0x0A
    local cmd = 0x20

    local payload = ''
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.GetSDRRepoInfo.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
    assert(rsp.RecordCountL == 9, 'actual: ' .. rsp.RecordCountL)
    assert(rsp.SDRVersion == 0x51, 'actual: ' .. rsp.SDRVersion)
end

function ipmi_interface.test_get_sdr_repo_alloc(bus)
    log:info('== Testing ipmi get sdr repository alloc info ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x21'
    local netfn = 0x0A
    local cmd = 0x21

    local payload = ''
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.GetSDRRepoAllocInfo.encode):unpack(ret)
    local unit_count = (rsp.UnitCntH << 8) | rsp.UnitCntL
    local unit_size = (rsp.UnitSizeH << 8) | rsp.UnitSizeL
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
    assert(unit_count == 1024, 'actual unit count: ' .. unit_count)
    assert(unit_size == 64, 'actual unit count: ' .. unit_size)
end

function ipmi_interface.test_run_initialization_agent(bus)
    log:info('== Testing ipmi run sdr initialization agent ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x2C <flag>'
    local netfn = 0x0A
    local cmd = 0x2C

    -- 测试重新初始化SDR
    local payload = '\x01'
    local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    local rsp = bs.new(ipmi.RunSDRInitAgent.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
    assert(rsp.Progress == 0, 'actual: ' .. rsp.Progress)

    -- 初始化之后查询进度为1
    payload = '\x00'
    ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
    assert(ok)
    rsp = bs.new(ipmi.RunSDRInitAgent.encode):unpack(ret)
    assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
    assert(rsp.Progress == 1, 'actual: ' .. rsp.Progress)
end

function ipmi_interface.test_delete_sdr(bus)
    log:info('== Testing ipmi delete sdr ...')

    -- ipmi command: 'ipmitool raw 0x0A 0x26 <reserve_idL> <reserve_idH> <record_idL> <record_idH>'
    local netfn = 0x0A
    local cmd = 0x26
    
    do  -- Case1: 正常删除成功
        local req = ipmi.DeleteSDR.req.new(0x00, 0x00, 0x01, 0x00)
        local payload = bs.new(ipmi.DeleteSDR.decode):pack(req)

        local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
        assert(ok)

        local rsp = bs.new(ipmi.DeleteSDR.encode):unpack(ret)
        assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
        assert(rsp.RecordIdH == 0x00 and rsp.RecordIdL == 0x01)
    end

    do -- Case2: RecordId = 0 -> OUT_OF_RANGE
        local req = ipmi.DeleteSDR.req.new(0x00, 0x00, 0x00, 0x00)
        local payload = bs.new(ipmi.DeleteSDR.decode):pack(req)

        local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
        assert(ok)

        local cc = ret:byte(1)
        assert(cc == 0xc9, 'actual: ' .. cc)
    end

    do  -- Case3: RecordId 超出范围 -> OUT_OF_RANGE
        local req = ipmi.DeleteSDR.req.new(0x00, 0x00, 0xFF, 0xFF)
        local payload = bs.new(ipmi.DeleteSDR.decode):pack(req)

        local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
        assert(ok)

        local cc = ret:byte(1)
        assert(cc == 0xc9, 'actual: ' .. cc)
    end

    do 
        ipmi_interface.test_exit_sdr_repo_update(bus)
        local req = ipmi.DeleteSDR.req.new(0x00, 0x00, 0x01, 0x00)
        local payload = bs.new(ipmi.DeleteSDR.decode):pack(req)

        local ok, ret = common.call_ipmi(bus, netfn, cmd, payload)
        assert(ok)

        local cc = ret:byte(1)
        assert(cc == 0xd5, 'actual: ' .. cc)
        ipmi_interface.test_enter_sdr_repo_update(bus)
    end

    do -- Case4: 主机通道正常删除=
        ipmi_interface.test_add_sdr(bus)
        
        local req = ipmi.DeleteSDR.req.new(0x00, 0x00, 0x01, 0x00)
        local payload = bs.new(ipmi.DeleteSDR.decode):pack(req)

        local ok, ret = common.call_ipmi(bus, netfn, cmd, payload, 1)
        assert(ok)
        
        local rsp = bs.new(ipmi.DeleteSDR.encode):unpack(ret)
        assert(rsp.CompletionCode == 0x00, 'actual: ' .. rsp.CompletionCode)
        assert(rsp.RecordIdH == 0x00 and rsp.RecordIdL == 0x01)
    end
end    

function ipmi_interface.test_entry_part1(bus)
    log:info('================ test ipmi sdr interface part1 start ================')

    ipmi_interface.test_get_sdr_reserve_id(bus)
    ipmi_interface.test_get_sdr(bus)
    ipmi_interface.test_get_device_sdr_info(bus)
    ipmi_interface.test_get_device_sdr(bus)
    ipmi_interface.test_reserve_device_sdr(bus)
    ipmi_interface.test_enter_sdr_repo_update(bus)
    ipmi_interface.test_exit_sdr_repo_update(bus)
    ipmi_interface.test_get_sdr_repo_info(bus)
    ipmi_interface.test_get_sdr_repo_alloc(bus)

    log:info('================ test ipmi sdr interface part1 complete ================')
end

function ipmi_interface.test_entry_part2(bus)
    log:info('================ test ipmi sdr interface part2 start ================')

    ipmi_interface.test_clear_sdr(bus)
    ipmi_interface.test_enter_sdr_repo_update(bus)
    ipmi_interface.test_add_partial_sdr(bus)
    ipmi_interface.test_add_sdr(bus)
    ipmi_interface.test_delete_sdr(bus)
    ipmi_interface.test_exit_sdr_repo_update(bus)
    ipmi_interface.test_run_initialization_agent(bus)

    log:info('================ test ipmi sdr interface part2 complete ================')
end

return ipmi_interface