xref: /openbmc/qemu/hw/ipmi/smbus_ipmi.c (revision a68694cd)
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"
33 typedef struct SMBusIPMIDevice SMBusIPMIDevice;
34 DECLARE_INSTANCE_CHECKER(SMBusIPMIDevice, SMBUS_IPMI,
35                          TYPE_SMBUS_IPMI)
36 
37 #define SSIF_IPMI_REQUEST                       2
38 #define SSIF_IPMI_MULTI_PART_REQUEST_START      6
39 #define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE     7
40 #define SSIF_IPMI_MULTI_PART_REQUEST_END        8
41 #define SSIF_IPMI_RESPONSE                      3
42 #define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE    9
43 #define SSIF_IPMI_MULTI_PART_RETRY              0xa
44 
45 #define MAX_SSIF_IPMI_MSG_SIZE 255
46 #define MAX_SSIF_IPMI_MSG_CHUNK 32
47 
48 #define IPMI_GET_SYS_INTF_CAP_CMD 0x57
49 
50 struct SMBusIPMIDevice {
51     SMBusDevice parent;
52 
53     IPMIBmc *bmc;
54 
55     uint8_t outmsg[MAX_SSIF_IPMI_MSG_SIZE];
56     uint32_t outlen;
57     uint32_t currblk;
58 
59     /* Holds the SMBUS message currently being sent to the host. */
60     uint8_t outbuf[MAX_SSIF_IPMI_MSG_CHUNK + 1]; /* len + message. */
61     uint32_t outpos;
62 
63     uint8_t inmsg[MAX_SSIF_IPMI_MSG_SIZE];
64     uint32_t inlen;
65 
66     /*
67      * This is a response number that we send with the command to make
68      * sure that the response matches the command.
69      */
70     uint8_t waiting_rsp;
71 
72     uint32_t uuid;
73 };
74 
75 static void smbus_ipmi_handle_event(IPMIInterface *ii)
76 {
77     /* No interrupts, so nothing to do here. */
78 }
79 
80 static void smbus_ipmi_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
81                                   unsigned char *rsp, unsigned int rsp_len)
82 {
83     SMBusIPMIDevice *sid = SMBUS_IPMI(ii);
84 
85     if (sid->waiting_rsp == msg_id) {
86         sid->waiting_rsp++;
87 
88         if (rsp_len > MAX_SSIF_IPMI_MSG_SIZE) {
89             rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED;
90             rsp_len = MAX_SSIF_IPMI_MSG_SIZE;
91         }
92         memcpy(sid->outmsg, rsp, rsp_len);
93         sid->outlen = rsp_len;
94         sid->outpos = 0;
95         sid->currblk = 0;
96     }
97 }
98 
99 static void smbus_ipmi_set_atn(IPMIInterface *ii, int val, int irq)
100 {
101     /* This is where PEC would go. */
102 }
103 
104 static void smbus_ipmi_set_irq_enable(IPMIInterface *ii, int val)
105 {
106 }
107 
108 static void smbus_ipmi_send_msg(SMBusIPMIDevice *sid)
109 {
110     uint8_t *msg = sid->inmsg;
111     uint32_t len = sid->inlen;
112     IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(sid->bmc);
113 
114     sid->outlen = 0;
115     sid->outpos = 0;
116     sid->currblk = 0;
117 
118     if (msg[0] == (IPMI_NETFN_APP << 2) && msg[1] == IPMI_GET_SYS_INTF_CAP_CMD)
119     {
120         /* We handle this ourself. */
121         sid->outmsg[0] = (IPMI_NETFN_APP + 1) << 2;
122         sid->outmsg[1] = msg[1];
123         if (len < 3) {
124             sid->outmsg[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
125             sid->outlen = 3;
126         } else if ((msg[2] & 0x0f) != 0) {
127             sid->outmsg[2] = IPMI_CC_INVALID_DATA_FIELD;
128             sid->outlen = 3;
129         } else {
130             sid->outmsg[2] = 0;
131             sid->outmsg[3] = 0;
132             sid->outmsg[4] = (2 << 6); /* Multi-part supported. */
133             sid->outmsg[5] = MAX_SSIF_IPMI_MSG_SIZE;
134             sid->outmsg[6] = MAX_SSIF_IPMI_MSG_SIZE;
135             sid->outlen = 7;
136         }
137         return;
138     }
139 
140     bk->handle_command(sid->bmc, sid->inmsg, sid->inlen, sizeof(sid->inmsg),
141                        sid->waiting_rsp);
142 }
143 
144 static uint8_t ipmi_receive_byte(SMBusDevice *dev)
145 {
146     SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
147 
148     if (sid->outpos >= sizeof(sid->outbuf)) {
149         return 0xff;
150     }
151 
152     return sid->outbuf[sid->outpos++];
153 }
154 
155 static int ipmi_load_readbuf(SMBusIPMIDevice *sid)
156 {
157     unsigned int block = sid->currblk, pos, len;
158 
159     if (sid->outlen == 0) {
160         return -1;
161     }
162 
163     if (sid->outlen <= 32) {
164         if (block != 0) {
165             return -1;
166         }
167         sid->outbuf[0] = sid->outlen;
168         memcpy(sid->outbuf + 1, sid->outmsg, sid->outlen);
169         sid->outpos = 0;
170         return 0;
171     }
172 
173     if (block == 0) {
174         sid->outbuf[0] = 32;
175         sid->outbuf[1] = 0;
176         sid->outbuf[2] = 1;
177         memcpy(sid->outbuf + 3, sid->outmsg, 30);
178         sid->outpos = 0;
179         return 0;
180     }
181 
182     /*
183      * Calculate the position in outmsg.  30 for the first block, 31
184      * for the rest of the blocks.
185      */
186     pos = 30 + (block - 1) * 31;
187 
188     if (pos >= sid->outlen) {
189         return -1;
190     }
191 
192     len = sid->outlen - pos;
193     if (len > 31) {
194         /* More chunks after this. */
195         len = 31;
196         /* Blocks start at 0 for the first middle transaction. */
197         sid->outbuf[1] = block - 1;
198     } else {
199         sid->outbuf[1] = 0xff; /* End of message marker. */
200     }
201 
202     sid->outbuf[0] = len + 1;
203     memcpy(sid->outbuf + 2, sid->outmsg + pos, len);
204     sid->outpos = 0;
205     return 0;
206 }
207 
208 static int ipmi_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len)
209 {
210     SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
211     bool send = false;
212     uint8_t cmd;
213     int ret = 0;
214 
215     /* length is guaranteed to be >= 1. */
216     cmd = *buf++;
217     len--;
218 
219     /* Handle read request, which don't have any data in the write part. */
220     switch (cmd) {
221     case SSIF_IPMI_RESPONSE:
222         sid->currblk = 0;
223         ret = ipmi_load_readbuf(sid);
224         break;
225 
226     case SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE:
227         sid->currblk++;
228         ret = ipmi_load_readbuf(sid);
229         break;
230 
231     case SSIF_IPMI_MULTI_PART_RETRY:
232         if (len >= 1) {
233             sid->currblk = buf[0];
234             ret = ipmi_load_readbuf(sid);
235         } else {
236             ret = -1;
237         }
238         break;
239 
240     default:
241         break;
242     }
243 
244     /* This should be a message write, make the length is there and correct. */
245     if (len >= 1) {
246         if (*buf != len - 1 || *buf > MAX_SSIF_IPMI_MSG_CHUNK) {
247             return -1; /* Bogus message */
248         }
249         buf++;
250         len--;
251     }
252 
253     switch (cmd) {
254     case SSIF_IPMI_REQUEST:
255         send = true;
256         /* FALLTHRU */
257     case SSIF_IPMI_MULTI_PART_REQUEST_START:
258         if (len < 2) {
259             return -1; /* Bogus. */
260         }
261         memcpy(sid->inmsg, buf, len);
262         sid->inlen = len;
263         break;
264 
265     case SSIF_IPMI_MULTI_PART_REQUEST_END:
266         send = true;
267         /* FALLTHRU */
268     case SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE:
269         if (!sid->inlen) {
270             return -1; /* Bogus. */
271         }
272         if (sid->inlen + len > MAX_SSIF_IPMI_MSG_SIZE) {
273             sid->inlen = 0; /* Discard the message. */
274             return -1; /* Bogus. */
275         }
276         if (len < 32) {
277             /*
278              * Special hack, a multi-part middle that is less than 32 bytes
279              * marks the end of a message.  The specification is fairly
280              * confusing, so some systems to this, even sending a zero
281              * length end message to mark the end.
282              */
283             send = true;
284         }
285         memcpy(sid->inmsg + sid->inlen, buf, len);
286         sid->inlen += len;
287         break;
288     }
289 
290     if (send && sid->inlen) {
291         smbus_ipmi_send_msg(sid);
292     }
293 
294     return ret;
295 }
296 
297 static const VMStateDescription vmstate_smbus_ipmi = {
298     .name = TYPE_SMBUS_IPMI,
299     .version_id = 1,
300     .minimum_version_id = 1,
301     .fields      = (VMStateField[]) {
302         VMSTATE_SMBUS_DEVICE(parent, SMBusIPMIDevice),
303         VMSTATE_UINT8(waiting_rsp, SMBusIPMIDevice),
304         VMSTATE_UINT32(outlen, SMBusIPMIDevice),
305         VMSTATE_UINT32(currblk, SMBusIPMIDevice),
306         VMSTATE_UINT8_ARRAY(outmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
307         VMSTATE_UINT32(outpos, SMBusIPMIDevice),
308         VMSTATE_UINT8_ARRAY(outbuf, SMBusIPMIDevice,
309                             MAX_SSIF_IPMI_MSG_CHUNK + 1),
310         VMSTATE_UINT32(inlen, SMBusIPMIDevice),
311         VMSTATE_UINT8_ARRAY(inmsg, SMBusIPMIDevice, MAX_SSIF_IPMI_MSG_SIZE),
312         VMSTATE_END_OF_LIST()
313     }
314 };
315 
316 static void smbus_ipmi_realize(DeviceState *dev, Error **errp)
317 {
318     SMBusIPMIDevice *sid = SMBUS_IPMI(dev);
319     IPMIInterface *ii = IPMI_INTERFACE(dev);
320 
321     if (!sid->bmc) {
322         error_setg(errp, "IPMI device requires a bmc attribute to be set");
323         return;
324     }
325 
326     sid->uuid = ipmi_next_uuid();
327 
328     sid->bmc->intf = ii;
329 }
330 
331 static void smbus_ipmi_init(Object *obj)
332 {
333     SMBusIPMIDevice *sid = SMBUS_IPMI(obj);
334 
335     ipmi_bmc_find_and_link(obj, (Object **) &sid->bmc);
336 }
337 
338 static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info)
339 {
340     SMBusIPMIDevice *sid = SMBUS_IPMI(ii);
341 
342     info->interface_name = "smbus";
343     info->interface_type = IPMI_SMBIOS_SSIF;
344     info->ipmi_spec_major_revision = 2;
345     info->ipmi_spec_minor_revision = 0;
346     info->i2c_slave_address = sid->bmc->slave_addr;
347     info->base_address = sid->parent.i2c.address;
348     info->memspace = IPMI_MEMSPACE_SMBUS;
349     info->register_spacing = 1;
350     info->uuid = sid->uuid;
351 }
352 
353 static void smbus_ipmi_class_init(ObjectClass *oc, void *data)
354 {
355     DeviceClass *dc = DEVICE_CLASS(oc);
356     IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
357     SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc);
358 
359     sc->receive_byte = ipmi_receive_byte;
360     sc->write_data = ipmi_write_data;
361     dc->vmsd = &vmstate_smbus_ipmi;
362     dc->realize = smbus_ipmi_realize;
363     iic->set_atn = smbus_ipmi_set_atn;
364     iic->handle_rsp = smbus_ipmi_handle_rsp;
365     iic->handle_if_event = smbus_ipmi_handle_event;
366     iic->set_irq_enable = smbus_ipmi_set_irq_enable;
367     iic->get_fwinfo = smbus_ipmi_get_fwinfo;
368 }
369 
370 static const TypeInfo smbus_ipmi_info = {
371     .name          = TYPE_SMBUS_IPMI,
372     .parent        = TYPE_SMBUS_DEVICE,
373     .instance_size = sizeof(SMBusIPMIDevice),
374     .instance_init = smbus_ipmi_init,
375     .class_init    = smbus_ipmi_class_init,
376     .interfaces = (InterfaceInfo[]) {
377         { TYPE_IPMI_INTERFACE },
378         { }
379     }
380 };
381 
382 static void smbus_ipmi_register_types(void)
383 {
384     type_register_static(&smbus_ipmi_info);
385 }
386 
387 type_init(smbus_ipmi_register_types)
388