On 11/13/2013 08:12 AM, Bret Ketchum wrote: > Having trouble with this patch (after the prior patches were > successfully applied):
I was a couple of days behind and some other changes that came in conflicted with this. -corey > > git status > # On branch master > # Changes not staged for commit: > # (use "git add <file>..." to update what will be committed) > # (use "git checkout -- <file>..." to discard changes in working > directory) > # > # modified: ../../../backends/baum.c > # modified: ../../../backends/msmouse.c > # modified: ../../../default-configs/i386-softmmu.mak > # modified: ../../../default-configs/x86_64-softmmu.mak > # modified: ../../../hw/Makefile.objs > # modified: ../../../hw/misc/ivshmem.c > # modified: ../../../include/hw/nvram/fw_cfg.h > # modified: ../../../include/sysemu/char.h > # modified: ../../../include/ui/console.h > # modified: ../../../include/ui/qemu-spice.h > # modified: ../../../qemu-char.c > # modified: ../../../qemu-doc.texi > # modified: ../../../qemu-options.hx > # modified: ../../../spice-qemu-char.c > # modified: ../../../ui/console.c > # > # Untracked files: > # (use "git add <file>..." to include in what will be committed) > # > # ../../ > # ../../../hw/ipmi/ > no changes added to commit (use "git add" and/or "git commit -a") > [ > > git apply --verbos --check ../ipmi-patches/290712.mbox > Checking patch tests/Makefile... > error: while searching for: > gcov-files-i386-y += hw/hd-geometry.c > check-qtest-i386-y += tests/boot-order-test$(EXESUF) > check-qtest-i386-y += tests/rtc-test$(EXESUF) > check-qtest-i386-y += tests/i440fx-test$(EXESUF) > check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) > check-qtest-x86_64-y = $(check-qtest-i386-y) > > error: patch failed: tests/Makefile:65 > error: tests/Makefile: patch does not apply > Checking patch tests/ipmi-bt-test.c... > Checking patch tests/ipmi-kcs-test.c... > > > > I believe the patch for tests/Makefile should look like: > > diff --git a/tests/Makefile b/tests/Makefile > index f414f2c..0395cd4 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -65,6 +65,8 @@ check-qtest-i386-y += tests/hd-geo-test$(EXESUF) > gcov-files-i386-y += hw/hd-geometry.c > check-qtest-i386-y += tests/boot-order-test$(EXESUF) > check-qtest-i386-y += tests/rtc-test$(EXESUF) > +check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF) > +check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF) > check-qtest-i386-y += tests/i440fx-test$(EXESUF) > check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) > check-qtest-i386-y += tests/qom-test$(EXESUF) > @@ -194,6 +196,8 @@ tests/m48t59-test$(EXESUF): tests/m48t59-test.o > tests/endianness-test$(EXESUF): tests/endianness-test.o > tests/fdc-test$(EXESUF): tests/fdc-test.o > tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) > +tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o > +tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o > tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o > tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) > tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) > > > > On Tue, Nov 12, 2013 at 10:33 AM, Corey Minyard <miny...@acm.org > <mailto:miny...@acm.org>> wrote: > > Test the KCS interface with a local BMC and a BT interface with an > external BMC. > > Signed-off-by: Corey Minyard <cminy...@mvista.com > <mailto:cminy...@mvista.com>> > --- > tests/Makefile | 4 + > tests/ipmi-bt-test.c | 440 > ++++++++++++++++++++++++++++++++++++++++++++++++++ > tests/ipmi-kcs-test.c | 294 +++++++++++++++++++++++++++++++++ > 3 files changed, 738 insertions(+) > create mode 100644 tests/ipmi-bt-test.c > create mode 100644 tests/ipmi-kcs-test.c > > diff --git a/tests/Makefile b/tests/Makefile > index fa4c9f0..044e7a8 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -65,6 +65,8 @@ check-qtest-i386-y += tests/hd-geo-test$(EXESUF) > gcov-files-i386-y += hw/hd-geometry.c > check-qtest-i386-y += tests/boot-order-test$(EXESUF) > check-qtest-i386-y += tests/rtc-test$(EXESUF) > +check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF) > +check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF) > check-qtest-i386-y += tests/i440fx-test$(EXESUF) > check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) > check-qtest-x86_64-y = $(check-qtest-i386-y) > @@ -169,6 +171,8 @@ tests/m48t59-test$(EXESUF): tests/m48t59-test.o > tests/endianness-test$(EXESUF): tests/endianness-test.o > tests/fdc-test$(EXESUF): tests/fdc-test.o > tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) > +tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o > +tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o > tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o > tests/boot-order-test$(EXESUF): tests/boot-order-test.o > $(libqos-obj-y) > tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) > diff --git a/tests/ipmi-bt-test.c b/tests/ipmi-bt-test.c > new file mode 100644 > index 0000000..c1da325 > --- /dev/null > +++ b/tests/ipmi-bt-test.c > @@ -0,0 +1,440 @@ > +/* > + * IPMI BT test cases, using the external interface for checking > + * > + * Copyright (c) 2012 Corey Minyard <cminy...@mvista.com > <mailto:cminy...@mvista.com>> > + * > + * Permission is hereby granted, free of charge, to any person > obtaining a copy > + * of this software and associated documentation files (the > "Software"), to deal > + * in the Software without restriction, including without > limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, > and/or sell > + * copies of the Software, and to permit persons to whom the > Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be > included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY > KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO > EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, > DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR > OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#include <sys/types.h> > +#include <stdint.h> > +#include <string.h> > +#include <stdio.h> > + > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <netinet/ip.h> > +#include <netinet/tcp.h> > + > +#include <glib.h> > + > +#include "libqtest.h" > +#include "qemu-common.h" > + > +#define IPMI_IRQ 5 > + > +#define IPMI_BT_BASE 0xe4 > + > +#define IPMI_BT_CTLREG_CLR_WR_PTR 0 > +#define IPMI_BT_CTLREG_CLR_RD_PTR 1 > +#define IPMI_BT_CTLREG_H2B_ATN 2 > +#define IPMI_BT_CTLREG_B2H_ATN 3 > +#define IPMI_BT_CTLREG_SMS_ATN 4 > +#define IPMI_BT_CTLREG_H_BUSY 6 > +#define IPMI_BT_CTLREG_B_BUSY 7 > + > +#define IPMI_BT_CTLREG_GET(b) ((bt_get_ctrlreg() >> (b)) & 1) > +#define IPMI_BT_CTLREG_GET_H2B_ATN() > IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H2B_ATN) > +#define IPMI_BT_CTLREG_GET_B2H_ATN() > IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B2H_ATN) > +#define IPMI_BT_CTLREG_GET_SMS_ATN() > IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_SMS_ATN) > +#define IPMI_BT_CTLREG_GET_H_BUSY() > IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H_BUSY) > +#define IPMI_BT_CTLREG_GET_B_BUSY() > IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B_BUSY) > + > +#define IPMI_BT_CTLREG_SET(b) bt_write_ctrlreg(1 << (b)) > +#define IPMI_BT_CTLREG_SET_CLR_WR_PTR() IPMI_BT_CTLREG_SET( \ > + > IPMI_BT_CTLREG_CLR_WR_PTR) > +#define IPMI_BT_CTLREG_SET_CLR_RD_PTR() IPMI_BT_CTLREG_SET( \ > + > IPMI_BT_CTLREG_CLR_RD_PTR) > +#define IPMI_BT_CTLREG_SET_H2B_ATN() > IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H2B_ATN) > +#define IPMI_BT_CTLREG_SET_B2H_ATN() > IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_B2H_ATN) > +#define IPMI_BT_CTLREG_SET_SMS_ATN() > IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_SMS_ATN) > +#define IPMI_BT_CTLREG_SET_H_BUSY() > IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H_BUSY) > + > +static int bt_ints_enabled; > + > +static uint8_t bt_get_ctrlreg(void) > +{ > + return inb(IPMI_BT_BASE); > +} > + > +static void bt_write_ctrlreg(uint8_t val) > +{ > + outb(IPMI_BT_BASE, val); > +} > + > +static uint8_t bt_get_buf(void) > +{ > + return inb(IPMI_BT_BASE + 1); > +} > + > +static void bt_write_buf(uint8_t val) > +{ > + outb(IPMI_BT_BASE + 1, val); > +} > + > +static uint8_t bt_get_irqreg(void) > +{ > + return inb(IPMI_BT_BASE + 2); > +} > + > +static void bt_write_irqreg(uint8_t val) > +{ > + outb(IPMI_BT_BASE + 2, val); > +} > + > +static void bt_wait_b_busy(void) > +{ > + unsigned int count = 1000; > + while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) { > + g_assert(--count != 0); > + } > +} > + > +static void bt_wait_b2h_atn(void) > +{ > + unsigned int count = 1000; > + while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) { > + g_assert(--count != 0); > + } > +} > + > + > +static int emu_lfd; > +static int emu_fd; > +static in_port_t emu_port; > +static uint8_t inbuf[100]; > +static unsigned int inbuf_len; > +static unsigned int inbuf_pos; > +static int last_was_aa; > + > +static void read_emu_data(void) > +{ > + fd_set readfds; > + int rv; > + struct timeval tv; > + > + FD_ZERO(&readfds); > + FD_SET(emu_fd, &readfds); > + tv.tv_sec = 10; > + tv.tv_usec = 0; > + rv = select(emu_fd + 1, &readfds, NULL, NULL, &tv); > + if (rv == -1) { > + perror("select"); > + } > + g_assert(rv == 1); > + rv = read(emu_fd, inbuf, sizeof(inbuf)); > + if (rv == -1) { > + perror("read"); > + } > + g_assert(rv > 0); > + inbuf_len = rv; > + inbuf_pos = 0; > +} > + > +static void write_emu_msg(uint8_t *msg, unsigned int len) > +{ > + int rv; > + > +#ifdef DEBUG_TEST > + { > + unsigned int i; > + printf("sending:"); > + for (i = 0; i < len; i++) { > + printf(" %2.2x", msg[i]); > + } > + printf("\n"); > + } > +#endif > + rv = write(emu_fd, msg, len); > + g_assert(rv == len); > +} > + > +static void get_emu_msg(uint8_t *msg, unsigned int *len) > +{ > + unsigned int outpos = 0; > + > + for (;;) { > + while (inbuf_pos < inbuf_len) { > + uint8_t ch = inbuf[inbuf_pos++]; > + > + g_assert(outpos < *len); > + if (last_was_aa) { > + assert(ch & 0x10); > + msg[outpos++] = ch & ~0x10; > + last_was_aa = 0; > + } else if (ch == 0xaa) { > + last_was_aa = 1; > + } else { > + msg[outpos++] = ch; > + if ((ch == 0xa0) || (ch == 0xa1)) { > + /* Message complete */ > + *len = outpos; > + goto done; > + } > + } > + } > + read_emu_data(); > + } > + done: > +#ifdef DEBUG_TEST > + { > + unsigned int i; > + printf("Msg:"); > + for (i = 0; i < outpos; i++) { > + printf(" %2.2x", msg[i]); > + } > + printf("\n"); > + } > +#endif > + return; > +} > + > +static uint8_t > +ipmb_checksum(const unsigned char *data, int size, unsigned char > start) > +{ > + unsigned char csum = start; > + > + for (; size > 0; size--, data++) { > + csum += *data; > + } > + return csum; > +} > + > +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; > +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, > 0x00, 0x00, > + 0x02, 0x09, 0x00, 0x00, 0x00, > 0x00, 0x00 }; > + > +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; > +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; > +static uint8_t enable_irq_cmd[] = { 0x05, 0xa1 }; > + > +static void emu_msg_handler(void) > +{ > + uint8_t msg[100]; > + unsigned int msg_len = sizeof(msg); > + > + get_emu_msg(msg, &msg_len); > + g_assert(msg_len >= 5); > + g_assert(msg[msg_len - 1] == 0xa0); > + msg_len--; > + g_assert(ipmb_checksum(msg, msg_len, 0) == 0); > + msg_len--; > + if ((msg[1] == get_dev_id_cmd[0]) && (msg[2] == > get_dev_id_cmd[1])) { > + memcpy(msg + 1, get_dev_id_rsp, sizeof(get_dev_id_rsp)); > + msg_len = sizeof(get_dev_id_rsp) + 1; > + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); > + msg_len++; > + msg[msg_len++] = 0xa0; > + write_emu_msg(msg, msg_len); > + } else if ((msg[1] == set_bmc_globals_cmd[0]) && > + (msg[2] == set_bmc_globals_cmd[1])) { > + memcpy(msg + 1, set_bmc_globals_rsp, > sizeof(set_bmc_globals_rsp)); > + msg_len = sizeof(set_bmc_globals_rsp) + 1; > + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); > + msg_len++; > + msg[msg_len++] = 0xa0; > + write_emu_msg(msg, msg_len); > + write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd)); > + } else { > + g_assert(0); > + } > +} > + > +static void bt_cmd(uint8_t *cmd, unsigned int cmd_len, > + uint8_t *rsp, unsigned int *rsp_len) > +{ > + unsigned int i, len, j = 0; > + uint8_t seq = 5; > + > + /* Should be idle */ > + g_assert(bt_get_ctrlreg() == 0); > + > + bt_wait_b_busy(); > + IPMI_BT_CTLREG_SET_CLR_WR_PTR(); > + bt_write_buf(cmd_len + 1); > + bt_write_buf(cmd[0]); > + bt_write_buf(seq); > + for (i = 1; i < cmd_len; i++) { > + bt_write_buf(cmd[i]); > + } > + IPMI_BT_CTLREG_SET_H2B_ATN(); > + > + emu_msg_handler(); /* We should get a message on the socket > here. */ > + > + bt_wait_b2h_atn(); > + if (bt_ints_enabled) { > + g_assert((bt_get_irqreg() & 0x02) == 0x02); > + g_assert(get_irq(IPMI_IRQ)); > + bt_write_irqreg(0x03); > + } else { > + g_assert(!get_irq(IPMI_IRQ)); > + } > + IPMI_BT_CTLREG_SET_H_BUSY(); > + IPMI_BT_CTLREG_SET_B2H_ATN(); > + IPMI_BT_CTLREG_SET_CLR_RD_PTR(); > + len = bt_get_buf(); > + g_assert(len >= 4); > + rsp[0] = bt_get_buf(); > + assert(bt_get_buf() == seq); > + len--; > + for (j = 1; j < len; j++) { > + rsp[j] = bt_get_buf(); > + } > + IPMI_BT_CTLREG_SET_H_BUSY(); > + *rsp_len = j; > +} > + > + > +/* > + * We should get a connect request and a short message with > capabilities. > + */ > +static void test_connect(void) > +{ > + fd_set readfds; > + int rv; > + int val; > + struct timeval tv; > + uint8_t msg[100]; > + unsigned int msglen; > + static uint8_t exp1[] = { 0xff, 0x01, 0xa1 }; /* A protocol > version */ > + static uint8_t exp2[] = { 0x08, 0x1f, 0xa1 }; /* A > capabilities cmd */ > + static uint8_t exp3[] = { 0x04, 0xa1 }; /* A reset is reported */ > + > + FD_ZERO(&readfds); > + FD_SET(emu_lfd, &readfds); > + tv.tv_sec = 10; > + tv.tv_usec = 0; > + rv = select(emu_lfd + 1, &readfds, NULL, NULL, &tv); > + g_assert(rv == 1); > + emu_fd = accept(emu_lfd, NULL, 0); > + if (emu_fd < 0) { > + perror("accept"); > + } > + g_assert(emu_fd >= 0); > + > + val = 1; > + rv = setsockopt(emu_fd, IPPROTO_TCP, TCP_NODELAY, &val, > sizeof(val)); > + g_assert(rv != -1); > + > + /* Report our version */ > + write_emu_msg(exp1, sizeof(exp1)); > + > + /* Validate that we get the info we expect. */ > + msglen = sizeof(msg); > + get_emu_msg(msg, &msglen); > + g_assert(msglen == sizeof(exp1)); > + g_assert(memcmp(msg, exp1, msglen) == 0); > + msglen = sizeof(msg); > + get_emu_msg(msg, &msglen); > + g_assert(msglen == sizeof(exp2)); > + g_assert(memcmp(msg, exp2, msglen) == 0); > + msglen = sizeof(msg); > + get_emu_msg(msg, &msglen); > + g_assert(msglen == sizeof(exp3)); > + g_assert(memcmp(msg, exp3, msglen) == 0); > +} > + > +/* > + * Send a get_device_id to do a basic test. > + */ > +static void test_bt_base(void) > +{ > + uint8_t rsp[20]; > + unsigned int rsplen = sizeof(rsp); > + > + bt_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); > + g_assert(rsplen == sizeof(get_dev_id_rsp)); > + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); > +} > + > +/* > + * Enable IRQs for the interface. > + */ > +static void test_enable_irq(void) > +{ > + uint8_t rsp[20]; > + unsigned int rsplen = sizeof(rsp); > + > + bt_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, > &rsplen); > + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); > + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); > + bt_write_irqreg(0x01); > + bt_ints_enabled = 1; > +} > + > +/* > + * Create a local TCP socket with any port, then save off the > port we got. > + */ > +static void open_socket(void) > +{ > + struct sockaddr_in myaddr; > + socklen_t addrlen; > + > + myaddr.sin_family = AF_INET; > + myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); > + myaddr.sin_port = 0; > + emu_lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); > + if (emu_lfd == -1) { > + perror("socket"); > + exit(1); > + } > + if (bind(emu_lfd, (struct sockaddr *) &myaddr, > sizeof(myaddr)) == -1) { > + perror("bind"); > + exit(1); > + } > + addrlen = sizeof(myaddr); > + if (getsockname(emu_lfd, (struct sockaddr *) &myaddr , > &addrlen) == -1) { > + perror("getsockname"); > + exit(1); > + } > + emu_port = ntohs(myaddr.sin_port); > + assert(listen(emu_lfd, 1) != -1); > +} > + > +int main(int argc, char **argv) > +{ > + const char *arch = qtest_get_arch(); > + char *cmdline; > + int ret; > + > + /* Check architecture */ > + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { > + g_test_message("Skipping test for non-x86\n"); > + return 0; > + } > + > + open_socket(); > + > + /* Run the tests */ > + g_test_init(&argc, &argv, NULL); > + > + cmdline = g_strdup_printf("-vnc none" > + " -chardev > socket,id=ipmi0,host=localhost,port=%d,reconnect=10" > + " -device isa-ipmi,interface=bt,chardev=ipmi0", emu_port); > + qtest_start(cmdline); > + qtest_irq_intercept_in(global_qtest, "ioapic"); > + qtest_add_func("/ipmi/extern/connect", test_connect); > + qtest_add_func("/ipmi/extern/bt_base", test_bt_base); > + qtest_add_func("/ipmi/extern/bt_enable_irq", test_enable_irq); > + qtest_add_func("/ipmi/extern/bt_base_irq", test_bt_base); > + ret = g_test_run(); > + qtest_quit(global_qtest); > + > + return ret; > +} > diff --git a/tests/ipmi-kcs-test.c b/tests/ipmi-kcs-test.c > new file mode 100644 > index 0000000..e2e1bdb > --- /dev/null > +++ b/tests/ipmi-kcs-test.c > @@ -0,0 +1,294 @@ > +/* > + * IPMI KCS test cases, using the local interface. > + * > + * Copyright (c) 2012 Corey Minyard <cminy...@mvista.com > <mailto:cminy...@mvista.com>> > + * > + * Permission is hereby granted, free of charge, to any person > obtaining a copy > + * of this software and associated documentation files (the > "Software"), to deal > + * in the Software without restriction, including without > limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, > and/or sell > + * copies of the Software, and to permit persons to whom the > Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be > included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY > KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO > EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, > DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR > OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#include <stdint.h> > +#include <string.h> > +#include <stdio.h> > + > +#include <glib.h> > + > +#include "libqtest.h" > + > +#define IPMI_IRQ 5 > + > +#define IPMI_KCS_BASE 0xca2 > + > +#define IPMI_KCS_STATUS_ABORT 0x60 > +#define IPMI_KCS_CMD_WRITE_START 0x61 > +#define IPMI_KCS_CMD_WRITE_END 0x62 > +#define IPMI_KCS_CMD_READ 0x68 > + > +#define IPMI_KCS_ABORTED_BY_CMD 0x01 > + > +#define IPMI_KCS_CMDREG_GET_STATE() ((kcs_get_cmdreg() >> 6) & 3) > +#define IPMI_KCS_STATE_IDLE 0 > +#define IPMI_KCS_STATE_READ 1 > +#define IPMI_KCS_STATE_WRITE 2 > +#define IPMI_KCS_STATE_ERROR 3 > +#define IPMI_KCS_CMDREG_GET_CD() ((kcs_get_cmdreg() >> 3) & 1) > +#define IPMI_KCS_CMDREG_GET_ATN() ((kcs_get_cmdreg() >> 2) & 1) > +#define IPMI_KCS_CMDREG_GET_IBF() ((kcs_get_cmdreg() >> 1) & 1) > +#define IPMI_KCS_CMDREG_GET_OBF() ((kcs_get_cmdreg() >> 0) & 1) > + > +static int kcs_ints_enabled; > + > +static uint8_t kcs_get_cmdreg(void) > +{ > + return inb(IPMI_KCS_BASE + 1); > +} > + > +static void kcs_write_cmdreg(uint8_t val) > +{ > + outb(IPMI_KCS_BASE + 1, val); > +} > + > +static uint8_t kcs_get_datareg(void) > +{ > + return inb(IPMI_KCS_BASE); > +} > + > +static void kcs_write_datareg(uint8_t val) > +{ > + outb(IPMI_KCS_BASE, val); > +} > + > +static void kcs_wait_ibf(void) > +{ > + unsigned int count = 1000; > + while (IPMI_KCS_CMDREG_GET_IBF() != 0) { > + g_assert(--count != 0); > + } > +} > + > +static void kcs_wait_obf(void) > +{ > + unsigned int count = 1000; > + while (IPMI_KCS_CMDREG_GET_OBF() == 0) { > + g_assert(--count != 0); > + } > +} > + > +static void kcs_clear_obf(void) > +{ > + if (kcs_ints_enabled) { > + g_assert(get_irq(IPMI_IRQ)); > + } else { > + g_assert(!get_irq(IPMI_IRQ)); > + } > + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 1); > + kcs_get_datareg(); > + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 0); > + g_assert(!get_irq(IPMI_IRQ)); > +} > + > +static void kcs_check_state(uint8_t state) > +{ > + g_assert(IPMI_KCS_CMDREG_GET_STATE() == state); > +} > + > +static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len, > + uint8_t *rsp, unsigned int *rsp_len) > +{ > + unsigned int i, j = 0; > + > + /* Should be idle */ > + g_assert(kcs_get_cmdreg() == 0); > + > + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); > + kcs_wait_ibf(); > + kcs_check_state(IPMI_KCS_STATE_WRITE); > + kcs_clear_obf(); > + for (i = 0; i < cmd_len; i++) { > + kcs_write_datareg(cmd[i]); > + kcs_wait_ibf(); > + kcs_check_state(IPMI_KCS_STATE_WRITE); > + kcs_clear_obf(); > + } > + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); > + kcs_wait_ibf(); > + kcs_check_state(IPMI_KCS_STATE_WRITE); > + kcs_clear_obf(); > + kcs_write_datareg(0); > + next_read_byte: > + kcs_wait_ibf(); > + switch (IPMI_KCS_CMDREG_GET_STATE()) { > + case IPMI_KCS_STATE_READ: > + kcs_wait_obf(); > + g_assert(j < *rsp_len); > + rsp[j++] = kcs_get_datareg(); > + kcs_write_datareg(IPMI_KCS_CMD_READ); > + goto next_read_byte; > + break; > + > + case IPMI_KCS_STATE_IDLE: > + kcs_wait_obf(); > + kcs_get_datareg(); > + break; > + > + default: > + g_assert(0); > + } > + *rsp_len = j; > +} > + > +static void kcs_abort(uint8_t *cmd, unsigned int cmd_len, > + uint8_t *rsp, unsigned int *rsp_len) > +{ > + unsigned int i, j = 0; > + unsigned int retries = 4; > + > + /* Should be idle */ > + g_assert(kcs_get_cmdreg() == 0); > + > + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); > + kcs_wait_ibf(); > + kcs_check_state(IPMI_KCS_STATE_WRITE); > + kcs_clear_obf(); > + for (i = 0; i < cmd_len; i++) { > + kcs_write_datareg(cmd[i]); > + kcs_wait_ibf(); > + kcs_check_state(IPMI_KCS_STATE_WRITE); > + kcs_clear_obf(); > + } > + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); > + kcs_wait_ibf(); > + kcs_check_state(IPMI_KCS_STATE_WRITE); > + kcs_clear_obf(); > + kcs_write_datareg(0); > + kcs_wait_ibf(); > + switch (IPMI_KCS_CMDREG_GET_STATE()) { > + case IPMI_KCS_STATE_READ: > + kcs_wait_obf(); > + g_assert(j < *rsp_len); > + rsp[j++] = kcs_get_datareg(); > + kcs_write_datareg(IPMI_KCS_CMD_READ); > + break; > + > + default: > + g_assert(0); > + } > + > + /* Start the abort here */ > + retry_abort: > + g_assert(retries > 0); > + > + kcs_wait_ibf(); > + kcs_write_cmdreg(IPMI_KCS_STATUS_ABORT); > + kcs_wait_ibf(); > + kcs_clear_obf(); > + kcs_write_datareg(0); > + kcs_wait_ibf(); > + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_READ) { > + retries--; > + goto retry_abort; > + } > + kcs_wait_obf(); > + rsp[0] = kcs_get_datareg(); > + kcs_write_datareg(IPMI_KCS_CMD_READ); > + kcs_wait_ibf(); > + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_IDLE) { > + retries--; > + goto retry_abort; > + } > + kcs_wait_obf(); > + kcs_clear_obf(); > + > + *rsp_len = j; > +} > + > + > +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; > +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, > 0x00, 0x00, > + 0x02, 0x07, 0x00, 0x00, 0x00, > 0x00, 0x00 }; > + > +/* > + * Send a get_device_id to do a basic test. > + */ > +static void test_kcs_base(void) > +{ > + uint8_t rsp[20]; > + unsigned int rsplen = sizeof(rsp); > + > + kcs_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); > + g_assert(rsplen == sizeof(get_dev_id_rsp)); > + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); > +} > + > +/* > + * Abort a kcs operation while reading > + */ > +static void test_kcs_abort(void) > +{ > + uint8_t rsp[20]; > + unsigned int rsplen = sizeof(rsp); > + > + kcs_abort(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); > + g_assert(rsp[0] == IPMI_KCS_ABORTED_BY_CMD); > +} > + > +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; > +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; > + > +/* > + * Enable interrupts > + */ > +static void test_enable_irq(void) > +{ > + uint8_t rsp[20]; > + unsigned int rsplen = sizeof(rsp); > + > + kcs_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), > rsp, &rsplen); > + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); > + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); > + kcs_ints_enabled = 1; > +} > + > +int main(int argc, char **argv) > +{ > + const char *arch = qtest_get_arch(); > + char *cmdline; > + int ret; > + > + /* Check architecture */ > + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { > + g_test_message("Skipping test for non-x86\n"); > + return 0; > + } > + > + /* Run the tests */ > + g_test_init(&argc, &argv, NULL); > + > + cmdline = g_strdup_printf("-vnc none -device isa-ipmi"); > + qtest_start(cmdline); > + qtest_irq_intercept_in(global_qtest, "ioapic"); > + qtest_add_func("/ipmi/local/kcs_base", test_kcs_base); > + qtest_add_func("/ipmi/local/kcs_abort", test_kcs_abort); > + qtest_add_func("/ipmi/local/kcs_enable_irq", test_enable_irq); > + qtest_add_func("/ipmi/local/kcs_base_irq", test_kcs_base); > + qtest_add_func("/ipmi/local/kcs_abort_irq", test_kcs_abort); > + ret = g_test_run(); > + qtest_quit(global_qtest); > + > + return ret; > +} > -- > 1.8.3.1 > >