Message ID | 20210909230620.511815-6-wuhaotsh@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Handing IPMI for emulating BMC | expand |
On Thu, Sep 09, 2021 at 04:06:17PM -0700, Hao Wu wrote: > This patch refactors ipmi_bmc_extern.c and takes out the parts that can > be used both ipmi_bmc_extern.c and bmc_host_extern.c to a common file > ipmi_extern.c. > > Now we have a connection called IPMIExtern which handles the connection, > and IPMIBmcExtern that handles core-side emulation specific stuff. > > Basically most of the message transaction are moved. The stuff remained > are basically hardware operations like handle_reset and handle_hw_op. > These stuff have different behaviors in core-side and BMC-side > emulation. Yeah, you are going to need this. -corey > > Signed-off-by: Hao Wu <wuhaotsh@google.com> > --- > hw/ipmi/ipmi_bmc_extern.c | 420 ++++---------------------------------- > hw/ipmi/ipmi_extern.c | 415 +++++++++++++++++++++++++++++++++++++ > hw/ipmi/ipmi_extern.h | 90 ++++++++ > hw/ipmi/meson.build | 2 +- > 4 files changed, 543 insertions(+), 384 deletions(-) > create mode 100644 hw/ipmi/ipmi_extern.c > create mode 100644 hw/ipmi/ipmi_extern.h > > diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c > index a0c3a40e7c..24979ecfd5 100644 > --- a/hw/ipmi/ipmi_bmc_extern.c > +++ b/hw/ipmi/ipmi_bmc_extern.c > @@ -34,211 +34,43 @@ > #include "qemu/timer.h" > #include "chardev/char-fe.h" > #include "hw/ipmi/ipmi.h" > +#include "hw/ipmi/ipmi_extern.h" > #include "hw/qdev-properties.h" > #include "hw/qdev-properties-system.h" > #include "migration/vmstate.h" > #include "qom/object.h" > > -#define VM_MSG_CHAR 0xA0 /* Marks end of message */ > -#define VM_CMD_CHAR 0xA1 /* Marks end of a command */ > -#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */ > - > -#define VM_PROTOCOL_VERSION 1 > -#define VM_CMD_VERSION 0xff /* A version number byte follows */ > -#define VM_CMD_NOATTN 0x00 > -#define VM_CMD_ATTN 0x01 > -#define VM_CMD_ATTN_IRQ 0x02 > -#define VM_CMD_POWEROFF 0x03 > -#define VM_CMD_RESET 0x04 > -#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */ > -#define VM_CMD_DISABLE_IRQ 0x06 > -#define VM_CMD_SEND_NMI 0x07 > -#define VM_CMD_CAPABILITIES 0x08 > -#define VM_CAPABILITIES_POWER 0x01 > -#define VM_CAPABILITIES_RESET 0x02 > -#define VM_CAPABILITIES_IRQ 0x04 > -#define VM_CAPABILITIES_NMI 0x08 > -#define VM_CAPABILITIES_ATTN 0x10 > -#define VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20 > -#define VM_CMD_GRACEFUL_SHUTDOWN 0x09 > - > #define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern" > OBJECT_DECLARE_SIMPLE_TYPE(IPMIBmcExtern, IPMI_BMC_EXTERN) > + > struct IPMIBmcExtern { > IPMIBmc parent; > > - CharBackend chr; > - > - bool connected; > - > - unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2]; > - unsigned int inpos; > - bool in_escape; > - bool in_too_many; > - bool waiting_rsp; > - bool sending_cmd; > - > - unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1]; > - unsigned int outpos; > - unsigned int outlen; > - > - struct QEMUTimer *extern_timer; > + IPMIExtern conn; > > /* A reset event is pending to be sent upstream. */ > bool send_reset; > }; > > -static unsigned char > -ipmb_checksum(const unsigned char *data, int size, unsigned char start) > +static void continue_send_bmc(IPMIBmcExtern *ibe) > { > - unsigned char csum = start; > - > - for (; size > 0; size--, data++) { > - csum += *data; > - } > - return csum; > -} > - > -static void continue_send(IPMIBmcExtern *ibe) > -{ > - int ret; > - if (ibe->outlen == 0) { > - goto check_reset; > - } > - send: > - ret = qemu_chr_fe_write(&ibe->chr, ibe->outbuf + ibe->outpos, > - ibe->outlen - ibe->outpos); > - if (ret > 0) { > - ibe->outpos += ret; > - } > - if (ibe->outpos < ibe->outlen) { > - /* Not fully transmitted, try again in a 10ms */ > - timer_mod_ns(ibe->extern_timer, > - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000); > - } else { > - /* Sent */ > - ibe->outlen = 0; > - ibe->outpos = 0; > - if (!ibe->sending_cmd) { > - ibe->waiting_rsp = true; > - } else { > - ibe->sending_cmd = false; > - } > - check_reset: > - if (ibe->connected && ibe->send_reset) { > + if (continue_send(&ibe->conn)) { > + if (ibe->conn.connected && ibe->send_reset) { > /* Send the reset */ > - ibe->outbuf[0] = VM_CMD_RESET; > - ibe->outbuf[1] = VM_CMD_CHAR; > - ibe->outlen = 2; > - ibe->outpos = 0; > + ibe->conn.outbuf[0] = VM_CMD_RESET; > + ibe->conn.outbuf[1] = VM_CMD_CHAR; > + ibe->conn.outlen = 2; > + ibe->conn.outpos = 0; > ibe->send_reset = false; > - ibe->sending_cmd = true; > - goto send; > - } > - > - if (ibe->waiting_rsp) { > - /* Make sure we get a response within 4 seconds. */ > - timer_mod_ns(ibe->extern_timer, > - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); > + ibe->conn.sending_cmd = true; > + continue_send(&ibe->conn); > } > } > - return; > } > > -static void extern_timeout(void *opaque) > +static void ipmi_bmc_handle_hw_op(IPMICore *ic, unsigned char hw_op, > + uint8_t operand) > { > - IPMICore *ic = opaque; > - IPMIBmcExtern *ibe = opaque; > - IPMIInterface *s = ic->intf; > - > - if (ibe->connected) { > - if (ibe->waiting_rsp && (ibe->outlen == 0)) { > - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > - /* The message response timed out, return an error. */ > - ibe->waiting_rsp = false; > - ibe->inbuf[1] = ibe->outbuf[1] | 0x04; > - ibe->inbuf[2] = ibe->outbuf[2]; > - ibe->inbuf[3] = IPMI_CC_TIMEOUT; > - k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); > - } else { > - continue_send(ibe); > - } > - } > -} > - > -static void addchar(IPMIBmcExtern *ibe, unsigned char ch) > -{ > - switch (ch) { > - case VM_MSG_CHAR: > - case VM_CMD_CHAR: > - case VM_ESCAPE_CHAR: > - ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR; > - ibe->outlen++; > - ch |= 0x10; > - /* fall through */ > - default: > - ibe->outbuf[ibe->outlen] = ch; > - ibe->outlen++; > - } > -} > - > -static void ipmi_bmc_extern_handle_command(IPMIBmc *b, > - uint8_t *cmd, unsigned int cmd_len, > - unsigned int max_cmd_len, > - uint8_t msg_id) > -{ > - IPMICore *ic = IPMI_CORE(b); > - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); > - IPMIInterface *s = ic->intf; > - uint8_t err = 0, csum; > - unsigned int i; > - > - if (ibe->outlen) { > - /* We already have a command queued. Shouldn't ever happen. */ > - error_report("IPMI KCS: Got command when not finished with the" > - " previous command"); > - abort(); > - } > - > - /* If it's too short or it was truncated, return an error. */ > - if (cmd_len < 2) { > - err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; > - } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) { > - err = IPMI_CC_REQUEST_DATA_TRUNCATED; > - } else if (!ibe->connected) { > - err = IPMI_CC_BMC_INIT_IN_PROGRESS; > - } > - if (err) { > - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > - unsigned char rsp[3]; > - rsp[0] = cmd[0] | 0x04; > - rsp[1] = cmd[1]; > - rsp[2] = err; > - ibe->waiting_rsp = false; > - k->handle_msg(s, msg_id, rsp, 3); > - goto out; > - } > - > - addchar(ibe, msg_id); > - for (i = 0; i < cmd_len; i++) { > - addchar(ibe, cmd[i]); > - } > - csum = ipmb_checksum(&msg_id, 1, 0); > - addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum)); > - > - ibe->outbuf[ibe->outlen] = VM_MSG_CHAR; > - ibe->outlen++; > - > - /* Start the transmit */ > - continue_send(ibe); > - > - out: > - return; > -} > - > -static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) > -{ > - IPMICore *ic = IPMI_CORE(ibe); > IPMIInterface *s = ic->intf; > IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > > @@ -285,169 +117,22 @@ static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) > } > } > > -static void handle_msg(IPMIBmcExtern *ibe) > -{ > - IPMICore *ic = IPMI_CORE(ibe); > - IPMIInterface *s = ic->intf; > - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > - > - if (ibe->in_escape) { > - ipmi_debug("msg escape not ended\n"); > - return; > - } > - if (ibe->inpos < 5) { > - ipmi_debug("msg too short\n"); > - return; > - } > - if (ibe->in_too_many) { > - ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED; > - ibe->inpos = 4; > - } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) { > - ipmi_debug("msg checksum failure\n"); > - return; > - } else { > - ibe->inpos--; /* Remove checkum */ > - } > - > - timer_del(ibe->extern_timer); > - ibe->waiting_rsp = false; > - k->handle_msg(s, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1); > -} > - > -static int can_receive(void *opaque) > -{ > - return 1; > -} > - > -static void receive(void *opaque, const uint8_t *buf, int size) > +static void ipmi_bmc_extern_handle_command(IPMIBmc *b, > + uint8_t *cmd, unsigned int cmd_len, > + unsigned int max_cmd_len, > + uint8_t msg_id) > { > - IPMIBmcExtern *ibe = opaque; > - int i; > - unsigned char hw_op; > - > - for (i = 0; i < size; i++) { > - unsigned char ch = buf[i]; > - > - switch (ch) { > - case VM_MSG_CHAR: > - handle_msg(ibe); > - ibe->in_too_many = false; > - ibe->inpos = 0; > - break; > - > - case VM_CMD_CHAR: > - if (ibe->in_too_many) { > - ipmi_debug("cmd in too many\n"); > - ibe->in_too_many = false; > - ibe->inpos = 0; > - break; > - } > - if (ibe->in_escape) { > - ipmi_debug("cmd in escape\n"); > - ibe->in_too_many = false; > - ibe->inpos = 0; > - ibe->in_escape = false; > - break; > - } > - ibe->in_too_many = false; > - if (ibe->inpos < 1) { > - break; > - } > - hw_op = ibe->inbuf[0]; > - ibe->inpos = 0; > - goto out_hw_op; > - break; > - > - case VM_ESCAPE_CHAR: > - ibe->in_escape = true; > - break; > - > - default: > - if (ibe->in_escape) { > - ch &= ~0x10; > - ibe->in_escape = false; > - } > - if (ibe->in_too_many) { > - break; > - } > - if (ibe->inpos >= sizeof(ibe->inbuf)) { > - ibe->in_too_many = true; > - break; > - } > - ibe->inbuf[ibe->inpos] = ch; > - ibe->inpos++; > - break; > - } > - } > - return; > + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); > > - out_hw_op: > - handle_hw_op(ibe, hw_op); > + ipmi_extern_handle_command(&ibe->conn, cmd, cmd_len, max_cmd_len, msg_id); > } > > -static void chr_event(void *opaque, QEMUChrEvent event) > +static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) > { > - IPMICore *ic = opaque; > - IPMIBmcExtern *ibe = opaque; > - IPMIInterface *s = ic->intf; > - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > - unsigned char v; > - > - switch (event) { > - case CHR_EVENT_OPENED: > - ibe->connected = true; > - ibe->outpos = 0; > - ibe->outlen = 0; > - addchar(ibe, VM_CMD_VERSION); > - addchar(ibe, VM_PROTOCOL_VERSION); > - ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; > - ibe->outlen++; > - addchar(ibe, VM_CMD_CAPABILITIES); > - v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN; > - if (k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1) == 0) { > - v |= VM_CAPABILITIES_POWER; > - } > - if (k->do_hw_op(s, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 1) > - == 0) { > - v |= VM_CAPABILITIES_GRACEFUL_SHUTDOWN; > - } > - if (k->do_hw_op(s, IPMI_RESET_CHASSIS, 1) == 0) { > - v |= VM_CAPABILITIES_RESET; > - } > - if (k->do_hw_op(s, IPMI_SEND_NMI, 1) == 0) { > - v |= VM_CAPABILITIES_NMI; > - } > - addchar(ibe, v); > - ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; > - ibe->outlen++; > - ibe->sending_cmd = false; > - continue_send(ibe); > - break; > - > - case CHR_EVENT_CLOSED: > - if (!ibe->connected) { > - return; > - } > - ibe->connected = false; > - /* > - * Don't hang the OS trying to handle the ATN bit, other end will > - * resend on a reconnect. > - */ > - k->set_atn(s, 0, 0); > - if (ibe->waiting_rsp) { > - ibe->waiting_rsp = false; > - ibe->inbuf[1] = ibe->outbuf[1] | 0x04; > - ibe->inbuf[2] = ibe->outbuf[2]; > - ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; > - k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); > - } > - break; > + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); > > - case CHR_EVENT_BREAK: > - case CHR_EVENT_MUX_IN: > - case CHR_EVENT_MUX_OUT: > - /* Ignore */ > - break; > + if (!qdev_realize(DEVICE(&ibe->conn), NULL, errp)) { > + return; > } > } > > @@ -456,42 +141,14 @@ static void ipmi_bmc_extern_handle_reset(IPMIBmc *b) > IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); > > ibe->send_reset = true; > - continue_send(ibe); > -} > - > -static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) > -{ > - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); > - > - if (!qemu_chr_fe_backend_connected(&ibe->chr)) { > - error_setg(errp, "IPMI external bmc requires chardev attribute"); > - return; > - } > - > - qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, > - chr_event, NULL, ibe, NULL, true); > + continue_send_bmc(ibe); > } > > static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) > { > - IPMIBmcExtern *ibe = opaque; > - > - /* > - * We don't directly restore waiting_rsp, Instead, we return an > - * error on the interface if a response was being waited for. > - */ > - if (ibe->waiting_rsp) { > - IPMICore *ic = opaque; > - IPMIInterface *ii = ic->intf; > - IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); > - > - ibe->waiting_rsp = false; > - ibe->inbuf[1] = ibe->outbuf[1] | 0x04; > - ibe->inbuf[2] = ibe->outbuf[2]; > - ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; > - iic->handle_msg(ii, ibe->outbuf[0], ibe->inbuf + 1, 3); > - } > - return 0; > + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(opaque); > + > + return ipmi_extern_post_migrate(&ibe->conn, version_id); > } > > static const VMStateDescription vmstate_ipmi_bmc_extern = { > @@ -501,28 +158,24 @@ static const VMStateDescription vmstate_ipmi_bmc_extern = { > .post_load = ipmi_bmc_extern_post_migrate, > .fields = (VMStateField[]) { > VMSTATE_BOOL(send_reset, IPMIBmcExtern), > - VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern), > + VMSTATE_BOOL(conn.waiting_rsp, IPMIBmcExtern), > VMSTATE_END_OF_LIST() > } > }; > > static void ipmi_bmc_extern_init(Object *obj) > { > + IPMICore *ic = IPMI_CORE(obj); > IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); > > - ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); > + object_initialize_child(obj, "extern", &ibe->conn, > + TYPE_IPMI_EXTERN); > + ibe->conn.core = ic; > vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); > } > > -static void ipmi_bmc_extern_finalize(Object *obj) > -{ > - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); > - > - timer_free(ibe->extern_timer); > -} > - > static Property ipmi_bmc_extern_properties[] = { > - DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), > + DEFINE_PROP_CHR("chardev", IPMIBmcExtern, conn.chr), > DEFINE_PROP_END_OF_LIST(), > }; > > @@ -530,9 +183,11 @@ static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) > { > DeviceClass *dc = DEVICE_CLASS(oc); > IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); > + IPMICoreClass *ck = IPMI_CORE_CLASS(oc); > > bk->handle_command = ipmi_bmc_extern_handle_command; > bk->handle_reset = ipmi_bmc_extern_handle_reset; > + ck->handle_hw_op = ipmi_bmc_handle_hw_op; > dc->hotpluggable = false; > dc->realize = ipmi_bmc_extern_realize; > device_class_set_props(dc, ipmi_bmc_extern_properties); > @@ -543,9 +198,8 @@ static const TypeInfo ipmi_bmc_extern_type = { > .parent = TYPE_IPMI_BMC, > .instance_size = sizeof(IPMIBmcExtern), > .instance_init = ipmi_bmc_extern_init, > - .instance_finalize = ipmi_bmc_extern_finalize, > .class_init = ipmi_bmc_extern_class_init, > - }; > +}; > > static void ipmi_bmc_extern_register_types(void) > { > diff --git a/hw/ipmi/ipmi_extern.c b/hw/ipmi/ipmi_extern.c > new file mode 100644 > index 0000000000..f139eaef24 > --- /dev/null > +++ b/hw/ipmi/ipmi_extern.c > @@ -0,0 +1,415 @@ > +/* > + * IPMI external connection > + * > + * Copyright (c) 2015 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 "qemu/osdep.h" > +#include "qemu/error-report.h" > +#include "qemu/module.h" > +#include "qapi/error.h" > +#include "qemu/timer.h" > +#include "chardev/char-fe.h" > +#include "hw/ipmi/ipmi.h" > +#include "hw/ipmi/ipmi_extern.h" > +#include "hw/qdev-properties.h" > +#include "migration/vmstate.h" > +#include "qom/object.h" > + > +static unsigned char > +ipmb_checksum(const unsigned char *data, int size, unsigned char start) > +{ > + unsigned char csum = start; > + > + for (; size > 0; size--, data++) { > + csum += *data; > + } > + return csum; > +} > + > +/* Returns whether check_reset is required for IPMI_BMC_EXTERN. */ > +bool continue_send(IPMIExtern *ibe) > +{ > + int ret; > + if (ibe->outlen == 0) { > + return true; > + } > + ret = qemu_chr_fe_write(&ibe->chr, ibe->outbuf + ibe->outpos, > + ibe->outlen - ibe->outpos); > + if (ret > 0) { > + ibe->outpos += ret; > + } > + if (ibe->outpos < ibe->outlen) { > + /* Not fully transmitted, try again in a 10ms */ > + timer_mod_ns(ibe->extern_timer, > + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000); > + return false; > + } else { > + /* Sent */ > + ibe->outlen = 0; > + ibe->outpos = 0; > + if (!ibe->bmc_side && !ibe->sending_cmd) { > + ibe->waiting_rsp = true; > + } else { > + ibe->sending_cmd = false; > + } > + > + if (ibe->waiting_rsp) { > + /* Make sure we get a response within 4 seconds. */ > + timer_mod_ns(ibe->extern_timer, > + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); > + } > + return true; > + } > +} > + > +static void extern_timeout(void *opaque) > +{ > + IPMIExtern *ibe = opaque; > + IPMIInterface *s = ibe->core->intf; > + > + if (ibe->connected) { > + /*TODO: only core-side */ > + if (ibe->waiting_rsp && (ibe->outlen == 0)) { > + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > + /* The message response timed out, return an error. */ > + ibe->waiting_rsp = false; > + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; > + ibe->inbuf[2] = ibe->outbuf[2]; > + ibe->inbuf[3] = IPMI_CC_TIMEOUT; > + k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); > + } else { > + continue_send(ibe); > + } > + } > +} > + > +static void addchar(IPMIExtern *ibe, unsigned char ch) > +{ > + switch (ch) { > + case VM_MSG_CHAR: > + case VM_CMD_CHAR: > + case VM_ESCAPE_CHAR: > + ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR; > + ibe->outlen++; > + ch |= 0x10; > + /* fall through */ > + default: > + ibe->outbuf[ibe->outlen] = ch; > + ibe->outlen++; > + } > +} > + > +void ipmi_extern_handle_command(IPMIExtern *ibe, > + uint8_t *cmd, unsigned int cmd_len, > + unsigned int max_cmd_len, > + uint8_t msg_id) > +{ > + IPMIInterface *s = ibe->core->intf; > + uint8_t err = 0, csum; > + unsigned int i; > + > + if (ibe->outlen) { > + /* We already have a command queued. Shouldn't ever happen. */ > + error_report("IPMI KCS: Got command when not finished with the" > + " previous command"); > + abort(); > + } > + > + /* If it's too short or it was truncated, return an error. */ > + if (cmd_len < 2) { > + err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; > + } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) { > + err = IPMI_CC_REQUEST_DATA_TRUNCATED; > + } else if (!ibe->connected) { > + err = IPMI_CC_BMC_INIT_IN_PROGRESS; > + } > + if (err) { > + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > + unsigned char rsp[3]; > + rsp[0] = cmd[0] | 0x04; > + rsp[1] = cmd[1]; > + rsp[2] = err; > + ibe->waiting_rsp = false; > + k->handle_msg(s, msg_id, rsp, 3); > + goto out; > + } > + > + addchar(ibe, msg_id); > + for (i = 0; i < cmd_len; i++) { > + addchar(ibe, cmd[i]); > + } > + csum = ipmb_checksum(&msg_id, 1, 0); > + addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum)); > + > + ibe->outbuf[ibe->outlen] = VM_MSG_CHAR; > + ibe->outlen++; > + > + /* Start the transmit */ > + continue_send(ibe); > + > + out: > + return; > +} > + > +static void handle_msg(IPMIExtern *ibe) > +{ > + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->core->intf); > + > + if (ibe->in_escape) { > + ipmi_debug("msg escape not ended\n"); > + return; > + } > + if (ibe->inpos < (ibe->bmc_side ? 4 : 5)) { > + ipmi_debug("msg too short\n"); > + return; > + } > + if (ibe->in_too_many) { > + ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED; > + ibe->inpos = 4; > + } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) { > + ipmi_debug("msg checksum failure\n"); > + return; > + } else { > + ibe->inpos--; /* Remove checkum */ > + } > + > + timer_del(ibe->extern_timer); > + ibe->waiting_rsp = false; > + k->handle_msg(ibe->core->intf, ibe->inbuf[0], > + ibe->inbuf + 1, ibe->inpos - 1); > +} > + > +static int can_receive(void *opaque) > +{ > + return 1; > +} > + > +static void receive(void *opaque, const uint8_t *buf, int size) > +{ > + IPMIExtern *ibe = opaque; > + IPMICoreClass *ck = IPMI_CORE_GET_CLASS(ibe->core); > + int i; > + unsigned char hw_op; > + unsigned char hw_operand = 0; > + > + for (i = 0; i < size; i++) { > + unsigned char ch = buf[i]; > + > + switch (ch) { > + case VM_MSG_CHAR: > + handle_msg(ibe); > + ibe->in_too_many = false; > + ibe->inpos = 0; > + break; > + > + case VM_CMD_CHAR: > + if (ibe->in_too_many) { > + ipmi_debug("cmd in too many\n"); > + ibe->in_too_many = false; > + ibe->inpos = 0; > + break; > + } > + if (ibe->in_escape) { > + ipmi_debug("cmd in escape\n"); > + ibe->in_too_many = false; > + ibe->inpos = 0; > + ibe->in_escape = false; > + break; > + } > + ibe->in_too_many = false; > + if (ibe->inpos < 1) { > + break; > + } > + hw_op = ibe->inbuf[0]; > + if (ibe->inpos > 1) { > + hw_operand = ibe->inbuf[1]; > + } > + ibe->inpos = 0; > + goto out_hw_op; > + break; > + > + case VM_ESCAPE_CHAR: > + ibe->in_escape = true; > + break; > + > + default: > + if (ibe->in_escape) { > + ch &= ~0x10; > + ibe->in_escape = false; > + } > + if (ibe->in_too_many) { > + break; > + } > + if (ibe->inpos >= sizeof(ibe->inbuf)) { > + ibe->in_too_many = true; > + break; > + } > + ibe->inbuf[ibe->inpos] = ch; > + ibe->inpos++; > + break; > + } > + } > + return; > + > + out_hw_op: > + ck->handle_hw_op(ibe->core, hw_op, hw_operand); > +} > + > +static void chr_event(void *opaque, QEMUChrEvent event) > +{ > + IPMIExtern *ibe = opaque; > + IPMIInterface *s = ibe->core->intf; > + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); > + unsigned char v; > + > + switch (event) { > + case CHR_EVENT_OPENED: > + ibe->connected = true; > + ibe->outpos = 0; > + ibe->outlen = 0; > + addchar(ibe, VM_CMD_VERSION); > + addchar(ibe, VM_PROTOCOL_VERSION); > + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; > + ibe->outlen++; > + /* Only send capability for core side. */ > + if (!ibe->bmc_side) { > + addchar(ibe, VM_CMD_CAPABILITIES); > + v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN; > + if (k->do_hw_op(ibe->core->intf, IPMI_POWEROFF_CHASSIS, 1) == 0) { > + v |= VM_CAPABILITIES_POWER; > + } > + if (k->do_hw_op(ibe->core->intf, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 1) > + == 0) { > + v |= VM_CAPABILITIES_GRACEFUL_SHUTDOWN; > + } > + if (k->do_hw_op(ibe->core->intf, IPMI_RESET_CHASSIS, 1) == 0) { > + v |= VM_CAPABILITIES_RESET; > + } > + if (k->do_hw_op(ibe->core->intf, IPMI_SEND_NMI, 1) == 0) { > + v |= VM_CAPABILITIES_NMI; > + } > + addchar(ibe, v); > + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; > + ibe->outlen++; > + } > + ibe->sending_cmd = false; > + continue_send(ibe); > + break; > + > + case CHR_EVENT_CLOSED: > + if (!ibe->connected) { > + return; > + } > + ibe->connected = false; > + /* > + * Don't hang the OS trying to handle the ATN bit, other end will > + * resend on a reconnect. > + */ > + k->set_atn(s, 0, 0); > + if (ibe->waiting_rsp) { > + ibe->waiting_rsp = false; > + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; > + ibe->inbuf[2] = ibe->outbuf[2]; > + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; > + k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); > + } > + break; > + > + case CHR_EVENT_BREAK: > + case CHR_EVENT_MUX_IN: > + case CHR_EVENT_MUX_OUT: > + /* Ignore */ > + break; > + } > +} > + > +static void ipmi_extern_realize(DeviceState *dev, Error **errp) > +{ > + IPMIExtern *ibe = IPMI_EXTERN(dev); > + > + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { > + error_setg(errp, "IPMI external bmc requires chardev attribute"); > + return; > + } > + > + qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, > + chr_event, NULL, ibe, NULL, true); > +} > + > +int ipmi_extern_post_migrate(IPMIExtern *ibe, int version_id) > +{ > + /* > + * We don't directly restore waiting_rsp, Instead, we return an > + * error on the interface if a response was being waited for. > + */ > + if (ibe->waiting_rsp) { > + IPMIInterface *ii = ibe->core->intf; > + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); > + > + ibe->waiting_rsp = false; > + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; > + ibe->inbuf[2] = ibe->outbuf[2]; > + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; > + iic->handle_msg(ii, ibe->outbuf[0], ibe->inbuf + 1, 3); > + } > + return 0; > +} > + > +static void ipmi_extern_init(Object *obj) > +{ > + IPMIExtern *ibe = IPMI_EXTERN(obj); > + > + ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); > +} > + > +static void ipmi_extern_finalize(Object *obj) > +{ > + IPMIExtern *ibe = IPMI_EXTERN(obj); > + > + timer_del(ibe->extern_timer); > + timer_free(ibe->extern_timer); > +} > + > + > +static void ipmi_extern_class_init(ObjectClass *oc, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(oc); > + > + dc->hotpluggable = false; > + dc->realize = ipmi_extern_realize; > +} > + > +static const TypeInfo ipmi_extern_type = { > + .name = TYPE_IPMI_EXTERN, > + .parent = TYPE_DEVICE, > + .instance_size = sizeof(IPMIExtern), > + .instance_init = ipmi_extern_init, > + .instance_finalize = ipmi_extern_finalize, > + .class_init = ipmi_extern_class_init, > + }; > + > +static void ipmi_extern_register_types(void) > +{ > + type_register_static(&ipmi_extern_type); > +} > + > +type_init(ipmi_extern_register_types) > diff --git a/hw/ipmi/ipmi_extern.h b/hw/ipmi/ipmi_extern.h > new file mode 100644 > index 0000000000..e4aa80a0f6 > --- /dev/null > +++ b/hw/ipmi/ipmi_extern.h > @@ -0,0 +1,90 @@ > +/* > + * IPMI external connection > + * > + * Copyright (c) 2015 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_EXTERN_H > +#define HW_IPMI_EXTERN_H > + > +#include "qemu/osdep.h" > +#include "hw/ipmi/ipmi.h" > + > +#define VM_MSG_CHAR 0xA0 /* Marks end of message */ > +#define VM_CMD_CHAR 0xA1 /* Marks end of a command */ > +#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */ > + > +#define VM_PROTOCOL_VERSION 1 > +#define VM_CMD_VERSION 0xff /* A version number byte follows */ > +#define VM_CMD_NOATTN 0x00 > +#define VM_CMD_ATTN 0x01 > +#define VM_CMD_ATTN_IRQ 0x02 > +#define VM_CMD_POWEROFF 0x03 > +#define VM_CMD_RESET 0x04 > +#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */ > +#define VM_CMD_DISABLE_IRQ 0x06 > +#define VM_CMD_SEND_NMI 0x07 > +#define VM_CMD_CAPABILITIES 0x08 > +#define VM_CAPABILITIES_POWER 0x01 > +#define VM_CAPABILITIES_RESET 0x02 > +#define VM_CAPABILITIES_IRQ 0x04 > +#define VM_CAPABILITIES_NMI 0x08 > +#define VM_CAPABILITIES_ATTN 0x10 > +#define VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20 > +#define VM_CMD_GRACEFUL_SHUTDOWN 0x09 > + > +typedef struct IPMIExtern { > + DeviceState parent; > + > + CharBackend chr; > + > + IPMICore *core; > + > + bool bmc_side; > + bool connected; > + > + unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2]; > + unsigned int inpos; > + bool in_escape; > + bool in_too_many; > + bool waiting_rsp; > + bool sending_cmd; > + > + unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1]; > + unsigned int outpos; > + unsigned int outlen; > + > + struct QEMUTimer *extern_timer; > +} IPMIExtern; > + > +#define TYPE_IPMI_EXTERN "ipmi-extern" > +#define IPMI_EXTERN(obj) \ > + OBJECT_CHECK(IPMIExtern, (obj), TYPE_IPMI_EXTERN) > + > +void ipmi_extern_handle_command(IPMIExtern *ibe, > + uint8_t *cmd, unsigned int cmd_len, > + unsigned int max_cmd_len, > + uint8_t msg_id); > + > +bool continue_send(IPMIExtern *ibe); > +int ipmi_extern_post_migrate(IPMIExtern *ibe, int version_id); > + > +#endif /* HW_IPMI_EXTERN_H */ > diff --git a/hw/ipmi/meson.build b/hw/ipmi/meson.build > index 9622ea2a2c..edd0bf9af9 100644 > --- a/hw/ipmi/meson.build > +++ b/hw/ipmi/meson.build > @@ -1,5 +1,5 @@ > ipmi_ss = ss.source_set() > -ipmi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c', 'ipmi_kcs.c', 'ipmi_bt.c')) > +ipmi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c', 'ipmi_kcs.c', 'ipmi_bt.c', 'ipmi_extern.c')) > ipmi_ss.add(when: 'CONFIG_IPMI_LOCAL', if_true: files('ipmi_bmc_sim.c')) > ipmi_ss.add(when: 'CONFIG_IPMI_EXTERN', if_true: files('ipmi_bmc_extern.c')) > ipmi_ss.add(when: 'CONFIG_ISA_IPMI_KCS', if_true: files('isa_ipmi_kcs.c')) > -- > 2.33.0.309.g3052b89438-goog >
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index a0c3a40e7c..24979ecfd5 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -34,211 +34,43 @@ #include "qemu/timer.h" #include "chardev/char-fe.h" #include "hw/ipmi/ipmi.h" +#include "hw/ipmi/ipmi_extern.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" #include "qom/object.h" -#define VM_MSG_CHAR 0xA0 /* Marks end of message */ -#define VM_CMD_CHAR 0xA1 /* Marks end of a command */ -#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */ - -#define VM_PROTOCOL_VERSION 1 -#define VM_CMD_VERSION 0xff /* A version number byte follows */ -#define VM_CMD_NOATTN 0x00 -#define VM_CMD_ATTN 0x01 -#define VM_CMD_ATTN_IRQ 0x02 -#define VM_CMD_POWEROFF 0x03 -#define VM_CMD_RESET 0x04 -#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */ -#define VM_CMD_DISABLE_IRQ 0x06 -#define VM_CMD_SEND_NMI 0x07 -#define VM_CMD_CAPABILITIES 0x08 -#define VM_CAPABILITIES_POWER 0x01 -#define VM_CAPABILITIES_RESET 0x02 -#define VM_CAPABILITIES_IRQ 0x04 -#define VM_CAPABILITIES_NMI 0x08 -#define VM_CAPABILITIES_ATTN 0x10 -#define VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20 -#define VM_CMD_GRACEFUL_SHUTDOWN 0x09 - #define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern" OBJECT_DECLARE_SIMPLE_TYPE(IPMIBmcExtern, IPMI_BMC_EXTERN) + struct IPMIBmcExtern { IPMIBmc parent; - CharBackend chr; - - bool connected; - - unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2]; - unsigned int inpos; - bool in_escape; - bool in_too_many; - bool waiting_rsp; - bool sending_cmd; - - unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1]; - unsigned int outpos; - unsigned int outlen; - - struct QEMUTimer *extern_timer; + IPMIExtern conn; /* A reset event is pending to be sent upstream. */ bool send_reset; }; -static unsigned char -ipmb_checksum(const unsigned char *data, int size, unsigned char start) +static void continue_send_bmc(IPMIBmcExtern *ibe) { - unsigned char csum = start; - - for (; size > 0; size--, data++) { - csum += *data; - } - return csum; -} - -static void continue_send(IPMIBmcExtern *ibe) -{ - int ret; - if (ibe->outlen == 0) { - goto check_reset; - } - send: - ret = qemu_chr_fe_write(&ibe->chr, ibe->outbuf + ibe->outpos, - ibe->outlen - ibe->outpos); - if (ret > 0) { - ibe->outpos += ret; - } - if (ibe->outpos < ibe->outlen) { - /* Not fully transmitted, try again in a 10ms */ - timer_mod_ns(ibe->extern_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000); - } else { - /* Sent */ - ibe->outlen = 0; - ibe->outpos = 0; - if (!ibe->sending_cmd) { - ibe->waiting_rsp = true; - } else { - ibe->sending_cmd = false; - } - check_reset: - if (ibe->connected && ibe->send_reset) { + if (continue_send(&ibe->conn)) { + if (ibe->conn.connected && ibe->send_reset) { /* Send the reset */ - ibe->outbuf[0] = VM_CMD_RESET; - ibe->outbuf[1] = VM_CMD_CHAR; - ibe->outlen = 2; - ibe->outpos = 0; + ibe->conn.outbuf[0] = VM_CMD_RESET; + ibe->conn.outbuf[1] = VM_CMD_CHAR; + ibe->conn.outlen = 2; + ibe->conn.outpos = 0; ibe->send_reset = false; - ibe->sending_cmd = true; - goto send; - } - - if (ibe->waiting_rsp) { - /* Make sure we get a response within 4 seconds. */ - timer_mod_ns(ibe->extern_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); + ibe->conn.sending_cmd = true; + continue_send(&ibe->conn); } } - return; } -static void extern_timeout(void *opaque) +static void ipmi_bmc_handle_hw_op(IPMICore *ic, unsigned char hw_op, + uint8_t operand) { - IPMICore *ic = opaque; - IPMIBmcExtern *ibe = opaque; - IPMIInterface *s = ic->intf; - - if (ibe->connected) { - if (ibe->waiting_rsp && (ibe->outlen == 0)) { - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); - /* The message response timed out, return an error. */ - ibe->waiting_rsp = false; - ibe->inbuf[1] = ibe->outbuf[1] | 0x04; - ibe->inbuf[2] = ibe->outbuf[2]; - ibe->inbuf[3] = IPMI_CC_TIMEOUT; - k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); - } else { - continue_send(ibe); - } - } -} - -static void addchar(IPMIBmcExtern *ibe, unsigned char ch) -{ - switch (ch) { - case VM_MSG_CHAR: - case VM_CMD_CHAR: - case VM_ESCAPE_CHAR: - ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR; - ibe->outlen++; - ch |= 0x10; - /* fall through */ - default: - ibe->outbuf[ibe->outlen] = ch; - ibe->outlen++; - } -} - -static void ipmi_bmc_extern_handle_command(IPMIBmc *b, - uint8_t *cmd, unsigned int cmd_len, - unsigned int max_cmd_len, - uint8_t msg_id) -{ - IPMICore *ic = IPMI_CORE(b); - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); - IPMIInterface *s = ic->intf; - uint8_t err = 0, csum; - unsigned int i; - - if (ibe->outlen) { - /* We already have a command queued. Shouldn't ever happen. */ - error_report("IPMI KCS: Got command when not finished with the" - " previous command"); - abort(); - } - - /* If it's too short or it was truncated, return an error. */ - if (cmd_len < 2) { - err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; - } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) { - err = IPMI_CC_REQUEST_DATA_TRUNCATED; - } else if (!ibe->connected) { - err = IPMI_CC_BMC_INIT_IN_PROGRESS; - } - if (err) { - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); - unsigned char rsp[3]; - rsp[0] = cmd[0] | 0x04; - rsp[1] = cmd[1]; - rsp[2] = err; - ibe->waiting_rsp = false; - k->handle_msg(s, msg_id, rsp, 3); - goto out; - } - - addchar(ibe, msg_id); - for (i = 0; i < cmd_len; i++) { - addchar(ibe, cmd[i]); - } - csum = ipmb_checksum(&msg_id, 1, 0); - addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum)); - - ibe->outbuf[ibe->outlen] = VM_MSG_CHAR; - ibe->outlen++; - - /* Start the transmit */ - continue_send(ibe); - - out: - return; -} - -static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) -{ - IPMICore *ic = IPMI_CORE(ibe); IPMIInterface *s = ic->intf; IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); @@ -285,169 +117,22 @@ static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) } } -static void handle_msg(IPMIBmcExtern *ibe) -{ - IPMICore *ic = IPMI_CORE(ibe); - IPMIInterface *s = ic->intf; - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); - - if (ibe->in_escape) { - ipmi_debug("msg escape not ended\n"); - return; - } - if (ibe->inpos < 5) { - ipmi_debug("msg too short\n"); - return; - } - if (ibe->in_too_many) { - ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED; - ibe->inpos = 4; - } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) { - ipmi_debug("msg checksum failure\n"); - return; - } else { - ibe->inpos--; /* Remove checkum */ - } - - timer_del(ibe->extern_timer); - ibe->waiting_rsp = false; - k->handle_msg(s, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1); -} - -static int can_receive(void *opaque) -{ - return 1; -} - -static void receive(void *opaque, const uint8_t *buf, int size) +static void ipmi_bmc_extern_handle_command(IPMIBmc *b, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id) { - IPMIBmcExtern *ibe = opaque; - int i; - unsigned char hw_op; - - for (i = 0; i < size; i++) { - unsigned char ch = buf[i]; - - switch (ch) { - case VM_MSG_CHAR: - handle_msg(ibe); - ibe->in_too_many = false; - ibe->inpos = 0; - break; - - case VM_CMD_CHAR: - if (ibe->in_too_many) { - ipmi_debug("cmd in too many\n"); - ibe->in_too_many = false; - ibe->inpos = 0; - break; - } - if (ibe->in_escape) { - ipmi_debug("cmd in escape\n"); - ibe->in_too_many = false; - ibe->inpos = 0; - ibe->in_escape = false; - break; - } - ibe->in_too_many = false; - if (ibe->inpos < 1) { - break; - } - hw_op = ibe->inbuf[0]; - ibe->inpos = 0; - goto out_hw_op; - break; - - case VM_ESCAPE_CHAR: - ibe->in_escape = true; - break; - - default: - if (ibe->in_escape) { - ch &= ~0x10; - ibe->in_escape = false; - } - if (ibe->in_too_many) { - break; - } - if (ibe->inpos >= sizeof(ibe->inbuf)) { - ibe->in_too_many = true; - break; - } - ibe->inbuf[ibe->inpos] = ch; - ibe->inpos++; - break; - } - } - return; + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); - out_hw_op: - handle_hw_op(ibe, hw_op); + ipmi_extern_handle_command(&ibe->conn, cmd, cmd_len, max_cmd_len, msg_id); } -static void chr_event(void *opaque, QEMUChrEvent event) +static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) { - IPMICore *ic = opaque; - IPMIBmcExtern *ibe = opaque; - IPMIInterface *s = ic->intf; - IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); - unsigned char v; - - switch (event) { - case CHR_EVENT_OPENED: - ibe->connected = true; - ibe->outpos = 0; - ibe->outlen = 0; - addchar(ibe, VM_CMD_VERSION); - addchar(ibe, VM_PROTOCOL_VERSION); - ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; - ibe->outlen++; - addchar(ibe, VM_CMD_CAPABILITIES); - v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN; - if (k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1) == 0) { - v |= VM_CAPABILITIES_POWER; - } - if (k->do_hw_op(s, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 1) - == 0) { - v |= VM_CAPABILITIES_GRACEFUL_SHUTDOWN; - } - if (k->do_hw_op(s, IPMI_RESET_CHASSIS, 1) == 0) { - v |= VM_CAPABILITIES_RESET; - } - if (k->do_hw_op(s, IPMI_SEND_NMI, 1) == 0) { - v |= VM_CAPABILITIES_NMI; - } - addchar(ibe, v); - ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; - ibe->outlen++; - ibe->sending_cmd = false; - continue_send(ibe); - break; - - case CHR_EVENT_CLOSED: - if (!ibe->connected) { - return; - } - ibe->connected = false; - /* - * Don't hang the OS trying to handle the ATN bit, other end will - * resend on a reconnect. - */ - k->set_atn(s, 0, 0); - if (ibe->waiting_rsp) { - ibe->waiting_rsp = false; - ibe->inbuf[1] = ibe->outbuf[1] | 0x04; - ibe->inbuf[2] = ibe->outbuf[2]; - ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; - k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); - } - break; + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - case CHR_EVENT_BREAK: - case CHR_EVENT_MUX_IN: - case CHR_EVENT_MUX_OUT: - /* Ignore */ - break; + if (!qdev_realize(DEVICE(&ibe->conn), NULL, errp)) { + return; } } @@ -456,42 +141,14 @@ static void ipmi_bmc_extern_handle_reset(IPMIBmc *b) IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); ibe->send_reset = true; - continue_send(ibe); -} - -static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) -{ - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - - if (!qemu_chr_fe_backend_connected(&ibe->chr)) { - error_setg(errp, "IPMI external bmc requires chardev attribute"); - return; - } - - qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, - chr_event, NULL, ibe, NULL, true); + continue_send_bmc(ibe); } static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) { - IPMIBmcExtern *ibe = opaque; - - /* - * We don't directly restore waiting_rsp, Instead, we return an - * error on the interface if a response was being waited for. - */ - if (ibe->waiting_rsp) { - IPMICore *ic = opaque; - IPMIInterface *ii = ic->intf; - IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); - - ibe->waiting_rsp = false; - ibe->inbuf[1] = ibe->outbuf[1] | 0x04; - ibe->inbuf[2] = ibe->outbuf[2]; - ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; - iic->handle_msg(ii, ibe->outbuf[0], ibe->inbuf + 1, 3); - } - return 0; + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(opaque); + + return ipmi_extern_post_migrate(&ibe->conn, version_id); } static const VMStateDescription vmstate_ipmi_bmc_extern = { @@ -501,28 +158,24 @@ static const VMStateDescription vmstate_ipmi_bmc_extern = { .post_load = ipmi_bmc_extern_post_migrate, .fields = (VMStateField[]) { VMSTATE_BOOL(send_reset, IPMIBmcExtern), - VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern), + VMSTATE_BOOL(conn.waiting_rsp, IPMIBmcExtern), VMSTATE_END_OF_LIST() } }; static void ipmi_bmc_extern_init(Object *obj) { + IPMICore *ic = IPMI_CORE(obj); IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); - ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); + object_initialize_child(obj, "extern", &ibe->conn, + TYPE_IPMI_EXTERN); + ibe->conn.core = ic; vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); } -static void ipmi_bmc_extern_finalize(Object *obj) -{ - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); - - timer_free(ibe->extern_timer); -} - static Property ipmi_bmc_extern_properties[] = { - DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), + DEFINE_PROP_CHR("chardev", IPMIBmcExtern, conn.chr), DEFINE_PROP_END_OF_LIST(), }; @@ -530,9 +183,11 @@ static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); + IPMICoreClass *ck = IPMI_CORE_CLASS(oc); bk->handle_command = ipmi_bmc_extern_handle_command; bk->handle_reset = ipmi_bmc_extern_handle_reset; + ck->handle_hw_op = ipmi_bmc_handle_hw_op; dc->hotpluggable = false; dc->realize = ipmi_bmc_extern_realize; device_class_set_props(dc, ipmi_bmc_extern_properties); @@ -543,9 +198,8 @@ static const TypeInfo ipmi_bmc_extern_type = { .parent = TYPE_IPMI_BMC, .instance_size = sizeof(IPMIBmcExtern), .instance_init = ipmi_bmc_extern_init, - .instance_finalize = ipmi_bmc_extern_finalize, .class_init = ipmi_bmc_extern_class_init, - }; +}; static void ipmi_bmc_extern_register_types(void) { diff --git a/hw/ipmi/ipmi_extern.c b/hw/ipmi/ipmi_extern.c new file mode 100644 index 0000000000..f139eaef24 --- /dev/null +++ b/hw/ipmi/ipmi_extern.c @@ -0,0 +1,415 @@ +/* + * IPMI external connection + * + * Copyright (c) 2015 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 "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "qemu/timer.h" +#include "chardev/char-fe.h" +#include "hw/ipmi/ipmi.h" +#include "hw/ipmi/ipmi_extern.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qom/object.h" + +static unsigned char +ipmb_checksum(const unsigned char *data, int size, unsigned char start) +{ + unsigned char csum = start; + + for (; size > 0; size--, data++) { + csum += *data; + } + return csum; +} + +/* Returns whether check_reset is required for IPMI_BMC_EXTERN. */ +bool continue_send(IPMIExtern *ibe) +{ + int ret; + if (ibe->outlen == 0) { + return true; + } + ret = qemu_chr_fe_write(&ibe->chr, ibe->outbuf + ibe->outpos, + ibe->outlen - ibe->outpos); + if (ret > 0) { + ibe->outpos += ret; + } + if (ibe->outpos < ibe->outlen) { + /* Not fully transmitted, try again in a 10ms */ + timer_mod_ns(ibe->extern_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000); + return false; + } else { + /* Sent */ + ibe->outlen = 0; + ibe->outpos = 0; + if (!ibe->bmc_side && !ibe->sending_cmd) { + ibe->waiting_rsp = true; + } else { + ibe->sending_cmd = false; + } + + if (ibe->waiting_rsp) { + /* Make sure we get a response within 4 seconds. */ + timer_mod_ns(ibe->extern_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); + } + return true; + } +} + +static void extern_timeout(void *opaque) +{ + IPMIExtern *ibe = opaque; + IPMIInterface *s = ibe->core->intf; + + if (ibe->connected) { + /*TODO: only core-side */ + if (ibe->waiting_rsp && (ibe->outlen == 0)) { + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + /* The message response timed out, return an error. */ + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_TIMEOUT; + k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); + } else { + continue_send(ibe); + } + } +} + +static void addchar(IPMIExtern *ibe, unsigned char ch) +{ + switch (ch) { + case VM_MSG_CHAR: + case VM_CMD_CHAR: + case VM_ESCAPE_CHAR: + ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR; + ibe->outlen++; + ch |= 0x10; + /* fall through */ + default: + ibe->outbuf[ibe->outlen] = ch; + ibe->outlen++; + } +} + +void ipmi_extern_handle_command(IPMIExtern *ibe, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id) +{ + IPMIInterface *s = ibe->core->intf; + uint8_t err = 0, csum; + unsigned int i; + + if (ibe->outlen) { + /* We already have a command queued. Shouldn't ever happen. */ + error_report("IPMI KCS: Got command when not finished with the" + " previous command"); + abort(); + } + + /* If it's too short or it was truncated, return an error. */ + if (cmd_len < 2) { + err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; + } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) { + err = IPMI_CC_REQUEST_DATA_TRUNCATED; + } else if (!ibe->connected) { + err = IPMI_CC_BMC_INIT_IN_PROGRESS; + } + if (err) { + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned char rsp[3]; + rsp[0] = cmd[0] | 0x04; + rsp[1] = cmd[1]; + rsp[2] = err; + ibe->waiting_rsp = false; + k->handle_msg(s, msg_id, rsp, 3); + goto out; + } + + addchar(ibe, msg_id); + for (i = 0; i < cmd_len; i++) { + addchar(ibe, cmd[i]); + } + csum = ipmb_checksum(&msg_id, 1, 0); + addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum)); + + ibe->outbuf[ibe->outlen] = VM_MSG_CHAR; + ibe->outlen++; + + /* Start the transmit */ + continue_send(ibe); + + out: + return; +} + +static void handle_msg(IPMIExtern *ibe) +{ + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->core->intf); + + if (ibe->in_escape) { + ipmi_debug("msg escape not ended\n"); + return; + } + if (ibe->inpos < (ibe->bmc_side ? 4 : 5)) { + ipmi_debug("msg too short\n"); + return; + } + if (ibe->in_too_many) { + ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED; + ibe->inpos = 4; + } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) { + ipmi_debug("msg checksum failure\n"); + return; + } else { + ibe->inpos--; /* Remove checkum */ + } + + timer_del(ibe->extern_timer); + ibe->waiting_rsp = false; + k->handle_msg(ibe->core->intf, ibe->inbuf[0], + ibe->inbuf + 1, ibe->inpos - 1); +} + +static int can_receive(void *opaque) +{ + return 1; +} + +static void receive(void *opaque, const uint8_t *buf, int size) +{ + IPMIExtern *ibe = opaque; + IPMICoreClass *ck = IPMI_CORE_GET_CLASS(ibe->core); + int i; + unsigned char hw_op; + unsigned char hw_operand = 0; + + for (i = 0; i < size; i++) { + unsigned char ch = buf[i]; + + switch (ch) { + case VM_MSG_CHAR: + handle_msg(ibe); + ibe->in_too_many = false; + ibe->inpos = 0; + break; + + case VM_CMD_CHAR: + if (ibe->in_too_many) { + ipmi_debug("cmd in too many\n"); + ibe->in_too_many = false; + ibe->inpos = 0; + break; + } + if (ibe->in_escape) { + ipmi_debug("cmd in escape\n"); + ibe->in_too_many = false; + ibe->inpos = 0; + ibe->in_escape = false; + break; + } + ibe->in_too_many = false; + if (ibe->inpos < 1) { + break; + } + hw_op = ibe->inbuf[0]; + if (ibe->inpos > 1) { + hw_operand = ibe->inbuf[1]; + } + ibe->inpos = 0; + goto out_hw_op; + break; + + case VM_ESCAPE_CHAR: + ibe->in_escape = true; + break; + + default: + if (ibe->in_escape) { + ch &= ~0x10; + ibe->in_escape = false; + } + if (ibe->in_too_many) { + break; + } + if (ibe->inpos >= sizeof(ibe->inbuf)) { + ibe->in_too_many = true; + break; + } + ibe->inbuf[ibe->inpos] = ch; + ibe->inpos++; + break; + } + } + return; + + out_hw_op: + ck->handle_hw_op(ibe->core, hw_op, hw_operand); +} + +static void chr_event(void *opaque, QEMUChrEvent event) +{ + IPMIExtern *ibe = opaque; + IPMIInterface *s = ibe->core->intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned char v; + + switch (event) { + case CHR_EVENT_OPENED: + ibe->connected = true; + ibe->outpos = 0; + ibe->outlen = 0; + addchar(ibe, VM_CMD_VERSION); + addchar(ibe, VM_PROTOCOL_VERSION); + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; + ibe->outlen++; + /* Only send capability for core side. */ + if (!ibe->bmc_side) { + addchar(ibe, VM_CMD_CAPABILITIES); + v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN; + if (k->do_hw_op(ibe->core->intf, IPMI_POWEROFF_CHASSIS, 1) == 0) { + v |= VM_CAPABILITIES_POWER; + } + if (k->do_hw_op(ibe->core->intf, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 1) + == 0) { + v |= VM_CAPABILITIES_GRACEFUL_SHUTDOWN; + } + if (k->do_hw_op(ibe->core->intf, IPMI_RESET_CHASSIS, 1) == 0) { + v |= VM_CAPABILITIES_RESET; + } + if (k->do_hw_op(ibe->core->intf, IPMI_SEND_NMI, 1) == 0) { + v |= VM_CAPABILITIES_NMI; + } + addchar(ibe, v); + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; + ibe->outlen++; + } + ibe->sending_cmd = false; + continue_send(ibe); + break; + + case CHR_EVENT_CLOSED: + if (!ibe->connected) { + return; + } + ibe->connected = false; + /* + * Don't hang the OS trying to handle the ATN bit, other end will + * resend on a reconnect. + */ + k->set_atn(s, 0, 0); + if (ibe->waiting_rsp) { + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; + k->handle_msg(s, ibe->outbuf[0], ibe->inbuf + 1, 3); + } + break; + + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } +} + +static void ipmi_extern_realize(DeviceState *dev, Error **errp) +{ + IPMIExtern *ibe = IPMI_EXTERN(dev); + + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { + error_setg(errp, "IPMI external bmc requires chardev attribute"); + return; + } + + qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, + chr_event, NULL, ibe, NULL, true); +} + +int ipmi_extern_post_migrate(IPMIExtern *ibe, int version_id) +{ + /* + * We don't directly restore waiting_rsp, Instead, we return an + * error on the interface if a response was being waited for. + */ + if (ibe->waiting_rsp) { + IPMIInterface *ii = ibe->core->intf; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; + iic->handle_msg(ii, ibe->outbuf[0], ibe->inbuf + 1, 3); + } + return 0; +} + +static void ipmi_extern_init(Object *obj) +{ + IPMIExtern *ibe = IPMI_EXTERN(obj); + + ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); +} + +static void ipmi_extern_finalize(Object *obj) +{ + IPMIExtern *ibe = IPMI_EXTERN(obj); + + timer_del(ibe->extern_timer); + timer_free(ibe->extern_timer); +} + + +static void ipmi_extern_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->hotpluggable = false; + dc->realize = ipmi_extern_realize; +} + +static const TypeInfo ipmi_extern_type = { + .name = TYPE_IPMI_EXTERN, + .parent = TYPE_DEVICE, + .instance_size = sizeof(IPMIExtern), + .instance_init = ipmi_extern_init, + .instance_finalize = ipmi_extern_finalize, + .class_init = ipmi_extern_class_init, + }; + +static void ipmi_extern_register_types(void) +{ + type_register_static(&ipmi_extern_type); +} + +type_init(ipmi_extern_register_types) diff --git a/hw/ipmi/ipmi_extern.h b/hw/ipmi/ipmi_extern.h new file mode 100644 index 0000000000..e4aa80a0f6 --- /dev/null +++ b/hw/ipmi/ipmi_extern.h @@ -0,0 +1,90 @@ +/* + * IPMI external connection + * + * Copyright (c) 2015 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_EXTERN_H +#define HW_IPMI_EXTERN_H + +#include "qemu/osdep.h" +#include "hw/ipmi/ipmi.h" + +#define VM_MSG_CHAR 0xA0 /* Marks end of message */ +#define VM_CMD_CHAR 0xA1 /* Marks end of a command */ +#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */ + +#define VM_PROTOCOL_VERSION 1 +#define VM_CMD_VERSION 0xff /* A version number byte follows */ +#define VM_CMD_NOATTN 0x00 +#define VM_CMD_ATTN 0x01 +#define VM_CMD_ATTN_IRQ 0x02 +#define VM_CMD_POWEROFF 0x03 +#define VM_CMD_RESET 0x04 +#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */ +#define VM_CMD_DISABLE_IRQ 0x06 +#define VM_CMD_SEND_NMI 0x07 +#define VM_CMD_CAPABILITIES 0x08 +#define VM_CAPABILITIES_POWER 0x01 +#define VM_CAPABILITIES_RESET 0x02 +#define VM_CAPABILITIES_IRQ 0x04 +#define VM_CAPABILITIES_NMI 0x08 +#define VM_CAPABILITIES_ATTN 0x10 +#define VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20 +#define VM_CMD_GRACEFUL_SHUTDOWN 0x09 + +typedef struct IPMIExtern { + DeviceState parent; + + CharBackend chr; + + IPMICore *core; + + bool bmc_side; + bool connected; + + unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2]; + unsigned int inpos; + bool in_escape; + bool in_too_many; + bool waiting_rsp; + bool sending_cmd; + + unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1]; + unsigned int outpos; + unsigned int outlen; + + struct QEMUTimer *extern_timer; +} IPMIExtern; + +#define TYPE_IPMI_EXTERN "ipmi-extern" +#define IPMI_EXTERN(obj) \ + OBJECT_CHECK(IPMIExtern, (obj), TYPE_IPMI_EXTERN) + +void ipmi_extern_handle_command(IPMIExtern *ibe, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id); + +bool continue_send(IPMIExtern *ibe); +int ipmi_extern_post_migrate(IPMIExtern *ibe, int version_id); + +#endif /* HW_IPMI_EXTERN_H */ diff --git a/hw/ipmi/meson.build b/hw/ipmi/meson.build index 9622ea2a2c..edd0bf9af9 100644 --- a/hw/ipmi/meson.build +++ b/hw/ipmi/meson.build @@ -1,5 +1,5 @@ ipmi_ss = ss.source_set() -ipmi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c', 'ipmi_kcs.c', 'ipmi_bt.c')) +ipmi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c', 'ipmi_kcs.c', 'ipmi_bt.c', 'ipmi_extern.c')) ipmi_ss.add(when: 'CONFIG_IPMI_LOCAL', if_true: files('ipmi_bmc_sim.c')) ipmi_ss.add(when: 'CONFIG_IPMI_EXTERN', if_true: files('ipmi_bmc_extern.c')) ipmi_ss.add(when: 'CONFIG_ISA_IPMI_KCS', if_true: files('isa_ipmi_kcs.c'))
This patch refactors ipmi_bmc_extern.c and takes out the parts that can be used both ipmi_bmc_extern.c and bmc_host_extern.c to a common file ipmi_extern.c. Now we have a connection called IPMIExtern which handles the connection, and IPMIBmcExtern that handles core-side emulation specific stuff. Basically most of the message transaction are moved. The stuff remained are basically hardware operations like handle_reset and handle_hw_op. These stuff have different behaviors in core-side and BMC-side emulation. Signed-off-by: Hao Wu <wuhaotsh@google.com> --- hw/ipmi/ipmi_bmc_extern.c | 420 ++++---------------------------------- hw/ipmi/ipmi_extern.c | 415 +++++++++++++++++++++++++++++++++++++ hw/ipmi/ipmi_extern.h | 90 ++++++++ hw/ipmi/meson.build | 2 +- 4 files changed, 543 insertions(+), 384 deletions(-) create mode 100644 hw/ipmi/ipmi_extern.c create mode 100644 hw/ipmi/ipmi_extern.h