From patchwork Fri Nov 20 21:33:15 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "H. Peter Anvin" X-Patchwork-Id: 38987 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 3B484B6EEE for ; Sun, 22 Nov 2009 05:21:19 +1100 (EST) Received: from localhost ([127.0.0.1]:51151 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NBuaA-0005g7-Tc for incoming@patchwork.ozlabs.org; Sat, 21 Nov 2009 13:21:14 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NBb6Z-0000uT-HO for qemu-devel@nongnu.org; Fri, 20 Nov 2009 16:33:23 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1NBb6V-0000rx-FT for qemu-devel@nongnu.org; Fri, 20 Nov 2009 16:33:23 -0500 Received: from [199.232.76.173] (port=33685 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NBb6V-0000ru-CQ for qemu-devel@nongnu.org; Fri, 20 Nov 2009 16:33:19 -0500 Received: from mga06.intel.com ([134.134.136.21]:5058 helo=orsmga101.jf.intel.com) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1NBb6U-0004QK-KN for qemu-devel@nongnu.org; Fri, 20 Nov 2009 16:33:19 -0500 Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP; 20 Nov 2009 13:18:42 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.47,260,1257148800"; d="scan'208";a="571616236" Received: from anacreon.sc.intel.com (HELO localhost.localdomain) ([143.183.85.138]) by orsmga001.jf.intel.com with ESMTP; 20 Nov 2009 13:33:06 -0800 From: "H. Peter Anvin" To: qemu-devel@nongnu.org Date: Fri, 20 Nov 2009 13:33:15 -0800 Message-Id: <1258752795-15434-1-git-send-email-hpa@linux.intel.com> X-Mailer: git-send-email 1.6.2.5 X-detected-operating-system: by monty-python.gnu.org: Genre and OS details not recognized. X-Mailman-Approved-At: Sat, 21 Nov 2009 13:19:01 -0500 Cc: "H. Peter Anvin" Subject: [Qemu-devel] [PATCH] debugcon: support for debugging consoles (e.g. Bochs port 0xe9) X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add generic support for debugging consoles (simple I/O ports which when written to cause debugging output to be written to a target.) The current implementation matches Bochs' port 0xe9, allowing the same debugging code to be used for both Bochs and Qemu. There is no vm state associated with the debugging port, simply because it has none -- the entire interface is a single, stateless, write-only port. Most of the code was cribbed from the serial port driver. Signed-off-by: H. Peter Anvin --- Makefile.target | 2 +- hw/debugcon.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/pc.c | 6 ++ hw/pc.h | 4 + qemu-common.h | 1 + qemu-options.hx | 11 +++ sysemu.h | 6 ++ vl.c | 40 ++++++++++++ 8 files changed, 258 insertions(+), 1 deletions(-) create mode 100644 hw/debugcon.c diff --git a/Makefile.target b/Makefile.target index ab774e5..689cbf0 100644 --- a/Makefile.target +++ b/Makefile.target @@ -197,7 +197,7 @@ obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o obj-i386-y += cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o -obj-i386-y += ne2000-isa.o +obj-i386-y += ne2000-isa.o debugcon.o # shared objects obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o diff --git a/hw/debugcon.c b/hw/debugcon.c new file mode 100644 index 0000000..6a724bb --- /dev/null +++ b/hw/debugcon.c @@ -0,0 +1,189 @@ +/* + * QEMU Bochs-style debug console ("port E9") emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix Systems, Inc. + * Copyright (c) Intel Corporation; author: H. Peter Anvin + * + * 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 "hw.h" +#include "qemu-char.h" +#include "isa.h" +#include "pc.h" + +//#define DEBUG_DEBUGCON + +struct DebugconState { + CharDriverState *chr; +}; + +typedef struct ISADebugconState { + ISADevice dev; + uint32_t index; + uint32_t iobase; + DebugconState state; +} ISADebugconState; + +static void debugcon_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + DebugconState *s = opaque; + unsigned char ch = val; + +#ifdef DEBUG_DEBUGCON + printf("debugcon: write addr=0x%04x val=0x%02x\n", addr, val); +#endif + + qemu_chr_write(s->chr, &ch, 1); +} + +static void debugcon_init_core(DebugconState *s) +{ + if (!s->chr) { + fprintf(stderr, "Can't create debugcon device, empty char device\n"); + exit(1); + } + + qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); +} + +static const uint16_t isa_debugcon_iobase[MAX_DEBUGCON_PORTS] = { 0xe9 }; + +static int debugcon_isa_initfn(ISADevice *dev) +{ + static int index; + ISADebugconState *isa = DO_UPCAST(ISADebugconState, dev, dev); + DebugconState *s = &isa->state; + + if (isa->index == -1) + isa->index = index; + if (isa->index >= MAX_DEBUGCON_PORTS) + return -1; + if (isa->iobase == -1) + isa->iobase = isa_debugcon_iobase[isa->index]; + index++; + + debugcon_init_core(s); + register_ioport_write(isa->iobase, 1, 1, debugcon_ioport_write, s); + return 0; +} + +DebugconState *debugcon_isa_init(int index, CharDriverState *chr) +{ + ISADevice *dev; + + dev = isa_create("isa-debugcon"); + qdev_prop_set_chr(&dev->qdev, "chardev", chr); + if (qdev_init(&dev->qdev) < 0) + return NULL; + return &DO_UPCAST(ISADebugconState, dev, dev)->state; +} + +#if 0 /* Non-ISA interfaces, available for the future */ + +DebugconState *debugcon_init(int base, qemu_irq irq, int baudbase, + CharDriverState *chr) +{ + DebugconState *s; + + s = qemu_mallocz(sizeof(DebugconState)); + + s->chr = chr; + debugcon_init_core(s); + + register_ioport_write(base, 8, 1, debugcon_ioport_write, s); + return s; +} + +/* Memory mapped interface */ +static void debugcon_mm_writeb(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + DebugconState *s = opaque; + + debugcon_ioport_write(s, addr >> s->it_shift, value & 0xFF); +} + +static void debugcon_mm_writew(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + DebugconState *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap16(value); +#endif + debugcon_ioport_write(s, addr >> s->it_shift, value & 0xFFFF); +} + +static void debugcon_mm_writel(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + DebugconState *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap32(value); +#endif + debugcon_ioport_write(s, addr >> s->it_shift, value); +} + +static CPUWriteMemoryFunc * const debugcon_mm_write[] = { + &debugcon_mm_writeb, + &debugcon_mm_writew, + &debugcon_mm_writel, +}; + +DebugconState *debugcon_mm_init (target_phys_addr_t base, int it_shift, + qemu_irq irq, int baudbase, + CharDriverState *chr, int ioregister) +{ + DebugconState *s; + int s_io_memory; + + s = qemu_mallocz(sizeof(DebugconState)); + + s->chr = chr; + + debugcon_init_core(s); + + if (ioregister) { + s_io_memory = cpu_register_io_memory(NULL, debugcon_mm_write, s); + cpu_register_physical_memory(base, 1 << it_shift, s_io_memory); + } + return s; +} + +#endif /* Non-ISA interfaces */ + +static ISADeviceInfo debugcon_isa_info = { + .qdev.name = "isa-debugcon", + .qdev.size = sizeof(ISADebugconState), + .init = debugcon_isa_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_HEX32("index", ISADebugconState, index, -1), + DEFINE_PROP_HEX32("iobase", ISADebugconState, iobase, -1), + DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void debugcon_register_devices(void) +{ + isa_qdev_register(&debugcon_isa_info); +} + +device_init(debugcon_register_devices) diff --git a/hw/pc.c b/hw/pc.c index 7c791c4..3957c78 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -1159,6 +1159,12 @@ static void pc_init1(ram_addr_t ram_size, } } + for(i = 0; i < MAX_DEBUGCON_PORTS; i++) { + if (debugcon_hds[i]) { + debugcon_isa_init(i, debugcon_hds[i]); + } + } + for(i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; diff --git a/hw/pc.h b/hw/pc.h index 03ffc91..4048be5 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -21,6 +21,10 @@ typedef struct ParallelState ParallelState; ParallelState *parallel_init(int index, CharDriverState *chr); ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr); +/* debugcon.c */ + +DebugconState *debugcon_isa_init(int index, CharDriverState *chr); + /* i8259.c */ typedef struct PicState2 PicState2; diff --git a/qemu-common.h b/qemu-common.h index b779cfe..9dd3ca8 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -208,6 +208,7 @@ typedef struct uWireSlave uWireSlave; typedef struct I2SCodec I2SCodec; typedef struct DeviceState DeviceState; typedef struct SSIBus SSIBus; +typedef struct DebugconState DebugconState; /* CPU save/load. */ void cpu_save(QEMUFile *f, void *opaque); diff --git a/qemu-options.hx b/qemu-options.hx index b65fd74..92ade30 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1586,6 +1586,17 @@ The default device is @code{vc} in graphical mode and @code{stdio} in non graphical mode. ETEXI +DEF("debugcon", HAS_ARG, QEMU_OPTION_debugcon, \ + "-debugcon dev redirect the debug console to char device 'dev'\n") +STEXI +@item -monitor @var{dev} +Redirect the debug console to host device @var{dev} (same devices as the +serial port). The debug console is an I/O port which is typically port +0xe9; writing to that I/O port sends output to this device. +The default device is @code{vc} in graphical mode and @code{stdio} in +non graphical mode. +ETEXI + DEF("pidfile", HAS_ARG, QEMU_OPTION_pidfile, \ "-pidfile file write PID to 'file'\n") STEXI diff --git a/sysemu.h b/sysemu.h index b1887ef..e56b111 100644 --- a/sysemu.h +++ b/sysemu.h @@ -225,6 +225,12 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; extern CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; +/* debugging ports */ + +#define MAX_DEBUGCON_PORTS 1 + +extern CharDriverState *debugcon_hds[MAX_DEBUGCON_PORTS]; + #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) #ifdef HAS_AUDIO diff --git a/vl.c b/vl.c index c3f3c8f..978f53b 100644 --- a/vl.c +++ b/vl.c @@ -212,6 +212,7 @@ int no_quit = 0; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; +CharDriverState *debugcon_hds[MAX_DEBUGCON_PORTS]; #ifdef TARGET_I386 int win2k_install_hack = 0; int rtc_td_hack = 0; @@ -4598,6 +4599,8 @@ int main(int argc, char **argv, char **envp) int parallel_device_index; const char *virtio_consoles[MAX_VIRTIO_CONSOLES]; int virtio_console_index; + const char *debugcon_devices[MAX_DEBUGCON_PORTS]; + int debugcon_device_index; const char *loadvm = NULL; QEMUMachine *machine; const char *cpu_model; @@ -4683,6 +4686,11 @@ int main(int argc, char **argv, char **envp) } monitor_device_index = 0; + debugcon_devices[0] = "vc:80Cx24C"; + for(i = 1; i < MAX_DEBUGCON_PORTS; i++) + debugcon_devices[i] = NULL; + debugcon_device_index = 0; + for (i = 0; i < MAX_NODES; i++) { node_mem[i] = 0; node_cpumask[i] = 0; @@ -5123,6 +5131,14 @@ int main(int argc, char **argv, char **envp) serial_devices[serial_device_index] = optarg; serial_device_index++; break; + case QEMU_OPTION_debugcon: + if (debugcon_device_index >= MAX_DEBUGCON_PORTS) { + fprintf(stderr, "qemu: too many debugcon ports\n"); + exit(1); + } + debugcon_devices[debugcon_device_index] = optarg; + debugcon_device_index++; + break; case QEMU_OPTION_watchdog: if (watchdog) { fprintf(stderr, @@ -5430,6 +5446,8 @@ int main(int argc, char **argv, char **envp) if (strncmp(monitor_devices[0], "vc", 2) == 0) { monitor_devices[0] = "stdio"; } + if (debugcon_device_index == 0) + debugcon_devices[0] = "stdio"; } #ifndef _WIN32 @@ -5691,6 +5709,20 @@ int main(int argc, char **argv, char **envp) } } + for(i = 0; i < MAX_DEBUGCON_PORTS; i++) { + const char *devname = debugcon_devices[i]; + if (devname && strcmp(devname, "none")) { + char label[32]; + snprintf(label, sizeof(label), "debugcon%d", i); + debugcon_hds[i] = qemu_chr_open(label, devname, NULL); + if (!debugcon_hds[i]) { + fprintf(stderr, "qemu: could not open debugcon device '%s': %s\n", + devname, strerror(errno)); + exit(1); + } + } + } + module_call_init(MODULE_INIT_DEVICE); if (watchdog) { @@ -5826,6 +5858,14 @@ int main(int argc, char **argv, char **envp) } } + for(i = 0; i < MAX_DEBUGCON_PORTS; i++) { + const char *devname = debugcon_devices[i]; + if (devname && strcmp(devname, "none")) { + if (strstart(devname, "vc", 0)) + qemu_chr_printf(debugcon_hds[i], "debugcon%d console\r\n", i); + } + } + if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) { fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n", gdbstub_dev);