xref: /openbmc/qemu/hw/ipmi/ipmi_bt.c (revision 09c6ac6d)
1 /*
2  * QEMU IPMI BT emulation
3  *
4  * Copyright (c) 2015 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 "qemu/log.h"
27 #include "qapi/error.h"
28 #include "hw/ipmi/ipmi_bt.h"
29 
30 /* Control register */
31 #define IPMI_BT_CLR_WR_BIT         0
32 #define IPMI_BT_CLR_RD_BIT         1
33 #define IPMI_BT_H2B_ATN_BIT        2
34 #define IPMI_BT_B2H_ATN_BIT        3
35 #define IPMI_BT_SMS_ATN_BIT        4
36 #define IPMI_BT_HBUSY_BIT          6
37 #define IPMI_BT_BBUSY_BIT          7
38 
39 #define IPMI_BT_GET_CLR_WR(d)      (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
40 
41 #define IPMI_BT_GET_CLR_RD(d)      (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
42 
43 #define IPMI_BT_GET_H2B_ATN(d)     (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
44 
45 #define IPMI_BT_B2H_ATN_MASK       (1 << IPMI_BT_B2H_ATN_BIT)
46 #define IPMI_BT_GET_B2H_ATN(d)     (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1)
47 #define IPMI_BT_SET_B2H_ATN(d, v)  ((d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \
48                                         (!!(v) << IPMI_BT_B2H_ATN_BIT)))
49 
50 #define IPMI_BT_SMS_ATN_MASK       (1 << IPMI_BT_SMS_ATN_BIT)
51 #define IPMI_BT_GET_SMS_ATN(d)     (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1)
52 #define IPMI_BT_SET_SMS_ATN(d, v)  ((d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \
53                                         (!!(v) << IPMI_BT_SMS_ATN_BIT)))
54 
55 #define IPMI_BT_HBUSY_MASK         (1 << IPMI_BT_HBUSY_BIT)
56 #define IPMI_BT_GET_HBUSY(d)       (((d) >> IPMI_BT_HBUSY_BIT) & 0x1)
57 #define IPMI_BT_SET_HBUSY(d, v)    ((d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \
58                                        (!!(v) << IPMI_BT_HBUSY_BIT)))
59 
60 #define IPMI_BT_BBUSY_MASK         (1 << IPMI_BT_BBUSY_BIT)
61 #define IPMI_BT_SET_BBUSY(d, v)    ((d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
62                                        (!!(v) << IPMI_BT_BBUSY_BIT)))
63 
64 
65 /* Mask register */
66 #define IPMI_BT_B2H_IRQ_EN_BIT     0
67 #define IPMI_BT_B2H_IRQ_BIT        1
68 
69 #define IPMI_BT_B2H_IRQ_EN_MASK      (1 << IPMI_BT_B2H_IRQ_EN_BIT)
70 #define IPMI_BT_GET_B2H_IRQ_EN(d)    (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1)
71 #define IPMI_BT_SET_B2H_IRQ_EN(d, v) ((d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) |\
72                                         (!!(v) << IPMI_BT_B2H_IRQ_EN_BIT)))
73 
74 #define IPMI_BT_B2H_IRQ_MASK         (1 << IPMI_BT_B2H_IRQ_BIT)
75 #define IPMI_BT_GET_B2H_IRQ(d)       (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1)
76 #define IPMI_BT_SET_B2H_IRQ(d, v)    ((d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \
77                                         (!!(v) << IPMI_BT_B2H_IRQ_BIT)))
78 
79 #define IPMI_CMD_GET_BT_INTF_CAP        0x36
80 
ipmi_bt_raise_irq(IPMIBT * ib)81 static void ipmi_bt_raise_irq(IPMIBT *ib)
82 {
83     if (ib->use_irq && ib->irqs_enabled && ib->raise_irq) {
84         ib->raise_irq(ib);
85     }
86 }
87 
ipmi_bt_lower_irq(IPMIBT * ib)88 static void ipmi_bt_lower_irq(IPMIBT *ib)
89 {
90     if (ib->lower_irq) {
91         ib->lower_irq(ib);
92     }
93 }
94 
ipmi_bt_handle_event(IPMIInterface * ii)95 static void ipmi_bt_handle_event(IPMIInterface *ii)
96 {
97     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
98     IPMIBT *ib = iic->get_backend_data(ii);
99 
100     if (ib->inlen < 4) {
101         goto out;
102     }
103     /* Note that overruns are handled by handle_command */
104     if (ib->inmsg[0] != (ib->inlen - 1)) {
105         /* Length mismatch, just ignore. */
106         IPMI_BT_SET_BBUSY(ib->control_reg, 1);
107         ib->inlen = 0;
108         goto out;
109     }
110     if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) &&
111                         (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) {
112         /* We handle this one ourselves. */
113         ib->outmsg[0] = 9;
114         ib->outmsg[1] = ib->inmsg[1] | 0x04;
115         ib->outmsg[2] = ib->inmsg[2];
116         ib->outmsg[3] = ib->inmsg[3];
117         ib->outmsg[4] = 0;
118         ib->outmsg[5] = 1; /* Only support 1 outstanding request. */
119         if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */
120             ib->outmsg[6] = 0xff;
121         } else {
122             ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg);
123         }
124         if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */
125             ib->outmsg[7] = 0xff;
126         } else {
127             ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg);
128         }
129         ib->outmsg[8] = 10; /* Max request to response time */
130         ib->outmsg[9] = 0; /* Don't recommend retries */
131         ib->outlen = 10;
132         IPMI_BT_SET_BBUSY(ib->control_reg, 0);
133         IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
134         if (!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
135                 IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
136             IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
137             ipmi_bt_raise_irq(ib);
138         }
139         goto out;
140     }
141     ib->waiting_seq = ib->inmsg[2];
142     ib->inmsg[2] = ib->inmsg[1];
143     {
144         IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc);
145         bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2,
146                            sizeof(ib->inmsg), ib->waiting_rsp);
147     }
148  out:
149     return;
150 }
151 
ipmi_bt_handle_rsp(IPMIInterface * ii,uint8_t msg_id,unsigned char * rsp,unsigned int rsp_len)152 static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
153                                 unsigned char *rsp, unsigned int rsp_len)
154 {
155     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
156     IPMIBT *ib = iic->get_backend_data(ii);
157 
158     if (ib->waiting_rsp == msg_id) {
159         ib->waiting_rsp++;
160         if (rsp_len > (sizeof(ib->outmsg) - 2)) {
161             ib->outmsg[0] = 4;
162             ib->outmsg[1] = rsp[0];
163             ib->outmsg[2] = ib->waiting_seq;
164             ib->outmsg[3] = rsp[1];
165             ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
166             ib->outlen = 5;
167         } else {
168             ib->outmsg[0] = rsp_len + 1;
169             ib->outmsg[1] = rsp[0];
170             ib->outmsg[2] = ib->waiting_seq;
171             memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1);
172             ib->outlen = rsp_len + 2;
173         }
174         IPMI_BT_SET_BBUSY(ib->control_reg, 0);
175         IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
176         if (!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
177                 IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
178             IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
179             ipmi_bt_raise_irq(ib);
180         }
181     }
182 }
183 
184 
ipmi_bt_ioport_read(void * opaque,hwaddr addr,unsigned size)185 static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
186 {
187     IPMIInterface *ii = opaque;
188     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
189     IPMIBT *ib = iic->get_backend_data(ii);
190     uint32_t ret = 0xff;
191 
192     switch (addr & ib->size_mask) {
193     case 0:
194         ret = ib->control_reg;
195         break;
196     case 1:
197         if (ib->outpos < ib->outlen) {
198             ret = ib->outmsg[ib->outpos];
199             ib->outpos++;
200             if (ib->outpos == ib->outlen) {
201                 ib->outpos = 0;
202                 ib->outlen = 0;
203             }
204         } else {
205             ret = 0xff;
206         }
207         break;
208     case 2:
209         ret = ib->mask_reg;
210         break;
211     default:
212         ret = 0xff;
213         break;
214     }
215     return ret;
216 }
217 
ipmi_bt_signal(IPMIBT * ib,IPMIInterface * ii)218 static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii)
219 {
220     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
221 
222     ib->do_wake = 1;
223     while (ib->do_wake) {
224         ib->do_wake = 0;
225         iic->handle_if_event(ii);
226     }
227 }
228 
ipmi_bt_ioport_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)229 static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
230                                  unsigned size)
231 {
232     IPMIInterface *ii = opaque;
233     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
234     IPMIBT *ib = iic->get_backend_data(ii);
235 
236     switch (addr & ib->size_mask) {
237     case 0:
238         if (IPMI_BT_GET_CLR_WR(val)) {
239             ib->inlen = 0;
240         }
241         if (IPMI_BT_GET_CLR_RD(val)) {
242             ib->outpos = 0;
243         }
244         if (IPMI_BT_GET_B2H_ATN(val)) {
245             IPMI_BT_SET_B2H_ATN(ib->control_reg, 0);
246         }
247         if (IPMI_BT_GET_SMS_ATN(val)) {
248             IPMI_BT_SET_SMS_ATN(ib->control_reg, 0);
249         }
250         if (IPMI_BT_GET_HBUSY(val)) {
251             /* Toggle */
252             IPMI_BT_SET_HBUSY(ib->control_reg,
253                               !IPMI_BT_GET_HBUSY(ib->control_reg));
254         }
255         if (IPMI_BT_GET_H2B_ATN(val)) {
256             IPMI_BT_SET_BBUSY(ib->control_reg, 1);
257             ipmi_bt_signal(ib, ii);
258         }
259         break;
260 
261     case 1:
262         if (ib->inlen < sizeof(ib->inmsg)) {
263             ib->inmsg[ib->inlen] = val;
264         }
265         ib->inlen++;
266         break;
267 
268     case 2:
269         if (IPMI_BT_GET_B2H_IRQ_EN(val) !=
270                         IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
271             if (IPMI_BT_GET_B2H_IRQ_EN(val)) {
272                 if (IPMI_BT_GET_B2H_ATN(ib->control_reg) ||
273                         IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
274                     IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
275                     ipmi_bt_raise_irq(ib);
276                 }
277                 IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1);
278             } else {
279                 if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
280                     IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
281                     ipmi_bt_lower_irq(ib);
282                 }
283                 IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
284             }
285         }
286         if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
287             IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
288             ipmi_bt_lower_irq(ib);
289         }
290         break;
291     default:
292         /* Ignore. */
293         break;
294     }
295 }
296 
297 static const MemoryRegionOps ipmi_bt_io_ops = {
298     .read = ipmi_bt_ioport_read,
299     .write = ipmi_bt_ioport_write,
300     .impl = {
301         .min_access_size = 1,
302         .max_access_size = 1,
303     },
304     .endianness = DEVICE_LITTLE_ENDIAN,
305 };
306 
ipmi_bt_set_atn(IPMIInterface * ii,int val,int irq)307 static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq)
308 {
309     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
310     IPMIBT *ib = iic->get_backend_data(ii);
311 
312     if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
313         return;
314     }
315 
316     IPMI_BT_SET_SMS_ATN(ib->control_reg, val);
317     if (val) {
318         if (irq && !IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
319                 IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
320             IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
321             ipmi_bt_raise_irq(ib);
322         }
323     } else {
324         if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
325                 IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
326             IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
327             ipmi_bt_lower_irq(ib);
328         }
329     }
330 }
331 
ipmi_bt_handle_reset(IPMIInterface * ii,bool is_cold)332 static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold)
333 {
334     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
335     IPMIBT *ib = iic->get_backend_data(ii);
336 
337     if (is_cold) {
338         /* Disable the BT interrupt on reset */
339         if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
340             IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
341             ipmi_bt_lower_irq(ib);
342         }
343         IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
344     }
345 }
346 
ipmi_bt_set_irq_enable(IPMIInterface * ii,int val)347 static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val)
348 {
349     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
350     IPMIBT *ib = iic->get_backend_data(ii);
351 
352     ib->irqs_enabled = val;
353 }
354 
ipmi_bt_init(IPMIInterface * ii,unsigned int min_size,Error ** errp)355 static void ipmi_bt_init(IPMIInterface *ii, unsigned int min_size, Error **errp)
356 {
357     IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
358     IPMIBT *ib = iic->get_backend_data(ii);
359 
360     if (min_size == 0) {
361         min_size = 4;
362     }
363     ib->size_mask = min_size - 1;
364     ib->io_length = 3;
365 
366     memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt",
367                           min_size);
368 }
369 
ipmi_bt_vmstate_post_load(void * opaque,int version)370 int ipmi_bt_vmstate_post_load(void *opaque, int version)
371 {
372     IPMIBT *ib = opaque;
373 
374     /* Make sure all the values are sane. */
375     if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE ||
376         ib->outpos >= ib->outlen) {
377         qemu_log_mask(LOG_GUEST_ERROR,
378                       "ipmi:bt: vmstate transfer received bad out values: %d %d\n",
379                       ib->outpos, ib->outlen);
380         ib->outpos = 0;
381         ib->outlen = 0;
382     }
383 
384     if (ib->inlen >= MAX_IPMI_MSG_SIZE) {
385         qemu_log_mask(LOG_GUEST_ERROR,
386                       "ipmi:bt: vmstate transfer received bad in value: %d\n",
387                       ib->inlen);
388         ib->inlen = 0;
389     }
390 
391     return 0;
392 }
393 
394 const VMStateDescription vmstate_IPMIBT = {
395     .name = TYPE_IPMI_INTERFACE_PREFIX "bt",
396     .version_id = 1,
397     .minimum_version_id = 1,
398     .post_load = ipmi_bt_vmstate_post_load,
399     .fields = (const VMStateField[]) {
400         VMSTATE_BOOL(obf_irq_set, IPMIBT),
401         VMSTATE_BOOL(atn_irq_set, IPMIBT),
402         VMSTATE_BOOL(irqs_enabled, IPMIBT),
403         VMSTATE_UINT32(outpos, IPMIBT),
404         VMSTATE_UINT32(outlen, IPMIBT),
405         VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
406         VMSTATE_UINT32(inlen, IPMIBT),
407         VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE),
408         VMSTATE_UINT8(control_reg, IPMIBT),
409         VMSTATE_UINT8(mask_reg, IPMIBT),
410         VMSTATE_UINT8(waiting_rsp, IPMIBT),
411         VMSTATE_UINT8(waiting_seq, IPMIBT),
412         VMSTATE_END_OF_LIST()
413     }
414 };
415 
ipmi_bt_get_fwinfo(struct IPMIBT * ib,IPMIFwInfo * info)416 void ipmi_bt_get_fwinfo(struct IPMIBT *ib, IPMIFwInfo *info)
417 {
418     info->interface_name = "bt";
419     info->interface_type = IPMI_SMBIOS_BT;
420     info->ipmi_spec_major_revision = 2;
421     info->ipmi_spec_minor_revision = 0;
422     info->base_address = ib->io_base;
423     info->register_length = ib->io_length;
424     info->register_spacing = 1;
425     info->memspace = IPMI_MEMSPACE_IO;
426     info->irq_type = IPMI_LEVEL_IRQ;
427 }
428 
ipmi_bt_class_init(IPMIInterfaceClass * iic)429 void ipmi_bt_class_init(IPMIInterfaceClass *iic)
430 {
431     iic->init = ipmi_bt_init;
432     iic->set_atn = ipmi_bt_set_atn;
433     iic->handle_rsp = ipmi_bt_handle_rsp;
434     iic->handle_if_event = ipmi_bt_handle_event;
435     iic->set_irq_enable = ipmi_bt_set_irq_enable;
436     iic->reset = ipmi_bt_handle_reset;
437 }
438