xref: /openbmc/qemu/hw/ipmi/smbus_ipmi.c (revision 46627f41)
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 OBJECT_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 
48 struct 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 
73 static void smbus_ipmi_handle_event(IPMIInterface *ii)
74 {
75     /* No interrupts, so nothing to do here. */
76 }
77 
78 static 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 
97 static void smbus_ipmi_set_atn(IPMIInterface *ii, int val, int irq)
98 {
99     /* This is where PEC would go. */
100 }
101 
102 static void smbus_ipmi_set_irq_enable(IPMIInterface *ii, int val)
103 {
104 }
105 
106 static 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 
142 static 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 
153 static 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 
206 static 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 
295 static 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 
314 static 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 
329 static 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 
336 static 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 
351 static 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 
368 static 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 
380 static void smbus_ipmi_register_types(void)
381 {
382     type_register_static(&smbus_ipmi_info);
383 }
384 
385 type_init(smbus_ipmi_register_types)
386