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