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