cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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)