qemu/hw/ipmi/ipmi_bt.c
<<
>>
Prefs
   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}
 438