qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

smbus_ipmi.c (11212B)


      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         if (len > 0) {
    285             memcpy(sid->inmsg + sid->inlen, buf, len);
    286         }
    287         sid->inlen += len;
    288         break;
    289     }
    290 
    291     if (send && sid->inlen) {
    292         smbus_ipmi_send_msg(sid);
    293     }
    294 
    295     return ret;
    296 }
    297 
    298 static const VMStateDescription vmstate_smbus_ipmi = {
    299     .name = TYPE_SMBUS_IPMI,
    300     .version_id = 1,
    301     .minimum_version_id = 1,
    302     .fields      = (VMStateField[]) {
    303         VMSTATE_SMBUS_DEVICE(parent, SMBusIPMIDevice),
    304         VMSTATE_UINT8(waiting_rsp, SMBusIPMIDevice),
    305         VMSTATE_UINT32(outlen, SMBusIPMIDevice),
    306         VMSTATE_UINT32(currblk, SMBusIPMIDevice),
    307         VMSTATE_UINT8_ARRAY(outmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
    308         VMSTATE_UINT32(outpos, SMBusIPMIDevice),
    309         VMSTATE_UINT8_ARRAY(outbuf, SMBusIPMIDevice,
    310                             MAX_SSIF_IPMI_MSG_CHUNK + 1),
    311         VMSTATE_UINT32(inlen, SMBusIPMIDevice),
    312         VMSTATE_UINT8_ARRAY(inmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
    313         VMSTATE_END_OF_LIST()
    314     }
    315 };
    316 
    317 static void smbus_ipmi_realize(DeviceState *dev, Error **errp)
    318 {
    319     SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
    320     IPMIInterface *ii = IPMI_INTERFACE(dev);
    321 
    322     if (!sid->bmc) {
    323         error_setg(errp, "IPMI device requires a bmc attribute to be set");
    324         return;
    325     }
    326 
    327     sid->uuid = ipmi_next_uuid();
    328 
    329     sid->bmc->intf = ii;
    330 }
    331 
    332 static void smbus_ipmi_init(Object *obj)
    333 {
    334     SMBusIPMIDevice *sid = SMBUS_IPMI(obj);
    335 
    336     ipmi_bmc_find_and_link(obj, (Object **) &sid->bmc);
    337 }
    338 
    339 static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info)
    340 {
    341     SMBusIPMIDevice *sid = SMBUS_IPMI(ii);
    342 
    343     info->interface_name = "smbus";
    344     info->interface_type = IPMI_SMBIOS_SSIF;
    345     info->ipmi_spec_major_revision = 2;
    346     info->ipmi_spec_minor_revision = 0;
    347     info->i2c_slave_address = sid->bmc->slave_addr;
    348     info->base_address = sid->parent.i2c.address;
    349     info->memspace = IPMI_MEMSPACE_SMBUS;
    350     info->register_spacing = 1;
    351     info->uuid = sid->uuid;
    352 }
    353 
    354 static void smbus_ipmi_class_init(ObjectClass *oc, void *data)
    355 {
    356     DeviceClass *dc = DEVICE_CLASS(oc);
    357     IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
    358     SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc);
    359     AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc);
    360 
    361     sc->receive_byte = ipmi_receive_byte;
    362     sc->write_data = ipmi_write_data;
    363     dc->vmsd = &vmstate_smbus_ipmi;
    364     dc->realize = smbus_ipmi_realize;
    365     iic->set_atn = smbus_ipmi_set_atn;
    366     iic->handle_rsp = smbus_ipmi_handle_rsp;
    367     iic->handle_if_event = smbus_ipmi_handle_event;
    368     iic->set_irq_enable = smbus_ipmi_set_irq_enable;
    369     iic->get_fwinfo = smbus_ipmi_get_fwinfo;
    370     adevc->build_dev_aml = build_ipmi_dev_aml;
    371 }
    372 
    373 static const TypeInfo smbus_ipmi_info = {
    374     .name          = TYPE_SMBUS_IPMI,
    375     .parent        = TYPE_SMBUS_DEVICE,
    376     .instance_size = sizeof(SMBusIPMIDevice),
    377     .instance_init = smbus_ipmi_init,
    378     .class_init    = smbus_ipmi_class_init,
    379     .interfaces = (InterfaceInfo[]) {
    380         { TYPE_IPMI_INTERFACE },
    381         { TYPE_ACPI_DEV_AML_IF },
    382         { }
    383     }
    384 };
    385 
    386 static void smbus_ipmi_register_types(void)
    387 {
    388     type_register_static(&smbus_ipmi_info);
    389 }
    390 
    391 type_init(smbus_ipmi_register_types)