Commit e3f7320c authored by Cédric Le Goater's avatar Cédric Le Goater Committed by Corey Minyard
Browse files

ipmi: add SET_SENSOR_READING command



SET_SENSOR_READING is a complex IPMI command (see IPMI spec 35.17)
which enables the host software to set the reading value and the event
status of sensors supporting it.

Below is a proposal for all the operations (reading, assert, deassert,
event data) with the following limitations :

 - No event are generated for threshold-based sensors.
 - The case in which the BMC needs to generate its own events is not
   supported.

Signed-off-by: default avatarCédric Le Goater <clg@kaod.org>
Reviewed-by: default avatarCorey Minyard <cminyard@mvista.com>
Message-Id: <20191118092429.16149-1-clg@kaod.org>
[Moved the break statement for case SENSOR_GEN_EVENT_DATA above the
 closing brace to keep the indention consistent.]
Signed-off-by: default avatarCorey Minyard <cminyard@mvista.com>
parent 789101b7
Loading
Loading
Loading
Loading
+223 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@
#define IPMI_CMD_GET_SENSOR_READING       0x2d
#define IPMI_CMD_SET_SENSOR_TYPE          0x2e
#define IPMI_CMD_GET_SENSOR_TYPE          0x2f
#define IPMI_CMD_SET_SENSOR_READING       0x30

/* #define IPMI_NETFN_APP             0x06 In ipmi.h */

@@ -1747,6 +1748,227 @@ static void get_sensor_type(IPMIBmcSim *ibs,
    rsp_buffer_push(rsp, sens->evt_reading_type_code);
}

/*
 * bytes   parameter
 *    1    sensor number
 *    2    operation (see below for bits meaning)
 *    3    sensor reading
 *  4:5    assertion states (optional)
 *  6:7    deassertion states (optional)
 *  8:10   event data 1,2,3 (optional)
 */
static void set_sensor_reading(IPMIBmcSim *ibs,
                               uint8_t *cmd, unsigned int cmd_len,
                               RspBuffer *rsp)
{
    IPMISensor *sens;
    uint8_t evd1 = 0;
    uint8_t evd2 = 0;
    uint8_t evd3 = 0;
    uint8_t new_reading = 0;
    uint16_t new_assert_states = 0;
    uint16_t new_deassert_states = 0;
    bool change_reading = false;
    bool change_assert = false;
    bool change_deassert = false;
    enum {
        SENSOR_GEN_EVENT_NONE,
        SENSOR_GEN_EVENT_DATA,
        SENSOR_GEN_EVENT_BMC,
    } do_gen_event = SENSOR_GEN_EVENT_NONE;

    if ((cmd[2] >= MAX_SENSORS) ||
            !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
        rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
        return;
    }

    sens = ibs->sensors + cmd[2];

    /* [1:0] Sensor Reading operation */
    switch ((cmd[3]) & 0x3) {
    case 0: /* Do not change */
        break;
    case 1: /* write given value to sensor reading byte */
        new_reading = cmd[4];
        if (sens->reading != new_reading) {
            change_reading = true;
        }
        break;
    case 2:
    case 3:
        rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
        return;
    }

    /* [3:2] Deassertion bits operation */
    switch ((cmd[3] >> 2) & 0x3) {
    case 0: /* Do not change */
        break;
    case 1: /* write given value */
        if (cmd_len > 7) {
            new_deassert_states = cmd[7];
            change_deassert = true;
        }
        if (cmd_len > 8) {
            new_deassert_states |= (cmd[8] << 8);
        }
        break;

    case 2: /* mask on */
        if (cmd_len > 7) {
            new_deassert_states = (sens->deassert_states | cmd[7]);
            change_deassert = true;
        }
        if (cmd_len > 8) {
            new_deassert_states |= (sens->deassert_states | (cmd[8] << 8));
        }
        break;

    case 3: /* mask off */
        if (cmd_len > 7) {
            new_deassert_states = (sens->deassert_states & cmd[7]);
            change_deassert = true;
        }
        if (cmd_len > 8) {
            new_deassert_states |= (sens->deassert_states & (cmd[8] << 8));
        }
        break;
    }

    if (change_deassert && (new_deassert_states == sens->deassert_states)) {
        change_deassert = false;
    }

    /* [5:4] Assertion bits operation */
    switch ((cmd[3] >> 4) & 0x3) {
    case 0: /* Do not change */
        break;
    case 1: /* write given value */
        if (cmd_len > 5) {
            new_assert_states = cmd[5];
            change_assert = true;
        }
        if (cmd_len > 6) {
            new_assert_states |= (cmd[6] << 8);
        }
        break;

    case 2: /* mask on */
        if (cmd_len > 5) {
            new_assert_states = (sens->assert_states | cmd[5]);
            change_assert = true;
        }
        if (cmd_len > 6) {
            new_assert_states |= (sens->assert_states | (cmd[6] << 8));
        }
        break;

    case 3: /* mask off */
        if (cmd_len > 5) {
            new_assert_states = (sens->assert_states & cmd[5]);
            change_assert = true;
        }
        if (cmd_len > 6) {
            new_assert_states |= (sens->assert_states & (cmd[6] << 8));
        }
        break;
    }

    if (change_assert && (new_assert_states == sens->assert_states)) {
        change_assert = false;
    }

    if (cmd_len > 9) {
        evd1 = cmd[9];
    }
    if (cmd_len > 10) {
        evd2 = cmd[10];
    }
    if (cmd_len > 11) {
        evd3 = cmd[11];
    }

    /* [7:6] Event Data Bytes operation */
    switch ((cmd[3] >> 6) & 0x3) {
    case 0: /*
             * Don’t use Event Data bytes from this command. BMC will
             * generate it's own Event Data bytes based on its sensor
             * implementation.
             */
        evd1 = evd2 = evd3 = 0x0;
        do_gen_event = SENSOR_GEN_EVENT_BMC;
        break;
    case 1: /*
             * Write given values to event data bytes including bits
             * [3:0] Event Data 1.
             */
        do_gen_event = SENSOR_GEN_EVENT_DATA;
        break;
    case 2: /*
             * Write given values to event data bytes excluding bits
             * [3:0] Event Data 1.
             */
        evd1 &= 0xf0;
        do_gen_event = SENSOR_GEN_EVENT_DATA;
        break;
    case 3:
        rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
        return;
    }

    /*
     * Event Data Bytes operation and parameter are inconsistent. The
     * Specs are not clear on that topic but generating an error seems
     * correct.
     */
    if (do_gen_event == SENSOR_GEN_EVENT_DATA && cmd_len < 10) {
        rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
        return;
    }

    /* commit values */
    if (change_reading) {
        sens->reading = new_reading;
    }

    if (change_assert) {
        sens->assert_states = new_assert_states;
    }

    if (change_deassert) {
        sens->deassert_states = new_deassert_states;
    }

    /* TODO: handle threshold sensor */
    if (!IPMI_SENSOR_IS_DISCRETE(sens)) {
        return;
    }

    switch (do_gen_event) {
    case SENSOR_GEN_EVENT_DATA: {
        unsigned int bit = evd1 & 0xf;
        uint16_t mask = (1 << bit);

        if (sens->assert_states & mask & sens->assert_enable) {
            gen_event(ibs, cmd[2], 0, evd1, evd2, evd3);
        }

        if (sens->deassert_states & mask & sens->deassert_enable) {
            gen_event(ibs, cmd[2], 1, evd1, evd2, evd3);
        }
        break;
    }
    case SENSOR_GEN_EVENT_BMC:
        /*
         * TODO: generate event and event data bytes depending on the
         * sensor
         */
        break;
    case SENSOR_GEN_EVENT_NONE:
        break;
    }
}

static const IPMICmdHandler chassis_cmds[] = {
    [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = { chassis_capabilities },
@@ -1768,6 +1990,7 @@ static const IPMICmdHandler sensor_event_cmds[] = {
    [IPMI_CMD_GET_SENSOR_READING] = { get_sensor_reading, 3 },
    [IPMI_CMD_SET_SENSOR_TYPE] = { set_sensor_type, 5 },
    [IPMI_CMD_GET_SENSOR_TYPE] = { get_sensor_type, 3 },
    [IPMI_CMD_SET_SENSOR_READING] = { set_sensor_reading, 5 },
};
static const IPMINetfn sensor_event_netfn = {
    .cmd_nums = ARRAY_SIZE(sensor_event_cmds),