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