smbus_ipmi.c (11010B)
1/* 2 * QEMU IPMI SMBus (SSIF) emulation 3 * 4 * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24#include "qemu/osdep.h" 25#include "migration/vmstate.h" 26#include "hw/i2c/smbus_slave.h" 27#include "qapi/error.h" 28#include "qemu/error-report.h" 29#include "hw/ipmi/ipmi.h" 30#include "qom/object.h" 31 32#define TYPE_SMBUS_IPMI "smbus-ipmi" 33OBJECT_DECLARE_SIMPLE_TYPE(SMBusIPMIDevice, SMBUS_IPMI) 34 35#define SSIF_IPMI_REQUEST 2 36#define SSIF_IPMI_MULTI_PART_REQUEST_START 6 37#define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7 38#define SSIF_IPMI_MULTI_PART_REQUEST_END 8 39#define SSIF_IPMI_RESPONSE 3 40#define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9 41#define SSIF_IPMI_MULTI_PART_RETRY 0xa 42 43#define MAX_SSIF_IPMI_MSG_SIZE 255 44#define MAX_SSIF_IPMI_MSG_CHUNK 32 45 46#define IPMI_GET_SYS_INTF_CAP_CMD 0x57 47 48struct SMBusIPMIDevice { 49 SMBusDevice parent; 50 51 IPMIBmc *bmc; 52 53 uint8_t outmsg[MAX_SSIF_IPMI_MSG_SIZE]; 54 uint32_t outlen; 55 uint32_t currblk; 56 57 /* Holds the SMBUS message currently being sent to the host. */ 58 uint8_t outbuf[MAX_SSIF_IPMI_MSG_CHUNK + 1]; /* len + message. */ 59 uint32_t outpos; 60 61 uint8_t inmsg[MAX_SSIF_IPMI_MSG_SIZE]; 62 uint32_t inlen; 63 64 /* 65 * This is a response number that we send with the command to make 66 * sure that the response matches the command. 67 */ 68 uint8_t waiting_rsp; 69 70 uint32_t uuid; 71}; 72 73static void smbus_ipmi_handle_event(IPMIInterface *ii) 74{ 75 /* No interrupts, so nothing to do here. */ 76} 77 78static void smbus_ipmi_handle_rsp(IPMIInterface *ii, uint8_t msg_id, 79 unsigned char *rsp, unsigned int rsp_len) 80{ 81 SMBusIPMIDevice *sid = SMBUS_IPMI(ii); 82 83 if (sid->waiting_rsp == msg_id) { 84 sid->waiting_rsp++; 85 86 if (rsp_len > MAX_SSIF_IPMI_MSG_SIZE) { 87 rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED; 88 rsp_len = MAX_SSIF_IPMI_MSG_SIZE; 89 } 90 memcpy(sid->outmsg, rsp, rsp_len); 91 sid->outlen = rsp_len; 92 sid->outpos = 0; 93 sid->currblk = 0; 94 } 95} 96 97static void smbus_ipmi_set_atn(IPMIInterface *ii, int val, int irq) 98{ 99 /* This is where PEC would go. */ 100} 101 102static void smbus_ipmi_set_irq_enable(IPMIInterface *ii, int val) 103{ 104} 105 106static void smbus_ipmi_send_msg(SMBusIPMIDevice *sid) 107{ 108 uint8_t *msg = sid->inmsg; 109 uint32_t len = sid->inlen; 110 IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(sid->bmc); 111 112 sid->outlen = 0; 113 sid->outpos = 0; 114 sid->currblk = 0; 115 116 if (msg[0] == (IPMI_NETFN_APP << 2) && msg[1] == IPMI_GET_SYS_INTF_CAP_CMD) 117 { 118 /* We handle this ourself. */ 119 sid->outmsg[0] = (IPMI_NETFN_APP + 1) << 2; 120 sid->outmsg[1] = msg[1]; 121 if (len < 3) { 122 sid->outmsg[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; 123 sid->outlen = 3; 124 } else if ((msg[2] & 0x0f) != 0) { 125 sid->outmsg[2] = IPMI_CC_INVALID_DATA_FIELD; 126 sid->outlen = 3; 127 } else { 128 sid->outmsg[2] = 0; 129 sid->outmsg[3] = 0; 130 sid->outmsg[4] = (2 << 6); /* Multi-part supported. */ 131 sid->outmsg[5] = MAX_SSIF_IPMI_MSG_SIZE; 132 sid->outmsg[6] = MAX_SSIF_IPMI_MSG_SIZE; 133 sid->outlen = 7; 134 } 135 return; 136 } 137 138 bk->handle_command(sid->bmc, sid->inmsg, sid->inlen, sizeof(sid->inmsg), 139 sid->waiting_rsp); 140} 141 142static uint8_t ipmi_receive_byte(SMBusDevice *dev) 143{ 144 SMBusIPMIDevice *sid = SMBUS_IPMI(dev); 145 146 if (sid->outpos >= sizeof(sid->outbuf)) { 147 return 0xff; 148 } 149 150 return sid->outbuf[sid->outpos++]; 151} 152 153static int ipmi_load_readbuf(SMBusIPMIDevice *sid) 154{ 155 unsigned int block = sid->currblk, pos, len; 156 157 if (sid->outlen == 0) { 158 return -1; 159 } 160 161 if (sid->outlen <= 32) { 162 if (block != 0) { 163 return -1; 164 } 165 sid->outbuf[0] = sid->outlen; 166 memcpy(sid->outbuf + 1, sid->outmsg, sid->outlen); 167 sid->outpos = 0; 168 return 0; 169 } 170 171 if (block == 0) { 172 sid->outbuf[0] = 32; 173 sid->outbuf[1] = 0; 174 sid->outbuf[2] = 1; 175 memcpy(sid->outbuf + 3, sid->outmsg, 30); 176 sid->outpos = 0; 177 return 0; 178 } 179 180 /* 181 * Calculate the position in outmsg. 30 for the first block, 31 182 * for the rest of the blocks. 183 */ 184 pos = 30 + (block - 1) * 31; 185 186 if (pos >= sid->outlen) { 187 return -1; 188 } 189 190 len = sid->outlen - pos; 191 if (len > 31) { 192 /* More chunks after this. */ 193 len = 31; 194 /* Blocks start at 0 for the first middle transaction. */ 195 sid->outbuf[1] = block - 1; 196 } else { 197 sid->outbuf[1] = 0xff; /* End of message marker. */ 198 } 199 200 sid->outbuf[0] = len + 1; 201 memcpy(sid->outbuf + 2, sid->outmsg + pos, len); 202 sid->outpos = 0; 203 return 0; 204} 205 206static int ipmi_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len) 207{ 208 SMBusIPMIDevice *sid = SMBUS_IPMI(dev); 209 bool send = false; 210 uint8_t cmd; 211 int ret = 0; 212 213 /* length is guaranteed to be >= 1. */ 214 cmd = *buf++; 215 len--; 216 217 /* Handle read request, which don't have any data in the write part. */ 218 switch (cmd) { 219 case SSIF_IPMI_RESPONSE: 220 sid->currblk = 0; 221 ret = ipmi_load_readbuf(sid); 222 break; 223 224 case SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE: 225 sid->currblk++; 226 ret = ipmi_load_readbuf(sid); 227 break; 228 229 case SSIF_IPMI_MULTI_PART_RETRY: 230 if (len >= 1) { 231 sid->currblk = buf[0]; 232 ret = ipmi_load_readbuf(sid); 233 } else { 234 ret = -1; 235 } 236 break; 237 238 default: 239 break; 240 } 241 242 /* This should be a message write, make the length is there and correct. */ 243 if (len >= 1) { 244 if (*buf != len - 1 || *buf > MAX_SSIF_IPMI_MSG_CHUNK) { 245 return -1; /* Bogus message */ 246 } 247 buf++; 248 len--; 249 } 250 251 switch (cmd) { 252 case SSIF_IPMI_REQUEST: 253 send = true; 254 /* FALLTHRU */ 255 case SSIF_IPMI_MULTI_PART_REQUEST_START: 256 if (len < 2) { 257 return -1; /* Bogus. */ 258 } 259 memcpy(sid->inmsg, buf, len); 260 sid->inlen = len; 261 break; 262 263 case SSIF_IPMI_MULTI_PART_REQUEST_END: 264 send = true; 265 /* FALLTHRU */ 266 case SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE: 267 if (!sid->inlen) { 268 return -1; /* Bogus. */ 269 } 270 if (sid->inlen + len > MAX_SSIF_IPMI_MSG_SIZE) { 271 sid->inlen = 0; /* Discard the message. */ 272 return -1; /* Bogus. */ 273 } 274 if (len < 32) { 275 /* 276 * Special hack, a multi-part middle that is less than 32 bytes 277 * marks the end of a message. The specification is fairly 278 * confusing, so some systems to this, even sending a zero 279 * length end message to mark the end. 280 */ 281 send = true; 282 } 283 memcpy(sid->inmsg + sid->inlen, buf, len); 284 sid->inlen += len; 285 break; 286 } 287 288 if (send && sid->inlen) { 289 smbus_ipmi_send_msg(sid); 290 } 291 292 return ret; 293} 294 295static const VMStateDescription vmstate_smbus_ipmi = { 296 .name = TYPE_SMBUS_IPMI, 297 .version_id = 1, 298 .minimum_version_id = 1, 299 .fields = (VMStateField[]) { 300 VMSTATE_SMBUS_DEVICE(parent, SMBusIPMIDevice), 301 VMSTATE_UINT8(waiting_rsp, SMBusIPMIDevice), 302 VMSTATE_UINT32(outlen, SMBusIPMIDevice), 303 VMSTATE_UINT32(currblk, SMBusIPMIDevice), 304 VMSTATE_UINT8_ARRAY(outmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE), 305 VMSTATE_UINT32(outpos, SMBusIPMIDevice), 306 VMSTATE_UINT8_ARRAY(outbuf, SMBusIPMIDevice, 307 MAX_SSIF_IPMI_MSG_CHUNK + 1), 308 VMSTATE_UINT32(inlen, SMBusIPMIDevice), 309 VMSTATE_UINT8_ARRAY(inmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE), 310 VMSTATE_END_OF_LIST() 311 } 312}; 313 314static void smbus_ipmi_realize(DeviceState *dev, Error **errp) 315{ 316 SMBusIPMIDevice *sid = SMBUS_IPMI(dev); 317 IPMIInterface *ii = IPMI_INTERFACE(dev); 318 319 if (!sid->bmc) { 320 error_setg(errp, "IPMI device requires a bmc attribute to be set"); 321 return; 322 } 323 324 sid->uuid = ipmi_next_uuid(); 325 326 sid->bmc->intf = ii; 327} 328 329static void smbus_ipmi_init(Object *obj) 330{ 331 SMBusIPMIDevice *sid = SMBUS_IPMI(obj); 332 333 ipmi_bmc_find_and_link(obj, (Object **) &sid->bmc); 334} 335 336static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) 337{ 338 SMBusIPMIDevice *sid = SMBUS_IPMI(ii); 339 340 info->interface_name = "smbus"; 341 info->interface_type = IPMI_SMBIOS_SSIF; 342 info->ipmi_spec_major_revision = 2; 343 info->ipmi_spec_minor_revision = 0; 344 info->i2c_slave_address = sid->bmc->slave_addr; 345 info->base_address = sid->parent.i2c.address; 346 info->memspace = IPMI_MEMSPACE_SMBUS; 347 info->register_spacing = 1; 348 info->uuid = sid->uuid; 349} 350 351static void smbus_ipmi_class_init(ObjectClass *oc, void *data) 352{ 353 DeviceClass *dc = DEVICE_CLASS(oc); 354 IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); 355 SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc); 356 357 sc->receive_byte = ipmi_receive_byte; 358 sc->write_data = ipmi_write_data; 359 dc->vmsd = &vmstate_smbus_ipmi; 360 dc->realize = smbus_ipmi_realize; 361 iic->set_atn = smbus_ipmi_set_atn; 362 iic->handle_rsp = smbus_ipmi_handle_rsp; 363 iic->handle_if_event = smbus_ipmi_handle_event; 364 iic->set_irq_enable = smbus_ipmi_set_irq_enable; 365 iic->get_fwinfo = smbus_ipmi_get_fwinfo; 366} 367 368static const TypeInfo smbus_ipmi_info = { 369 .name = TYPE_SMBUS_IPMI, 370 .parent = TYPE_SMBUS_DEVICE, 371 .instance_size = sizeof(SMBusIPMIDevice), 372 .instance_init = smbus_ipmi_init, 373 .class_init = smbus_ipmi_class_init, 374 .interfaces = (InterfaceInfo[]) { 375 { TYPE_IPMI_INTERFACE }, 376 { } 377 } 378}; 379 380static void smbus_ipmi_register_types(void) 381{ 382 type_register_static(&smbus_ipmi_info); 383} 384 385type_init(smbus_ipmi_register_types)