From patchwork Mon Jul 9 19:17:08 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Corey Minyard X-Patchwork-Id: 169938 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AAA7D2C0081 for ; Tue, 10 Jul 2012 05:24:32 +1000 (EST) Received: from localhost ([::1]:37032 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoJTe-0001OE-Vh for incoming@patchwork.ozlabs.org; Mon, 09 Jul 2012 15:18:34 -0400 Received: from eggs.gnu.org ([208.118.235.92]:40601) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoJSz-0000TO-6M for qemu-devel@nongnu.org; Mon, 09 Jul 2012 15:17:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SoJSt-0003LL-Jd for qemu-devel@nongnu.org; Mon, 09 Jul 2012 15:17:52 -0400 Received: from vms173015pub.verizon.net ([206.46.173.15]:65531) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SoJSt-0003KP-B2 for qemu-devel@nongnu.org; Mon, 09 Jul 2012 15:17:47 -0400 Received: from wf-rch.minyard.home ([unknown] [173.57.151.210]) by vms173015.mailsrvcs.net (Sun Java(tm) System Messaging Server 7u2-7.02 32bit (built Apr 16 2009)) with ESMTPA id <0M6W00E7BQWWZWC0@vms173015.mailsrvcs.net> for qemu-devel@nongnu.org; Mon, 09 Jul 2012 14:17:22 -0500 (CDT) Received: from i.minyard.home (i2.minyard.home [192.168.27.116]) by wf-rch.minyard.home (Postfix) with ESMTP id 549821F955; Mon, 09 Jul 2012 14:17:13 -0500 (CDT) Received: by i.minyard.home (Postfix, from userid 1000) id 7F7E281E64; Mon, 09 Jul 2012 14:17:12 -0500 (CDT) From: minyard@acm.org To: qemu-devel@nongnu.org Date: Mon, 09 Jul 2012 14:17:08 -0500 Message-id: <1341861429-6297-9-git-send-email-minyard@acm.org> X-Mailer: git-send-email 1.7.4.1 In-reply-to: <1341861429-6297-1-git-send-email-minyard@acm.org> References: <1341861429-6297-1-git-send-email-minyard@acm.org> X-detected-operating-system: by eggs.gnu.org: Solaris 10 (1203?) X-Received-From: 206.46.173.15 Cc: Corey Minyard Subject: [Qemu-devel] [PATCH 8/9] IPMI: Add a local BMC simulation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Corey Minyard This provides a minimal local BMC, basically enough to comply with the spec and provide a complete watchdog timer (including a sensor, SDR, and event). Signed-off-by: Corey Minyard --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/Makefile.objs | 1 + hw/ipmi_sim.c | 1273 ++++++++++++++++++++++++++++++++++++ hw/ipmi_sim.h | 270 ++++++++ 5 files changed, 1546 insertions(+), 0 deletions(-) create mode 100644 hw/ipmi_sim.c create mode 100644 hw/ipmi_sim.h diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index f8f8e6d..8c99d5d 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -11,6 +11,7 @@ CONFIG_IPMI=y CONFIG_ISA_IPMI=y CONFIG_IPMI_KCS=y CONFIG_IPMI_BT=y +CONFIG_IPMI_LOCAL=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 8c1177d..4d01883 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -11,6 +11,7 @@ CONFIG_IPMI=y CONFIG_ISA_IPMI=y CONFIG_IPMI_KCS=y CONFIG_IPMI_BT=y +CONFIG_IPMI_LOCAL=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index e1d30cc..193227d 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -24,6 +24,7 @@ hw-obj-$(CONFIG_IPMI) += ipmi.o hw-obj-$(CONFIG_ISA_IPMI) += isa_ipmi.o hw-obj-$(CONFIG_IPMI_KCS) += ipmi_kcs.o hw-obj-$(CONFIG_IPMI_BT) += ipmi_bt.o +hw-obj-$(CONFIG_IPMI_LOCAL) += ipmi_sim.o hw-obj-$(CONFIG_SERIAL) += serial.o hw-obj-$(CONFIG_PARALLEL) += parallel.o diff --git a/hw/ipmi_sim.c b/hw/ipmi_sim.c new file mode 100644 index 0000000..6813a86 --- /dev/null +++ b/hw/ipmi_sim.c @@ -0,0 +1,1273 @@ +/* + * IPMI BMC emulation + * + * Copyright (c) 2012 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include "ipmi_sim.h" + +static void ipmi_sim_handle_timeout(IPMIState *s); + +static void ipmi_gettime(struct ipmi_time *time) +{ + int64_t stime; + + stime = get_clock_realtime(); + time->tv_sec = stime / 1000000000LL; + time->tv_nsec = stime % 1000000000LL; +} + +static int64_t ipmi_getmonotime(void) +{ + return qemu_get_clock_ns(vm_clock); +} + +static void ipmi_timeout(void *opaque) +{ + IPMIState *s = opaque; + + ipmi_sim_handle_timeout(s); +} + +static void set_timestamp(IPMISimState *ss, uint8_t *ts) +{ + unsigned int val; + struct ipmi_time now; + + ipmi_gettime(&now); + val = now.tv_sec + ss->sel.time_offset; + ts[0] = val & 0xff; + ts[1] = (val >> 8) & 0xff; + ts[2] = (val >> 16) & 0xff; + ts[3] = (val >> 24) & 0xff; +} + +static void sdr_inc_reservation(IPMISdr *sdr) +{ + sdr->reservation++; + if (sdr->reservation == 0) + sdr->reservation = 1; +} + +static int sdr_add_entry(IPMISimState *ss, const uint8_t *entry, + unsigned int len, uint16_t *recid) +{ + if ((len < 5) || (len > 255)) + return 1; + + if (entry[ss->sdr.next_free + 4] != len) + return 1; + + if (ss->sdr.next_free + len > MAX_SDR_SIZE) { + ss->sdr.overflow = 1; + return 1; + } + + memcpy(ss->sdr.sdr + ss->sdr.next_free, entry, len); + ss->sdr.sdr[ss->sdr.next_free] = ss->sdr.next_rec_id & 0xff; + ss->sdr.sdr[ss->sdr.next_free+1] = (ss->sdr.next_rec_id >> 8) & 0xff; + ss->sdr.sdr[ss->sdr.next_free+2] = 0x51; /* Conform to IPMI 1.5 spec */ + + if (recid) + *recid = ss->sdr.next_rec_id; + ss->sdr.next_rec_id++; + set_timestamp(ss, ss->sdr.last_addition); + ss->sdr.next_free += len; + sdr_inc_reservation(&ss->sdr); + return 0; +} + +static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, + unsigned int *retpos, uint16_t *nextrec) +{ + unsigned int pos = *retpos; + + while (pos < sdr->next_free) { + uint16_t trec = sdr->sdr[pos] | (sdr->sdr[pos + 1] << 8); + unsigned int nextpos = pos + sdr->sdr[pos + 4]; + + if (trec == recid) { + if (nextrec) { + if (nextpos >= sdr->next_free) + *nextrec = 0xffff; + else + *nextrec = (sdr->sdr[nextpos] | + (sdr->sdr[nextpos + 1] << 8)); + } + *retpos = pos; + return 0; + } + pos = nextpos; + } + return 1; +} + +static void sel_inc_reservation(IPMISel *sel) +{ + sel->reservation++; + if (sel->reservation == 0) + sel->reservation = 1; +} + +/* Returns 1 if the SEL is full and can't hold the event. */ +static int sel_add_event(IPMISimState *ss, uint8_t *event) +{ + event[0] = 0xff; + event[1] = 0xff; + set_timestamp(ss, event + 3); + if (ss->sel.next_free == MAX_SEL_SIZE) { + ss->sel.overflow = 1; + return 1; + } + event[0] = ss->sel.next_free & 0xff; + event[1] = (ss->sel.next_free >> 8) & 0xff; + memcpy(ss->sel.last_addition, event + 3, 4); + memcpy(ss->sel.sel[ss->sel.next_free], event, 16); + ss->sel.next_free++; + sel_inc_reservation(&ss->sel); + return 0; +} + +static int attn_irq_enabled(IPMISimState *s) +{ + return (IPMI_BMC_MSG_INTS_ON(s) && IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s)) + || (IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) && + IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s)); +} + +static void gen_event(IPMIState *s, unsigned int sens_num, uint8_t deassert, + uint8_t evd1, uint8_t evd2, uint8_t evd3) +{ + IPMISimState *ss = s->ifdata; + uint8_t evt[16]; + IPMISensor *sens = ss->sensors + sens_num; + + if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ss)) + return; + if (!IPMI_SENSOR_GET_EVENTS_ON(sens)) + return; + + evt[2] = 0x2; /* System event record */ + evt[7] = s->slave_addr; + evt[8] = 0; + evt[9] = 0x04; /* Format version */ + evt[10] = sens->sensor_type; + evt[11] = sens_num; + evt[12] = sens->evt_reading_type_code | (!!deassert << 7); + evt[13] = evd1; + evt[14] = evd2; + evt[15] = evd3; + + if (IPMI_BMC_EVENT_LOG_ENABLED(ss)) + sel_add_event(ss, evt); + + if (ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) + goto out; + + ss->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + memcpy(ss->evtbuf, evt, 16); + if (s->set_atn && + ~(ss->msg_flags & IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK)) + s->set_atn(s, 1, attn_irq_enabled(ss)); + out: + return; +} + +static void sensor_set_discrete_bit(IPMIState *s, unsigned int sensor, + unsigned int bit, unsigned int val, + uint8_t evd1, uint8_t evd2, uint8_t evd3) +{ + IPMISimState *ss = s->ifdata; + IPMISensor *sens; + uint16_t mask; + + if (sensor >= MAX_SENSORS) + return; + if (bit >= 16) + return; + + mask = (1 << bit); + sens = ss->sensors + sensor; + if (val) { + sens->states |= mask & sens->states_suppt; + if (sens->assert_states & mask) + return; /* Already asserted */ + sens->assert_states |= mask & sens->assert_suppt; + if (sens->assert_enable & mask & sens->assert_states) { + /* Send an event on assert */ + gen_event(s, sensor, 0, evd1, evd2, evd3); + } + } else { + sens->states &= ~(mask & sens->states_suppt); + if (sens->deassert_states & mask) + return; /* Already deasserted */ + sens->deassert_states |= mask & sens->deassert_suppt; + if (sens->deassert_enable & mask & sens->deassert_states) { + /* Send an event on deassert */ + gen_event(s, sensor, 1, evd1, evd2, evd3); + } + } +} + +static void ipmi_init_sensors_from_sdrs(IPMISimState *s) +{ + unsigned int i, pos; + IPMISensor *sens; + + for (i = 0; i < MAX_SENSORS; i++) + memset(s->sensors + i, 0, sizeof(*sens)); + + pos = 0; + for (i = 0; !sdr_find_entry(&s->sdr, i, &pos, NULL); i++) { + uint8_t *sdr = s->sdr.sdr + pos; + unsigned int len = sdr[4]; + + if (len < 20) + continue; + if ((sdr[3] < 1) || (sdr[3] > 2)) + continue; /* Not a sensor SDR we set from */ + + if (sdr[7] > MAX_SENSORS) + continue; + sens = s->sensors + sdr[7]; + + IPMI_SENSOR_SET_PRESENT(sens, 1); + IPMI_SENSOR_SET_SCAN_ON(sens, (sdr[10] >> 6) & 1); + IPMI_SENSOR_SET_EVENTS_ON(sens, (sdr[10] >> 5) & 1); + sens->assert_suppt = sdr[14] | (sdr[15] << 8); + sens->deassert_suppt = sdr[16] | (sdr[17] << 8); + sens->states_suppt = sdr[18] | (sdr[19] << 8); + sens->sensor_type = sdr[12]; + sens->evt_reading_type_code = sdr[13] & 0x7f; + + /* Enable all the events that are supported. */ + sens->assert_enable = sens->assert_suppt; + sens->deassert_enable = sens->deassert_suppt; + } +} + +int ipmi_register_netfn(IPMISimState *s, unsigned int netfn, IPMINetfn *netfnd) +{ + if ((netfn & 1) || (netfn > MAX_NETFNS) || (s->netfns[netfn / 2])) + return -1; + s->netfns[netfn / 2] = netfnd; + return 0; +} + +int ipmi_register_cmd(IPMISimState *s, unsigned int netfn, unsigned int cmd, + IPMICmdHandler handler) +{ + if ((netfn | 1) || (netfn > MAX_NETFNS) || (!s->netfns[netfn / 2])) + return -1; + if (cmd >= s->netfns[netfn / 2]->cmd_nums) + return -1; + if (s->netfns[netfn / 2]->cmd_handlers[cmd]) + return -1; + s->netfns[netfn / 2]->cmd_handlers[cmd] = handler; + return 0; +} + +static void next_timeout(IPMISimState *ss) +{ + int64_t next; + if (ss->watchdog_running) { + next = ss->watchdog_expiry; + } else { + /* Wait a minute */ + next = ipmi_getmonotime() + 60 * 1000000000LL; + } + qemu_mod_timer_ns(ss->timer, next); +} + +static void ipmi_sim_handle_command(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id) +{ + IPMISimState *ss = s->ifdata; + unsigned int netfn; + uint8_t rsp[MAX_IPMI_MSG_SIZE]; + unsigned int rsp_len_holder =- 0; + unsigned int *rsp_len = &rsp_len_holder; + unsigned int max_rsp_len = sizeof(rsp); + + /* Set up the response, set the low bit of NETFN. */ + /* Note that max_rsp_len must be at least 3 */ + IPMI_ADD_RSP_DATA(cmd[0] | 0x04); + IPMI_ADD_RSP_DATA(cmd[1]); + IPMI_ADD_RSP_DATA(0); /* Assume success */ + + /* If it's too short or it was truncated, return an error. */ + if (cmd_len < 2) { + rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; + goto out; + } + if (cmd_len > max_cmd_len) { + rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED; + goto out; + } + + if ((cmd[0] & 0x03) != 0) { + /* Only have stuff on LUN 0 */ + rsp[2] = IPMI_CC_COMMAND_INVALID_FOR_LUN; + goto out; + } + + netfn = cmd[0] >> 2; + + /* Odd netfns are not valid, make sure the command is registered */ + if ((netfn & 1) || !ss->netfns[netfn / 2] || + (cmd[1] >= ss->netfns[netfn / 2]->cmd_nums) || + (!ss->netfns[netfn / 2]->cmd_handlers[cmd[1]])) { + rsp[2] = IPMI_CC_INVALID_CMD; + goto out; + } + + ss->netfns[netfn / 2]->cmd_handlers[cmd[1]](s, cmd, cmd_len, rsp, rsp_len, + max_rsp_len); + + out: + s->handle_rsp(s, msg_id, rsp, *rsp_len); + + next_timeout(ss); +} + +static void ipmi_sim_handle_timeout(IPMIState *s) +{ + IPMISimState *ss = s->ifdata; + + if (!ss->watchdog_running) + goto out; + + if (! ss->watchdog_preaction_ran) { + switch (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ss)) { + case IPMI_BMC_WATCHDOG_PRE_NMI: + ss->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; + s->do_hw_op(s, IPMI_SEND_NMI, 0); + sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 8, 1, + 0xc8, (2 << 4) | 0xf, 0xff); + break; + + case IPMI_BMC_WATCHDOG_PRE_MSG_INT: + ss->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; + if (s->set_atn && + !(ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) + s->set_atn(s, 1, attn_irq_enabled(ss)); + sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 8, 1, + 0xc8, (3 << 4) | 0xf, 0xff); + break; + + default: + goto do_full_expiry; + } + + ss->watchdog_preaction_ran = 1; + /* Issued the pretimeout, do the rest of the timeout now. */ + ss->watchdog_expiry = ipmi_getmonotime(); + ss->watchdog_expiry += ss->watchdog_pretimeout * 1000000000LL; + goto out; + } + + do_full_expiry: + ss->watchdog_running = 0; /* Stop the watchdog on a timeout */ + ss->watchdog_expired |= (1 << IPMI_BMC_WATCHDOG_GET_USE(ss)); + switch (IPMI_BMC_WATCHDOG_GET_ACTION(ss)) { + case IPMI_BMC_WATCHDOG_ACTION_NONE: + sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 0, 1, + 0xc0, ss->watchdog_use & 0xf, 0xff); + break; + + case IPMI_BMC_WATCHDOG_ACTION_RESET: + sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 1, 1, + 0xc1, ss->watchdog_use & 0xf, 0xff); + s->do_hw_op(s, IPMI_RESET_CHASSIS, 0); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: + sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 2, 1, + 0xc2, ss->watchdog_use & 0xf, 0xff); + s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: + sensor_set_discrete_bit(s, IPMI_WATCHDOG_SENSOR, 2, 1, + 0xc3, ss->watchdog_use & 0xf, 0xff); + s->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0); + break; + } + + out: + next_timeout(ss); +} + +static void chassis_capabilities(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(s->slave_addr); + IPMI_ADD_RSP_DATA(s->slave_addr); + IPMI_ADD_RSP_DATA(s->slave_addr); + IPMI_ADD_RSP_DATA(s->slave_addr); + out: + return; +} + +static void chassis_status(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(0x61); /* Unknown power restore, power is on */ + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(0); + out: + return; +} + +static void chassis_control(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_CHECK_CMD_LEN(3); + switch (cmd[2] & 0xf) { + case 0: /* power down */ + rsp[2] = s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); + break; + case 1: /* power up */ + rsp[2] = s->do_hw_op(s, IPMI_POWERON_CHASSIS, 0); + break; + case 2: /* power cycle */ + rsp[2] = s->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0); + break; + case 3: /* hard reset */ + rsp[2] = s->do_hw_op(s, IPMI_RESET_CHASSIS, 0); + break; + case 4: /* pulse diagnostic interrupt */ + rsp[2] = s->do_hw_op(s, IPMI_PULSE_DIAG_IRQ, 0); + break; + case 5: /* soft shutdown via ACPI by overtemp emulation */ + rsp[2] = s->do_hw_op(s, + IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 0); + break; + default: + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + out: + return; +} + +static void get_device_id(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_ADD_RSP_DATA(ss->device_id); + IPMI_ADD_RSP_DATA(ss->device_rev & 0xf); + IPMI_ADD_RSP_DATA(ss->fwrev1 & 0x7f); + IPMI_ADD_RSP_DATA(ss->fwrev2); + IPMI_ADD_RSP_DATA(ss->ipmi_version); + IPMI_ADD_RSP_DATA(0x07); /* sensor, SDR, and SEL. */ + IPMI_ADD_RSP_DATA(ss->mfg_id[0]); + IPMI_ADD_RSP_DATA(ss->mfg_id[1]); + IPMI_ADD_RSP_DATA(ss->mfg_id[2]); + IPMI_ADD_RSP_DATA(ss->product_id[0]); + IPMI_ADD_RSP_DATA(ss->product_id[1]); + out: + return; +} + +static void set_bmc_global_enables(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + uint8_t old_val = ss->bmc_global_enables; + IPMI_CHECK_CMD_LEN(3); + ss->bmc_global_enables = cmd[2]; + if ((old_val & (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT)) + != (cmd[2] & (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT))) { + s->set_irq_enable(s, + !!(cmd[2] & (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT))); + } + out: + return; +} + +static void get_bmc_global_enables(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_ADD_RSP_DATA(ss->bmc_global_enables); + out: + return; +} + +static void clr_msg_flags(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_CHECK_CMD_LEN(3); + if (cmd[2] & IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK) { + if (s->set_atn && + !(ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) + s->set_atn(s, 0, attn_irq_enabled(ss)); + } + ss->msg_flags &= ~cmd[2]; + out: + return; +} + +static void get_msg_flags(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_ADD_RSP_DATA(ss->msg_flags); + out: + return; +} + +static void read_evt_msg_buf(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + unsigned int i; + + if (!(ss->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) { + rsp[2] = 0x80; + goto out; + } + for (i = 0; i < 16; i++) + IPMI_ADD_RSP_DATA(ss->evtbuf[i]); + ss->msg_flags &= ~IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + if (s->set_atn && + ~(ss->msg_flags & IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK)) + s->set_atn(s, 0, attn_irq_enabled(ss)); + out: + return; +} + +static void do_watchdog_reset(IPMISimState *ss) +{ + if (IPMI_BMC_WATCHDOG_GET_ACTION(ss) == + IPMI_BMC_WATCHDOG_ACTION_NONE) { + ss->watchdog_running = 0; + return; + } + ss->watchdog_preaction_ran = 0; + + + /* Timeout is in tenths of a second, offset is in seconds */ + ss->watchdog_expiry = ipmi_getmonotime(); + ss->watchdog_expiry += ss->watchdog_timeout * 100000000LL; + if (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ss) != IPMI_BMC_WATCHDOG_PRE_NONE) { + ss->watchdog_expiry -= ss->watchdog_pretimeout * 1000000000LL; + } + ss->watchdog_running = 1; +} + +static void reset_watchdog_timer(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + if (!ss->watchdog_initialized) { + rsp[2] = 0x80; + goto out; + } + do_watchdog_reset(ss); + out: + return; +} + +static void set_watchdog_timer(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + unsigned int val; + + IPMI_CHECK_CMD_LEN(8); + val = cmd[2] & 0x7; /* Validate use */ + if (val == 0 || val > 5) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + val = cmd[3] & 0x7; /* Validate action */ + switch (val) { + case IPMI_BMC_WATCHDOG_ACTION_NONE: + break; + + case IPMI_BMC_WATCHDOG_ACTION_RESET: + rsp[2] = s->do_hw_op(s, IPMI_RESET_CHASSIS, 1); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: + rsp[2] = s->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: + rsp[2] = s->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 1); + break; + + default: + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + } + if (rsp[2]) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + + val = (cmd[3] >> 4) & 0x7; /* Validate preaction */ + switch (val) { + case IPMI_BMC_WATCHDOG_PRE_MSG_INT: + case IPMI_BMC_WATCHDOG_PRE_NONE: + break; + + case IPMI_BMC_WATCHDOG_PRE_NMI: + if (!s->do_hw_op(s, IPMI_SEND_NMI, 1)) { + /* NMI not supported. */ + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + default: + /* We don't support PRE_SMI */ + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + + ss->watchdog_initialized = 1; + ss->watchdog_use = cmd[2] & IPMI_BMC_WATCHDOG_USE_MASK; + ss->watchdog_action = cmd[3] & IPMI_BMC_WATCHDOG_ACTION_MASK; + ss->watchdog_pretimeout = cmd[4]; + ss->watchdog_expired &= ~cmd[5]; + ss->watchdog_timeout = cmd[6] | (((uint16_t) cmd[7]) << 8); + if (ss->watchdog_running & IPMI_BMC_WATCHDOG_GET_DONT_STOP(ss)) + do_watchdog_reset(ss); + else + ss->watchdog_running = 0; + out: + return; +} + +static void get_watchdog_timer(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_ADD_RSP_DATA(ss->watchdog_use); + IPMI_ADD_RSP_DATA(ss->watchdog_action); + IPMI_ADD_RSP_DATA(ss->watchdog_pretimeout); + IPMI_ADD_RSP_DATA(ss->watchdog_expired); + if (ss->watchdog_running) { + long timeout; + timeout = ((ss->watchdog_expiry - ipmi_getmonotime() + 50000000) + / 100000000); + IPMI_ADD_RSP_DATA(timeout & 0xff); + IPMI_ADD_RSP_DATA((timeout >> 8) & 0xff); + } else { + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(0); + } + out: + return; +} + +static void get_sdr_rep_info(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + unsigned int i; + + IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 spec */ + IPMI_ADD_RSP_DATA(ss->sdr.next_rec_id & 0xff); + IPMI_ADD_RSP_DATA((ss->sdr.next_rec_id >> 8) & 0xff); + IPMI_ADD_RSP_DATA((MAX_SDR_SIZE - ss->sdr.next_free) & 0xff); + IPMI_ADD_RSP_DATA(((MAX_SDR_SIZE - ss->sdr.next_free) >> 8) & 0xff); + for (i = 0; i < 4; i++) + IPMI_ADD_RSP_DATA(ss->sdr.last_addition[i]); + for (i = 0; i < 4; i++) + IPMI_ADD_RSP_DATA(ss->sdr.last_clear[i]); + /* Only modal support, reserve supported */ + IPMI_ADD_RSP_DATA((ss->sdr.overflow << 7) | 0x22); + out: + return; +} + +static void reserve_sdr_rep(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_ADD_RSP_DATA(ss->sdr.reservation & 0xff); + IPMI_ADD_RSP_DATA((ss->sdr.reservation >> 8) & 0xff); + out: + return; +} + +static void get_sdr(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + unsigned int pos; + uint16_t nextrec; + + IPMI_CHECK_CMD_LEN(8); + if (cmd[6]) + IPMI_CHECK_RESERVATION(2, ss->sdr.reservation); + pos = 0; + if (sdr_find_entry(&ss->sdr, cmd[4] | (cmd[5] << 8), + &pos, &nextrec)) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + if (cmd[6] > (ss->sdr.sdr[pos + 4])) { + rsp[2] = IPMI_CC_PARM_OUT_OF_RANGE; + goto out; + } + + IPMI_ADD_RSP_DATA(nextrec & 0xff); + IPMI_ADD_RSP_DATA((nextrec >> 8) & 0xff); + + if (cmd[7] == 0xff) + cmd[7] = ss->sdr.sdr[pos + 4] - cmd[6]; + + if ((cmd[7] + *rsp_len) > max_rsp_len) { + rsp[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + goto out; + } + memcpy(rsp + *rsp_len, ss->sdr.sdr + pos + cmd[6], cmd[7]); + *rsp_len += cmd[7]; + out: + return; +} + +static void add_sdr(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + uint16_t recid; + + if (sdr_add_entry(ss, cmd + 2, cmd_len - 2, &recid)) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + IPMI_ADD_RSP_DATA(recid & 0xff); + IPMI_ADD_RSP_DATA((recid >> 8) & 0xff); + out: + return; +} + +static void clear_sdr_rep(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_CHECK_CMD_LEN(8); + IPMI_CHECK_RESERVATION(2, ss->sdr.reservation); + if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + if (cmd[7] == 0xaa) { + ss->sdr.next_free = 0; + ss->sdr.overflow = 0; + set_timestamp(ss, ss->sdr.last_clear); + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + sdr_inc_reservation(&ss->sdr); + } else if (cmd[7] == 0) { + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + } else { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + out: + return; +} + +static void get_sel_info(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + unsigned int i, val; + + IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 */ + IPMI_ADD_RSP_DATA(ss->sel.next_free & 0xff); + IPMI_ADD_RSP_DATA((ss->sel.next_free >> 8) & 0xff); + val = (MAX_SEL_SIZE - ss->sel.next_free) * 16; + IPMI_ADD_RSP_DATA(val & 0xff); + IPMI_ADD_RSP_DATA((val >> 8) & 0xff); + for (i = 0; i < 4; i++) + IPMI_ADD_RSP_DATA(ss->sel.last_addition[i]); + for (i = 0; i < 4; i++) + IPMI_ADD_RSP_DATA(ss->sel.last_clear[i]); + /* Only support Reserve SEL */ + IPMI_ADD_RSP_DATA((ss->sel.overflow << 7) | 0x02); + out: + return; +} + +static void reserve_sel(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_ADD_RSP_DATA(ss->sel.reservation & 0xff); + IPMI_ADD_RSP_DATA((ss->sel.reservation >> 8) & 0xff); + out: + return; +} + +static void get_sel_entry(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + unsigned int val; + + IPMI_CHECK_CMD_LEN(8); + if (cmd[6]) + IPMI_CHECK_RESERVATION(2, ss->sel.reservation); + if (ss->sel.next_free == 0) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + if (cmd[6] > 15) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + if (cmd[7] == 0xff) + cmd[7] = 16; + else if ((cmd[7] + cmd[6]) > 16) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } else + cmd[7] += cmd[6]; + + val = cmd[4] | (cmd[5] << 8); + if (val == 0xffff) + val = ss->sel.next_free - 1; + else if (val >= ss->sel.next_free) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + if ((val + 1) == ss->sel.next_free) { + IPMI_ADD_RSP_DATA(0xff); + IPMI_ADD_RSP_DATA(0xff); + } else { + IPMI_ADD_RSP_DATA((val + 1) & 0xff); + IPMI_ADD_RSP_DATA(((val + 1) >> 8) & 0xff); + } + for (; cmd[6] < cmd[7]; cmd[6]++) + IPMI_ADD_RSP_DATA(ss->sel.sel[val][cmd[6]]); + out: + return; +} + +static void add_sel_entry(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_CHECK_CMD_LEN(18); + if (sel_add_event(ss, cmd + 2)) { + rsp[2] = IPMI_CC_OUT_OF_SPACE; + goto out; + } + /* sel_add_event fills in the record number. */ + IPMI_ADD_RSP_DATA(cmd[2]); + IPMI_ADD_RSP_DATA(cmd[3]); + out: + return; +} + +static void clear_sel(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMI_CHECK_CMD_LEN(8); + IPMI_CHECK_RESERVATION(2, ss->sel.reservation); + if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + if (cmd[7] == 0xaa) { + ss->sel.next_free = 0; + ss->sel.overflow = 0; + set_timestamp(ss, ss->sdr.last_clear); + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + sel_inc_reservation(&ss->sel); + } else if (cmd[7] == 0) { + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + } else { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + out: + return; +} + +static void get_sel_time(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + uint32_t val; + struct ipmi_time now; + + ipmi_gettime(&now); + val = now.tv_sec + ss->sel.time_offset; + IPMI_ADD_RSP_DATA(val & 0xff); + IPMI_ADD_RSP_DATA((val >> 8) & 0xff); + IPMI_ADD_RSP_DATA((val >> 16) & 0xff); + IPMI_ADD_RSP_DATA((val >> 24) & 0xff); + out: + return; +} + +static void set_sel_time(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + uint32_t val; + struct ipmi_time now; + + IPMI_CHECK_CMD_LEN(6); + val = cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24); + ipmi_gettime(&now); + ss->sel.time_offset = now.tv_sec - ((long) val); + out: + return; +} + +static void set_sensor_evt_enable(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(4); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ss->sensors + cmd[2]; + switch ((cmd[3] >> 4) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* Enable bits */ + if (cmd_len > 4) + sens->assert_enable |= cmd[4]; + if (cmd_len > 5) + sens->assert_enable |= cmd[5] << 8; + if (cmd_len > 6) + sens->deassert_enable |= cmd[6]; + if (cmd_len > 7) + sens->deassert_enable |= cmd[7] << 8; + break; + case 2: /* Disable bits */ + if (cmd_len > 4) + sens->assert_enable &= ~cmd[4]; + if (cmd_len > 5) + sens->assert_enable &= ~(cmd[5] << 8); + if (cmd_len > 6) + sens->deassert_enable &= ~cmd[6]; + if (cmd_len > 7) + sens->deassert_enable &= ~(cmd[7] << 8); + break; + case 3: + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + IPMI_SENSOR_SET_RET_STATUS(sens, cmd[3]); + out: + return; +} + +static void get_sensor_evt_enable(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(3); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ss->sensors + cmd[2]; + IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens)); + IPMI_ADD_RSP_DATA(sens->assert_enable & 0xff); + IPMI_ADD_RSP_DATA((sens->assert_enable >> 8) & 0xff); + IPMI_ADD_RSP_DATA(sens->deassert_enable & 0xff); + IPMI_ADD_RSP_DATA((sens->deassert_enable >> 8) & 0xff); + out: + return; +} + +static void rearm_sensor_evts(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(4); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ss->sensors + cmd[2]; + + if ((cmd[3] & 0x80) == 0) { + /* Just clear everything */ + sens->states = 0; + goto out; + } + out: + return; +} + +static void get_sensor_evt_status(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(3); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ss->sensors + cmd[2]; + IPMI_ADD_RSP_DATA(sens->reading); + IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens)); + IPMI_ADD_RSP_DATA(sens->assert_states & 0xff); + IPMI_ADD_RSP_DATA((sens->assert_states >> 8) & 0xff); + IPMI_ADD_RSP_DATA(sens->deassert_states & 0xff); + IPMI_ADD_RSP_DATA((sens->deassert_states >> 8) & 0xff); + out: + return; +} + +static void get_sensor_reading(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISimState *ss = s->ifdata; + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(3); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ss->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ss->sensors + cmd[2]; + IPMI_ADD_RSP_DATA(sens->reading); + IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens)); + IPMI_ADD_RSP_DATA(sens->states & 0xff); + if (IPMI_SENSOR_IS_DISCRETE(sens)) + IPMI_ADD_RSP_DATA((sens->states >> 8) & 0xff); + out: + return; +} + +static IPMICmdHandler chassis_cmds[IPMI_NETFN_CHASSIS_MAXCMD] = { + [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = chassis_capabilities, + [IPMI_CMD_GET_CHASSIS_STATUS] = chassis_status, + [IPMI_CMD_CHASSIS_CONTROL] = chassis_control +}; +static IPMINetfn chassis_netfn = { + .cmd_nums = IPMI_NETFN_CHASSIS_MAXCMD, + .cmd_handlers = chassis_cmds +}; + +static IPMICmdHandler sensor_event_cmds[IPMI_NETFN_SENSOR_EVENT_MAXCMD] = { + [IPMI_CMD_SET_SENSOR_EVT_ENABLE] = set_sensor_evt_enable, + [IPMI_CMD_GET_SENSOR_EVT_ENABLE] = get_sensor_evt_enable, + [IPMI_CMD_REARM_SENSOR_EVTS] = rearm_sensor_evts, + [IPMI_CMD_GET_SENSOR_EVT_STATUS] = get_sensor_evt_status, + [IPMI_CMD_GET_SENSOR_READING] = get_sensor_reading +}; +static IPMINetfn sensor_event_netfn = { + .cmd_nums = IPMI_NETFN_SENSOR_EVENT_MAXCMD, + .cmd_handlers = sensor_event_cmds +}; + +static IPMICmdHandler app_cmds[IPMI_NETFN_APP_MAXCMD] = { + [IPMI_CMD_GET_DEVICE_ID] = get_device_id, + [IPMI_CMD_SET_BMC_GLOBAL_ENABLES] = set_bmc_global_enables, + [IPMI_CMD_GET_BMC_GLOBAL_ENABLES] = get_bmc_global_enables, + [IPMI_CMD_CLR_MSG_FLAGS] = clr_msg_flags, + [IPMI_CMD_GET_MSG_FLAGS] = get_msg_flags, + [IPMI_CMD_READ_EVT_MSG_BUF] = read_evt_msg_buf, + [IPMI_CMD_RESET_WATCHDOG_TIMER] = reset_watchdog_timer, + [IPMI_CMD_SET_WATCHDOG_TIMER] = set_watchdog_timer, + [IPMI_CMD_GET_WATCHDOG_TIMER] = get_watchdog_timer, +}; +static IPMINetfn app_netfn = { + .cmd_nums = IPMI_NETFN_APP_MAXCMD, + .cmd_handlers = app_cmds +}; + +static IPMICmdHandler storage_cmds[IPMI_NETFN_STORAGE_MAXCMD] = { + [IPMI_CMD_GET_SDR_REP_INFO] = get_sdr_rep_info, + [IPMI_CMD_RESERVE_SDR_REP] = reserve_sdr_rep, + [IPMI_CMD_GET_SDR] = get_sdr, + [IPMI_CMD_ADD_SDR] = add_sdr, + [IPMI_CMD_CLEAR_SDR_REP] = clear_sdr_rep, + [IPMI_CMD_GET_SEL_INFO] = get_sel_info, + [IPMI_CMD_RESERVE_SEL] = reserve_sel, + [IPMI_CMD_GET_SEL_ENTRY] = get_sel_entry, + [IPMI_CMD_ADD_SEL_ENTRY] = add_sel_entry, + [IPMI_CMD_CLEAR_SEL] = clear_sel, + [IPMI_CMD_GET_SEL_TIME] = get_sel_time, + [IPMI_CMD_SET_SEL_TIME] = set_sel_time, +}; +static IPMINetfn storage_netfn = { + .cmd_nums = IPMI_NETFN_STORAGE_MAXCMD, + .cmd_handlers = storage_cmds +}; + +static void register_cmds(IPMISimState *s) +{ + ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn); + ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn); + ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn); + ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn); +} + +static const uint8_t init_sdrs[] = { + /* Watchdog device */ + 0x00, 0x00, 0x51, 0x02, 40, 0x20, 0x00, 0x00, + 0x23, 0x01, 0x63, 0x00, 0x23, 0x6f, 0x0f, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, + 'W', 'a', 't', 'c', 'h', 'd', 'o', 'g', + /* End */ + 0xff, 0xff, 0x00, 0x00, 0x00 +}; + +static void ipmi_sim_init(IPMIState *s) +{ + unsigned int i; + unsigned int next_recid, recid; + IPMISimState *ss; + + ss = g_malloc0(sizeof(*ss)); + ss->bmc_global_enables = (1 << IPMI_BMC_EVENT_LOG_BIT); + ss->device_id = 0x20; + ss->ipmi_version = 0x02; /* IPMI 2.0 */ + for (i = 0; i < 4; i++) { + ss->sel.last_addition[i] = 0xff; + ss->sel.last_clear[i] = 0xff; + ss->sdr.last_addition[i] = 0xff; + ss->sdr.last_clear[i] = 0xff; + } + + next_recid = 0; + for (i = 0;;) { + int len; + if ((i + 5) > sizeof(init_sdrs)) { + ipmi_debug("Problem with recid 0x%4.4x\n", i); + break; + } + len = init_sdrs[i + 4]; + recid = init_sdrs[i] | (init_sdrs[i + 1] << 8); + if (recid == 0xffff) + break; + if ((i + len) > sizeof(init_sdrs)) { + ipmi_debug("Problem with recid 0x%4.4x\n", i); + break; + } + if (recid != next_recid) { + ipmi_debug("Problem with recid 0x%4.4x\n", i); + break; + } + sdr_add_entry(ss, init_sdrs + i, len, NULL); + i += len; + } + + ipmi_init_sensors_from_sdrs(ss); + register_cmds(ss); + + s->ifdata = ss; + s->handle_command = ipmi_sim_handle_command; + + ss->timer = qemu_new_timer_ns(vm_clock, ipmi_timeout, s); +} + +static void ipmi_sim_register_types(void) +{ + register_ipmi_sim(IPMI_SIM_INTERNAL, ipmi_sim_init); +} + +type_init(ipmi_sim_register_types) diff --git a/hw/ipmi_sim.h b/hw/ipmi_sim.h new file mode 100644 index 0000000..498036e --- /dev/null +++ b/hw/ipmi_sim.h @@ -0,0 +1,270 @@ +/* + * IPMI BMC emulation + * + * Copyright (c) 2012 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_IPMI_SIM_H +#define HW_IPMI_SIM_H + +#include +#include "qemu-timer.h" +#include "ipmi.h" + +#define IPMI_NETFN_CHASSIS 0x00 +#define IPMI_NETFN_CHASSIS_MAXCMD 0x03 + +#define IPMI_CMD_GET_CHASSIS_CAPABILITIES 0x00 +#define IPMI_CMD_GET_CHASSIS_STATUS 0x01 +#define IPMI_CMD_CHASSIS_CONTROL 0x02 + +#define IPMI_NETFN_SENSOR_EVENT 0x04 +#define IPMI_NETFN_SENSOR_EVENT_MAXCMD 0x2e + +#define IPMI_CMD_SET_SENSOR_EVT_ENABLE 0x28 +#define IPMI_CMD_GET_SENSOR_EVT_ENABLE 0x29 +#define IPMI_CMD_REARM_SENSOR_EVTS 0x2a +#define IPMI_CMD_GET_SENSOR_EVT_STATUS 0x2b +#define IPMI_CMD_GET_SENSOR_READING 0x2d + +/* #define IPMI_NETFN_APP 0x06 In ipmi.h */ +#define IPMI_NETFN_APP_MAXCMD 0x36 + +#define IPMI_CMD_GET_DEVICE_ID 0x01 +#define IPMI_CMD_RESET_WATCHDOG_TIMER 0x22 +#define IPMI_CMD_SET_WATCHDOG_TIMER 0x24 +#define IPMI_CMD_GET_WATCHDOG_TIMER 0x25 +#define IPMI_CMD_SET_BMC_GLOBAL_ENABLES 0x2e +#define IPMI_CMD_GET_BMC_GLOBAL_ENABLES 0x2f +#define IPMI_CMD_CLR_MSG_FLAGS 0x30 +#define IPMI_CMD_GET_MSG_FLAGS 0x31 +#define IPMI_CMD_READ_EVT_MSG_BUF 0x35 + +#define IPMI_NETFN_STORAGE 0x0a +#define IPMI_NETFN_STORAGE_MAXCMD 0x4a + +#define IPMI_CMD_GET_SDR_REP_INFO 0x20 +#define IPMI_CMD_GET_SDR_REP_ALLOC_INFO 0x21 +#define IPMI_CMD_RESERVE_SDR_REP 0x22 +#define IPMI_CMD_GET_SDR 0x23 +#define IPMI_CMD_ADD_SDR 0x24 +#define IPMI_CMD_PARTIAL_ADD_SDR 0x25 +#define IPMI_CMD_DELETE_SDR 0x26 +#define IPMI_CMD_CLEAR_SDR_REP 0x27 +#define IPMI_CMD_GET_SDR_REP_TIME 0x28 +#define IPMI_CMD_SET_SDR_REP_TIME 0x29 +#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A +#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B +#define IPMI_CMD_RUN_INIT_AGENT 0x2C +#define IPMI_CMD_GET_SEL_INFO 0x40 +#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41 +#define IPMI_CMD_RESERVE_SEL 0x42 +#define IPMI_CMD_GET_SEL_ENTRY 0x43 +#define IPMI_CMD_ADD_SEL_ENTRY 0x44 +#define IPMI_CMD_PARTIAL_ADD_SEL_ENTRY 0x45 +#define IPMI_CMD_DELETE_SEL_ENTRY 0x46 +#define IPMI_CMD_CLEAR_SEL 0x47 +#define IPMI_CMD_GET_SEL_TIME 0x48 +#define IPMI_CMD_SET_SEL_TIME 0x49 + + +/* Same as a timespec struct. */ +struct ipmi_time +{ + long tv_sec; + long tv_nsec; +}; + +#define MAX_SEL_SIZE 128 + +typedef struct IPMISel { + uint8_t sel[MAX_SEL_SIZE][16]; + unsigned int next_free; + long time_offset; + uint16_t reservation; + uint8_t last_addition[4]; + uint8_t last_clear[4]; + uint8_t overflow; +} IPMISel; + +#define MAX_SDR_SIZE 16384 + +typedef struct IPMISdr { + uint8_t sdr[MAX_SDR_SIZE]; + unsigned int next_free; + uint16_t next_rec_id; + uint16_t reservation; + uint8_t last_addition[4]; + uint8_t last_clear[4]; + uint8_t overflow; +} IPMISdr; + +typedef struct IPMISensor { + uint8_t status; + uint8_t reading; + uint16_t states_suppt; + uint16_t assert_suppt; + uint16_t deassert_suppt; + uint16_t states; + uint16_t assert_states; + uint16_t deassert_states; + uint16_t assert_enable; + uint16_t deassert_enable; + uint8_t sensor_type; + uint8_t evt_reading_type_code; +} IPMISensor; +#define IPMI_SENSOR_GET_PRESENT(s) ((s)->status & 0x01) +#define IPMI_SENSOR_SET_PRESENT(s,v) ((s)->status = (s->status & ~0x01) | \ + !!(v)) +#define IPMI_SENSOR_GET_SCAN_ON(s) ((s)->status & 0x40) +#define IPMI_SENSOR_SET_SCAN_ON(s,v) ((s)->status = (s->status & ~0x40) | \ + ((!!(v)) << 6)) +#define IPMI_SENSOR_GET_EVENTS_ON(s) ((s)->status & 0x80) +#define IPMI_SENSOR_SET_EVENTS_ON(s,v) ((s)->status = (s->status & ~0x80) | \ + ((!!(v)) << 7)) +#define IPMI_SENSOR_GET_RET_STATUS(s) ((s)->status & 0xc0) +#define IPMI_SENSOR_SET_RET_STATUS(s,v) ((s)->status = (s->status & ~0xc0) | \ + (v & 0xc0)) +#define IPMI_SENSOR_IS_DISCRETE(s) ((s)->evt_reading_type_code != 1) + +#define MAX_SENSORS 20 +#define IPMI_WATCHDOG_SENSOR 0 + +typedef struct IPMISimState IPMISimState; + +#define MAX_NETFNS 64 +typedef void (*IPMICmdHandler)(IPMIState *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len); +typedef struct IPMINetfn { + unsigned int cmd_nums; + IPMICmdHandler *cmd_handlers; +} IPMINetfn; + +struct IPMISimState { + QEMUTimer *timer; + + uint8_t bmc_global_enables; + uint8_t msg_flags; + + int8_t watchdog_initialized; + uint8_t watchdog_use; + uint8_t watchdog_action; + uint8_t watchdog_pretimeout; /* In seconds */ + uint8_t watchdog_expired; + uint16_t watchdog_timeout; /* in 100's of milliseconds */ + + int8_t watchdog_running; + int8_t watchdog_preaction_ran; + int64_t watchdog_expiry; + + uint8_t device_id; + uint8_t ipmi_version; + uint8_t device_rev; + uint8_t fwrev1; + uint8_t fwrev2; + uint8_t mfg_id[3]; + uint8_t product_id[2]; + + IPMISel sel; + IPMISdr sdr; + IPMISensor sensors[MAX_SENSORS]; + + /* Odd netfns are for responses, so we only need the even ones. */ + IPMINetfn *netfns[MAX_NETFNS / 2]; + + /* We allow one event in the buffer */ + uint8_t evtbuf[16]; +}; + +#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK (1 << 3) +#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL (1 << 1) +#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE (1 << 0) +#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(s) \ + (IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK & (s)->msg_flags) +#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s) \ + (IPMI_BMC_MSG_FLAG_EVT_BUF_FULL & (s)->msg_flags) +#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \ + (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags) + +#define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT 0 +#define IPMI_BMC_EVBUF_FULL_INT_BIT 1 +#define IPMI_BMC_EVENT_MSG_BUF_BIT 2 +#define IPMI_BMC_EVENT_LOG_BIT 3 +#define IPMI_BMC_MSG_INTS_ON(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT)) +#define IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVBUF_FULL_INT_BIT)) +#define IPMI_BMC_EVENT_LOG_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVENT_LOG_BIT)) +#define IPMI_BMC_EVENT_MSG_BUF_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVENT_MSG_BUF_BIT)) + +#define IPMI_BMC_WATCHDOG_USE_MASK 0xc7 +#define IPMI_BMC_WATCHDOG_ACTION_MASK 0x77 +#define IPMI_BMC_WATCHDOG_GET_USE(s) ((s)->watchdog_use & 0x7) +#define IPMI_BMC_WATCHDOG_GET_DONT_LOG(s) (((s)->watchdog_use >> 7) & 0x1) +#define IPMI_BMC_WATCHDOG_GET_DONT_STOP(s) (((s)->watchdog_use >> 6) & 0x1) +#define IPMI_BMC_WATCHDOG_GET_PRE_ACTION(s) (((s)->watchdog_action >> 4) & 0x7) +#define IPMI_BMC_WATCHDOG_PRE_NONE 0 +#define IPMI_BMC_WATCHDOG_PRE_SMI 1 +#define IPMI_BMC_WATCHDOG_PRE_NMI 2 +#define IPMI_BMC_WATCHDOG_PRE_MSG_INT 3 +#define IPMI_BMC_WATCHDOG_GET_ACTION(s) ((s)->watchdog_action & 0x7) +#define IPMI_BMC_WATCHDOG_ACTION_NONE 0 +#define IPMI_BMC_WATCHDOG_ACTION_RESET 1 +#define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2 +#define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3 + + +int ipmi_register_netfn(IPMISimState *s, unsigned int netfn, IPMINetfn *netfnd); +int ipmi_register_cmd(IPMISimState *s, unsigned int netfn, unsigned int cmd, + IPMICmdHandler handler); + +/* Add a byte to the response. */ +#define IPMI_ADD_RSP_DATA(b) \ + do { \ + if (*rsp_len >= max_rsp_len) { \ + rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED; \ + goto out; \ + } \ + rsp[(*rsp_len)++] = (b); \ + } while(0) + +/* Verify that the received command is a certain length. */ +#define IPMI_CHECK_CMD_LEN(l) \ + if (cmd_len < l) { \ + rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; \ + goto out; \ + } + +/* Check that the reservation in the command is valid. */ +#define IPMI_CHECK_RESERVATION(off, r) \ + do { \ + if ((cmd[off] | (cmd[off + 1] << 8)) != r) { \ + rsp[2] = IPMI_CC_INVALID_RESERVATION; \ + goto out; \ + } \ + } while(0) + + +#endif