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