Rudolf Marek (r.marek@assembler.cz) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1094
-gerrit
commit d602fc79e963057e4db00a4e2dca38d48ad6b484 Author: Rudolf Marek r.marek@assembler.cz Date: Tue Jun 5 00:26:06 2012 +0200
Add host side of serialICE which uses ulinux.
This is copied from serialICE qemu and strtoul is from libpayload. this must be fixed maybe into string.h?
Change-Id: If71737e6866c5c4dd62b9c26c676686ebfd20bcc Signed-off-by: Rudolf Marek r.marek@assembler.cz --- src/include/serialice_host.h | 66 ++++ src/lib/serialice_host.c | 676 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 742 insertions(+), 0 deletions(-)
diff --git a/src/include/serialice_host.h b/src/include/serialice_host.h new file mode 100644 index 0000000..f099325 --- /dev/null +++ b/src/include/serialice_host.h @@ -0,0 +1,66 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2009 coresystems GmbH + * + * 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. + */ + +#ifndef SERIALICE_HH +#define SERIALICE_HH +#include <stdint.h> +extern const char *serialice_device; +extern int serialice_active; + +void serialice_init(void); +void serialice_exit(void); +const char *serialice_lua_execute(const char *cmd); + +uint8_t serialice_inb(uint16_t port); +uint16_t serialice_inw(uint16_t port); +uint32_t serialice_inl(uint16_t port); + +void serialice_outb(uint8_t data, uint16_t port); +void serialice_outw(uint16_t data, uint16_t port); +void serialice_outl(uint32_t data, uint16_t port); + +uint8_t serialice_readb(uint32_t addr); +uint16_t serialice_readw(uint32_t addr); +uint32_t serialice_readl(uint32_t addr); + +void serialice_writeb(uint8_t data, uint32_t addr); +void serialice_writew(uint16_t data, uint32_t addr); +void serialice_writel(uint32_t data, uint32_t addr); + +uint64_t serialice_rdmsr(uint32_t addr, uint32_t key); +void serialice_wrmsr(uint64_t data, uint32_t addr, uint32_t key); + +typedef struct { + uint32_t eax, ebx, ecx, edx; +} cpuid_regs_t; + +cpuid_regs_t serialice_cpuid(uint32_t eax, uint32_t ecx); + +int serialice_handle_load(uint32_t addr, uint32_t * result, + unsigned int data_size); +void serialice_log_load(int caught, uint32_t addr, uint32_t result, + unsigned int data_size); +int serialice_handle_store(uint32_t addr, uint32_t val, unsigned int data_size); + +#endif diff --git a/src/lib/serialice_host.c b/src/lib/serialice_host.c new file mode 100644 index 0000000..4d318b2 --- /dev/null +++ b/src/lib/serialice_host.c @@ -0,0 +1,676 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2009 coresystems GmbH + * + * 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. + */ + +/* Indented with: + * gnuindent -npro -kr -i4 -nut -bap -sob -l80 -ss -ncs serialice.* + */ + +/* System includes */ +#include <stdlib.h> +#include <stdint.h> + +#include <string.h> +#ifdef WIN32 +#include <windows.h> +#include <conio.h> +#else +#include <termios.h> +#endif + +#include <console/console.h> +#include <serialice_host.h> +#include <ulinux.h> + +const char *serialice_device = CONFIG_SERIALICE_HOST_DEV; + +/* Check that a character is in the valid range for the + given base +*/ + +//#warning "move this to string.h? got it from libpayload" + +#define UINT32_MAX 0xffffffffU +static int _valid(char ch, int base) +{ + char end = (base > 9) ? '9' : '0' + (base - 1); + + /* all bases will be some subset of the 0-9 range */ + + if (ch >= '0' && ch <= end) + return 1; + + /* Bases > 11 will also have to match in the a-z range */ + + if (base > 11) { + if (tolower(ch) >= 'a' && + tolower(ch) <= 'a' + (base - 11)) + return 1; + } + + return 0; +} + +/* Return the "value" of the character in the given base */ + +static int _offset(char ch, int base) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else + return 10 + tolower(ch) - 'a'; +} + +/** + * Convert the initial portion of a string into an unsigned int + * @param ptr A pointer to the string to convert + * @param endptr A pointer to the unconverted part of the string + * @param base The base of the number to convert, or 0 for auto + * @return An unsigned integer representation of the string + */ + +static unsigned long long strtoull(const char *ptr, char **endptr, unsigned int base) +{ + unsigned long long ret = 0; + + if (endptr != NULL) + *endptr = (char *) ptr; + + /* Purge whitespace */ + + for( ; *ptr && isspace(*ptr); ptr++); + + if (!*ptr) + return 0; + + /* Determine the base */ + + if (base == 0) { + if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) + base = 16; + else if (ptr[0] == '0') { + base = 8; + ptr++; + } + else + base = 10; + } + + /* Base 16 allows the 0x on front - so skip over it */ + + if (base == 16) { + if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) + ptr += 2; + } + + /* If the first character isn't valid, then don't + * bother */ + + if (!*ptr || !_valid(*ptr, base)) + return 0; + + for( ; *ptr && _valid(*ptr, base); ptr++) + ret = (ret * base) + _offset(*ptr, base); + + if (endptr != NULL) + *endptr = (char *) ptr; + + return ret; +} + +static unsigned long strtoul(const char *ptr, char **endptr, unsigned int base) +{ + unsigned long long val = strtoull(ptr, endptr, base); + if (val > UINT32_MAX) return UINT32_MAX; + return val; +} + +#define SERIALICE_DEBUG 3 +#define BUFFER_SIZE 1024 +typedef struct { +#ifdef WIN32 + HANDLE fd; +#else + int fd; +#endif + char *buffer; + char *command; +} SerialICEState; + +/* Make it static, so it works from start */ +static char bb[BUFFER_SIZE]; +static char cc[BUFFER_SIZE]; + +static SerialICEState ss = { + .buffer = bb, + .command = cc, + }; + +static SerialICEState *s = &ss; +int serialice_active = 0; + +#ifndef WIN32 +static struct termios options; +#endif + +/* SerialICE output loggers */ + +#define LOG_IO 0 +#define LOG_MEMORY 1 +#define LOG_READ 0 +#define LOG_WRITE 2 +// these two are separate +#define LOG_QEMU 4 +#define LOG_TARGET 8 + +static void serialice_log(int flags, uint32_t data, uint32_t addr, int size) +{ + if ((flags & LOG_WRITE) && (flags & LOG_MEMORY)) { + printk(BIOS_INFO, "MEM WRITE: [%08x] %08x size %d\n", addr, data, size); + } else if (!(flags & LOG_WRITE) && (flags & LOG_MEMORY)) { + printk(BIOS_INFO, "MEM READ: [%08x] %08x size %d\n", addr, data, size); + } else if ((flags & LOG_WRITE) && !(flags & LOG_MEMORY)) { + printk(BIOS_INFO, "IO WRITE: [%08x] %08x size %d\n", addr, data, size); + } else { // if (!(flags & LOG_WRITE) && !(flags & LOG_MEMORY)) + printk(BIOS_INFO, "IO READ: [%08x] %08x size %d\n", addr, data, size); + } + +} + +static void serialice_msr_log(int flags, uint32_t addr, uint32_t hi, + uint32_t lo, int filtered) +{ + + if (flags & LOG_WRITE) { + printk(BIOS_INFO, "MSR READ: [%08x] %08x%08x\n", addr,hi,lo); + } else { // if (!(flags & LOG_WRITE)) + printk(BIOS_INFO, "MSR WRITE: [%08x] %08x%08x\n", addr,hi,lo); + } + +} + +static void serialice_cpuid_log(uint32_t eax, uint32_t ecx, cpuid_regs_t res, + int filtered) +{ + + printk(BIOS_INFO, "CPUID todo\n"); +} + +// ************************************************************************** +// low level communication with the SerialICE shell (serial communication) + +static int serialice_read(SerialICEState * state, void *buf, size_t nbyte) +{ + int bytes_read = 0; + + while (1) { +#ifdef WIN32 + int ret = 0; + ReadFile(state->fd, buf, nbyte - bytes_read, &ret, NULL); + if (!ret) { + break; + } +#else + int ret = ulinux_read(state->fd, buf, nbyte - bytes_read); + + if (ret == -1 && ulinux_errno == EINTR) { + continue; + } + + if (ret == -1) { + break; + } +#endif + + bytes_read += ret; + buf += ret; + + if (bytes_read >= (int)nbyte) { + break; + } + } + + return bytes_read; +} + +static int handshake_mode = 0; + +static int serialice_write(SerialICEState * state, const void *buf, + size_t nbyte) +{ + char *buffer = (char *)buf; + char c; + int i; + for (i = 0; i < (int)nbyte; i++) { +#ifdef WIN32 + int ret = 0; + while (ret == 0) { + WriteFile(state->fd, buffer + i, 1, &ret, NULL); + } + ret = 0; + while (ret == 0) { + ReadFile(state->fd, &c, 1, &ret, NULL); + } +#else + while (ulinux_write(state->fd, buffer + i, 1) != 1) ; + while (ulinux_read(state->fd, &c, 1) != 1) ; +#endif + if (c != buffer[i] && !handshake_mode) { + printk(BIOS_ERR, "Readback error! %x/%x\n", c, buffer[i]); + } + } + + return nbyte; +} + +static int serialice_wait_prompt(void) +{ + char buf[3]; + int l; + + l = serialice_read(s, buf, 3); + + if (l == -1) { + perror("SerialICE: Could not read from target"); + ulinux_exit(1); + } + + while (buf[0] != '\n' || buf[1] != '>' || buf[2] != ' ') { + buf[0] = buf[1]; + buf[1] = buf[2]; + l = serialice_read(s, buf + 2, 1); + if (l == -1) { + perror("SerialICE: Could not read from target"); + ulinux_exit(1); + } + } + + return 0; +} + +static void serialice_command(const char *command, int reply_len) +{ +#if SERIALICE_DEBUG > 5 + int i; +#endif + int l; + + if (!serialice_active) { + printk(BIOS_ERR, "SerialICE not ready yet (ignoring)\n"); + return; + } + + serialice_wait_prompt(); + + serialice_write(s, command, strlen(command)); + + memset(s->buffer, 0, reply_len + 1); // clear enough of the buffer + + l = serialice_read(s, s->buffer, reply_len); + + if (l == -1) { + perror("SerialICE: Could not read from target"); + ulinux_exit(1); + } + // compensate for CR on the wire. Needed on Win32 + if (s->buffer[0] == '\r') { + memmove(s->buffer, s->buffer + 1, reply_len); + serialice_read(s, s->buffer + reply_len - 1, 1); + } + + if (l != reply_len) { + printk(BIOS_ERR, "SerialICE: command was not answered sufficiently: " + "(%d/%d bytes)\n'%s'\n", l, reply_len, s->buffer); + ulinux_exit(1); + } +#if SERIALICE_DEBUG > 5 + for (i = 0; i < reply_len; i++) { + printk(BIOS_ERR, "%02x ", s->buffer[i]); + } + printk(BIOS_ERR, "\n"); +#endif +} + +// ************************************************************************** +// high level communication with the SerialICE shell + +static void serialice_get_version(void) +{ + int len = 0; + printk(BIOS_INFO, "SerialICE: Version.....: "); + serialice_command("*vi", 0); + + memset(s->buffer, 0, BUFFER_SIZE); + serialice_read(s, s->buffer, 1); + serialice_read(s, s->buffer, 1); + while (s->buffer[len++] != '\n') { + serialice_read(s, s->buffer + len, 1); + } + s->buffer[len - 1] = '\0'; + + printk(BIOS_INFO, "%s\n", s->buffer); +} + +static void serialice_get_mainboard(void) +{ +} + +uint8_t serialice_inb(uint16_t port) +{ + uint8_t ret; + + sprintf(s->command, "*ri%04x.b", port); + // command read back: "\n00" (3 characters) + serialice_command(s->command, 3); + ret = (uint8_t) strtoul(s->buffer + 1, (char **)NULL, 16); + + serialice_log(LOG_READ | LOG_IO, ret, port, 1); + + return ret; +} + +uint16_t serialice_inw(uint16_t port) +{ + uint16_t ret; + + sprintf(s->command, "*ri%04x.w", port); + // command read back: "\n0000" (5 characters) + serialice_command(s->command, 5); + ret = (uint16_t) strtoul(s->buffer + 1, (char **)NULL, 16); + + serialice_log(LOG_READ | LOG_IO, ret, port, 2); + + return ret; +} + +uint32_t serialice_inl(uint16_t port) +{ + uint32_t ret; + + sprintf(s->command, "*ri%04x.l", port); + // command read back: "\n00000000" (9 characters) + serialice_command(s->command, 9); + ret = (uint32_t) strtoul(s->buffer + 1, (char **)NULL, 16); + + serialice_log(LOG_READ | LOG_IO, ret, port, 4); + + return ret; +} + +void serialice_outb(uint8_t data, uint16_t port) +{ + serialice_log(LOG_WRITE | LOG_IO, data, port, 1); + + sprintf(s->command, "*wi%04x.b=%02x", port, data); + serialice_command(s->command, 0); +} + +void serialice_outw(uint16_t data, uint16_t port) +{ + serialice_log(LOG_WRITE | LOG_IO, data, port, 2); + + sprintf(s->command, "*wi%04x.w=%04x", port, data); + serialice_command(s->command, 0); +} + +void serialice_outl(uint32_t data, uint16_t port) +{ + serialice_log(LOG_WRITE | LOG_IO, data, port, 4); + + sprintf(s->command, "*wi%04x.l=%08x", port, data); + serialice_command(s->command, 0); +} + +uint8_t serialice_readb(uint32_t addr) +{ + uint8_t ret; + sprintf(s->command, "*rm%08x.b", addr); + // command read back: "\n00" (3 characters) + serialice_command(s->command, 3); + ret = (uint8_t) strtoul(s->buffer + 1, (char **)NULL, 16); + serialice_log(LOG_READ | LOG_MEMORY, ret, addr, 1); + + return ret; +} + +uint16_t serialice_readw(uint32_t addr) +{ + uint16_t ret; + sprintf(s->command, "*rm%08x.w", addr); + // command read back: "\n0000" (5 characters) + serialice_command(s->command, 5); + ret = (uint16_t) strtoul(s->buffer + 1, (char **)NULL, 16); + serialice_log(LOG_READ | LOG_MEMORY, ret, addr, 2); + + return ret; +} + +uint32_t serialice_readl(uint32_t addr) +{ + uint32_t ret; + sprintf(s->command, "*rm%08x.l", addr); + // command read back: "\n00000000" (9 characters) + serialice_command(s->command, 9); + ret = (uint32_t) strtoul(s->buffer + 1, (char **)NULL, 16); + serialice_log(LOG_READ | LOG_MEMORY, ret, addr, 4); + + return ret; +} + +void serialice_writeb(uint8_t data, uint32_t addr) +{ + serialice_log(LOG_WRITE | LOG_MEMORY, data, addr, 1); + sprintf(s->command, "*wm%08x.b=%02x", addr, data); + serialice_command(s->command, 0); +} + +void serialice_writew(uint16_t data, uint32_t addr) +{ + serialice_log(LOG_WRITE | LOG_MEMORY, data, addr, 2); + sprintf(s->command, "*wm%08x.w=%04x", addr, data); + serialice_command(s->command, 0); +} + +void serialice_writel(uint32_t data, uint32_t addr) +{ + serialice_log(LOG_WRITE | LOG_MEMORY, data, addr, 4); + sprintf(s->command, "*wm%08x.l=%08x", addr, data); + serialice_command(s->command, 0); +} + +uint64_t serialice_rdmsr(uint32_t addr, uint32_t key) +{ + uint32_t hi, lo; + uint64_t ret; + int filtered = 0; + + sprintf(s->command, "*rc%08x.%08x", addr, key); + + // command read back: "\n00000000.00000000" (18 characters) + serialice_command(s->command, 18); + + s->buffer[9] = 0; // . -> \0 + hi = (uint32_t) strtoul(s->buffer + 1, (char **)NULL, 16); + lo = (uint32_t) strtoul(s->buffer + 10, (char **)NULL, 16); + + ret = hi; + ret <<= 32; + ret |= lo; + + serialice_msr_log(LOG_READ, addr, hi, lo, filtered); + + return ret; +} + +void serialice_wrmsr(uint64_t data, uint32_t addr, uint32_t key) +{ + uint32_t hi, lo; + int filtered = 0; + + hi = (data >> 32); + lo = (data & 0xffffffff); + + if (!filtered) { + sprintf(s->command, "*wc%08x.%08x=%08x.%08x", addr, key, hi, lo); + serialice_command(s->command, 0); + } + + serialice_msr_log(LOG_WRITE, addr, hi, lo, filtered); +} + +cpuid_regs_t serialice_cpuid(uint32_t eax, uint32_t ecx) +{ + cpuid_regs_t ret; + + ret.eax = eax; + ret.ebx = 0; // either set by filter or by target + ret.ecx = ecx; + ret.edx = 0; // either set by filter or by target + + sprintf(s->command, "*ci%08x.%08x", eax, ecx); + + // command read back: "\n000006f2.00000000.00001234.12340324" + // (36 characters) + serialice_command(s->command, 36); + + s->buffer[9] = 0; // . -> \0 + s->buffer[18] = 0; // . -> \0 + s->buffer[27] = 0; // . -> \0 + ret.eax = (uint32_t) strtoul(s->buffer + 1, (char **)NULL, 16); + ret.ebx = (uint32_t) strtoul(s->buffer + 10, (char **)NULL, 16); + ret.ecx = (uint32_t) strtoul(s->buffer + 19, (char **)NULL, 16); + ret.edx = (uint32_t) strtoul(s->buffer + 28, (char **)NULL, 16); + + serialice_cpuid_log(eax, ecx, ret, 0); + + return ret; +} + +// ************************************************************************** +// initialization and exit + +void serialice_init(void) +{ + printk(BIOS_INFO, "SerialICE: Open connection to target hardware...\n"); + + if (serialice_device == NULL) { + printk(BIOS_INFO, "You need to specify a serial device to use SerialICE.\n"); + ulinux_exit(1); + } +#ifdef WIN32 + s->fd = CreateFile(serialice_device, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + + if (s->fd == INVALID_HANDLE_VALUE) { + perror("SerialICE: Could not connect to target TTY"); + ulinux_exit(1); + } + + DCB dcb; + if (!GetCommState(s->fd, &dcb)) { + perror("SerialICE: Could not load config for target TTY"); + ulinux_exit(1); + } + + dcb.BaudRate = CBR_115200; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + + if (!SetCommState(s->fd, &dcb)) { + perror("SerialICE: Could not store config for target TTY"); + ulinux_exit(1); + } +#else + s->fd = ulinux_open(serialice_device, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (s->fd == -1) { + perror("SerialICE: Could not connect to target TTY"); + ulinux_exit(1); + } + + if (ulinux_ioctl(s->fd, TIOCEXCL) == -1) { + perror("SerialICE: TTY not exclusively available"); + ulinux_exit(1); + } + + if (ulinux_fcntl(s->fd, F_SETFL, 0) == -1) { + perror("SerialICE: Could not switch to blocking I/O"); + ulinux_exit(1); + } + + if (ulinux_tcgetattr(s->fd, &options) == -1) { + perror("SerialICE: Could not get TTY attributes"); + ulinux_exit(1); + } + + ulinux_cfsetispeed(&options, B115200); + ulinux_cfsetospeed(&options, B115200); + + /* set raw input, 1 second timeout */ + options.c_cflag |= (CLOCAL | CREAD); + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + options.c_oflag &= ~OPOST; + options.c_iflag |= IGNCR; + options.c_cc[VMIN] = 0; + options.c_cc[VTIME] = 100; + + ulinux_tcsetattr(s->fd, TCSANOW, &options); + + ulinux_tcflush(s->fd, TCIOFLUSH); +#endif + + s->buffer = bb; + s->command = cc; + + printk(BIOS_INFO, "SerialICE: Waiting for handshake with target... "); + + handshake_mode = 1; // Readback errors are to be expected in this phase. + + /* Trigger a prompt */ + serialice_write(s, "\n\n\n\n", 4); + serialice_write(s, "\n\n\n\n", 4); + + /* ... and wait for it to appear */ + if (serialice_wait_prompt() == 0) { + printk(BIOS_INFO, "target alive!\n"); + serialice_active = 1; + } else { + printk(BIOS_INFO, "target not ok!\n"); + ulinux_exit(1); + } + + /* Each serialice_command() waits for a prompt, so trigger one for the + * first command, as we consumed the last one for the handshake + */ + serialice_write(s, "@", 1); + + handshake_mode = 0; // from now on, warn about readback errors. + + serialice_get_version(); + + serialice_get_mainboard(); + + /* Let the rest of Qemu know we're alive */ +} +