ipmi_bt.c (14404B)
1/* 2 * QEMU IPMI BT emulation 3 * 4 * Copyright (c) 2015 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 "qemu/log.h" 27#include "qapi/error.h" 28#include "hw/ipmi/ipmi_bt.h" 29 30/* Control register */ 31#define IPMI_BT_CLR_WR_BIT 0 32#define IPMI_BT_CLR_RD_BIT 1 33#define IPMI_BT_H2B_ATN_BIT 2 34#define IPMI_BT_B2H_ATN_BIT 3 35#define IPMI_BT_SMS_ATN_BIT 4 36#define IPMI_BT_HBUSY_BIT 6 37#define IPMI_BT_BBUSY_BIT 7 38 39#define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) 40 41#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) 42 43#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) 44 45#define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) 46#define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) 47#define IPMI_BT_SET_B2H_ATN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ 48 (!!(v) << IPMI_BT_B2H_ATN_BIT))) 49 50#define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) 51#define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) 52#define IPMI_BT_SET_SMS_ATN(d, v) ((d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ 53 (!!(v) << IPMI_BT_SMS_ATN_BIT))) 54 55#define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) 56#define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) 57#define IPMI_BT_SET_HBUSY(d, v) ((d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ 58 (!!(v) << IPMI_BT_HBUSY_BIT))) 59 60#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) 61#define IPMI_BT_SET_BBUSY(d, v) ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ 62 (!!(v) << IPMI_BT_BBUSY_BIT))) 63 64 65/* Mask register */ 66#define IPMI_BT_B2H_IRQ_EN_BIT 0 67#define IPMI_BT_B2H_IRQ_BIT 1 68 69#define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) 70#define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) 71#define IPMI_BT_SET_B2H_IRQ_EN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) |\ 72 (!!(v) << IPMI_BT_B2H_IRQ_EN_BIT))) 73 74#define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) 75#define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) 76#define IPMI_BT_SET_B2H_IRQ(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ 77 (!!(v) << IPMI_BT_B2H_IRQ_BIT))) 78 79#define IPMI_CMD_GET_BT_INTF_CAP 0x36 80 81static void ipmi_bt_raise_irq(IPMIBT *ib) 82{ 83 if (ib->use_irq && ib->irqs_enabled && ib->raise_irq) { 84 ib->raise_irq(ib); 85 } 86} 87 88static void ipmi_bt_lower_irq(IPMIBT *ib) 89{ 90 if (ib->lower_irq) { 91 ib->lower_irq(ib); 92 } 93} 94 95static void ipmi_bt_handle_event(IPMIInterface *ii) 96{ 97 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 98 IPMIBT *ib = iic->get_backend_data(ii); 99 100 if (ib->inlen < 4) { 101 goto out; 102 } 103 /* Note that overruns are handled by handle_command */ 104 if (ib->inmsg[0] != (ib->inlen - 1)) { 105 /* Length mismatch, just ignore. */ 106 IPMI_BT_SET_BBUSY(ib->control_reg, 1); 107 ib->inlen = 0; 108 goto out; 109 } 110 if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && 111 (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { 112 /* We handle this one ourselves. */ 113 ib->outmsg[0] = 9; 114 ib->outmsg[1] = ib->inmsg[1] | 0x04; 115 ib->outmsg[2] = ib->inmsg[2]; 116 ib->outmsg[3] = ib->inmsg[3]; 117 ib->outmsg[4] = 0; 118 ib->outmsg[5] = 1; /* Only support 1 outstanding request. */ 119 if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */ 120 ib->outmsg[6] = 0xff; 121 } else { 122 ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg); 123 } 124 if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */ 125 ib->outmsg[7] = 0xff; 126 } else { 127 ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg); 128 } 129 ib->outmsg[8] = 10; /* Max request to response time */ 130 ib->outmsg[9] = 0; /* Don't recommend retries */ 131 ib->outlen = 10; 132 IPMI_BT_SET_BBUSY(ib->control_reg, 0); 133 IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); 134 if (!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && 135 IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { 136 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); 137 ipmi_bt_raise_irq(ib); 138 } 139 goto out; 140 } 141 ib->waiting_seq = ib->inmsg[2]; 142 ib->inmsg[2] = ib->inmsg[1]; 143 { 144 IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc); 145 bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, 146 sizeof(ib->inmsg), ib->waiting_rsp); 147 } 148 out: 149 return; 150} 151 152static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, 153 unsigned char *rsp, unsigned int rsp_len) 154{ 155 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 156 IPMIBT *ib = iic->get_backend_data(ii); 157 158 if (ib->waiting_rsp == msg_id) { 159 ib->waiting_rsp++; 160 if (rsp_len > (sizeof(ib->outmsg) - 2)) { 161 ib->outmsg[0] = 4; 162 ib->outmsg[1] = rsp[0]; 163 ib->outmsg[2] = ib->waiting_seq; 164 ib->outmsg[3] = rsp[1]; 165 ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; 166 ib->outlen = 5; 167 } else { 168 ib->outmsg[0] = rsp_len + 1; 169 ib->outmsg[1] = rsp[0]; 170 ib->outmsg[2] = ib->waiting_seq; 171 memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1); 172 ib->outlen = rsp_len + 2; 173 } 174 IPMI_BT_SET_BBUSY(ib->control_reg, 0); 175 IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); 176 if (!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && 177 IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { 178 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); 179 ipmi_bt_raise_irq(ib); 180 } 181 } 182} 183 184 185static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size) 186{ 187 IPMIInterface *ii = opaque; 188 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 189 IPMIBT *ib = iic->get_backend_data(ii); 190 uint32_t ret = 0xff; 191 192 switch (addr & ib->size_mask) { 193 case 0: 194 ret = ib->control_reg; 195 break; 196 case 1: 197 if (ib->outpos < ib->outlen) { 198 ret = ib->outmsg[ib->outpos]; 199 ib->outpos++; 200 if (ib->outpos == ib->outlen) { 201 ib->outpos = 0; 202 ib->outlen = 0; 203 } 204 } else { 205 ret = 0xff; 206 } 207 break; 208 case 2: 209 ret = ib->mask_reg; 210 break; 211 default: 212 ret = 0xff; 213 break; 214 } 215 return ret; 216} 217 218static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii) 219{ 220 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 221 222 ib->do_wake = 1; 223 while (ib->do_wake) { 224 ib->do_wake = 0; 225 iic->handle_if_event(ii); 226 } 227} 228 229static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val, 230 unsigned size) 231{ 232 IPMIInterface *ii = opaque; 233 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 234 IPMIBT *ib = iic->get_backend_data(ii); 235 236 switch (addr & ib->size_mask) { 237 case 0: 238 if (IPMI_BT_GET_CLR_WR(val)) { 239 ib->inlen = 0; 240 } 241 if (IPMI_BT_GET_CLR_RD(val)) { 242 ib->outpos = 0; 243 } 244 if (IPMI_BT_GET_B2H_ATN(val)) { 245 IPMI_BT_SET_B2H_ATN(ib->control_reg, 0); 246 } 247 if (IPMI_BT_GET_SMS_ATN(val)) { 248 IPMI_BT_SET_SMS_ATN(ib->control_reg, 0); 249 } 250 if (IPMI_BT_GET_HBUSY(val)) { 251 /* Toggle */ 252 IPMI_BT_SET_HBUSY(ib->control_reg, 253 !IPMI_BT_GET_HBUSY(ib->control_reg)); 254 } 255 if (IPMI_BT_GET_H2B_ATN(val)) { 256 IPMI_BT_SET_BBUSY(ib->control_reg, 1); 257 ipmi_bt_signal(ib, ii); 258 } 259 break; 260 261 case 1: 262 if (ib->inlen < sizeof(ib->inmsg)) { 263 ib->inmsg[ib->inlen] = val; 264 } 265 ib->inlen++; 266 break; 267 268 case 2: 269 if (IPMI_BT_GET_B2H_IRQ_EN(val) != 270 IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { 271 if (IPMI_BT_GET_B2H_IRQ_EN(val)) { 272 if (IPMI_BT_GET_B2H_ATN(ib->control_reg) || 273 IPMI_BT_GET_SMS_ATN(ib->control_reg)) { 274 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); 275 ipmi_bt_raise_irq(ib); 276 } 277 IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1); 278 } else { 279 if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { 280 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); 281 ipmi_bt_lower_irq(ib); 282 } 283 IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); 284 } 285 } 286 if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { 287 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); 288 ipmi_bt_lower_irq(ib); 289 } 290 break; 291 default: 292 /* Ignore. */ 293 break; 294 } 295} 296 297static const MemoryRegionOps ipmi_bt_io_ops = { 298 .read = ipmi_bt_ioport_read, 299 .write = ipmi_bt_ioport_write, 300 .impl = { 301 .min_access_size = 1, 302 .max_access_size = 1, 303 }, 304 .endianness = DEVICE_LITTLE_ENDIAN, 305}; 306 307static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq) 308{ 309 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 310 IPMIBT *ib = iic->get_backend_data(ii); 311 312 if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) { 313 return; 314 } 315 316 IPMI_BT_SET_SMS_ATN(ib->control_reg, val); 317 if (val) { 318 if (irq && !IPMI_BT_GET_B2H_ATN(ib->control_reg) && 319 IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { 320 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); 321 ipmi_bt_raise_irq(ib); 322 } 323 } else { 324 if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) && 325 IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { 326 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); 327 ipmi_bt_lower_irq(ib); 328 } 329 } 330} 331 332static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold) 333{ 334 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 335 IPMIBT *ib = iic->get_backend_data(ii); 336 337 if (is_cold) { 338 /* Disable the BT interrupt on reset */ 339 if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { 340 IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); 341 ipmi_bt_lower_irq(ib); 342 } 343 IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); 344 } 345} 346 347static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val) 348{ 349 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 350 IPMIBT *ib = iic->get_backend_data(ii); 351 352 ib->irqs_enabled = val; 353} 354 355static void ipmi_bt_init(IPMIInterface *ii, unsigned int min_size, Error **errp) 356{ 357 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 358 IPMIBT *ib = iic->get_backend_data(ii); 359 360 if (min_size == 0) { 361 min_size = 4; 362 } 363 ib->size_mask = min_size - 1; 364 ib->io_length = 3; 365 366 memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 367 min_size); 368} 369 370int ipmi_bt_vmstate_post_load(void *opaque, int version) 371{ 372 IPMIBT *ib = opaque; 373 374 /* Make sure all the values are sane. */ 375 if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE || 376 ib->outpos >= ib->outlen) { 377 qemu_log_mask(LOG_GUEST_ERROR, 378 "ipmi:bt: vmstate transfer received bad out values: %d %d\n", 379 ib->outpos, ib->outlen); 380 ib->outpos = 0; 381 ib->outlen = 0; 382 } 383 384 if (ib->inlen >= MAX_IPMI_MSG_SIZE) { 385 qemu_log_mask(LOG_GUEST_ERROR, 386 "ipmi:bt: vmstate transfer received bad in value: %d\n", 387 ib->inlen); 388 ib->inlen = 0; 389 } 390 391 return 0; 392} 393 394const VMStateDescription vmstate_IPMIBT = { 395 .name = TYPE_IPMI_INTERFACE_PREFIX "bt", 396 .version_id = 1, 397 .minimum_version_id = 1, 398 .post_load = ipmi_bt_vmstate_post_load, 399 .fields = (VMStateField[]) { 400 VMSTATE_BOOL(obf_irq_set, IPMIBT), 401 VMSTATE_BOOL(atn_irq_set, IPMIBT), 402 VMSTATE_BOOL(irqs_enabled, IPMIBT), 403 VMSTATE_UINT32(outpos, IPMIBT), 404 VMSTATE_UINT32(outlen, IPMIBT), 405 VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE), 406 VMSTATE_UINT32(inlen, IPMIBT), 407 VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE), 408 VMSTATE_UINT8(control_reg, IPMIBT), 409 VMSTATE_UINT8(mask_reg, IPMIBT), 410 VMSTATE_UINT8(waiting_rsp, IPMIBT), 411 VMSTATE_UINT8(waiting_seq, IPMIBT), 412 VMSTATE_END_OF_LIST() 413 } 414}; 415 416void ipmi_bt_get_fwinfo(struct IPMIBT *ib, IPMIFwInfo *info) 417{ 418 info->interface_name = "bt"; 419 info->interface_type = IPMI_SMBIOS_BT; 420 info->ipmi_spec_major_revision = 2; 421 info->ipmi_spec_minor_revision = 0; 422 info->base_address = ib->io_base; 423 info->register_length = ib->io_length; 424 info->register_spacing = 1; 425 info->memspace = IPMI_MEMSPACE_IO; 426 info->irq_type = IPMI_LEVEL_IRQ; 427} 428 429void ipmi_bt_class_init(IPMIInterfaceClass *iic) 430{ 431 iic->init = ipmi_bt_init; 432 iic->set_atn = ipmi_bt_set_atn; 433 iic->handle_rsp = ipmi_bt_handle_rsp; 434 iic->handle_if_event = ipmi_bt_handle_event; 435 iic->set_irq_enable = ipmi_bt_set_irq_enable; 436 iic->reset = ipmi_bt_handle_reset; 437}