ipmi_kcs.c (13619B)
1/* 2 * QEMU IPMI KCS emulation 3 * 4 * Copyright (c) 2015,2017 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_kcs.h" 29 30#define IPMI_KCS_OBF_BIT 0 31#define IPMI_KCS_IBF_BIT 1 32#define IPMI_KCS_SMS_ATN_BIT 2 33#define IPMI_KCS_CD_BIT 3 34 35#define IPMI_KCS_OBF_MASK (1 << IPMI_KCS_OBF_BIT) 36#define IPMI_KCS_GET_OBF(d) (((d) >> IPMI_KCS_OBF_BIT) & 0x1) 37#define IPMI_KCS_SET_OBF(d, v) (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \ 38 (((v) & 1) << IPMI_KCS_OBF_BIT)) 39#define IPMI_KCS_IBF_MASK (1 << IPMI_KCS_IBF_BIT) 40#define IPMI_KCS_GET_IBF(d) (((d) >> IPMI_KCS_IBF_BIT) & 0x1) 41#define IPMI_KCS_SET_IBF(d, v) (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \ 42 (((v) & 1) << IPMI_KCS_IBF_BIT)) 43#define IPMI_KCS_SMS_ATN_MASK (1 << IPMI_KCS_SMS_ATN_BIT) 44#define IPMI_KCS_GET_SMS_ATN(d) (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1) 45#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \ 46 (((v) & 1) << IPMI_KCS_SMS_ATN_BIT)) 47#define IPMI_KCS_CD_MASK (1 << IPMI_KCS_CD_BIT) 48#define IPMI_KCS_GET_CD(d) (((d) >> IPMI_KCS_CD_BIT) & 0x1) 49#define IPMI_KCS_SET_CD(d, v) (d) = (((d) & ~IPMI_KCS_CD_MASK) | \ 50 (((v) & 1) << IPMI_KCS_CD_BIT)) 51 52#define IPMI_KCS_IDLE_STATE 0 53#define IPMI_KCS_READ_STATE 1 54#define IPMI_KCS_WRITE_STATE 2 55#define IPMI_KCS_ERROR_STATE 3 56 57#define IPMI_KCS_GET_STATE(d) (((d) >> 6) & 0x3) 58#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6)) 59 60#define IPMI_KCS_ABORT_STATUS_CMD 0x60 61#define IPMI_KCS_WRITE_START_CMD 0x61 62#define IPMI_KCS_WRITE_END_CMD 0x62 63#define IPMI_KCS_READ_CMD 0x68 64 65#define IPMI_KCS_STATUS_NO_ERR 0x00 66#define IPMI_KCS_STATUS_ABORTED_ERR 0x01 67#define IPMI_KCS_STATUS_BAD_CC_ERR 0x02 68#define IPMI_KCS_STATUS_LENGTH_ERR 0x06 69 70static void ipmi_kcs_raise_irq(IPMIKCS *ik) 71{ 72 if (ik->use_irq && ik->irqs_enabled && ik->raise_irq) { 73 ik->raise_irq(ik); 74 } 75} 76 77static void ipmi_kcs_lower_irq(IPMIKCS *ik) 78{ 79 if (ik->lower_irq) { 80 ik->lower_irq(ik); 81 } 82} 83 84#define SET_OBF() \ 85 do { \ 86 IPMI_KCS_SET_OBF(ik->status_reg, 1); \ 87 if (!ik->obf_irq_set) { \ 88 ik->obf_irq_set = 1; \ 89 if (!ik->atn_irq_set) { \ 90 ipmi_kcs_raise_irq(ik); \ 91 } \ 92 } \ 93 } while (0) 94 95static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii) 96{ 97 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 98 99 ik->do_wake = 1; 100 while (ik->do_wake) { 101 ik->do_wake = 0; 102 iic->handle_if_event(ii); 103 } 104} 105 106static void ipmi_kcs_handle_event(IPMIInterface *ii) 107{ 108 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 109 IPMIKCS *ik = iic->get_backend_data(ii); 110 111 if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) { 112 if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) { 113 ik->waiting_rsp++; /* Invalidate the message */ 114 ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR; 115 ik->outlen = 1; 116 ik->outpos = 0; 117 IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); 118 SET_OBF(); 119 } 120 goto out; 121 } 122 123 switch (IPMI_KCS_GET_STATE(ik->status_reg)) { 124 case IPMI_KCS_IDLE_STATE: 125 if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) { 126 IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE); 127 ik->cmd_reg = -1; 128 ik->write_end = 0; 129 ik->inlen = 0; 130 SET_OBF(); 131 } 132 break; 133 134 case IPMI_KCS_READ_STATE: 135 handle_read: 136 if (ik->outpos >= ik->outlen) { 137 IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE); 138 SET_OBF(); 139 } else if (ik->data_in_reg == IPMI_KCS_READ_CMD) { 140 ik->data_out_reg = ik->outmsg[ik->outpos]; 141 ik->outpos++; 142 SET_OBF(); 143 } else { 144 ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; 145 ik->outlen = 1; 146 ik->outpos = 0; 147 IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); 148 SET_OBF(); 149 goto out; 150 } 151 break; 152 153 case IPMI_KCS_WRITE_STATE: 154 if (ik->data_in_reg != -1) { 155 /* 156 * Don't worry about input overrun here, that will be 157 * handled in the BMC. 158 */ 159 if (ik->inlen < sizeof(ik->inmsg)) { 160 ik->inmsg[ik->inlen] = ik->data_in_reg; 161 } 162 ik->inlen++; 163 } 164 if (ik->write_end) { 165 IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc); 166 ik->outlen = 0; 167 ik->write_end = 0; 168 ik->outpos = 0; 169 bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg), 170 ik->waiting_rsp); 171 goto out_noibf; 172 } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) { 173 ik->cmd_reg = -1; 174 ik->write_end = 1; 175 } 176 SET_OBF(); 177 break; 178 179 case IPMI_KCS_ERROR_STATE: 180 if (ik->data_in_reg != -1) { 181 IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); 182 ik->data_in_reg = IPMI_KCS_READ_CMD; 183 goto handle_read; 184 } 185 break; 186 } 187 188 if (ik->cmd_reg != -1) { 189 /* Got an invalid command */ 190 ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; 191 ik->outlen = 1; 192 ik->outpos = 0; 193 IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); 194 } 195 196 out: 197 ik->cmd_reg = -1; 198 ik->data_in_reg = -1; 199 IPMI_KCS_SET_IBF(ik->status_reg, 0); 200 out_noibf: 201 return; 202} 203 204static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id, 205 unsigned char *rsp, unsigned int rsp_len) 206{ 207 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 208 IPMIKCS *ik = iic->get_backend_data(ii); 209 210 if (ik->waiting_rsp == msg_id) { 211 ik->waiting_rsp++; 212 if (rsp_len > sizeof(ik->outmsg)) { 213 ik->outmsg[0] = rsp[0]; 214 ik->outmsg[1] = rsp[1]; 215 ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; 216 ik->outlen = 3; 217 } else { 218 memcpy(ik->outmsg, rsp, rsp_len); 219 ik->outlen = rsp_len; 220 } 221 IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); 222 ik->data_in_reg = IPMI_KCS_READ_CMD; 223 ipmi_kcs_signal(ik, ii); 224 } 225} 226 227 228static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size) 229{ 230 IPMIInterface *ii = opaque; 231 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 232 IPMIKCS *ik = iic->get_backend_data(ii); 233 uint32_t ret; 234 235 switch (addr & ik->size_mask) { 236 case 0: 237 ret = ik->data_out_reg; 238 IPMI_KCS_SET_OBF(ik->status_reg, 0); 239 if (ik->obf_irq_set) { 240 ik->obf_irq_set = 0; 241 if (!ik->atn_irq_set) { 242 ipmi_kcs_lower_irq(ik); 243 } 244 } 245 break; 246 247 case 1: 248 ret = ik->status_reg; 249 if (ik->atn_irq_set) { 250 ik->atn_irq_set = 0; 251 if (!ik->obf_irq_set) { 252 ipmi_kcs_lower_irq(ik); 253 } 254 } 255 break; 256 257 default: 258 ret = 0xff; 259 } 260 return ret; 261} 262 263static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val, 264 unsigned size) 265{ 266 IPMIInterface *ii = opaque; 267 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 268 IPMIKCS *ik = iic->get_backend_data(ii); 269 270 if (IPMI_KCS_GET_IBF(ik->status_reg)) { 271 return; 272 } 273 274 switch (addr & ik->size_mask) { 275 case 0: 276 ik->data_in_reg = val; 277 break; 278 279 case 1: 280 ik->cmd_reg = val; 281 break; 282 283 default: 284 /* Ignore. */ 285 break; 286 } 287 IPMI_KCS_SET_IBF(ik->status_reg, 1); 288 ipmi_kcs_signal(ik, ii); 289} 290 291const MemoryRegionOps ipmi_kcs_io_ops = { 292 .read = ipmi_kcs_ioport_read, 293 .write = ipmi_kcs_ioport_write, 294 .impl = { 295 .min_access_size = 1, 296 .max_access_size = 1, 297 }, 298 .endianness = DEVICE_LITTLE_ENDIAN, 299}; 300 301static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq) 302{ 303 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 304 IPMIKCS *ik = iic->get_backend_data(ii); 305 306 IPMI_KCS_SET_SMS_ATN(ik->status_reg, val); 307 if (val) { 308 if (irq && !ik->atn_irq_set) { 309 ik->atn_irq_set = 1; 310 if (!ik->obf_irq_set) { 311 ipmi_kcs_raise_irq(ik); 312 } 313 } 314 } else { 315 if (ik->atn_irq_set) { 316 ik->atn_irq_set = 0; 317 if (!ik->obf_irq_set) { 318 ipmi_kcs_lower_irq(ik); 319 } 320 } 321 } 322} 323 324static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val) 325{ 326 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 327 IPMIKCS *ik = iic->get_backend_data(ii); 328 329 ik->irqs_enabled = val; 330} 331 332/* min_size must be a power of 2. */ 333static void ipmi_kcs_init(IPMIInterface *ii, unsigned int min_size, 334 Error **errp) 335{ 336 IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); 337 IPMIKCS *ik = iic->get_backend_data(ii); 338 339 if (min_size == 0) { 340 min_size = 2; 341 } 342 ik->size_mask = min_size - 1; 343 ik->io_length = 2; 344 memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 345 min_size); 346} 347 348int ipmi_kcs_vmstate_post_load(void *opaque, int version) 349{ 350 IPMIKCS *ik = opaque; 351 352 /* Make sure all the values are sane. */ 353 if (ik->outpos >= MAX_IPMI_MSG_SIZE || ik->outlen >= MAX_IPMI_MSG_SIZE || 354 ik->outpos >= ik->outlen) { 355 qemu_log_mask(LOG_GUEST_ERROR, 356 "ipmi:kcs: vmstate transfer received bad out values: %d %d\n", 357 ik->outpos, ik->outlen); 358 ik->outpos = 0; 359 ik->outlen = 0; 360 } 361 362 if (ik->inlen >= MAX_IPMI_MSG_SIZE) { 363 qemu_log_mask(LOG_GUEST_ERROR, 364 "ipmi:kcs: vmstate transfer received bad in value: %d\n", 365 ik->inlen); 366 ik->inlen = 0; 367 } 368 369 return 0; 370} 371 372static bool vmstate_kcs_before_version2(void *opaque, int version) 373{ 374 return version <= 1; 375} 376 377const VMStateDescription vmstate_IPMIKCS = { 378 .name = TYPE_IPMI_INTERFACE_PREFIX "kcs", 379 .version_id = 2, 380 .minimum_version_id = 1, 381 .post_load = ipmi_kcs_vmstate_post_load, 382 .fields = (VMStateField[]) { 383 VMSTATE_BOOL(obf_irq_set, IPMIKCS), 384 VMSTATE_BOOL(atn_irq_set, IPMIKCS), 385 VMSTATE_UNUSED_TEST(vmstate_kcs_before_version2, 1), /* Was use_irq */ 386 VMSTATE_BOOL(irqs_enabled, IPMIKCS), 387 VMSTATE_UINT32(outpos, IPMIKCS), 388 VMSTATE_UINT32_V(outlen, IPMIKCS, 2), 389 VMSTATE_UINT8_ARRAY(outmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), 390 VMSTATE_UINT32_V(inlen, IPMIKCS, 2), 391 VMSTATE_UINT8_ARRAY(inmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), 392 VMSTATE_BOOL(write_end, IPMIKCS), 393 VMSTATE_UINT8(status_reg, IPMIKCS), 394 VMSTATE_UINT8(data_out_reg, IPMIKCS), 395 VMSTATE_INT16(data_in_reg, IPMIKCS), 396 VMSTATE_INT16(cmd_reg, IPMIKCS), 397 VMSTATE_UINT8(waiting_rsp, IPMIKCS), 398 VMSTATE_END_OF_LIST() 399 } 400}; 401 402void ipmi_kcs_get_fwinfo(IPMIKCS *ik, IPMIFwInfo *info) 403{ 404 info->interface_name = "kcs"; 405 info->interface_type = IPMI_SMBIOS_KCS; 406 info->ipmi_spec_major_revision = 2; 407 info->ipmi_spec_minor_revision = 0; 408 info->base_address = ik->io_base; 409 info->i2c_slave_address = ik->bmc->slave_addr; 410 info->register_length = ik->io_length; 411 info->register_spacing = 1; 412 info->memspace = IPMI_MEMSPACE_IO; 413 info->irq_type = IPMI_LEVEL_IRQ; 414} 415 416void ipmi_kcs_class_init(IPMIInterfaceClass *iic) 417{ 418 iic->init = ipmi_kcs_init; 419 iic->set_atn = ipmi_kcs_set_atn; 420 iic->handle_rsp = ipmi_kcs_handle_rsp; 421 iic->handle_if_event = ipmi_kcs_handle_event; 422 iic->set_irq_enable = ipmi_kcs_set_irq_enable; 423}