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