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