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