138033052SCorey Minyard /* 238033052SCorey Minyard * QEMU IPMI SMBus (SSIF) emulation 338033052SCorey Minyard * 438033052SCorey Minyard * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC 538033052SCorey Minyard * 638033052SCorey Minyard * Permission is hereby granted, free of charge, to any person obtaining a copy 738033052SCorey Minyard * of this software and associated documentation files (the "Software"), to deal 838033052SCorey Minyard * in the Software without restriction, including without limitation the rights 938033052SCorey Minyard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1038033052SCorey Minyard * copies of the Software, and to permit persons to whom the Software is 1138033052SCorey Minyard * furnished to do so, subject to the following conditions: 1238033052SCorey Minyard * 1338033052SCorey Minyard * The above copyright notice and this permission notice shall be included in 1438033052SCorey Minyard * all copies or substantial portions of the Software. 1538033052SCorey Minyard * 1638033052SCorey Minyard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1738033052SCorey Minyard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1838033052SCorey Minyard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1938033052SCorey Minyard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2038033052SCorey Minyard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2138033052SCorey Minyard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2238033052SCorey Minyard * THE SOFTWARE. 2338033052SCorey Minyard */ 2438033052SCorey Minyard #include "qemu/osdep.h" 2538033052SCorey Minyard #include "migration/vmstate.h" 2638033052SCorey Minyard #include "hw/i2c/smbus_slave.h" 2738033052SCorey Minyard #include "qapi/error.h" 2838033052SCorey Minyard #include "qemu/error-report.h" 2938033052SCorey Minyard #include "hw/ipmi/ipmi.h" 30*db1015e9SEduardo Habkost #include "qom/object.h" 3138033052SCorey Minyard 3238033052SCorey Minyard #define TYPE_SMBUS_IPMI "smbus-ipmi" 33*db1015e9SEduardo Habkost typedef struct SMBusIPMIDevice SMBusIPMIDevice; 3438033052SCorey Minyard #define SMBUS_IPMI(obj) OBJECT_CHECK(SMBusIPMIDevice, (obj), TYPE_SMBUS_IPMI) 3538033052SCorey Minyard 3638033052SCorey Minyard #define SSIF_IPMI_REQUEST 2 3738033052SCorey Minyard #define SSIF_IPMI_MULTI_PART_REQUEST_START 6 3838033052SCorey Minyard #define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7 3938033052SCorey Minyard #define SSIF_IPMI_MULTI_PART_REQUEST_END 8 4038033052SCorey Minyard #define SSIF_IPMI_RESPONSE 3 4138033052SCorey Minyard #define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9 4238033052SCorey Minyard #define SSIF_IPMI_MULTI_PART_RETRY 0xa 4338033052SCorey Minyard 4438033052SCorey Minyard #define MAX_SSIF_IPMI_MSG_SIZE 255 4538033052SCorey Minyard #define MAX_SSIF_IPMI_MSG_CHUNK 32 4638033052SCorey Minyard 4738033052SCorey Minyard #define IPMI_GET_SYS_INTF_CAP_CMD 0x57 4838033052SCorey Minyard 49*db1015e9SEduardo Habkost struct SMBusIPMIDevice { 5038033052SCorey Minyard SMBusDevice parent; 5138033052SCorey Minyard 5238033052SCorey Minyard IPMIBmc *bmc; 5338033052SCorey Minyard 5438033052SCorey Minyard uint8_t outmsg[MAX_SSIF_IPMI_MSG_SIZE]; 5538033052SCorey Minyard uint32_t outlen; 5638033052SCorey Minyard uint32_t currblk; 5738033052SCorey Minyard 5838033052SCorey Minyard /* Holds the SMBUS message currently being sent to the host. */ 5938033052SCorey Minyard uint8_t outbuf[MAX_SSIF_IPMI_MSG_CHUNK + 1]; /* len + message. */ 6038033052SCorey Minyard uint32_t outpos; 6138033052SCorey Minyard 6238033052SCorey Minyard uint8_t inmsg[MAX_SSIF_IPMI_MSG_SIZE]; 6338033052SCorey Minyard uint32_t inlen; 6438033052SCorey Minyard 6538033052SCorey Minyard /* 6638033052SCorey Minyard * This is a response number that we send with the command to make 6738033052SCorey Minyard * sure that the response matches the command. 6838033052SCorey Minyard */ 6938033052SCorey Minyard uint8_t waiting_rsp; 7038033052SCorey Minyard 7138033052SCorey Minyard uint32_t uuid; 72*db1015e9SEduardo Habkost }; 7338033052SCorey Minyard 7438033052SCorey Minyard static void smbus_ipmi_handle_event(IPMIInterface *ii) 7538033052SCorey Minyard { 7638033052SCorey Minyard /* No interrupts, so nothing to do here. */ 7738033052SCorey Minyard } 7838033052SCorey Minyard 7938033052SCorey Minyard static void smbus_ipmi_handle_rsp(IPMIInterface *ii, uint8_t msg_id, 8038033052SCorey Minyard unsigned char *rsp, unsigned int rsp_len) 8138033052SCorey Minyard { 8238033052SCorey Minyard SMBusIPMIDevice *sid = SMBUS_IPMI(ii); 8338033052SCorey Minyard 8438033052SCorey Minyard if (sid->waiting_rsp == msg_id) { 8538033052SCorey Minyard sid->waiting_rsp++; 8638033052SCorey Minyard 8738033052SCorey Minyard if (rsp_len > MAX_SSIF_IPMI_MSG_SIZE) { 8838033052SCorey Minyard rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED; 8938033052SCorey Minyard rsp_len = MAX_SSIF_IPMI_MSG_SIZE; 9038033052SCorey Minyard } 9138033052SCorey Minyard memcpy(sid->outmsg, rsp, rsp_len); 9238033052SCorey Minyard sid->outlen = rsp_len; 9338033052SCorey Minyard sid->outpos = 0; 9438033052SCorey Minyard sid->currblk = 0; 9538033052SCorey Minyard } 9638033052SCorey Minyard } 9738033052SCorey Minyard 9838033052SCorey Minyard static void smbus_ipmi_set_atn(IPMIInterface *ii, int val, int irq) 9938033052SCorey Minyard { 10038033052SCorey Minyard /* This is where PEC would go. */ 10138033052SCorey Minyard } 10238033052SCorey Minyard 10338033052SCorey Minyard static void smbus_ipmi_set_irq_enable(IPMIInterface *ii, int val) 10438033052SCorey Minyard { 10538033052SCorey Minyard } 10638033052SCorey Minyard 10738033052SCorey Minyard static void smbus_ipmi_send_msg(SMBusIPMIDevice *sid) 10838033052SCorey Minyard { 10938033052SCorey Minyard uint8_t *msg = sid->inmsg; 11038033052SCorey Minyard uint32_t len = sid->inlen; 11138033052SCorey Minyard IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(sid->bmc); 11238033052SCorey Minyard 11338033052SCorey Minyard sid->outlen = 0; 11438033052SCorey Minyard sid->outpos = 0; 11538033052SCorey Minyard sid->currblk = 0; 11638033052SCorey Minyard 11738033052SCorey Minyard if (msg[0] == (IPMI_NETFN_APP << 2) && msg[1] == IPMI_GET_SYS_INTF_CAP_CMD) 11838033052SCorey Minyard { 11938033052SCorey Minyard /* We handle this ourself. */ 12038033052SCorey Minyard sid->outmsg[0] = (IPMI_NETFN_APP + 1) << 2; 12138033052SCorey Minyard sid->outmsg[1] = msg[1]; 12238033052SCorey Minyard if (len < 3) { 12338033052SCorey Minyard sid->outmsg[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; 12438033052SCorey Minyard sid->outlen = 3; 12538033052SCorey Minyard } else if ((msg[2] & 0x0f) != 0) { 12638033052SCorey Minyard sid->outmsg[2] = IPMI_CC_INVALID_DATA_FIELD; 12738033052SCorey Minyard sid->outlen = 3; 12838033052SCorey Minyard } else { 12938033052SCorey Minyard sid->outmsg[2] = 0; 13038033052SCorey Minyard sid->outmsg[3] = 0; 13138033052SCorey Minyard sid->outmsg[4] = (2 << 6); /* Multi-part supported. */ 13238033052SCorey Minyard sid->outmsg[5] = MAX_SSIF_IPMI_MSG_SIZE; 13338033052SCorey Minyard sid->outmsg[6] = MAX_SSIF_IPMI_MSG_SIZE; 13438033052SCorey Minyard sid->outlen = 7; 13538033052SCorey Minyard } 13638033052SCorey Minyard return; 13738033052SCorey Minyard } 13838033052SCorey Minyard 13938033052SCorey Minyard bk->handle_command(sid->bmc, sid->inmsg, sid->inlen, sizeof(sid->inmsg), 14038033052SCorey Minyard sid->waiting_rsp); 14138033052SCorey Minyard } 14238033052SCorey Minyard 14338033052SCorey Minyard static uint8_t ipmi_receive_byte(SMBusDevice *dev) 14438033052SCorey Minyard { 14538033052SCorey Minyard SMBusIPMIDevice *sid = SMBUS_IPMI(dev); 14638033052SCorey Minyard 14738033052SCorey Minyard if (sid->outpos >= sizeof(sid->outbuf)) { 14838033052SCorey Minyard return 0xff; 14938033052SCorey Minyard } 15038033052SCorey Minyard 15138033052SCorey Minyard return sid->outbuf[sid->outpos++]; 15238033052SCorey Minyard } 15338033052SCorey Minyard 15438033052SCorey Minyard static int ipmi_load_readbuf(SMBusIPMIDevice *sid) 15538033052SCorey Minyard { 15638033052SCorey Minyard unsigned int block = sid->currblk, pos, len; 15738033052SCorey Minyard 15838033052SCorey Minyard if (sid->outlen == 0) { 15938033052SCorey Minyard return -1; 16038033052SCorey Minyard } 16138033052SCorey Minyard 16238033052SCorey Minyard if (sid->outlen <= 32) { 16338033052SCorey Minyard if (block != 0) { 16438033052SCorey Minyard return -1; 16538033052SCorey Minyard } 16638033052SCorey Minyard sid->outbuf[0] = sid->outlen; 16738033052SCorey Minyard memcpy(sid->outbuf + 1, sid->outmsg, sid->outlen); 16838033052SCorey Minyard sid->outpos = 0; 16938033052SCorey Minyard return 0; 17038033052SCorey Minyard } 17138033052SCorey Minyard 17238033052SCorey Minyard if (block == 0) { 17338033052SCorey Minyard sid->outbuf[0] = 32; 17438033052SCorey Minyard sid->outbuf[1] = 0; 17538033052SCorey Minyard sid->outbuf[2] = 1; 17638033052SCorey Minyard memcpy(sid->outbuf + 3, sid->outmsg, 30); 17738033052SCorey Minyard sid->outpos = 0; 17838033052SCorey Minyard return 0; 17938033052SCorey Minyard } 18038033052SCorey Minyard 18138033052SCorey Minyard /* 18238033052SCorey Minyard * Calculate the position in outmsg. 30 for the first block, 31 18338033052SCorey Minyard * for the rest of the blocks. 18438033052SCorey Minyard */ 18538033052SCorey Minyard pos = 30 + (block - 1) * 31; 18638033052SCorey Minyard 18738033052SCorey Minyard if (pos >= sid->outlen) { 18838033052SCorey Minyard return -1; 18938033052SCorey Minyard } 19038033052SCorey Minyard 19138033052SCorey Minyard len = sid->outlen - pos; 19238033052SCorey Minyard if (len > 31) { 19338033052SCorey Minyard /* More chunks after this. */ 19438033052SCorey Minyard len = 31; 19538033052SCorey Minyard /* Blocks start at 0 for the first middle transaction. */ 19638033052SCorey Minyard sid->outbuf[1] = block - 1; 19738033052SCorey Minyard } else { 19838033052SCorey Minyard sid->outbuf[1] = 0xff; /* End of message marker. */ 19938033052SCorey Minyard } 20038033052SCorey Minyard 20138033052SCorey Minyard sid->outbuf[0] = len + 1; 20238033052SCorey Minyard memcpy(sid->outbuf + 2, sid->outmsg + pos, len); 20338033052SCorey Minyard sid->outpos = 0; 20438033052SCorey Minyard return 0; 20538033052SCorey Minyard } 20638033052SCorey Minyard 20738033052SCorey Minyard static int ipmi_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len) 20838033052SCorey Minyard { 20938033052SCorey Minyard SMBusIPMIDevice *sid = SMBUS_IPMI(dev); 21038033052SCorey Minyard bool send = false; 21138033052SCorey Minyard uint8_t cmd; 21238033052SCorey Minyard int ret = 0; 21338033052SCorey Minyard 21438033052SCorey Minyard /* length is guaranteed to be >= 1. */ 21538033052SCorey Minyard cmd = *buf++; 21638033052SCorey Minyard len--; 21738033052SCorey Minyard 21838033052SCorey Minyard /* Handle read request, which don't have any data in the write part. */ 21938033052SCorey Minyard switch (cmd) { 22038033052SCorey Minyard case SSIF_IPMI_RESPONSE: 22138033052SCorey Minyard sid->currblk = 0; 22238033052SCorey Minyard ret = ipmi_load_readbuf(sid); 22338033052SCorey Minyard break; 22438033052SCorey Minyard 22538033052SCorey Minyard case SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE: 22638033052SCorey Minyard sid->currblk++; 22738033052SCorey Minyard ret = ipmi_load_readbuf(sid); 22838033052SCorey Minyard break; 22938033052SCorey Minyard 23038033052SCorey Minyard case SSIF_IPMI_MULTI_PART_RETRY: 23138033052SCorey Minyard if (len >= 1) { 23238033052SCorey Minyard sid->currblk = buf[0]; 23338033052SCorey Minyard ret = ipmi_load_readbuf(sid); 23438033052SCorey Minyard } else { 23538033052SCorey Minyard ret = -1; 23638033052SCorey Minyard } 23738033052SCorey Minyard break; 23838033052SCorey Minyard 23938033052SCorey Minyard default: 24038033052SCorey Minyard break; 24138033052SCorey Minyard } 24238033052SCorey Minyard 24338033052SCorey Minyard /* This should be a message write, make the length is there and correct. */ 24438033052SCorey Minyard if (len >= 1) { 24538033052SCorey Minyard if (*buf != len - 1 || *buf > MAX_SSIF_IPMI_MSG_CHUNK) { 24638033052SCorey Minyard return -1; /* Bogus message */ 24738033052SCorey Minyard } 24838033052SCorey Minyard buf++; 24938033052SCorey Minyard len--; 25038033052SCorey Minyard } 25138033052SCorey Minyard 25238033052SCorey Minyard switch (cmd) { 25338033052SCorey Minyard case SSIF_IPMI_REQUEST: 25438033052SCorey Minyard send = true; 25538033052SCorey Minyard /* FALLTHRU */ 25638033052SCorey Minyard case SSIF_IPMI_MULTI_PART_REQUEST_START: 25738033052SCorey Minyard if (len < 2) { 25838033052SCorey Minyard return -1; /* Bogus. */ 25938033052SCorey Minyard } 26038033052SCorey Minyard memcpy(sid->inmsg, buf, len); 26138033052SCorey Minyard sid->inlen = len; 26238033052SCorey Minyard break; 26338033052SCorey Minyard 26438033052SCorey Minyard case SSIF_IPMI_MULTI_PART_REQUEST_END: 26538033052SCorey Minyard send = true; 26638033052SCorey Minyard /* FALLTHRU */ 26738033052SCorey Minyard case SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE: 26838033052SCorey Minyard if (!sid->inlen) { 26938033052SCorey Minyard return -1; /* Bogus. */ 27038033052SCorey Minyard } 27138033052SCorey Minyard if (sid->inlen + len > MAX_SSIF_IPMI_MSG_SIZE) { 27238033052SCorey Minyard sid->inlen = 0; /* Discard the message. */ 27338033052SCorey Minyard return -1; /* Bogus. */ 27438033052SCorey Minyard } 27538033052SCorey Minyard if (len < 32) { 27638033052SCorey Minyard /* 27738033052SCorey Minyard * Special hack, a multi-part middle that is less than 32 bytes 27838033052SCorey Minyard * marks the end of a message. The specification is fairly 27938033052SCorey Minyard * confusing, so some systems to this, even sending a zero 28038033052SCorey Minyard * length end message to mark the end. 28138033052SCorey Minyard */ 28238033052SCorey Minyard send = true; 28338033052SCorey Minyard } 28438033052SCorey Minyard memcpy(sid->inmsg + sid->inlen, buf, len); 28538033052SCorey Minyard sid->inlen += len; 28638033052SCorey Minyard break; 28738033052SCorey Minyard } 28838033052SCorey Minyard 28938033052SCorey Minyard if (send && sid->inlen) { 29038033052SCorey Minyard smbus_ipmi_send_msg(sid); 29138033052SCorey Minyard } 29238033052SCorey Minyard 29338033052SCorey Minyard return ret; 29438033052SCorey Minyard } 29538033052SCorey Minyard 29638033052SCorey Minyard static const VMStateDescription vmstate_smbus_ipmi = { 29738033052SCorey Minyard .name = TYPE_SMBUS_IPMI, 29838033052SCorey Minyard .version_id = 1, 29938033052SCorey Minyard .minimum_version_id = 1, 30038033052SCorey Minyard .fields = (VMStateField[]) { 30138033052SCorey Minyard VMSTATE_SMBUS_DEVICE(parent, SMBusIPMIDevice), 30238033052SCorey Minyard VMSTATE_UINT8(waiting_rsp, SMBusIPMIDevice), 30338033052SCorey Minyard VMSTATE_UINT32(outlen, SMBusIPMIDevice), 30438033052SCorey Minyard VMSTATE_UINT32(currblk, SMBusIPMIDevice), 30538033052SCorey Minyard VMSTATE_UINT8_ARRAY(outmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE), 30638033052SCorey Minyard VMSTATE_UINT32(outpos, SMBusIPMIDevice), 30738033052SCorey Minyard VMSTATE_UINT8_ARRAY(outbuf, SMBusIPMIDevice, 30838033052SCorey Minyard MAX_SSIF_IPMI_MSG_CHUNK + 1), 30938033052SCorey Minyard VMSTATE_UINT32(inlen, SMBusIPMIDevice), 31038033052SCorey Minyard VMSTATE_UINT8_ARRAY(inmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE), 31138033052SCorey Minyard VMSTATE_END_OF_LIST() 31238033052SCorey Minyard } 31338033052SCorey Minyard }; 31438033052SCorey Minyard 31538033052SCorey Minyard static void smbus_ipmi_realize(DeviceState *dev, Error **errp) 31638033052SCorey Minyard { 31738033052SCorey Minyard SMBusIPMIDevice *sid = SMBUS_IPMI(dev); 31838033052SCorey Minyard IPMIInterface *ii = IPMI_INTERFACE(dev); 31938033052SCorey Minyard 32038033052SCorey Minyard if (!sid->bmc) { 32138033052SCorey Minyard error_setg(errp, "IPMI device requires a bmc attribute to be set"); 32238033052SCorey Minyard return; 32338033052SCorey Minyard } 32438033052SCorey Minyard 32538033052SCorey Minyard sid->uuid = ipmi_next_uuid(); 32638033052SCorey Minyard 32738033052SCorey Minyard sid->bmc->intf = ii; 32838033052SCorey Minyard } 32938033052SCorey Minyard 33038033052SCorey Minyard static void smbus_ipmi_init(Object *obj) 33138033052SCorey Minyard { 33238033052SCorey Minyard SMBusIPMIDevice *sid = SMBUS_IPMI(obj); 33338033052SCorey Minyard 334688ffbb4SPhilippe Mathieu-Daudé ipmi_bmc_find_and_link(obj, (Object **) &sid->bmc); 33538033052SCorey Minyard } 33638033052SCorey Minyard 33738033052SCorey Minyard static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) 33838033052SCorey Minyard { 33938033052SCorey Minyard SMBusIPMIDevice *sid = SMBUS_IPMI(ii); 34038033052SCorey Minyard 34138033052SCorey Minyard info->interface_name = "smbus"; 34238033052SCorey Minyard info->interface_type = IPMI_SMBIOS_SSIF; 34338033052SCorey Minyard info->ipmi_spec_major_revision = 2; 34438033052SCorey Minyard info->ipmi_spec_minor_revision = 0; 34538033052SCorey Minyard info->i2c_slave_address = sid->bmc->slave_addr; 34638033052SCorey Minyard info->base_address = sid->parent.i2c.address; 34738033052SCorey Minyard info->memspace = IPMI_MEMSPACE_SMBUS; 34838033052SCorey Minyard info->register_spacing = 1; 34938033052SCorey Minyard info->uuid = sid->uuid; 35038033052SCorey Minyard } 35138033052SCorey Minyard 35238033052SCorey Minyard static void smbus_ipmi_class_init(ObjectClass *oc, void *data) 35338033052SCorey Minyard { 35438033052SCorey Minyard DeviceClass *dc = DEVICE_CLASS(oc); 35538033052SCorey Minyard IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); 35638033052SCorey Minyard SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc); 35738033052SCorey Minyard 35838033052SCorey Minyard sc->receive_byte = ipmi_receive_byte; 35938033052SCorey Minyard sc->write_data = ipmi_write_data; 36038033052SCorey Minyard dc->vmsd = &vmstate_smbus_ipmi; 36138033052SCorey Minyard dc->realize = smbus_ipmi_realize; 36238033052SCorey Minyard iic->set_atn = smbus_ipmi_set_atn; 36338033052SCorey Minyard iic->handle_rsp = smbus_ipmi_handle_rsp; 36438033052SCorey Minyard iic->handle_if_event = smbus_ipmi_handle_event; 36538033052SCorey Minyard iic->set_irq_enable = smbus_ipmi_set_irq_enable; 36638033052SCorey Minyard iic->get_fwinfo = smbus_ipmi_get_fwinfo; 36738033052SCorey Minyard } 36838033052SCorey Minyard 36938033052SCorey Minyard static const TypeInfo smbus_ipmi_info = { 37038033052SCorey Minyard .name = TYPE_SMBUS_IPMI, 37138033052SCorey Minyard .parent = TYPE_SMBUS_DEVICE, 37238033052SCorey Minyard .instance_size = sizeof(SMBusIPMIDevice), 37338033052SCorey Minyard .instance_init = smbus_ipmi_init, 37438033052SCorey Minyard .class_init = smbus_ipmi_class_init, 37538033052SCorey Minyard .interfaces = (InterfaceInfo[]) { 37638033052SCorey Minyard { TYPE_IPMI_INTERFACE }, 37738033052SCorey Minyard { } 37838033052SCorey Minyard } 37938033052SCorey Minyard }; 38038033052SCorey Minyard 38138033052SCorey Minyard static void smbus_ipmi_register_types(void) 38238033052SCorey Minyard { 38338033052SCorey Minyard type_register_static(&smbus_ipmi_info); 38438033052SCorey Minyard } 38538033052SCorey Minyard 38638033052SCorey Minyard type_init(smbus_ipmi_register_types) 387