xref: /openbmc/qemu/hw/misc/aspeed_ibt.c (revision f6df0268)
1 /*
2  * ASPEED iBT Device
3  *
4  * Copyright (c) 2016-2021 Cédric Le Goater, IBM Corporation.
5  *
6  * This code is licensed under the GPL version 2 or later.  See
7  * the COPYING file in the top-level directory.
8  */
9 
10 #include "qemu/osdep.h"
11 #include "hw/sysbus.h"
12 #include "sysemu/qtest.h"
13 #include "sysemu/sysemu.h"
14 #include "qemu/log.h"
15 #include "qapi/error.h"
16 #include "qemu/error-report.h"
17 #include "hw/irq.h"
18 #include "hw/qdev-properties.h"
19 #include "hw/qdev-properties-system.h"
20 #include "migration/vmstate.h"
21 #include "hw/misc/aspeed_ibt.h"
22 #include "trace.h"
23 
24 #define BT_IO_REGION_SIZE 0x1C
25 
26 #define TO_REG(o) (o >> 2)
27 
28 #define BT_CR0                0x0   /* iBT config */
29 #define   BT_CR0_IO_BASE         16
30 #define   BT_CR0_IRQ             12
31 #define   BT_CR0_EN_CLR_SLV_RDP  0x8
32 #define   BT_CR0_EN_CLR_SLV_WRP  0x4
33 #define   BT_CR0_ENABLE_IBT      0x1
34 #define BT_CR1                0x4  /* interrupt enable */
35 #define   BT_CR1_IRQ_H2B         0x01
36 #define   BT_CR1_IRQ_HBUSY       0x40
37 #define BT_CR2                0x8  /* interrupt status */
38 #define   BT_CR2_IRQ_H2B         0x01
39 #define   BT_CR2_IRQ_HBUSY       0x40
40 #define BT_CR3                0xc /* unused */
41 #define BT_CTRL                  0x10
42 #define   BT_CTRL_B_BUSY         0x80
43 #define   BT_CTRL_H_BUSY         0x40
44 #define   BT_CTRL_OEM0           0x20
45 #define   BT_CTRL_SMS_ATN        0x10
46 #define   BT_CTRL_B2H_ATN        0x08
47 #define   BT_CTRL_H2B_ATN        0x04
48 #define   BT_CTRL_CLR_RD_PTR     0x02
49 #define   BT_CTRL_CLR_WR_PTR     0x01
50 #define BT_BMC2HOST            0x14
51 #define BT_INTMASK             0x18
52 #define   BT_INTMASK_B2H_IRQEN   0x01
53 #define   BT_INTMASK_B2H_IRQ     0x02
54 #define   BT_INTMASK_BMC_HWRST   0x80
55 
56 /*
57  * VM IPMI defines
58  */
59 #define VM_MSG_CHAR        0xA0 /* Marks end of message */
60 #define VM_CMD_CHAR        0xA1 /* Marks end of a command */
61 #define VM_ESCAPE_CHAR     0xAA /* Set bit 4 from the next byte to 0 */
62 
63 #define VM_PROTOCOL_VERSION        1
64 #define VM_CMD_VERSION             0xff /* A version number byte follows */
65 #define VM_CMD_NOATTN              0x00
66 #define VM_CMD_ATTN                0x01
67 #define VM_CMD_ATTN_IRQ            0x02
68 #define VM_CMD_POWEROFF            0x03
69 #define VM_CMD_RESET               0x04
70 #define VM_CMD_ENABLE_IRQ          0x05 /* Enable/disable the messaging irq */
71 #define VM_CMD_DISABLE_IRQ         0x06
72 #define VM_CMD_SEND_NMI            0x07
73 #define VM_CMD_CAPABILITIES        0x08
74 #define   VM_CAPABILITIES_POWER    0x01
75 #define   VM_CAPABILITIES_RESET    0x02
76 #define   VM_CAPABILITIES_IRQ      0x04
77 #define   VM_CAPABILITIES_NMI      0x08
78 #define   VM_CAPABILITIES_ATTN     0x10
79 #define   VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20
80 #define VM_CMD_GRACEFUL_SHUTDOWN   0x09
81 
82 /*
83  * These routines are inspired by the 'ipmi-bmc-extern' model and by
84  * the lanserv simulator of OpenIPMI. See :
85  *    https://github.com/cminyard/openipmi/blob/master/lanserv/serial_ipmi.c
86  */
ipmb_checksum(const unsigned char * data,int size,unsigned char start)87 static unsigned char ipmb_checksum(const unsigned char *data, int size,
88                                    unsigned char start)
89 {
90         unsigned char csum = start;
91 
92         for (; size > 0; size--, data++) {
93                 csum += *data;
94         }
95         return csum;
96 }
97 
vm_add_char(unsigned char ch,unsigned char * c,unsigned int * pos)98 static void vm_add_char(unsigned char ch, unsigned char *c, unsigned int *pos)
99 {
100     switch (ch) {
101     case VM_MSG_CHAR:
102     case VM_CMD_CHAR:
103     case VM_ESCAPE_CHAR:
104         c[(*pos)++] = VM_ESCAPE_CHAR;
105         c[(*pos)++] = ch | 0x10;
106         break;
107 
108     default:
109         c[(*pos)++] = ch;
110     }
111 }
112 
aspeed_ibt_dump_msg(const char * func,unsigned char * msg,unsigned int len)113 static void aspeed_ibt_dump_msg(const char *func, unsigned char *msg,
114                                 unsigned int len)
115 {
116     if (trace_event_get_state_backends(TRACE_ASPEED_IBT_CHR_DUMP_MSG)) {
117         int size = len * 3 + 1;
118         char tmp[size];
119         int i, n = 0;
120 
121         for (i = 0; i < len; i++) {
122             n += snprintf(tmp + n, size - n, "%02x:", msg[i]);
123         }
124         tmp[size - 1] = 0;
125 
126         trace_aspeed_ibt_chr_dump_msg(func, tmp, len);
127     }
128 }
129 
aspeed_ibt_chr_write(AspeedIBTState * ibt,const uint8_t * buf,int len)130 static void aspeed_ibt_chr_write(AspeedIBTState *ibt, const uint8_t *buf,
131                                  int len)
132 {
133     int i;
134 
135     if (!qemu_chr_fe_get_driver(&ibt->chr)) {
136         return;
137     }
138 
139     aspeed_ibt_dump_msg(__func__, ibt->recv_msg, ibt->recv_msg_len);
140 
141     for (i = 0; i < len; i++) {
142         qemu_chr_fe_write(&ibt->chr, &buf[i], 1);
143     }
144 }
145 
vm_send(AspeedIBTState * ibt)146 static void vm_send(AspeedIBTState *ibt)
147 {
148     unsigned int i;
149     unsigned int len = 0;
150     unsigned char c[(ibt->send_msg_len + 7) * 2];
151     uint8_t netfn;
152 
153     /*
154      * The VM IPMI message format does not follow the IPMI BT
155      * interface format. The sequence and the netfn bytes need to be
156      * swapped.
157      */
158     netfn = ibt->send_msg[1];
159     ibt->send_msg[1] = ibt->send_msg[2];
160     ibt->send_msg[2] = netfn;
161 
162     /* No length byte in the VM IPMI message format. trim it */
163     for (i = 1; i < ibt->send_msg_len; i++) {
164         vm_add_char(ibt->send_msg[i], c, &len);
165     }
166 
167     vm_add_char(-ipmb_checksum(&ibt->send_msg[1], ibt->send_msg_len - 1, 0),
168                 c, &len);
169     c[len++] = VM_MSG_CHAR;
170 
171     aspeed_ibt_chr_write(ibt, c, len);
172 }
173 
aspeed_ibt_update_irq(AspeedIBTState * ibt)174 static void aspeed_ibt_update_irq(AspeedIBTState *ibt)
175 {
176     bool raise = false;
177 
178     /* H2B rising */
179     if ((ibt->regs[TO_REG(BT_CTRL)] & BT_CTRL_H2B_ATN) &&
180         ((ibt->regs[TO_REG(BT_CR1)] & BT_CR1_IRQ_H2B) == BT_CR1_IRQ_H2B)) {
181         ibt->regs[TO_REG(BT_CR2)] |= BT_CR2_IRQ_H2B;
182 
183         /*
184          * Also flag the fact that we are waiting for the guest/driver
185          * to read a received message
186          */
187         ibt->recv_waiting = true;
188         raise = true;
189     }
190 
191     /* H_BUSY falling (not supported) */
192     if ((ibt->regs[TO_REG(BT_CTRL)] & BT_CTRL_H_BUSY) &&
193         ((ibt->regs[TO_REG(BT_CR1)] & BT_CR1_IRQ_HBUSY) == BT_CR1_IRQ_HBUSY)) {
194         ibt->regs[TO_REG(BT_CR2)] |= BT_CR2_IRQ_HBUSY;
195 
196         raise = true;
197     }
198 
199     if (raise) {
200         qemu_irq_raise(ibt->irq);
201     }
202 }
203 
vm_handle_msg(AspeedIBTState * ibt,unsigned char * msg,unsigned int len)204 static void vm_handle_msg(AspeedIBTState *ibt, unsigned char *msg,
205                           unsigned int len)
206 {
207     uint8_t seq;
208 
209     aspeed_ibt_dump_msg(__func__, ibt->recv_msg, ibt->recv_msg_len);
210 
211     if (len < 4) {
212         qemu_log_mask(LOG_GUEST_ERROR, " %s: Message too short\n", __func__);
213         return;
214     }
215 
216     if (ipmb_checksum(ibt->recv_msg, ibt->recv_msg_len, 0) != 0) {
217         qemu_log_mask(LOG_GUEST_ERROR, " %s: Message checksum failure\n",
218                       __func__);
219         return;
220     }
221 
222     /* Trim the checksum byte */
223     ibt->recv_msg_len--;
224 
225     /*
226      * The VM IPMI message format does not follow the IPMI BT
227      * interface format. The sequence and the netfn bytes need to be
228      * swapped.
229      */
230     seq = ibt->recv_msg[0];
231     ibt->recv_msg[0] = ibt->recv_msg[1];
232     ibt->recv_msg[1] = seq;
233 
234     aspeed_ibt_update_irq(ibt);
235 }
236 
237 /* TODO: handle commands */
vm_handle_cmd(AspeedIBTState * ibt,unsigned char * msg,unsigned int len)238 static void vm_handle_cmd(AspeedIBTState *ibt, unsigned char *msg,
239                           unsigned int len)
240 {
241     aspeed_ibt_dump_msg(__func__, ibt->recv_msg, ibt->recv_msg_len);
242 
243     if (len < 1) {
244         qemu_log_mask(LOG_GUEST_ERROR, " %s: Command too short\n", __func__);
245         return;
246     }
247 
248     switch (msg[0]) {
249     case VM_CMD_VERSION:
250         break;
251 
252     case VM_CMD_CAPABILITIES:
253         if (len < 2) {
254             return;
255         }
256         break;
257 
258     case VM_CMD_RESET:
259         break;
260     }
261 }
262 
vm_handle_char(AspeedIBTState * ibt,unsigned char ch)263 static void vm_handle_char(AspeedIBTState *ibt, unsigned char ch)
264 {
265     unsigned int len = ibt->recv_msg_len;
266 
267     switch (ch) {
268     case VM_MSG_CHAR:
269     case VM_CMD_CHAR:
270         if (ibt->in_escape) {
271             qemu_log_mask(LOG_GUEST_ERROR, " %s: Message ended in escape\n",
272                           __func__);
273         } else if (ibt->recv_msg_too_many) {
274             qemu_log_mask(LOG_GUEST_ERROR, " %s: Message too long\n", __func__);
275         } else if (ibt->recv_msg_len == 0) {
276             /* Nothing to do */
277         } else if (ch == VM_MSG_CHAR) {
278             /* Last byte of message. Signal BMC as the host would do */
279             ibt->regs[TO_REG(BT_CTRL)] |= BT_CTRL_H2B_ATN;
280 
281             vm_handle_msg(ibt, ibt->recv_msg, ibt->recv_msg_len);
282 
283             /* Message is only handled when read by BMC (!B_BUSY) */
284         } else if (ch == VM_CMD_CHAR) {
285             vm_handle_cmd(ibt, ibt->recv_msg, ibt->recv_msg_len);
286 
287             /* Command is now handled. reset receive state */
288             ibt->in_escape = 0;
289             ibt->recv_msg_len = 0;
290             ibt->recv_msg_too_many = 0;
291         }
292         break;
293 
294     case VM_ESCAPE_CHAR:
295         if (!ibt->recv_msg_too_many) {
296             ibt->in_escape = 1;
297         }
298         break;
299 
300     default:
301         if (ibt->in_escape) {
302             ibt->in_escape = 0;
303             ch &= ~0x10;
304         }
305 
306         if (!ibt->recv_msg_too_many) {
307             if (len >= sizeof(ibt->recv_msg)) {
308                 ibt->recv_msg_too_many = 1;
309                 break;
310             }
311 
312             ibt->recv_msg[len] = ch;
313             ibt->recv_msg_len++;
314         }
315         break;
316     }
317 }
318 
vm_connected(AspeedIBTState * ibt)319 static void vm_connected(AspeedIBTState *ibt)
320 {
321     unsigned int len = 0;
322     unsigned char c[5];
323 
324     vm_add_char(VM_CMD_VERSION, c, &len);
325     vm_add_char(VM_PROTOCOL_VERSION, c, &len);
326     c[len++] = VM_CMD_CHAR;
327 
328     aspeed_ibt_chr_write(ibt, c, len);
329 }
330 
aspeed_ibt_chr_event(void * opaque,QEMUChrEvent event)331 static void aspeed_ibt_chr_event(void *opaque, QEMUChrEvent event)
332 {
333     AspeedIBTState *ibt = ASPEED_IBT(opaque);
334 
335     switch (event) {
336     case CHR_EVENT_OPENED:
337         vm_connected(ibt);
338         ibt->connected = true;
339         break;
340 
341     case CHR_EVENT_CLOSED:
342         if (!ibt->connected) {
343             return;
344         }
345         ibt->connected = false;
346         break;
347     case CHR_EVENT_BREAK:
348     case CHR_EVENT_MUX_IN:
349     case CHR_EVENT_MUX_OUT:
350         /* Ignore */
351         break;
352    }
353     trace_aspeed_ibt_chr_event(ibt->connected);
354 }
355 
aspeed_ibt_chr_can_receive(void * opaque)356 static int aspeed_ibt_chr_can_receive(void *opaque)
357 {
358     AspeedIBTState *ibt = ASPEED_IBT(opaque);
359 
360     return !ibt->recv_waiting && !(ibt->regs[TO_REG(BT_CTRL)] & BT_CTRL_B_BUSY);
361 }
362 
aspeed_ibt_chr_receive(void * opaque,const uint8_t * buf,int size)363 static void aspeed_ibt_chr_receive(void *opaque, const uint8_t *buf,
364                                    int size)
365 {
366     AspeedIBTState *ibt = ASPEED_IBT(opaque);
367     int i;
368 
369     if (!ibt->connected) {
370         qemu_log_mask(LOG_GUEST_ERROR, " %s: not connected !?\n", __func__);
371         return;
372     }
373 
374     for (i = 0; i < size; i++) {
375         vm_handle_char(ibt, buf[i]);
376     }
377 }
378 
aspeed_ibt_write(void * opaque,hwaddr offset,uint64_t data,unsigned size)379 static void aspeed_ibt_write(void *opaque, hwaddr offset, uint64_t data,
380                              unsigned size)
381 {
382     AspeedIBTState *ibt = ASPEED_IBT(opaque);
383 
384     trace_aspeed_ibt_write(offset, data);
385 
386     switch (offset) {
387     case BT_CTRL:
388         /* CLR_WR_PTR: cleared before a message is written */
389         if (data & BT_CTRL_CLR_WR_PTR) {
390             memset(ibt->send_msg, 0, sizeof(ibt->send_msg));
391             ibt->send_msg_len = 0;
392             trace_aspeed_ibt_event("CLR_WR_PTR");
393         }
394 
395         /* CLR_RD_PTR: cleared before a message is read */
396         else if (data & BT_CTRL_CLR_RD_PTR) {
397             ibt->recv_msg_index = -1;
398             trace_aspeed_ibt_event("CLR_RD_PTR");
399         }
400 
401         /*
402          * H2B_ATN: raised by host to end message, cleared by BMC
403          * before reading message
404          */
405         else if (data & BT_CTRL_H2B_ATN) {
406             ibt->regs[TO_REG(BT_CTRL)] &= ~BT_CTRL_H2B_ATN;
407             trace_aspeed_ibt_event("H2B_ATN");
408         }
409 
410         /* B_BUSY: raised and cleared by BMC when message is read */
411         else if (data & BT_CTRL_B_BUSY) {
412             ibt->regs[TO_REG(BT_CTRL)] ^= BT_CTRL_B_BUSY;
413             trace_aspeed_ibt_event("B_BUSY");
414         }
415 
416         /*
417          * B2H_ATN: raised by BMC and cleared by host
418          *
419          * Also simulate the host busy bit which is set while the host
420          * is reading the message from the BMC
421          */
422         else if (data & BT_CTRL_B2H_ATN) {
423             trace_aspeed_ibt_event("B2H_ATN");
424             ibt->regs[TO_REG(BT_CTRL)] |= (BT_CTRL_B2H_ATN | BT_CTRL_H_BUSY);
425 
426             vm_send(ibt);
427 
428             ibt->regs[TO_REG(BT_CTRL)] &= ~(BT_CTRL_B2H_ATN | BT_CTRL_H_BUSY);
429 
430             /* signal H_BUSY falling but that's a bit useless */
431             aspeed_ibt_update_irq(ibt);
432         }
433 
434         /* Anything else is unexpected */
435         else {
436             qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected CTRL setting\n",
437                           __func__);
438         }
439 
440         /* Message was read by BMC. we can reset the receive state */
441         if (!(ibt->regs[TO_REG(BT_CTRL)] & BT_CTRL_B_BUSY)) {
442             trace_aspeed_ibt_event("B_BUSY cleared");
443             ibt->recv_waiting = false;
444             ibt->in_escape = 0;
445             ibt->recv_msg_len = 0;
446             ibt->recv_msg_too_many = 0;
447         }
448         break;
449 
450     case BT_BMC2HOST:
451         if (ibt->send_msg_len < sizeof(ibt->send_msg)) {
452             trace_aspeed_ibt_event("BMC2HOST");
453             ibt->send_msg[ibt->send_msg_len++] = data & 0xff;
454         }
455         break;
456 
457     case BT_CR0: /* TODO: iBT config */
458     case BT_CR1: /* interrupt enable */
459     case BT_CR3: /* unused */
460     case BT_INTMASK:
461         ibt->regs[TO_REG(offset)] = (uint32_t) data;
462         break;
463     case BT_CR2: /* interrupt status. writing 1 clears. */
464         ibt->regs[TO_REG(offset)] ^= (uint32_t) data;
465         qemu_irq_lower(ibt->irq);
466         break;
467 
468     default:
469         qemu_log_mask(LOG_UNIMP, "%s: not implemented 0x%" HWADDR_PRIx "\n",
470                       __func__, offset);
471         break;
472     }
473 }
474 
aspeed_ibt_read(void * opaque,hwaddr offset,unsigned size)475 static uint64_t aspeed_ibt_read(void *opaque, hwaddr offset, unsigned size)
476 {
477     AspeedIBTState *ibt = ASPEED_IBT(opaque);
478     uint64_t val = 0;
479 
480     switch (offset) {
481     case BT_BMC2HOST:
482         trace_aspeed_ibt_event("BMC2HOST");
483         /*
484          * The IPMI BT interface requires the first byte to be the
485          * length of the message
486          */
487         if (ibt->recv_msg_index == -1) {
488             val = ibt->recv_msg_len;
489             ibt->recv_msg_index++;
490         } else if (ibt->recv_msg_index < ibt->recv_msg_len) {
491             val = ibt->recv_msg[ibt->recv_msg_index++];
492         }
493         break;
494 
495     case BT_CR0:
496     case BT_CR1:
497     case BT_CR2:
498     case BT_CR3:
499     case BT_CTRL:
500     case BT_INTMASK:
501         return ibt->regs[TO_REG(offset)];
502     default:
503         qemu_log_mask(LOG_UNIMP, "%s: not implemented 0x%" HWADDR_PRIx "\n",
504                       __func__, offset);
505         return 0;
506     }
507 
508     trace_aspeed_ibt_read(offset, val);
509     return val;
510 }
511 
512 static const MemoryRegionOps aspeed_ibt_ops = {
513     .read = aspeed_ibt_read,
514     .write = aspeed_ibt_write,
515     .endianness = DEVICE_LITTLE_ENDIAN,
516     .valid = {
517         .min_access_size = 1,
518         .max_access_size = 4,
519     },
520 };
521 
aspeed_ibt_reset(DeviceState * dev)522 static void aspeed_ibt_reset(DeviceState *dev)
523 {
524     AspeedIBTState *ibt = ASPEED_IBT(dev);
525 
526     memset(ibt->regs, 0, sizeof(ibt->regs));
527 
528     memset(ibt->recv_msg, 0, sizeof(ibt->recv_msg));
529     ibt->recv_msg_len = 0;
530     ibt->recv_msg_index = -1;
531     ibt->recv_msg_too_many = 0;
532     ibt->recv_waiting = false;
533     ibt->in_escape = 0;
534 
535     memset(ibt->send_msg, 0, sizeof(ibt->send_msg));
536     ibt->send_msg_len = 0;
537 }
538 
aspeed_ibt_realize(DeviceState * dev,Error ** errp)539 static void aspeed_ibt_realize(DeviceState *dev, Error **errp)
540 {
541     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
542     AspeedIBTState *ibt = ASPEED_IBT(dev);
543 
544     if (!qemu_chr_fe_get_driver(&ibt->chr) && !qtest_enabled()) {
545         warn_report("Aspeed iBT has no chardev backend");
546     } else {
547         qemu_chr_fe_set_handlers(&ibt->chr, aspeed_ibt_chr_can_receive,
548                                  aspeed_ibt_chr_receive, aspeed_ibt_chr_event,
549                                  NULL, ibt, NULL, true);
550     }
551 
552     sysbus_init_irq(sbd, &ibt->irq);
553     memory_region_init_io(&ibt->iomem, OBJECT(ibt), &aspeed_ibt_ops, ibt,
554                           TYPE_ASPEED_IBT, BT_IO_REGION_SIZE);
555 
556     sysbus_init_mmio(sbd, &ibt->iomem);
557 }
558 
559 static Property aspeed_ibt_props[] = {
560     DEFINE_PROP_CHR("chardev", AspeedIBTState, chr),
561     DEFINE_PROP_END_OF_LIST(),
562 };
563 
564 static const VMStateDescription vmstate_aspeed_ibt = {
565     .name = "aspeed.bt",
566     .version_id = 1,
567     .minimum_version_id = 1,
568     .fields = (VMStateField[]) {
569         VMSTATE_UINT32_ARRAY(regs, AspeedIBTState, ASPEED_IBT_NR_REGS),
570         VMSTATE_END_OF_LIST()
571     }
572 };
573 
aspeed_ibt_class_init(ObjectClass * klass,void * data)574 static void aspeed_ibt_class_init(ObjectClass *klass, void *data)
575 {
576     DeviceClass *dc = DEVICE_CLASS(klass);
577     dc->realize = aspeed_ibt_realize;
578     dc->reset = aspeed_ibt_reset;
579     dc->desc = "ASPEED iBT Device";
580     dc->vmsd = &vmstate_aspeed_ibt;
581     device_class_set_props(dc, aspeed_ibt_props);
582 }
583 
584 static const TypeInfo aspeed_ibt_info = {
585     .name = TYPE_ASPEED_IBT,
586     .parent = TYPE_SYS_BUS_DEVICE,
587     .instance_size = sizeof(AspeedIBTState),
588     .class_init = aspeed_ibt_class_init,
589 };
590 
aspeed_ibt_register_types(void)591 static void aspeed_ibt_register_types(void)
592 {
593     type_register_static(&aspeed_ibt_info);
594 }
595 
596 type_init(aspeed_ibt_register_types);
597