QEMU 0.11.0 SerialICE patch Adds SerialICE firmware debugger support to Qemu. Signed-off-by: Stefan Reinauer Makefile | 4 Makefile.target | 7 build.sh | 5 configure | 45 ++ exec-all.h | 4 hw/pc.c | 13 qemu-char.c | 4 qemu-options.hx | 11 serialice.c | 927 ++++++++++++++++++++++++++++++++++++++++++++++++ serialice.h | 66 +++ softmmu_template.h | 28 + target-i386/op_helper.c | 62 +++ vl.c | 8 13 files changed, 1182 insertions(+), 2 deletions(-) --- qemu-0.11.0/serialice.c +++ qemu-0.11.0/serialice.c @@ -0,0 +1,927 @@ +/* + * 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. + */ + +/* System includes */ +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#endif + +/* LUA includes */ +#include +#include +#include + +/* Local includes */ +#include "hw/hw.h" +#include "hw/pc.h" +#include "serialice.h" +#include "sysemu.h" + +#define SERIALICE_DEBUG 3 +#define BUFFER_SIZE 1024 +typedef struct { +#ifdef WIN32 + HANDLE fd; +#else + int fd; +#endif + char *buffer; + char *command; +} SerialICEState; + +static SerialICEState *s; + +int serialice_active = 0; +const char *serialice_lua_script="serialice.lua"; + +#ifndef WIN32 +static struct termios options; +#endif + +static lua_State *L; + +// ************************************************************************** +// LUA scripting interface and callbacks + +static int serialice_register_physical(lua_State *luastate) +{ + int n = lua_gettop(luastate); + uint32_t addr, size; + ram_addr_t phys; + + if (n != 2) { + fprintf(stderr, "ERROR: Not called as SerialICE_register_physical( )\n"); + return 0; + } + + addr = lua_tointeger(luastate, 1); + size = lua_tointeger(luastate, 2); + + printf("Registering physical memory at 0x%08x (0x%08x bytes)\n", addr, size); + phys = qemu_ram_alloc(size); + cpu_register_physical_memory(addr, size, phys); + + return 0; +} + +static int serialice_lua_init(void) +{ + int status; + + /* Create a LUA context and load LUA libraries */ + L = luaL_newstate(); + luaL_openlibs(L); + + /* Register C function callbacks */ + lua_register(L, "SerialICE_register_physical", serialice_register_physical); + + /* Load the script file */ + status = luaL_loadfile(L, serialice_lua_script); + if (status) { + fprintf(stderr, "Couldn't load SerialICE script: %s\n", lua_tostring(L, -1)); + exit(1); + } + + /* Ask Lua to run our little script */ + status = lua_pcall(L, 0, 1, 0); + if (status) { + fprintf(stderr, "Failed to run script: %s\n", lua_tostring(L, -1)); + exit(1); + } + lua_pop(L, 1); + + return 0; +} + +static int serialice_lua_exit(void) +{ + lua_close(L); + return 0; +} + +static int serialice_io_read_filter(uint32_t *data, uint16_t port, int size) +{ + int ret, result; + + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_io_read_filter"); + lua_pushinteger(L, port); // port + lua_pushinteger(L, size); // datasize + result = lua_pcall(L, 2, 2, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_io_read_filter: %s\n", lua_tostring(L, -1)); + exit(1); + } + *data = lua_tointeger(L, -1); + ret = lua_toboolean(L, -2); + lua_pop(L, 2); + + return ret; +} + +static int serialice_io_write_filter(uint32_t *data, uint16_t port, int size) +{ + int ret, result; + + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_io_write_filter"); + lua_pushinteger(L, port); // port + lua_pushinteger(L, size); // datasize + lua_pushinteger(L, *data); // data + + result = lua_pcall(L, 3, 2, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_io_write_filter: %s\n", lua_tostring(L, -1)); + exit(1); + } + *data = lua_tointeger(L, -1); + ret = lua_toboolean(L, -2); + lua_pop(L, 2); + + return ret; +} + + +#define READ_FROM_QEMU (1 << 0) +#define READ_FROM_SERIALICE (1 << 1) +static int serialice_memory_read_filter(uint32_t addr, uint32_t *data, int size) +{ + int ret = 0, result; + + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_memory_read_filter"); + lua_pushinteger(L, addr); // addr + lua_pushinteger(L, size); // datasize + result = lua_pcall(L, 2, 3, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_memory_read_filter: %s\n", lua_tostring(L, -1)); + exit(1); + } + + *data = lua_tointeger(L, -1); // result + + ret |= lua_toboolean(L, -2) ? READ_FROM_QEMU : 0; // to_qemu + ret |= lua_toboolean(L, -3) ? READ_FROM_SERIALICE : 0; // to_hw + + lua_pop(L, 3); + + return ret; +} + +#define WRITE_TO_QEMU (1 << 0) +#define WRITE_TO_SERIALICE (1 << 1) + +static int serialice_memory_write_filter(uint32_t addr, int size, uint32_t *data) +{ + int ret = 0, result; + int write_to_qemu, write_to_serialice; + + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_memory_write_filter"); + lua_pushinteger(L, addr); // address + lua_pushinteger(L, size); // datasize + lua_pushinteger(L, *data); // data + result = lua_pcall(L, 3, 3, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_memory_write_filter: %s\n", lua_tostring(L, -1)); + exit(1); + } + *data = lua_tointeger(L, -1); + write_to_qemu = lua_toboolean(L, -2); + write_to_serialice = lua_toboolean(L, -3); + lua_pop(L, 3); + + ret |= write_to_qemu ? WRITE_TO_QEMU : 0; + ret |= write_to_serialice ? WRITE_TO_SERIALICE : 0; + + return ret; +} + +#define FILTER_READ 0 +#define FILTER_WRITE 1 + +static int serialice_msr_filter(int flags, uint32_t addr, uint32_t *hi, uint32_t *lo) +{ + int ret, result; + + if (flags & FILTER_WRITE) + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_msr_write_filter"); + else + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_msr_read_filter"); + + lua_pushinteger(L, addr); // port + lua_pushinteger(L, *hi); // high + lua_pushinteger(L, *lo); // low + result = lua_pcall(L, 3, 3, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_msr_read_filter: %s\n", lua_tostring(L, -1)); + exit(1); + } + ret = lua_toboolean(L, -3); + if (ret) { + *hi = lua_tointeger(L, -1); + *lo = lua_tointeger(L, -2); + } + lua_pop(L, 3); + + return ret; +} + +static int serialice_cpuid_filter(cpuid_regs_t *regs) +{ + int ret, result; + + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_cpuid_filter"); + + lua_pushinteger(L, regs->eax); // eax + lua_pushinteger(L, regs->ecx); // ecx + result = lua_pcall(L, 2, 5, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_msr_read_filter: %s\n", lua_tostring(L, -1)); + exit(1); + } + + ret = lua_toboolean(L, -5); + if (ret) { + regs->eax = lua_tointeger(L, -1); + regs->ebx = lua_tointeger(L, -2); + regs->ecx = lua_tointeger(L, -3); + regs->edx = lua_tointeger(L, -4); + } + lua_pop(L, 5); + + return ret; +} + + +/* 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) +{ + int result; + + if ((flags & LOG_WRITE) && (flags & LOG_MEMORY)) + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_memory_write_log"); + else if (!(flags & LOG_WRITE) && (flags & LOG_MEMORY)) + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_memory_read_log"); + else if ((flags & LOG_WRITE) && !(flags & LOG_MEMORY)) + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_io_write_log"); + else // if (!(flags & LOG_WRITE) && !(flags & LOG_MEMORY)) + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_io_read_log"); + + lua_pushinteger(L, addr); // addr/port + lua_pushinteger(L, size); // datasize + lua_pushinteger(L, data); // data + lua_pushboolean(L, ((flags & LOG_TARGET) != 0)); + + result = lua_pcall(L, 4, 0, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_%s_%s_log: %s\n", + (flags & LOG_MEMORY)?"memory":"io", + (flags & LOG_WRITE)?"write":"read", + lua_tostring(L, -1)); + exit(1); + } +} + +static void serialice_msr_log(int flags, uint32_t addr, uint32_t hi, uint32_t lo, int filtered) +{ + int result; + + if (flags & LOG_WRITE) + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_msr_write_log"); + else // if (!(flags & LOG_WRITE)) + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_msr_read_log"); + + lua_pushinteger(L, addr); // addr/port + lua_pushinteger(L, hi); // datasize + lua_pushinteger(L, lo); // data + lua_pushboolean(L, filtered); // data + result = lua_pcall(L, 4, 0, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_msr_%s_log: %s\n", + (flags & LOG_WRITE)?"write":"read", + lua_tostring(L, -1)); + exit(1); + } +} + +static void serialice_cpuid_log(uint32_t eax, uint32_t ecx, cpuid_regs_t res, int filtered) +{ + int result; + + lua_getfield(L, LUA_GLOBALSINDEX, "SerialICE_cpuid_log"); + + lua_pushinteger(L, eax); // input: eax + lua_pushinteger(L, ecx); // input: ecx + lua_pushinteger(L, res.eax); // output: eax + lua_pushinteger(L, res.ebx); // output: ebx + lua_pushinteger(L, res.ecx); // output: ecx + lua_pushinteger(L, res.edx); // output: edx + lua_pushboolean(L, filtered); // data + result = lua_pcall(L, 7, 0, 0); + if (result) { + fprintf(stderr, "Failed to run function SerialICE_cpuid_log: %s\n", + lua_tostring(L, -1)); + exit(1); + } +} + + +// ************************************************************************** +// 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 = read(state->fd, buf, nbyte - bytes_read); + + if (ret == -1 && errno == EINTR) + continue; + + if (ret == -1) + break; +#endif + + bytes_read += ret; + buf += ret; + + if (bytes_read >= (int)nbyte) + break; + } + + return bytes_read; +} + +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 (write(state->fd, buffer + i, 1) != 1) ; + while (read(state->fd, &c, 1) != 1) ; +#endif + if (c != buffer[i]) { + printf("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"); + 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"); + exit(1); + } + } + + return 0; +} + +static void serialice_command(const char *command, int reply_len) +{ +#if SERIALICE_DEBUG > 5 + int i; +#endif + int l; + + 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"); + 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) { + printf("SerialICE: command was not answered sufficiently: " + "(%d/%d bytes)\n'%s'\n", l, reply_len, s->buffer); + exit(1); + } + +#if SERIALICE_DEBUG > 5 + for (i=0; i < reply_len; i++) + printf("%02x ", s->buffer[i]); + printf("\n"); +#endif +} + + +// ************************************************************************** +// high level communication with the SerialICE shell + +uint8_t serialice_inb(uint16_t port) +{ + uint8_t ret; + uint32_t data; + + if (serialice_io_read_filter(&data, port, 1)) + return data & 0xff; + + 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; + uint32_t data; + + if (serialice_io_read_filter(&data, port, 1)) + return data & 0xffff; + + 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; + uint32_t data; + + if (serialice_io_read_filter(&data, port, 1)) + return data; + + 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) +{ + uint32_t filtered_data = (uint32_t)data; + + serialice_log(LOG_WRITE|LOG_IO, data, port, 1); + + if (serialice_io_write_filter(&filtered_data, port, 1)) { + return; + } + + data = (uint8_t)filtered_data; + sprintf(s->command, "*wi%04x.b=%02x", port, data); + serialice_command(s->command, 0); +} + +void serialice_outw(uint16_t data, uint16_t port) +{ + uint32_t filtered_data = (uint32_t)data; + + serialice_log(LOG_WRITE|LOG_IO, data, port, 2); + + if (serialice_io_write_filter(&filtered_data, port, 2)) { + return; + } + + data = (uint16_t)filtered_data; + sprintf(s->command, "*wi%04x.w=%04x", port, data); + serialice_command(s->command, 0); +} + +void serialice_outl(uint32_t data, uint16_t port) +{ + uint32_t filtered_data = data; + + serialice_log(LOG_WRITE|LOG_IO, data, port, 4); + + if (serialice_io_write_filter(&filtered_data, port, 4)) { + return; + } + + data = filtered_data; + 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); + 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); + 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); + return ret; +} + +void serialice_writeb(uint8_t data, uint32_t addr) +{ + sprintf(s->command, "*wm%08x.b=%02x", addr, data); + serialice_command(s->command, 0); +} + +void serialice_writew(uint16_t data, uint32_t addr) +{ + sprintf(s->command, "*wm%08x.w=%04x", addr, data); + serialice_command(s->command, 0); +} + +void serialice_writel(uint32_t data, uint32_t addr) +{ + 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; + + filtered = serialice_msr_filter(FILTER_READ, addr, &hi, &lo); + if (!filtered) { + 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; + + hi = (data >> 32); + lo = (data & 0xffffffff); + + filtered = serialice_msr_filter(FILTER_WRITE, addr, &hi, &lo); + + 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; + int filtered; + + 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 + + filtered = serialice_cpuid_filter(&ret); + if (!filtered) { + 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, filtered); + + return ret; +} + +// ************************************************************************** +// memory load handling + +static uint32_t serialice_load_wrapper(uint32_t addr, unsigned int size) +{ + switch (size) { + case 1: return (uint32_t)serialice_readb(addr); + case 2: return (uint32_t)serialice_readw(addr); + case 4: return (uint32_t)serialice_readl(addr); + default: printf("WARNING: unknown read access size %d @%08x\n", size, addr); + } + return 0; +} + +/** + * This function is called by the softmmu engine to update the status + * of a load cycle + */ +void serialice_log_load(int caught, uint32_t addr, uint32_t result, unsigned int data_size) +{ + if (caught) + serialice_log(LOG_READ|LOG_MEMORY|LOG_TARGET, result, addr, data_size); + else + serialice_log(LOG_READ|LOG_MEMORY, result, addr, data_size); +} + +/* This function can grab Qemu load ops and forward them to the SerialICE + * target. + * + * @return 0: pass on to Qemu; 1: handled locally. + */ +int serialice_handle_load(uint32_t addr, uint32_t *result, unsigned int data_size) +{ + int source; + + source = serialice_memory_read_filter(addr, result, data_size); + + if (source & READ_FROM_SERIALICE) { + *result = serialice_load_wrapper(addr, data_size); + return 1; + } + + if (source & READ_FROM_QEMU) { + return 0; + } + + /* No source for load, so the source is the script */ + return 1; +} + +// ************************************************************************** +// memory store handling + +static void serialice_store_wrapper(uint32_t addr, unsigned int size, uint32_t data) +{ + switch (size) { + case 1: serialice_writeb((uint8_t)data, addr); break; + case 2: serialice_writew((uint16_t)data, addr); break; + case 4: serialice_writel((uint32_t)data, addr); break; + default: printf("WARNING: unknown write access size %d @%08x\n", size, addr); + } +} + +static void serialice_log_store(int caught, uint32_t addr, uint32_t val, unsigned int data_size) +{ + if (caught) + serialice_log(LOG_WRITE|LOG_MEMORY|LOG_TARGET, val, addr, data_size); + else + serialice_log(LOG_WRITE|LOG_MEMORY, val, addr, data_size); +} + +/* This function can grab Qemu store ops and forward them to the SerialICE + * target + * + * @return 0: Qemu exclusive or shared; 1: SerialICE exclusive. + */ + +int serialice_handle_store(uint32_t addr, uint32_t val, unsigned int data_size) +{ + int write_to_target, write_to_qemu, ret; + uint32_t filtered_data = val; + + ret = serialice_memory_write_filter(addr, data_size, &filtered_data); + + write_to_target = ((ret & WRITE_TO_SERIALICE) != 0); + write_to_qemu = ((ret & WRITE_TO_QEMU) != 0); + + serialice_log_store(write_to_target, addr, filtered_data, data_size); + + if (write_to_target) + serialice_store_wrapper(addr, data_size, filtered_data); + + return (write_to_qemu == 0); +} + +// ************************************************************************** +// initialization and exit + +void serialice_init(void) +{ + printf("SerialICE: Open connection to target hardware...\n"); + + if (serialice_device == NULL) { + printf("You need to specify a serial device to use SerialICE.\n"); + exit(1); + } + + s = qemu_mallocz(sizeof(SerialICEState)); +#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"); + exit(1); + } + + DCB dcb; + if (!GetCommState(s->fd, &dcb)) { + perror("SerialICE: Could not load config for target TTY"); + 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"); + exit(1); + } + +#else + s->fd = open(serialice_device, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (s->fd == -1) { + perror("SerialICE: Could not connect to target TTY"); + exit(1); + } + + if (ioctl(s->fd, TIOCEXCL) == -1) { + perror("SerialICE: TTY not exclusively available"); + exit(1); + } + + if (fcntl(s->fd, F_SETFL, 0) == -1) { + perror("SerialICE: Could not switch to blocking I/O"); + exit(1); + } + + if (tcgetattr(s->fd, &options) == -1) { + perror("SerialICE: Could not get TTY attributes"); + exit(1); + } + + cfsetispeed(&options, B115200); + 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; + + tcsetattr(s->fd, TCSANOW, &options); + + tcflush(s->fd, TCIOFLUSH); +#endif + + s->buffer = qemu_mallocz(BUFFER_SIZE); + s->command = qemu_mallocz(BUFFER_SIZE); + + printf("SerialICE: Waiting for handshake with target... "); + + /* Trigger a prompt */ + serialice_write(s, "\n", 1); + + /* ... and wait for it to appear */ + if (serialice_wait_prompt() == 0) { + printf("target alife!\n"); + } else { + printf("target not ok!\n" ); + 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, "\n", 1); + + printf("SerialICE: LUA init...\n"); + serialice_lua_init(); + + /* Let the rest of Qemu know we're alife */ + serialice_active = 1; +} + +void serialice_exit(void) +{ + serialice_lua_exit(); + qemu_free(s->command); + qemu_free(s->buffer); + qemu_free(s); +} + +device_init(serialice_init) +// no exit function + --- qemu-0.11.0/serialice.h +++ qemu-0.11.0/serialice.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_H +#define SERIALICE_H + +#include "config-host.h" + +extern const char *serialice_device; +extern int serialice_active; + +void serialice_init(void); +void serialice_exit(void); + +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 --- qemu-0.11.0/build.sh +++ qemu-0.11.0/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +./configure --disable-kvm --disable-sdl --enable-serialice \ + --target-list="x86_64-softmmu, i386-softmmu" + +make --- qemu-0.11.0/Makefile.target +++ qemu-0.11.0/Makefile.target @@ -488,6 +488,13 @@ # Generic watchdog support and some watchdog devices obj-y += wdt_ib700.o wdt_i6300esb.o +# Generic SerialICE support +ifdef CONFIG_SERIALICE +CPPFLAGS += $(CONFIG_SERIALICE_CFLAGS) +LIBS += $(CONFIG_SERIALICE_LIBS) +endif +obj-$(CONFIG_SERIALICE) += serialice.o + # Hardware support obj-i386-y = ide.o pckbd.o vga.o $(sound-obj-y) dma.o obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o --- qemu-0.11.0/vl.c +++ qemu-0.11.0/vl.c @@ -221,6 +221,9 @@ int win2k_install_hack = 0; int rtc_td_hack = 0; #endif +#ifdef CONFIG_SERIALICE +const char *serialice_device = NULL; +#endif int usb_enabled = 0; int singlestep = 0; int smp_cpus = 1; @@ -5108,6 +5111,11 @@ display_type = DT_CURSES; break; #endif +#ifdef CONFIG_SERIALICE + case QEMU_OPTION_serialice: + serialice_device = optarg; + break; +#endif case QEMU_OPTION_portrait: graphic_rotate = 1; break; --- qemu-0.11.0/qemu-char.c +++ qemu-0.11.0/qemu-char.c @@ -821,7 +821,7 @@ #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ - || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) typedef struct { int fd; @@ -2262,7 +2262,7 @@ } else #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ - || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) if (strstart(filename, "/dev/", NULL)) { chr = qemu_chr_open_tty(filename); } else --- qemu-0.11.0/softmmu_template.h +++ qemu-0.11.0/softmmu_template.h @@ -16,6 +16,9 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ + +#include "serialice.h" + #define DATA_SIZE (1 << SHIFT) #if DATA_SIZE == 8 @@ -91,6 +94,16 @@ target_phys_addr_t addend; void *retaddr; +#ifdef CONFIG_SERIALICE + uint32_t result; + int caught = 0; + if (serialice_active && serialice_handle_load((uint32_t)addr, &result, (unsigned int) DATA_SIZE)) { + res = (DATA_TYPE)result; + caught=1; + goto leave_ld; + } +#endif + /* test if there is match for unaligned or IO access */ /* XXX: could done more in memory macro in a non portable way */ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); @@ -134,6 +147,12 @@ tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr); goto redo; } + +#ifdef CONFIG_SERIALICE +leave_ld: + if (serialice_active) + serialice_log_load(caught, addr, (uint32_t)res, (unsigned int)DATA_SIZE); +#endif return res; } @@ -234,6 +253,15 @@ void *retaddr; int index; +#ifdef CONFIG_SERIALICE + if (serialice_active && serialice_handle_store((uint32_t)addr, (uint32_t)val, (unsigned int) DATA_SIZE)) { + // For now, we just always keep a backup of _all_ writes in qemu's + // memory. At this point we can later decide what to do, if it becomes + // necessary. + // return; + } +#endif + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); redo: tlb_addr = env->tlb_table[mmu_idx][index].addr_write; --- qemu-0.11.0/Makefile +++ qemu-0.11.0/Makefile @@ -212,6 +212,10 @@ bt-host.o: CFLAGS += $(CONFIG_BLUEZ_CFLAGS) +serialice.o: serialice.c serialice.h + +serialice.o: CFLAGS += $(CONFIG_SERIALICE_CFLAGS) + libqemu_common.a: $(obj-y) ####################################################################### --- qemu-0.11.0/qemu-options.hx +++ qemu-0.11.0/qemu-options.hx @@ -1661,3 +1661,14 @@ DEF("old-param", 0, QEMU_OPTION_old_param, "-old-param old param mode\n") #endif + +#ifdef CONFIG_SERIALICE +DEF("serialice", HAS_ARG, QEMU_OPTION_serialice, + "-serialice dev Enable SerialICE debugging on serial device 'dev'\n") +STEXI address@hidden -serialice @var{dev} +Enable SerialICE debugging on serial device @var{dev}. +ETEXI +#endif + + --- qemu-0.11.0/exec-all.h +++ qemu-0.11.0/exec-all.h @@ -21,6 +21,7 @@ #define _EXEC_ALL_H_ #include "qemu-common.h" +#include "serialice.h" /* allow to see translation results - the slowdown should be negligible, so we leave it */ #define DEBUG_DISAS @@ -328,6 +329,9 @@ #if defined(TARGET_SPARC) || defined(TARGET_MIPS) do_unassigned_access(addr, 0, 1, 0, 4); #else +#if defined(CONFIG_SERIALICE) + if (!serialice_active) +#endif cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr); #endif } --- qemu-0.11.0/configure +++ qemu-0.11.0/configure @@ -193,6 +193,7 @@ io_thread="no" nptl="yes" mixemu="no" +serialice="no" bluez="yes" kvm="no" kerneldir="" @@ -491,6 +492,8 @@ ;; --enable-mixemu) mixemu="yes" ;; + --enable-serialice) serialice="yes" + ;; --disable-pthread) pthread="no" ;; --disable-aio) aio="no" @@ -624,6 +627,7 @@ echo " --audio-card-list=LIST set list of emulated audio cards [$audio_card_list]" echo " Available cards: $audio_possible_cards" echo " --enable-mixemu enable mixer emulation" +echo " --enable-serialice enable SerialICE debugger support" echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" @@ -1064,6 +1068,40 @@ done ########################################## +# LUA probe + +if test "$serialice" = "yes" ; then + serialice=no + cat > $TMPC << EOF +#include +#include +#include +#include +#include +static lua_State *L; +int main(void) { L=luaL_newstate(); return 0; } +EOF + + LUA_CFLAGS="-I/usr/local/include" + LUA_LDFLAGS="-L/usr/local/lib -llua" + if $cc $EXTRA_CFLAGS $EXTRA_LDFLAGS $ARCH_CFLAGS $LUA_CFLAGS $LUA_LDFLAGS -o $TMPE $TMPC -llua > /dev/null 2> /dev/null ; then + serialice=yes + else + LUA_CFLAGS=`pkg-config --cflags lua` + LUA_LDFLAGS=`pkg-config --libs lua` + if $cc $EXTRA_CFLAGS $EXTRA_LDFLAGS $ARCH_CFLAGS $LUA_CFLAGS $LUA_LDFLAGS -o $TMPE $TMPC -llua > /dev/null 2> /dev/null ; then + serialice=yes + else + echo + echo "Error LUA not found, can't build with SerialICE support." + echo + exit 1 + fi + fi +fi # test "$serialice" + + +########################################## # BrlAPI probe if test -z "$brlapi" ; then @@ -1426,6 +1464,7 @@ echo "Audio drivers $audio_drv_list" echo "Extra audio cards $audio_card_list" echo "Mixer emulation $mixemu" +echo "SerialICE support $serialice" echo "VNC TLS support $vnc_tls" if test "$vnc_tls" = "yes" ; then echo " TLS CFLAGS $vnc_tls_cflags" @@ -1620,6 +1659,12 @@ echo "CONFIG_MIXEMU=y" >> $config_host_mak echo "#define CONFIG_MIXEMU 1" >> $config_host_h fi +if test "$serialice" = "yes" ; then + echo "CONFIG_SERIALICE=y" >> $config_host_mak + echo "CONFIG_SERIALICE_CFLAGS=$LUA_CFLAGS" >> $config_host_mak + echo "CONFIG_SERIALICE_LIBS=$LUA_LDFLAGS" >> $config_host_mak + echo "#define CONFIG_SERIALICE 1" >> $config_host_h +fi if test "$vnc_tls" = "yes" ; then echo "CONFIG_VNC_TLS=y" >> $config_host_mak echo "CONFIG_VNC_TLS_CFLAGS=$vnc_tls_cflags" >> $config_host_mak --- qemu-0.11.0/target-i386/op_helper.c +++ qemu-0.11.0/target-i386/op_helper.c @@ -23,6 +23,7 @@ //#define DEBUG_PCALL +#include "serialice.h" #ifdef DEBUG_PCALL # define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__) @@ -558,31 +559,64 @@ void helper_outb(uint32_t port, uint32_t data) { +#ifdef CONFIG_SERIALICE + if (serialice_active) { + serialice_outb(data & 0xff, port); + return; + } +#endif cpu_outb(env, port, data & 0xff); } target_ulong helper_inb(uint32_t port) { +#ifdef CONFIG_SERIALICE + if (serialice_active) { + return (target_ulong)serialice_inb(port); + } +#endif return cpu_inb(env, port); } void helper_outw(uint32_t port, uint32_t data) { +#ifdef CONFIG_SERIALICE + if (serialice_active) { + serialice_outw(data & 0xffff, port); + return; + } +#endif cpu_outw(env, port, data & 0xffff); } target_ulong helper_inw(uint32_t port) { +#ifdef CONFIG_SERIALICE + if (serialice_active) { + return (target_ulong)serialice_inw(port); + } +#endif return cpu_inw(env, port); } void helper_outl(uint32_t port, uint32_t data) { +#ifdef CONFIG_SERIALICE + if (serialice_active) { + serialice_outl(data & 0xffffffff, port); + return; + } +#endif cpu_outl(env, port, data); } target_ulong helper_inl(uint32_t port) { +#ifdef CONFIG_SERIALICE + if (serialice_active) { + return (target_ulong)serialice_inl(port); + } +#endif return cpu_inl(env, port); } @@ -1940,6 +1974,18 @@ helper_svm_check_intercept_param(SVM_EXIT_CPUID, 0); +#ifdef CONFIG_SERIALICE + if (serialice_active) { + cpuid_regs_t ret; + ret = serialice_cpuid((uint32_t)EAX, (uint32_t)ECX); + EAX = ret.eax; + EBX = ret.ebx; + ECX = ret.ecx; + EDX = ret.edx; + return; + } +#endif + cpu_x86_cpuid(env, (uint32_t)EAX, (uint32_t)ECX, &eax, &ebx, &ecx, &edx); EAX = eax; EBX = ebx; @@ -3030,6 +3076,13 @@ val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32); +#ifdef CONFIG_SERIALICE + if (serialice_active) { + serialice_wrmsr(val, (uint32_t)ECX, (uint32_t)EDI); + return; + } +#endif + switch((uint32_t)ECX) { case MSR_IA32_SYSENTER_CS: env->sysenter_cs = val & 0xffff; @@ -3160,6 +3213,15 @@ helper_svm_check_intercept_param(SVM_EXIT_MSR, 0); +#ifdef CONFIG_SERIALICE + if (serialice_active) { + val = serialice_rdmsr((uint32_t)ECX, (uint32_t)EDI); + EAX = (uint32_t)(val); + EDX = (uint32_t)(val >> 32); + return; + } +#endif + switch((uint32_t)ECX) { case MSR_IA32_SYSENTER_CS: val = env->sysenter_cs; --- qemu-0.11.0/hw/pc.c +++ qemu-0.11.0/hw/pc.c @@ -23,6 +23,7 @@ */ #include "hw.h" #include "pc.h" +#include "serialice.h" #include "fdc.h" #include "pci.h" #include "block.h" @@ -1152,6 +1153,10 @@ /* allocate RAM */ ram_addr = qemu_ram_alloc(0xa0000); +#ifdef CONFIG_SERIALICE + if (serialice_active) + ram_addr |= IO_MEM_UNASSIGNED; +#endif cpu_register_physical_memory(0, 0xa0000, ram_addr); /* Allocate, even though we won't register, so we don't break the @@ -1160,6 +1165,10 @@ */ ram_addr = qemu_ram_alloc(0x100000 - 0xa0000); ram_addr = qemu_ram_alloc(below_4g_mem_size - 0x100000); +#ifdef CONFIG_SERIALICE + if (serialice_active) + ram_addr |= IO_MEM_UNASSIGNED; +#endif cpu_register_physical_memory(0x100000, below_4g_mem_size - 0x100000, ram_addr); @@ -1170,6 +1179,10 @@ hw_error("To much RAM for 32-bit physical address"); #else ram_addr = qemu_ram_alloc(above_4g_mem_size); +#ifdef CONFIG_SERIALICE + if (serialice_active) + ram_addr |= IO_MEM_UNASSIGNED; +#endif cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, ram_addr);