PIIX4 SMBus host, EEPROM device emulation, by Ed Swierk.
[qemu] / vl.c
diff --git a/vl.c b/vl.c
index 47056f8..daeec82 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -1,7 +1,7 @@
 /*
  * QEMU System Emulator
  * 
- * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2003-2006 Fabrice Bellard
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -29,6 +29,7 @@
 #include <time.h>
 #include <errno.h>
 #include <sys/time.h>
+#include <zlib.h>
 
 #ifndef _WIN32
 #include <sys/times.h>
@@ -47,6 +48,7 @@
 #include <libutil.h>
 #endif
 #else
+#ifndef __sun__
 #include <linux/if.h>
 #include <linux/if_tun.h>
 #include <pty.h>
@@ -55,6 +57,7 @@
 #include <linux/ppdev.h>
 #endif
 #endif
+#endif
 
 #if defined(CONFIG_SLIRP)
 #include "libslirp.h"
 #include <malloc.h>
 #include <sys/timeb.h>
 #include <windows.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
 #define getopt_long_only getopt_long
 #define memalign(align, size) malloc(size)
 #endif
 
+#include "qemu_socket.h"
+
 #ifdef CONFIG_SDL
 #ifdef __APPLE__
 #include <SDL/SDL.h>
 #include "exec-all.h"
 
 #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
+#ifdef __sun__
+#define SMBD_COMMAND "/usr/sfw/sbin/smbd"
+#else
+#define SMBD_COMMAND "/usr/sbin/smbd"
+#endif
 
 //#define DEBUG_UNUSED_IOPORT
 //#define DEBUG_IOPORT
 
-#if !defined(CONFIG_SOFTMMU)
-#define PHYS_RAM_MAX_SIZE (256 * 1024 * 1024)
-#else
 #define PHYS_RAM_MAX_SIZE (2047 * 1024 * 1024)
-#endif
 
 #ifdef TARGET_PPC
 #define DEFAULT_RAM_SIZE 144
 /* in ms */
 #define GUI_REFRESH_INTERVAL 30
 
+/* Max number of USB devices that can be specified on the commandline.  */
+#define MAX_USB_CMDLINE 8
+
 /* XXX: use a two level table to limit memory usage */
 #define MAX_IOPORTS 65536
 
@@ -112,7 +119,11 @@ char phys_ram_file[1024];
 void *ioport_opaque[MAX_IOPORTS];
 IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
 IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
-BlockDriverState *bs_table[MAX_DISKS], *fd_table[MAX_FD];
+/* Note: bs_table[MAX_DISKS] is a dummy block driver if none available
+   to store the VM snapshots */
+BlockDriverState *bs_table[MAX_DISKS + 1], *fd_table[MAX_FD];
+/* point to the block driver where the snapshots are managed */
+BlockDriverState *bs_snapshots;
 int vga_ram_size;
 int bios_size;
 static DisplayState display_state;
@@ -137,17 +148,16 @@ int graphic_height = 600;
 #endif
 int graphic_depth = 15;
 int full_screen = 0;
-TextConsole *vga_console;
+int no_quit = 0;
 CharDriverState *serial_hds[MAX_SERIAL_PORTS];
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 #ifdef TARGET_I386
 int win2k_install_hack = 0;
 #endif
 int usb_enabled = 0;
-USBPort *vm_usb_ports[MAX_VM_USB_PORTS];
-USBDevice *vm_usb_hub;
 static VLANState *first_vlan;
 int smp_cpus = 1;
+const char *vnc_display;
 #if defined(TARGET_SPARC)
 #define MAX_CPUS 16
 #elif defined(TARGET_I386)
@@ -155,6 +165,14 @@ int smp_cpus = 1;
 #else
 #define MAX_CPUS 1
 #endif
+int acpi_enabled = 1;
+int fd_bootchk = 1;
+int no_reboot = 0;
+int daemonize = 0;
+const char *option_rom[MAX_OPTION_ROMS];
+int nb_option_roms;
+int semihosting_enabled = 0;
+int autostart = 1;
 
 /***********************************************************/
 /* x86 ISA bus support */
@@ -267,7 +285,7 @@ int register_ioport_write(int start, int length, int size,
     for(i = start; i < start + length; i += size) {
         ioport_write_table[bsize][i] = func;
         if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
-            hw_error("register_ioport_read: invalid opaque");
+            hw_error("register_ioport_write: invalid opaque");
         ioport_opaque[i] = opaque;
     }
     return 0;
@@ -290,78 +308,6 @@ void isa_unassign_ioport(int start, int length)
 
 /***********************************************************/
 
-void pstrcpy(char *buf, int buf_size, const char *str)
-{
-    int c;
-    char *q = buf;
-
-    if (buf_size <= 0)
-        return;
-
-    for(;;) {
-        c = *str++;
-        if (c == 0 || q >= buf + buf_size - 1)
-            break;
-        *q++ = c;
-    }
-    *q = '\0';
-}
-
-/* strcat and truncate. */
-char *pstrcat(char *buf, int buf_size, const char *s)
-{
-    int len;
-    len = strlen(buf);
-    if (len < buf_size) 
-        pstrcpy(buf + len, buf_size - len, s);
-    return buf;
-}
-
-int strstart(const char *str, const char *val, const char **ptr)
-{
-    const char *p, *q;
-    p = str;
-    q = val;
-    while (*q != '\0') {
-        if (*p != *q)
-            return 0;
-        p++;
-        q++;
-    }
-    if (ptr)
-        *ptr = p;
-    return 1;
-}
-
-/* return the size or -1 if error */
-int get_image_size(const char *filename)
-{
-    int fd, size;
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        return -1;
-    size = lseek(fd, 0, SEEK_END);
-    close(fd);
-    return size;
-}
-
-/* return the size or -1 if error */
-int load_image(const char *filename, uint8_t *addr)
-{
-    int fd, size;
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        return -1;
-    size = lseek(fd, 0, SEEK_END);
-    lseek(fd, 0, SEEK_SET);
-    if (read(fd, addr, size) != size) {
-        close(fd);
-        return -1;
-    }
-    close(fd);
-    return size;
-}
-
 void cpu_outb(CPUState *env, int addr, int val)
 {
 #ifdef DEBUG_IOPORT
@@ -369,6 +315,10 @@ void cpu_outb(CPUState *env, int addr, int val)
         fprintf(logfile, "outb: %04x %02x\n", addr, val);
 #endif    
     ioport_write_table[0][addr](ioport_opaque[addr], addr, val);
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
 }
 
 void cpu_outw(CPUState *env, int addr, int val)
@@ -378,6 +328,10 @@ void cpu_outw(CPUState *env, int addr, int val)
         fprintf(logfile, "outw: %04x %04x\n", addr, val);
 #endif    
     ioport_write_table[1][addr](ioport_opaque[addr], addr, val);
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
 }
 
 void cpu_outl(CPUState *env, int addr, int val)
@@ -387,6 +341,10 @@ void cpu_outl(CPUState *env, int addr, int val)
         fprintf(logfile, "outl: %04x %08x\n", addr, val);
 #endif
     ioport_write_table[2][addr](ioport_opaque[addr], addr, val);
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
 }
 
 int cpu_inb(CPUState *env, int addr)
@@ -397,6 +355,10 @@ int cpu_inb(CPUState *env, int addr)
     if (loglevel & CPU_LOG_IOPORT)
         fprintf(logfile, "inb : %04x %02x\n", addr, val);
 #endif
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
     return val;
 }
 
@@ -408,6 +370,10 @@ int cpu_inw(CPUState *env, int addr)
     if (loglevel & CPU_LOG_IOPORT)
         fprintf(logfile, "inw : %04x %04x\n", addr, val);
 #endif
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
     return val;
 }
 
@@ -419,6 +385,10 @@ int cpu_inl(CPUState *env, int addr)
     if (loglevel & CPU_LOG_IOPORT)
         fprintf(logfile, "inl : %04x %08x\n", addr, val);
 #endif
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
     return val;
 }
 
@@ -449,8 +419,8 @@ void hw_error(const char *fmt, ...)
 
 static QEMUPutKBDEvent *qemu_put_kbd_event;
 static void *qemu_put_kbd_event_opaque;
-static QEMUPutMouseEvent *qemu_put_mouse_event;
-static void *qemu_put_mouse_event_opaque;
+static QEMUPutMouseEntry *qemu_put_mouse_event_head;
+static QEMUPutMouseEntry *qemu_put_mouse_event_current;
 
 void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
 {
@@ -458,10 +428,68 @@ void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
     qemu_put_kbd_event = func;
 }
 
-void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque)
+QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
+                                                void *opaque, int absolute,
+                                                const char *name)
+{
+    QEMUPutMouseEntry *s, *cursor;
+
+    s = qemu_mallocz(sizeof(QEMUPutMouseEntry));
+    if (!s)
+        return NULL;
+
+    s->qemu_put_mouse_event = func;
+    s->qemu_put_mouse_event_opaque = opaque;
+    s->qemu_put_mouse_event_absolute = absolute;
+    s->qemu_put_mouse_event_name = qemu_strdup(name);
+    s->next = NULL;
+
+    if (!qemu_put_mouse_event_head) {
+        qemu_put_mouse_event_head = qemu_put_mouse_event_current = s;
+        return s;
+    }
+
+    cursor = qemu_put_mouse_event_head;
+    while (cursor->next != NULL)
+        cursor = cursor->next;
+
+    cursor->next = s;
+    qemu_put_mouse_event_current = s;
+
+    return s;
+}
+
+void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
 {
-    qemu_put_mouse_event_opaque = opaque;
-    qemu_put_mouse_event = func;
+    QEMUPutMouseEntry *prev = NULL, *cursor;
+
+    if (!qemu_put_mouse_event_head || entry == NULL)
+        return;
+
+    cursor = qemu_put_mouse_event_head;
+    while (cursor != NULL && cursor != entry) {
+        prev = cursor;
+        cursor = cursor->next;
+    }
+
+    if (cursor == NULL) // does not exist or list empty
+        return;
+    else if (prev == NULL) { // entry is head
+        qemu_put_mouse_event_head = cursor->next;
+        if (qemu_put_mouse_event_current == entry)
+            qemu_put_mouse_event_current = cursor->next;
+        qemu_free(entry->qemu_put_mouse_event_name);
+        qemu_free(entry);
+        return;
+    }
+
+    prev->next = entry->next;
+
+    if (qemu_put_mouse_event_current == entry)
+        qemu_put_mouse_event_current = prev;
+
+    qemu_free(entry->qemu_put_mouse_event_name);
+    qemu_free(entry);
 }
 
 void kbd_put_keycode(int keycode)
@@ -473,96 +501,198 @@ void kbd_put_keycode(int keycode)
 
 void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
 {
-    if (qemu_put_mouse_event) {
-        qemu_put_mouse_event(qemu_put_mouse_event_opaque, 
-                             dx, dy, dz, buttons_state);
+    QEMUPutMouseEvent *mouse_event;
+    void *mouse_event_opaque;
+
+    if (!qemu_put_mouse_event_current) {
+        return;
+    }
+
+    mouse_event =
+        qemu_put_mouse_event_current->qemu_put_mouse_event;
+    mouse_event_opaque =
+        qemu_put_mouse_event_current->qemu_put_mouse_event_opaque;
+
+    if (mouse_event) {
+        mouse_event(mouse_event_opaque, dx, dy, dz, buttons_state);
     }
 }
 
-/***********************************************************/
-/* timers */
+int kbd_mouse_is_absolute(void)
+{
+    if (!qemu_put_mouse_event_current)
+        return 0;
 
-#if defined(__powerpc__)
+    return qemu_put_mouse_event_current->qemu_put_mouse_event_absolute;
+}
 
-static inline uint32_t get_tbl(void) 
+void do_info_mice(void)
 {
-    uint32_t tbl;
-    asm volatile("mftb %0" : "=r" (tbl));
-    return tbl;
+    QEMUPutMouseEntry *cursor;
+    int index = 0;
+
+    if (!qemu_put_mouse_event_head) {
+        term_printf("No mouse devices connected\n");
+        return;
+    }
+
+    term_printf("Mouse devices available:\n");
+    cursor = qemu_put_mouse_event_head;
+    while (cursor != NULL) {
+        term_printf("%c Mouse #%d: %s\n",
+                    (cursor == qemu_put_mouse_event_current ? '*' : ' '),
+                    index, cursor->qemu_put_mouse_event_name);
+        index++;
+        cursor = cursor->next;
+    }
 }
 
-static inline uint32_t get_tbu(void) 
+void do_mouse_set(int index)
 {
-       uint32_t tbl;
-       asm volatile("mftbu %0" : "=r" (tbl));
-       return tbl;
+    QEMUPutMouseEntry *cursor;
+    int i = 0;
+
+    if (!qemu_put_mouse_event_head) {
+        term_printf("No mouse devices connected\n");
+        return;
+    }
+
+    cursor = qemu_put_mouse_event_head;
+    while (cursor != NULL && index != i) {
+        i++;
+        cursor = cursor->next;
+    }
+
+    if (cursor != NULL)
+        qemu_put_mouse_event_current = cursor;
+    else
+        term_printf("Mouse at given index not found\n");
 }
 
-int64_t cpu_get_real_ticks(void)
+/* compute with 96 bit intermediate result: (a*b)/c */
+uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
 {
-    uint32_t l, h, h1;
-    /* NOTE: we test if wrapping has occurred */
-    do {
-        h = get_tbu();
-        l = get_tbl();
-        h1 = get_tbu();
-    } while (h != h1);
-    return ((int64_t)h << 32) | l;
+    union {
+        uint64_t ll;
+        struct {
+#ifdef WORDS_BIGENDIAN
+            uint32_t high, low;
+#else
+            uint32_t low, high;
+#endif            
+        } l;
+    } u, res;
+    uint64_t rl, rh;
+
+    u.ll = a;
+    rl = (uint64_t)u.l.low * (uint64_t)b;
+    rh = (uint64_t)u.l.high * (uint64_t)b;
+    rh += (rl >> 32);
+    res.l.high = rh / c;
+    res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
+    return res.ll;
 }
 
-#elif defined(__i386__)
+/***********************************************************/
+/* real time host monotonic timer */
+
+#define QEMU_TIMER_BASE 1000000000LL
 
-int64_t cpu_get_real_ticks(void)
+#ifdef WIN32
+
+static int64_t clock_freq;
+
+static void init_get_clock(void)
 {
-    int64_t val;
-    asm volatile ("rdtsc" : "=A" (val));
-    return val;
+    LARGE_INTEGER freq;
+    int ret;
+    ret = QueryPerformanceFrequency(&freq);
+    if (ret == 0) {
+        fprintf(stderr, "Could not calibrate ticks\n");
+        exit(1);
+    }
+    clock_freq = freq.QuadPart;
 }
 
-#elif defined(__x86_64__)
-
-int64_t cpu_get_real_ticks(void)
+static int64_t get_clock(void)
 {
-    uint32_t low,high;
-    int64_t val;
-    asm volatile("rdtsc" : "=a" (low), "=d" (high));
-    val = high;
-    val <<= 32;
-    val |= low;
-    return val;
+    LARGE_INTEGER ti;
+    QueryPerformanceCounter(&ti);
+    return muldiv64(ti.QuadPart, QEMU_TIMER_BASE, clock_freq);
 }
 
-#elif defined(__ia64)
+#else
+
+static int use_rt_clock;
 
-int64_t cpu_get_real_ticks(void)
+static void init_get_clock(void)
 {
-       int64_t val;
-       asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory");
-       return val;
+    use_rt_clock = 0;
+#if defined(__linux__)
+    {
+        struct timespec ts;
+        if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+            use_rt_clock = 1;
+        }
+    }
+#endif
 }
 
-#elif defined(__s390__)
-
-int64_t cpu_get_real_ticks(void)
+static int64_t get_clock(void)
 {
-    int64_t val;
-    asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc");
-    return val;
+#if defined(__linux__)
+    if (use_rt_clock) {
+        struct timespec ts;
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+        return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+    } else 
+#endif
+    {
+        /* XXX: using gettimeofday leads to problems if the date
+           changes, so it should be avoided. */
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
+    }
 }
 
-#else
-#error unsupported CPU
 #endif
 
+/***********************************************************/
+/* guest cycle counter */
+
+static int64_t cpu_ticks_prev;
 static int64_t cpu_ticks_offset;
+static int64_t cpu_clock_offset;
 static int cpu_ticks_enabled;
 
-static inline int64_t cpu_get_ticks(void)
+/* return the host CPU cycle counter and handle stop/restart */
+int64_t cpu_get_ticks(void)
 {
     if (!cpu_ticks_enabled) {
         return cpu_ticks_offset;
     } else {
-        return cpu_get_real_ticks() + cpu_ticks_offset;
+        int64_t ticks;
+        ticks = cpu_get_real_ticks();
+        if (cpu_ticks_prev > ticks) {
+            /* Note: non increasing ticks may happen if the host uses
+               software suspend */
+            cpu_ticks_offset += cpu_ticks_prev - ticks;
+        }
+        cpu_ticks_prev = ticks;
+        return ticks + cpu_ticks_offset;
+    }
+}
+
+/* return the host CPU monotonic timer and handle stop/restart */
+static int64_t cpu_get_clock(void)
+{
+    int64_t ti;
+    if (!cpu_ticks_enabled) {
+        return cpu_clock_offset;
+    } else {
+        ti = get_clock();
+        return ti + cpu_clock_offset;
     }
 }
 
@@ -571,6 +701,7 @@ void cpu_enable_ticks(void)
 {
     if (!cpu_ticks_enabled) {
         cpu_ticks_offset -= cpu_get_real_ticks();
+        cpu_clock_offset -= get_clock();
         cpu_ticks_enabled = 1;
     }
 }
@@ -581,63 +712,14 @@ void cpu_disable_ticks(void)
 {
     if (cpu_ticks_enabled) {
         cpu_ticks_offset = cpu_get_ticks();
+        cpu_clock_offset = cpu_get_clock();
         cpu_ticks_enabled = 0;
     }
 }
 
-static int64_t get_clock(void)
-{
-#ifdef _WIN32
-    struct _timeb tb;
-    _ftime(&tb);
-    return ((int64_t)tb.time * 1000 + (int64_t)tb.millitm) * 1000;
-#else
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    return tv.tv_sec * 1000000LL + tv.tv_usec;
-#endif
-}
-
-void cpu_calibrate_ticks(void)
-{
-    int64_t usec, ticks;
-
-    usec = get_clock();
-    ticks = cpu_get_real_ticks();
-#ifdef _WIN32
-    Sleep(50);
-#else
-    usleep(50 * 1000);
-#endif
-    usec = get_clock() - usec;
-    ticks = cpu_get_real_ticks() - ticks;
-    ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;
-}
-
-/* compute with 96 bit intermediate result: (a*b)/c */
-uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
-{
-    union {
-        uint64_t ll;
-        struct {
-#ifdef WORDS_BIGENDIAN
-            uint32_t high, low;
-#else
-            uint32_t low, high;
-#endif            
-        } l;
-    } u, res;
-    uint64_t rl, rh;
-
-    u.ll = a;
-    rl = (uint64_t)u.l.low * (uint64_t)b;
-    rh = (uint64_t)u.l.high * (uint64_t)b;
-    rh += (rl >> 32);
-    res.l.high = rh / c;
-    res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
-    return res.ll;
-}
-
+/***********************************************************/
+/* timers */
 #define QEMU_TIMER_REALTIME 0
 #define QEMU_TIMER_VIRTUAL  1
 
@@ -660,6 +742,8 @@ QEMUClock *vm_clock;
 static QEMUTimer *active_timers[2];
 #ifdef _WIN32
 static MMRESULT timerID;
+static HANDLE host_alarm = NULL;
+static unsigned int period = 1;
 #else
 /* frequency of the times() clock tick */
 static int timer_freq;
@@ -774,28 +858,21 @@ int64_t qemu_get_clock(QEMUClock *clock)
 {
     switch(clock->type) {
     case QEMU_TIMER_REALTIME:
-#ifdef _WIN32
-        return GetTickCount();
-#else
-        {
-            struct tms tp;
-
-            /* Note that using gettimeofday() is not a good solution
-               for timers because its value change when the date is
-               modified. */
-            if (timer_freq == 100) {
-                return times(&tp) * 10;
-            } else {
-                return ((int64_t)times(&tp) * 1000) / timer_freq;
-            }
-        }
-#endif
+        return get_clock() / 1000000;
     default:
     case QEMU_TIMER_VIRTUAL:
-        return cpu_get_ticks();
+        return cpu_get_clock();
     }
 }
 
+static void init_timers(void)
+{
+    init_get_clock();
+    ticks_per_sec = QEMU_TIMER_BASE;
+    rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME);
+    vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL);
+}
+
 /* save a timer */
 void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
 {
@@ -828,17 +905,21 @@ static void timer_save(QEMUFile *f, void *opaque)
     }
     qemu_put_be64s(f, &cpu_ticks_offset);
     qemu_put_be64s(f, &ticks_per_sec);
+    qemu_put_be64s(f, &cpu_clock_offset);
 }
 
 static int timer_load(QEMUFile *f, void *opaque, int version_id)
 {
-    if (version_id != 1)
+    if (version_id != 1 && version_id != 2)
         return -EINVAL;
     if (cpu_ticks_enabled) {
         return -EINVAL;
     }
     qemu_get_be64s(f, &cpu_ticks_offset);
     qemu_get_be64s(f, &ticks_per_sec);
+    if (version_id == 2) {
+        qemu_get_be64s(f, &cpu_clock_offset);
+    }
     return 0;
 }
 
@@ -864,7 +945,7 @@ static void host_alarm_handler(int host_signum)
                 delta_max = delta;
             delta_cum += delta;
             if (++count == DISP_FREQ) {
-                printf("timer: min=%lld us max=%lld us avg=%lld us avg_freq=%0.3f Hz\n",
+                printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n",
                        muldiv64(delta_min, 1000000, ticks_per_sec),
                        muldiv64(delta_max, 1000000, ticks_per_sec),
                        muldiv64(delta_cum, 1000000 / DISP_FREQ, ticks_per_sec),
@@ -882,6 +963,9 @@ static void host_alarm_handler(int host_signum)
                            qemu_get_clock(vm_clock)) ||
         qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
                            qemu_get_clock(rt_clock))) {
+#ifdef _WIN32
+        SetEvent(host_alarm);
+#endif
         CPUState *env = cpu_single_env;
         if (env) {
             /* stop the currently executing cpu because a timer occured */
@@ -934,16 +1018,20 @@ static int start_rtc_timer(void)
 
 #endif /* !defined(_WIN32) */
 
-static void init_timers(void)
+static void init_timer_alarm(void)
 {
-    rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME);
-    vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL);
-
 #ifdef _WIN32
     {
         int count=0;
+        TIMECAPS tc;
+
+        ZeroMemory(&tc, sizeof(TIMECAPS));
+        timeGetDevCaps(&tc, sizeof(TIMECAPS));
+        if (period < tc.wPeriodMin)
+            period = tc.wPeriodMin;
+        timeBeginPeriod(period);
         timerID = timeSetEvent(1,     // interval (ms)
-                               0,     // resolution
+                               period,     // resolution
                                host_alarm_handler, // function
                                (DWORD)&count,  // user parameter
                                TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
@@ -951,6 +1039,12 @@ static void init_timers(void)
             perror("failed timer alarm");
             exit(1);
        }
+        host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (!host_alarm) {
+            perror("failed CreateEvent");
+            exit(1);
+        }
+        qemu_add_wait_object(host_alarm, NULL, NULL);
     }
     pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000;
 #else
@@ -980,7 +1074,11 @@ static void init_timers(void)
         getitimer(ITIMER_REAL, &itv);
 
 #if defined(__linux__)
-        if (itv.it_interval.tv_usec > 1000) {
+        /* XXX: force /dev/rtc usage because even 2.6 kernels may not
+           have timers with 1 ms resolution. The correct solution will
+           be to use the POSIX real time timers available in recent
+           2.6 kernels */
+        if (itv.it_interval.tv_usec > 1000 || 1) {
             /* try to use /dev/rtc to have a faster timer */
             if (start_rtc_timer() < 0)
                 goto use_itimer;
@@ -1010,12 +1108,40 @@ void quit_timers(void)
 {
 #ifdef _WIN32
     timeKillEvent(timerID);
+    timeEndPeriod(period);
+    if (host_alarm) {
+        CloseHandle(host_alarm);
+        host_alarm = NULL;
+    }
 #endif
 }
 
 /***********************************************************/
 /* character device */
 
+static void qemu_chr_event(CharDriverState *s, int event)
+{
+    if (!s->chr_event)
+        return;
+    s->chr_event(s->handler_opaque, event);
+}
+
+static void qemu_chr_reset_bh(void *opaque)
+{
+    CharDriverState *s = opaque;
+    qemu_chr_event(s, CHR_EVENT_RESET);
+    qemu_bh_delete(s->bh);
+    s->bh = NULL;
+}
+
+void qemu_chr_reset(CharDriverState *s)
+{
+    if (s->bh == NULL) {
+       s->bh = qemu_bh_new(qemu_chr_reset_bh, s);
+       qemu_bh_schedule(s->bh);
+    }
+}
+
 int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len)
 {
     return s->chr_write(s, buf, len);
@@ -1028,6 +1154,19 @@ int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg)
     return s->chr_ioctl(s, cmd, arg);
 }
 
+int qemu_chr_can_read(CharDriverState *s)
+{
+    if (!s->chr_can_read)
+        return 0;
+    return s->chr_can_read(s->handler_opaque);
+}
+
+void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len)
+{
+    s->chr_read(s->handler_opaque, buf, len);
+}
+
+
 void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
 {
     char buf[4096];
@@ -1044,30 +1183,26 @@ void qemu_chr_send_event(CharDriverState *s, int event)
         s->chr_send_event(s, event);
 }
 
-void qemu_chr_add_read_handler(CharDriverState *s, 
-                               IOCanRWHandler *fd_can_read, 
-                               IOReadHandler *fd_read, void *opaque)
+void qemu_chr_add_handlers(CharDriverState *s, 
+                           IOCanRWHandler *fd_can_read, 
+                           IOReadHandler *fd_read,
+                           IOEventHandler *fd_event,
+                           void *opaque)
 {
-    s->chr_add_read_handler(s, fd_can_read, fd_read, opaque);
+    s->chr_can_read = fd_can_read;
+    s->chr_read = fd_read;
+    s->chr_event = fd_event;
+    s->handler_opaque = opaque;
+    if (s->chr_update_read_handler)
+        s->chr_update_read_handler(s);
 }
              
-void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event)
-{
-    s->chr_event = chr_event;
-}
-
 static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     return len;
 }
 
-static void null_chr_add_read_handler(CharDriverState *chr, 
-                                    IOCanRWHandler *fd_can_read, 
-                                    IOReadHandler *fd_read, void *opaque)
-{
-}
-
-CharDriverState *qemu_chr_open_null(void)
+static CharDriverState *qemu_chr_open_null(void)
 {
     CharDriverState *chr;
 
@@ -1075,18 +1210,11 @@ CharDriverState *qemu_chr_open_null(void)
     if (!chr)
         return NULL;
     chr->chr_write = null_chr_write;
-    chr->chr_add_read_handler = null_chr_add_read_handler;
     return chr;
 }
 
 #ifdef _WIN32
 
-#define socket_error() WSAGetLastError()
-#undef EINTR
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#define EINTR       WSAEINTR
-#define EINPROGRESS WSAEINPROGRESS
-
 static void socket_cleanup(void)
 {
     WSACleanup();
@@ -1138,9 +1266,6 @@ void socket_set_nonblock(int fd)
 
 #else
 
-#define socket_error() errno
-#define closesocket(s) close(s)
-
 static int unix_write(int fd, const uint8_t *buf, int len1)
 {
     int ret, len;
@@ -1176,9 +1301,6 @@ void socket_set_nonblock(int fd)
 
 typedef struct {
     int fd_in, fd_out;
-    IOCanRWHandler *fd_can_read; 
-    IOReadHandler *fd_read;
-    void *fd_opaque;
     int max_size;
 } FDCharDriver;
 
@@ -1198,7 +1320,7 @@ static int fd_chr_read_poll(void *opaque)
     CharDriverState *chr = opaque;
     FDCharDriver *s = chr->opaque;
 
-    s->max_size = s->fd_can_read(s->fd_opaque);
+    s->max_size = qemu_chr_can_read(chr);
     return s->max_size;
 }
 
@@ -1215,21 +1337,21 @@ static void fd_chr_read(void *opaque)
     if (len == 0)
         return;
     size = read(s->fd_in, buf, len);
+    if (size == 0) {
+        /* FD has been closed. Remove it from the active list.  */
+        qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+        return;
+    }
     if (size > 0) {
-        s->fd_read(s->fd_opaque, buf, size);
+        qemu_chr_read(chr, buf, size);
     }
 }
 
-static void fd_chr_add_read_handler(CharDriverState *chr, 
-                                    IOCanRWHandler *fd_can_read, 
-                                    IOReadHandler *fd_read, void *opaque)
+static void fd_chr_update_read_handler(CharDriverState *chr)
 {
     FDCharDriver *s = chr->opaque;
 
     if (s->fd_in >= 0) {
-        s->fd_can_read = fd_can_read;
-        s->fd_read = fd_read;
-        s->fd_opaque = opaque;
         if (nographic && s->fd_in == 0) {
         } else {
             qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, 
@@ -1239,7 +1361,7 @@ static void fd_chr_add_read_handler(CharDriverState *chr,
 }
 
 /* open a character device to a unix fd */
-CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
+static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
 {
     CharDriverState *chr;
     FDCharDriver *s;
@@ -1256,28 +1378,42 @@ CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
     s->fd_out = fd_out;
     chr->opaque = s;
     chr->chr_write = fd_chr_write;
-    chr->chr_add_read_handler = fd_chr_add_read_handler;
+    chr->chr_update_read_handler = fd_chr_update_read_handler;
+
+    qemu_chr_reset(chr);
+
     return chr;
 }
 
-CharDriverState *qemu_chr_open_file_out(const char *file_out)
+static CharDriverState *qemu_chr_open_file_out(const char *file_out)
 {
     int fd_out;
 
-    fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY);
+    fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666);
     if (fd_out < 0)
         return NULL;
     return qemu_chr_open_fd(-1, fd_out);
 }
 
-CharDriverState *qemu_chr_open_pipe(const char *filename)
+static CharDriverState *qemu_chr_open_pipe(const char *filename)
 {
-    int fd;
-
-    fd = open(filename, O_RDWR | O_BINARY);
-    if (fd < 0)
-        return NULL;
-    return qemu_chr_open_fd(fd, fd);
+    int fd_in, fd_out;
+    char filename_in[256], filename_out[256];
+
+    snprintf(filename_in, 256, "%s.in", filename);
+    snprintf(filename_out, 256, "%s.out", filename);
+    fd_in = open(filename_in, O_RDWR | O_BINARY);
+    fd_out = open(filename_out, O_RDWR | O_BINARY);
+    if (fd_in < 0 || fd_out < 0) {
+       if (fd_in >= 0)
+           close(fd_in);
+       if (fd_out >= 0)
+           close(fd_out);
+        fd_in = fd_out = open(filename, O_RDWR | O_BINARY);
+        if (fd_in < 0)
+            return NULL;
+    }
+    return qemu_chr_open_fd(fd_in, fd_out);
 }
 
 
@@ -1290,7 +1426,9 @@ CharDriverState *qemu_chr_open_pipe(const char *filename)
 
 static int term_got_escape, client_index;
 static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
-int term_fifo_size;
+static int term_fifo_size;
+static int term_timestamps;
+static int64_t term_timestamps_start;
 
 void term_print_help(void)
 {
@@ -1299,6 +1437,7 @@ void term_print_help(void)
            "C-a x    exit emulator\n"
            "C-a s    save disk data back to file (if -snapshot)\n"
            "C-a b    send break (magic sysrq)\n"
+           "C-a t    toggle console timestamps\n"
            "C-a c    switch between console and monitor\n"
            "C-a C-a  send C-a\n"
            );
@@ -1332,7 +1471,7 @@ static void stdio_received_byte(int ch)
 
                 chr = stdio_clients[client_index];
                 s = chr->opaque;
-                chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK);
+                qemu_chr_event(chr, CHR_EVENT_BREAK);
             }
             break;
         case 'c':
@@ -1345,6 +1484,10 @@ static void stdio_received_byte(int ch)
                 goto send_char;
             }
             break;
+        case 't':
+            term_timestamps = !term_timestamps;
+            term_timestamps_start = -1;
+            break;
         case TERM_ESCAPE:
             goto send_char;
         }
@@ -1355,13 +1498,11 @@ static void stdio_received_byte(int ch)
         if (client_index < stdio_nb_clients) {
             uint8_t buf[1];
             CharDriverState *chr;
-            FDCharDriver *s;
             
             chr = stdio_clients[client_index];
-            s = chr->opaque;
-            if (s->fd_can_read(s->fd_opaque) > 0) {
+            if (qemu_chr_can_read(chr) > 0) {
                 buf[0] = ch;
-                s->fd_read(s->fd_opaque, buf, 1);
+                qemu_chr_read(chr, buf, 1);
             } else if (term_fifo_size == 0) {
                 term_fifo[term_fifo_size++] = ch;
             }
@@ -1372,14 +1513,12 @@ static void stdio_received_byte(int ch)
 static int stdio_read_poll(void *opaque)
 {
     CharDriverState *chr;
-    FDCharDriver *s;
 
     if (client_index < stdio_nb_clients) {
         chr = stdio_clients[client_index];
-        s = chr->opaque;
         /* try to flush the queue if needed */
-        if (term_fifo_size != 0 && s->fd_can_read(s->fd_opaque) > 0) {
-            s->fd_read(s->fd_opaque, term_fifo, 1);
+        if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
+            qemu_chr_read(chr, term_fifo, 1);
             term_fifo_size = 0;
         }
         /* see if we can absorb more chars */
@@ -1398,10 +1537,48 @@ static void stdio_read(void *opaque)
     uint8_t buf[1];
     
     size = read(0, buf, 1);
+    if (size == 0) {
+        /* stdin has been closed. Remove it from the active list.  */
+        qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+        return;
+    }
     if (size > 0)
         stdio_received_byte(buf[0]);
 }
 
+static int stdio_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    FDCharDriver *s = chr->opaque;
+    if (!term_timestamps) {
+        return unix_write(s->fd_out, buf, len);
+    } else {
+        int i;
+        char buf1[64];
+
+        for(i = 0; i < len; i++) {
+            unix_write(s->fd_out, buf + i, 1);
+            if (buf[i] == '\n') {
+                int64_t ti;
+                int secs;
+
+                ti = get_clock();
+                if (term_timestamps_start == -1)
+                    term_timestamps_start = ti;
+                ti -= term_timestamps_start;
+                secs = ti / 1000000000;
+                snprintf(buf1, sizeof(buf1), 
+                         "[%02d:%02d:%02d.%03d] ",
+                         secs / 3600,
+                         (secs / 60) % 60,
+                         secs % 60,
+                         (int)((ti / 1000000) % 1000));
+                unix_write(s->fd_out, buf1, strlen(buf1));
+            }
+        }
+        return len;
+    }
+}
+
 /* init terminal so that we can grab keys */
 static struct termios oldtty;
 static int old_fd0_flags;
@@ -1439,7 +1616,7 @@ static void term_init(void)
     fcntl(0, F_SETFL, O_NONBLOCK);
 }
 
-CharDriverState *qemu_chr_open_stdio(void)
+static CharDriverState *qemu_chr_open_stdio(void)
 {
     CharDriverState *chr;
 
@@ -1447,6 +1624,7 @@ CharDriverState *qemu_chr_open_stdio(void)
         if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
             return NULL;
         chr = qemu_chr_open_fd(0, 1);
+        chr->chr_write = stdio_write;
         if (stdio_nb_clients == 0)
             qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL);
         client_index = stdio_nb_clients;
@@ -1464,7 +1642,7 @@ CharDriverState *qemu_chr_open_stdio(void)
 }
 
 #if defined(__linux__)
-CharDriverState *qemu_chr_open_pty(void)
+static CharDriverState *qemu_chr_open_pty(void)
 {
     struct termios tty;
     char slave_name[1024];
@@ -1545,7 +1723,7 @@ static void tty_serial_init(int fd, int speed,
                           |INLCR|IGNCR|ICRNL|IXON);
     tty.c_oflag |= OPOST;
     tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
-    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS);
+    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
     switch(data_bits) {
     default:
     case 8:
@@ -1572,6 +1750,8 @@ static void tty_serial_init(int fd, int speed,
         tty.c_cflag |= PARENB | PARODD;
         break;
     }
+    if (stop_bits == 2)
+        tty.c_cflag |= CSTOPB;
     
     tcsetattr (fd, TCSANOW, &tty);
 }
@@ -1601,7 +1781,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-CharDriverState *qemu_chr_open_tty(const char *filename)
+static CharDriverState *qemu_chr_open_tty(const char *filename)
 {
     CharDriverState *chr;
     int fd;
@@ -1615,6 +1795,7 @@ CharDriverState *qemu_chr_open_tty(const char *filename)
     if (!chr)
         return NULL;
     chr->chr_ioctl = tty_serial_ioctl;
+    qemu_chr_reset(chr);
     return chr;
 }
 
@@ -1655,7 +1836,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
     return 0;
 }
 
-CharDriverState *qemu_chr_open_pp(const char *filename)
+static CharDriverState *qemu_chr_open_pp(const char *filename)
 {
     CharDriverState *chr;
     int fd;
@@ -1676,13 +1857,15 @@ CharDriverState *qemu_chr_open_pp(const char *filename)
     }
     chr->opaque = (void *)fd;
     chr->chr_write = null_chr_write;
-    chr->chr_add_read_handler = null_chr_add_read_handler;
     chr->chr_ioctl = pp_ioctl;
+
+    qemu_chr_reset(chr);
+
     return chr;
 }
 
 #else
-CharDriverState *qemu_chr_open_pty(void)
+static CharDriverState *qemu_chr_open_pty(void)
 {
     return NULL;
 }
@@ -1690,202 +1873,1130 @@ CharDriverState *qemu_chr_open_pty(void)
 
 #endif /* !defined(_WIN32) */
 
-CharDriverState *qemu_chr_open(const char *filename)
-{
-#ifndef _WIN32
-    const char *p;
-#endif
+#ifdef _WIN32
+typedef struct {
+    int max_size;
+    HANDLE hcom, hrecv, hsend;
+    OVERLAPPED orecv, osend;
+    BOOL fpipe;
+    DWORD len;
+} WinCharState;
 
-    if (!strcmp(filename, "vc")) {
-        return text_console_init(&display_state);
-    } else if (!strcmp(filename, "null")) {
-        return qemu_chr_open_null();
-    } else 
-#ifndef _WIN32
-    if (strstart(filename, "file:", &p)) {
-        return qemu_chr_open_file_out(p);
-    } else if (strstart(filename, "pipe:", &p)) {
-        return qemu_chr_open_pipe(p);
-    } else if (!strcmp(filename, "pty")) {
-        return qemu_chr_open_pty();
-    } else if (!strcmp(filename, "stdio")) {
-        return qemu_chr_open_stdio();
-    } else 
-#endif
-#if defined(__linux__)
-    if (strstart(filename, "/dev/parport", NULL)) {
-        return qemu_chr_open_pp(filename);
-    } else 
-    if (strstart(filename, "/dev/", NULL)) {
-        return qemu_chr_open_tty(filename);
-    } else 
-#endif
-    {
-        return NULL;
+#define NSENDBUF 2048
+#define NRECVBUF 2048
+#define MAXCONNECT 1
+#define NTIMEOUT 5000
+
+static int win_chr_poll(void *opaque);
+static int win_chr_pipe_poll(void *opaque);
+
+static void win_chr_close2(WinCharState *s)
+{
+    if (s->hsend) {
+        CloseHandle(s->hsend);
+        s->hsend = NULL;
+    }
+    if (s->hrecv) {
+        CloseHandle(s->hrecv);
+        s->hrecv = NULL;
     }
+    if (s->hcom) {
+        CloseHandle(s->hcom);
+        s->hcom = NULL;
+    }
+    if (s->fpipe)
+        qemu_del_polling_cb(win_chr_pipe_poll, s);
+    else
+        qemu_del_polling_cb(win_chr_poll, s);
 }
 
-/***********************************************************/
-/* network device redirectors */
+static void win_chr_close(CharDriverState *chr)
+{
+    WinCharState *s = chr->opaque;
+    win_chr_close2(s);
+}
 
-void hex_dump(FILE *f, const uint8_t *buf, int size)
+static int win_chr_init(WinCharState *s, const char *filename)
 {
-    int len, i, j, c;
+    COMMCONFIG comcfg;
+    COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
+    COMSTAT comstat;
+    DWORD size;
+    DWORD err;
+    
+    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hsend) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
+    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hrecv) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
 
-    for(i=0;i<size;i+=16) {
-        len = size - i;
-        if (len > 16)
-            len = 16;
-        fprintf(f, "%08x ", i);
-        for(j=0;j<16;j++) {
-            if (j < len)
-                fprintf(f, " %02x", buf[i+j]);
-            else
-                fprintf(f, "   ");
-        }
-        fprintf(f, " ");
-        for(j=0;j<len;j++) {
-            c = buf[i+j];
-            if (c < ' ' || c > '~')
-                c = '.';
-            fprintf(f, "%c", c);
-        }
-        fprintf(f, "\n");
+    s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+    if (s->hcom == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
+        s->hcom = NULL;
+        goto fail;
+    }
+    
+    if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
+        fprintf(stderr, "Failed SetupComm\n");
+        goto fail;
+    }
+    
+    ZeroMemory(&comcfg, sizeof(COMMCONFIG));
+    size = sizeof(COMMCONFIG);
+    GetDefaultCommConfig(filename, &comcfg, &size);
+    comcfg.dcb.DCBlength = sizeof(DCB);
+    CommConfigDialog(filename, NULL, &comcfg);
+
+    if (!SetCommState(s->hcom, &comcfg.dcb)) {
+        fprintf(stderr, "Failed SetCommState\n");
+        goto fail;
+    }
+
+    if (!SetCommMask(s->hcom, EV_ERR)) {
+        fprintf(stderr, "Failed SetCommMask\n");
+        goto fail;
+    }
+
+    cto.ReadIntervalTimeout = MAXDWORD;
+    if (!SetCommTimeouts(s->hcom, &cto)) {
+        fprintf(stderr, "Failed SetCommTimeouts\n");
+        goto fail;
+    }
+    
+    if (!ClearCommError(s->hcom, &err, &comstat)) {
+        fprintf(stderr, "Failed ClearCommError\n");
+        goto fail;
     }
+    qemu_add_polling_cb(win_chr_poll, s);
+    return 0;
+
+ fail:
+    win_chr_close2(s);
+    return -1;
 }
 
-static int parse_macaddr(uint8_t *macaddr, const char *p)
+static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
 {
-    int i;
-    for(i = 0; i < 6; i++) {
-        macaddr[i] = strtol(p, (char **)&p, 16);
-        if (i == 5) {
-            if (*p != '\0') 
-                return -1;
+    WinCharState *s = chr->opaque;
+    DWORD len, ret, size, err;
+
+    len = len1;
+    ZeroMemory(&s->osend, sizeof(s->osend));
+    s->osend.hEvent = s->hsend;
+    while (len > 0) {
+        if (s->hsend)
+            ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
+        else
+            ret = WriteFile(s->hcom, buf, len, &size, NULL);
+        if (!ret) {
+            err = GetLastError();
+            if (err == ERROR_IO_PENDING) {
+                ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
+                if (ret) {
+                    buf += size;
+                    len -= size;
+                } else {
+                    break;
+                }
+            } else {
+                break;
+            }
         } else {
-            if (*p != ':') 
-                return -1;
-            p++;
+            buf += size;
+            len -= size;
         }
     }
-    return 0;
+    return len1 - len;
 }
 
-static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+static int win_chr_read_poll(WinCharState *s)
 {
-    const char *p, *p1;
-    int len;
-    p = *pp;
-    p1 = strchr(p, sep);
-    if (!p1)
-        return -1;
-    len = p1 - p;
-    p1++;
-    if (buf_size > 0) {
-        if (len > buf_size - 1)
-            len = buf_size - 1;
-        memcpy(buf, p, len);
-        buf[len] = '\0';
-    }
-    *pp = p1;
-    return 0;
+    s->max_size = qemu_chr_can_read(s->chr);
+    return s->max_size;
 }
 
-int parse_host_port(struct sockaddr_in *saddr, const char *str)
+static void win_chr_readfile(WinCharState *s)
 {
-    char buf[512];
-    struct hostent *he;
-    const char *p, *r;
-    int port;
-
-    p = str;
-    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
-        return -1;
-    saddr->sin_family = AF_INET;
-    if (buf[0] == '\0') {
-        saddr->sin_addr.s_addr = 0;
-    } else {
-        if (isdigit(buf[0])) {
-            if (!inet_aton(buf, &saddr->sin_addr))
-                return -1;
-        } else {
-            if ((he = gethostbyname(buf)) == NULL)
-                return - 1;
-            saddr->sin_addr = *(struct in_addr *)he->h_addr;
+    int ret, err;
+    uint8_t buf[1024];
+    DWORD size;
+    
+    ZeroMemory(&s->orecv, sizeof(s->orecv));
+    s->orecv.hEvent = s->hrecv;
+    ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
+    if (!ret) {
+        err = GetLastError();
+        if (err == ERROR_IO_PENDING) {
+            ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
         }
     }
-    port = strtol(p, (char **)&r, 0);
-    if (r == p)
-        return -1;
-    saddr->sin_port = htons(port);
-    return 0;
+
+    if (size > 0) {
+        qemu_chr_read(s->chr, buf, size);
+    }
 }
 
-/* find or alloc a new VLAN */
-VLANState *qemu_find_vlan(int id)
+static void win_chr_read(WinCharState *s)
 {
-    VLANState **pvlan, *vlan;
-    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
-        if (vlan->id == id)
-            return vlan;
+    if (s->len > s->max_size)
+        s->len = s->max_size;
+    if (s->len == 0)
+        return;
+    
+    win_chr_readfile(s);
+}
+
+static int win_chr_poll(void *opaque)
+{
+    WinCharState *s = opaque;
+    COMSTAT status;
+    DWORD comerr;
+    
+    ClearCommError(s->hcom, &comerr, &status);
+    if (status.cbInQue > 0) {
+        s->len = status.cbInQue;
+        win_chr_read_poll(s);
+        win_chr_read(s);
+        return 1;
     }
-    vlan = qemu_mallocz(sizeof(VLANState));
-    if (!vlan)
-        return NULL;
-    vlan->id = id;
-    vlan->next = NULL;
-    pvlan = &first_vlan;
-    while (*pvlan != NULL)
-        pvlan = &(*pvlan)->next;
-    *pvlan = vlan;
-    return vlan;
+    return 0;
 }
 
-VLANClientState *qemu_new_vlan_client(VLANState *vlan,
-                                      IOReadHandler *fd_read, void *opaque)
+static CharDriverState *qemu_chr_open_win(const char *filename)
 {
-    VLANClientState *vc, **pvc;
-    vc = qemu_mallocz(sizeof(VLANClientState));
-    if (!vc)
+    CharDriverState *chr;
+    WinCharState *s;
+    
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
         return NULL;
-    vc->fd_read = fd_read;
-    vc->opaque = opaque;
-    vc->vlan = vlan;
+    s = qemu_mallocz(sizeof(WinCharState));
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    chr->opaque = s;
+    chr->chr_write = win_chr_write;
+    chr->chr_close = win_chr_close;
 
-    vc->next = NULL;
-    pvc = &vlan->first_client;
-    while (*pvc != NULL)
-        pvc = &(*pvc)->next;
-    *pvc = vc;
-    return vc;
+    if (win_chr_init(s, filename) < 0) {
+        free(s);
+        free(chr);
+        return NULL;
+    }
+    qemu_chr_reset(chr);
+    return chr;
 }
 
-void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
+static int win_chr_pipe_poll(void *opaque)
 {
-    VLANState *vlan = vc1->vlan;
-    VLANClientState *vc;
+    WinCharState *s = opaque;
+    DWORD size;
 
-#if 0
-    printf("vlan %d send:\n", vlan->id);
-    hex_dump(stdout, buf, size);
-#endif
-    for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
-        if (vc != vc1) {
-            vc->fd_read(vc->opaque, buf, size);
-        }
+    PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
+    if (size > 0) {
+        s->len = size;
+        win_chr_read_poll(s);
+        win_chr_read(s);
+        return 1;
     }
+    return 0;
 }
 
-#if defined(CONFIG_SLIRP)
-
-/* slirp network adapter */
+static int win_chr_pipe_init(WinCharState *s, const char *filename)
+{
+    OVERLAPPED ov;
+    int ret;
+    DWORD size;
+    char openname[256];
+    
+    s->fpipe = TRUE;
+
+    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hsend) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
+    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hrecv) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
+    
+    snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
+    s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                              PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+                              PIPE_WAIT,
+                              MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
+    if (s->hcom == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
+        s->hcom = NULL;
+        goto fail;
+    }
+
+    ZeroMemory(&ov, sizeof(ov));
+    ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ret = ConnectNamedPipe(s->hcom, &ov);
+    if (ret) {
+        fprintf(stderr, "Failed ConnectNamedPipe\n");
+        goto fail;
+    }
+
+    ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
+    if (!ret) {
+        fprintf(stderr, "Failed GetOverlappedResult\n");
+        if (ov.hEvent) {
+            CloseHandle(ov.hEvent);
+            ov.hEvent = NULL;
+        }
+        goto fail;
+    }
+
+    if (ov.hEvent) {
+        CloseHandle(ov.hEvent);
+        ov.hEvent = NULL;
+    }
+    qemu_add_polling_cb(win_chr_pipe_poll, s);
+    return 0;
+
+ fail:
+    win_chr_close2(s);
+    return -1;
+}
+
+
+static CharDriverState *qemu_chr_open_win_pipe(const char *filename)
+{
+    CharDriverState *chr;
+    WinCharState *s;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = qemu_mallocz(sizeof(WinCharState));
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    chr->opaque = s;
+    chr->chr_write = win_chr_write;
+    chr->chr_close = win_chr_close;
+    
+    if (win_chr_pipe_init(s, filename) < 0) {
+        free(s);
+        free(chr);
+        return NULL;
+    }
+    qemu_chr_reset(chr);
+    return chr;
+}
+
+static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
+{
+    CharDriverState *chr;
+    WinCharState *s;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = qemu_mallocz(sizeof(WinCharState));
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    s->hcom = fd_out;
+    chr->opaque = s;
+    chr->chr_write = win_chr_write;
+    qemu_chr_reset(chr);
+    return chr;
+}
+    
+static CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
+{
+    HANDLE fd_out;
+    
+    fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+                        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (fd_out == INVALID_HANDLE_VALUE)
+        return NULL;
+
+    return qemu_chr_open_win_file(fd_out);
+}
+#endif
+
+/***********************************************************/
+/* UDP Net console */
+
+typedef struct {
+    int fd;
+    struct sockaddr_in daddr;
+    char buf[1024];
+    int bufcnt;
+    int bufptr;
+    int max_size;
+} NetCharDriver;
+
+static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    NetCharDriver *s = chr->opaque;
+
+    return sendto(s->fd, buf, len, 0,
+                  (struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in));
+}
+
+static int udp_chr_read_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    NetCharDriver *s = chr->opaque;
+
+    s->max_size = qemu_chr_can_read(chr);
+
+    /* If there were any stray characters in the queue process them
+     * first
+     */
+    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+        qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+        s->bufptr++;
+        s->max_size = qemu_chr_can_read(chr);
+    }
+    return s->max_size;
+}
+
+static void udp_chr_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    NetCharDriver *s = chr->opaque;
+
+    if (s->max_size == 0)
+        return;
+    s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0);
+    s->bufptr = s->bufcnt;
+    if (s->bufcnt <= 0)
+        return;
+
+    s->bufptr = 0;
+    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+        qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+        s->bufptr++;
+        s->max_size = qemu_chr_can_read(chr);
+    }
+}
+
+static void udp_chr_update_read_handler(CharDriverState *chr)
+{
+    NetCharDriver *s = chr->opaque;
+
+    if (s->fd >= 0) {
+        qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
+                             udp_chr_read, NULL, chr);
+    }
+}
+
+int parse_host_port(struct sockaddr_in *saddr, const char *str);
+#ifndef _WIN32
+static int parse_unix_path(struct sockaddr_un *uaddr, const char *str);
+#endif
+int parse_host_src_port(struct sockaddr_in *haddr,
+                        struct sockaddr_in *saddr,
+                        const char *str);
+
+static CharDriverState *qemu_chr_open_udp(const char *def)
+{
+    CharDriverState *chr = NULL;
+    NetCharDriver *s = NULL;
+    int fd = -1;
+    struct sockaddr_in saddr;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        goto return_err;
+    s = qemu_mallocz(sizeof(NetCharDriver));
+    if (!s)
+        goto return_err;
+
+    fd = socket(PF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+        perror("socket(PF_INET, SOCK_DGRAM)");
+        goto return_err;
+    }
+
+    if (parse_host_src_port(&s->daddr, &saddr, def) < 0) {
+        printf("Could not parse: %s\n", def);
+        goto return_err;
+    }
+
+    if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
+    {
+        perror("bind");
+        goto return_err;
+    }
+
+    s->fd = fd;
+    s->bufcnt = 0;
+    s->bufptr = 0;
+    chr->opaque = s;
+    chr->chr_write = udp_chr_write;
+    chr->chr_update_read_handler = udp_chr_update_read_handler;
+    return chr;
+
+return_err:
+    if (chr)
+        free(chr);
+    if (s)
+        free(s);
+    if (fd >= 0)
+        closesocket(fd);
+    return NULL;
+}
+
+/***********************************************************/
+/* TCP Net console */
+
+typedef struct {
+    int fd, listen_fd;
+    int connected;
+    int max_size;
+    int do_telnetopt;
+    int do_nodelay;
+    int is_unix;
+} TCPCharDriver;
+
+static void tcp_chr_accept(void *opaque);
+
+static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    TCPCharDriver *s = chr->opaque;
+    if (s->connected) {
+        return send_all(s->fd, buf, len);
+    } else {
+        /* XXX: indicate an error ? */
+        return len;
+    }
+}
+
+static int tcp_chr_read_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+    if (!s->connected)
+        return 0;
+    s->max_size = qemu_chr_can_read(chr);
+    return s->max_size;
+}
+
+#define IAC 255
+#define IAC_BREAK 243
+static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
+                                      TCPCharDriver *s,
+                                      char *buf, int *size)
+{
+    /* Handle any telnet client's basic IAC options to satisfy char by
+     * char mode with no echo.  All IAC options will be removed from
+     * the buf and the do_telnetopt variable will be used to track the
+     * state of the width of the IAC information.
+     *
+     * IAC commands come in sets of 3 bytes with the exception of the
+     * "IAC BREAK" command and the double IAC.
+     */
+
+    int i;
+    int j = 0;
+
+    for (i = 0; i < *size; i++) {
+        if (s->do_telnetopt > 1) {
+            if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
+                /* Double IAC means send an IAC */
+                if (j != i)
+                    buf[j] = buf[i];
+                j++;
+                s->do_telnetopt = 1;
+            } else {
+                if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) {
+                    /* Handle IAC break commands by sending a serial break */
+                    qemu_chr_event(chr, CHR_EVENT_BREAK);
+                    s->do_telnetopt++;
+                }
+                s->do_telnetopt++;
+            }
+            if (s->do_telnetopt >= 4) {
+                s->do_telnetopt = 1;
+            }
+        } else {
+            if ((unsigned char)buf[i] == IAC) {
+                s->do_telnetopt = 2;
+            } else {
+                if (j != i)
+                    buf[j] = buf[i];
+                j++;
+            }
+        }
+    }
+    *size = j;
+}
+
+static void tcp_chr_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+    uint8_t buf[1024];
+    int len, size;
+
+    if (!s->connected || s->max_size <= 0)
+        return;
+    len = sizeof(buf);
+    if (len > s->max_size)
+        len = s->max_size;
+    size = recv(s->fd, buf, len, 0);
+    if (size == 0) {
+        /* connection closed */
+        s->connected = 0;
+        if (s->listen_fd >= 0) {
+            qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+        }
+        qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+        closesocket(s->fd);
+        s->fd = -1;
+    } else if (size > 0) {
+        if (s->do_telnetopt)
+            tcp_chr_process_IAC_bytes(chr, s, buf, &size);
+        if (size > 0)
+            qemu_chr_read(chr, buf, size);
+    }
+}
+
+static void tcp_chr_connect(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+
+    s->connected = 1;
+    qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
+                         tcp_chr_read, NULL, chr);
+    qemu_chr_reset(chr);
+}
+
+#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
+static void tcp_chr_telnet_init(int fd)
+{
+    char buf[3];
+    /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
+    IACSET(buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
+    send(fd, (char *)buf, 3, 0);
+    IACSET(buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
+    send(fd, (char *)buf, 3, 0);
+    IACSET(buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
+    send(fd, (char *)buf, 3, 0);
+    IACSET(buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
+    send(fd, (char *)buf, 3, 0);
+}
+
+static void socket_set_nodelay(int fd)
+{
+    int val = 1;
+    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
+}
+
+static void tcp_chr_accept(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+    struct sockaddr_in saddr;
+#ifndef _WIN32
+    struct sockaddr_un uaddr;
+#endif
+    struct sockaddr *addr;
+    socklen_t len;
+    int fd;
+
+    for(;;) {
+#ifndef _WIN32
+       if (s->is_unix) {
+           len = sizeof(uaddr);
+           addr = (struct sockaddr *)&uaddr;
+       } else
+#endif
+       {
+           len = sizeof(saddr);
+           addr = (struct sockaddr *)&saddr;
+       }
+        fd = accept(s->listen_fd, addr, &len);
+        if (fd < 0 && errno != EINTR) {
+            return;
+        } else if (fd >= 0) {
+            if (s->do_telnetopt)
+                tcp_chr_telnet_init(fd);
+            break;
+        }
+    }
+    socket_set_nonblock(fd);
+    if (s->do_nodelay)
+        socket_set_nodelay(fd);
+    s->fd = fd;
+    qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+    tcp_chr_connect(chr);
+}
+
+static void tcp_chr_close(CharDriverState *chr)
+{
+    TCPCharDriver *s = chr->opaque;
+    if (s->fd >= 0)
+        closesocket(s->fd);
+    if (s->listen_fd >= 0)
+        closesocket(s->listen_fd);
+    qemu_free(s);
+}
+
+static CharDriverState *qemu_chr_open_tcp(const char *host_str, 
+                                          int is_telnet,
+                                         int is_unix)
+{
+    CharDriverState *chr = NULL;
+    TCPCharDriver *s = NULL;
+    int fd = -1, ret, err, val;
+    int is_listen = 0;
+    int is_waitconnect = 1;
+    int do_nodelay = 0;
+    const char *ptr;
+    struct sockaddr_in saddr;
+#ifndef _WIN32
+    struct sockaddr_un uaddr;
+#endif
+    struct sockaddr *addr;
+    socklen_t addrlen;
+
+#ifndef _WIN32
+    if (is_unix) {
+       addr = (struct sockaddr *)&uaddr;
+       addrlen = sizeof(uaddr);
+       if (parse_unix_path(&uaddr, host_str) < 0)
+           goto fail;
+    } else
+#endif
+    {
+       addr = (struct sockaddr *)&saddr;
+       addrlen = sizeof(saddr);
+       if (parse_host_port(&saddr, host_str) < 0)
+           goto fail;
+    }
+
+    ptr = host_str;
+    while((ptr = strchr(ptr,','))) {
+        ptr++;
+        if (!strncmp(ptr,"server",6)) {
+            is_listen = 1;
+        } else if (!strncmp(ptr,"nowait",6)) {
+            is_waitconnect = 0;
+        } else if (!strncmp(ptr,"nodelay",6)) {
+            do_nodelay = 1;
+        } else {
+            printf("Unknown option: %s\n", ptr);
+            goto fail;
+        }
+    }
+    if (!is_listen)
+        is_waitconnect = 0;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        goto fail;
+    s = qemu_mallocz(sizeof(TCPCharDriver));
+    if (!s)
+        goto fail;
+
+#ifndef _WIN32
+    if (is_unix)
+       fd = socket(PF_UNIX, SOCK_STREAM, 0);
+    else
+#endif
+       fd = socket(PF_INET, SOCK_STREAM, 0);
+       
+    if (fd < 0) 
+        goto fail;
+
+    if (!is_waitconnect)
+        socket_set_nonblock(fd);
+
+    s->connected = 0;
+    s->fd = -1;
+    s->listen_fd = -1;
+    s->is_unix = is_unix;
+    s->do_nodelay = do_nodelay && !is_unix;
+
+    chr->opaque = s;
+    chr->chr_write = tcp_chr_write;
+    chr->chr_close = tcp_chr_close;
+
+    if (is_listen) {
+        /* allow fast reuse */
+#ifndef _WIN32
+       if (is_unix) {
+           char path[109];
+           strncpy(path, uaddr.sun_path, 108);
+           path[108] = 0;
+           unlink(path);
+       } else
+#endif
+       {
+           val = 1;
+           setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+       }
+        
+        ret = bind(fd, addr, addrlen);
+        if (ret < 0)
+            goto fail;
+
+        ret = listen(fd, 0);
+        if (ret < 0)
+            goto fail;
+
+        s->listen_fd = fd;
+        qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+        if (is_telnet)
+            s->do_telnetopt = 1;
+    } else {
+        for(;;) {
+            ret = connect(fd, addr, addrlen);
+            if (ret < 0) {
+                err = socket_error();
+                if (err == EINTR || err == EWOULDBLOCK) {
+                } else if (err == EINPROGRESS) {
+                    break;
+                } else {
+                    goto fail;
+                }
+            } else {
+                s->connected = 1;
+                break;
+            }
+        }
+        s->fd = fd;
+        socket_set_nodelay(fd);
+        if (s->connected)
+            tcp_chr_connect(chr);
+        else
+            qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
+    }
+    
+    if (is_listen && is_waitconnect) {
+        printf("QEMU waiting for connection on: %s\n", host_str);
+        tcp_chr_accept(chr);
+        socket_set_nonblock(s->listen_fd);
+    }
+
+    return chr;
+ fail:
+    if (fd >= 0)
+        closesocket(fd);
+    qemu_free(s);
+    qemu_free(chr);
+    return NULL;
+}
+
+CharDriverState *qemu_chr_open(const char *filename)
+{
+    const char *p;
+
+    if (!strcmp(filename, "vc")) {
+        return text_console_init(&display_state);
+    } else if (!strcmp(filename, "null")) {
+        return qemu_chr_open_null();
+    } else 
+    if (strstart(filename, "tcp:", &p)) {
+        return qemu_chr_open_tcp(p, 0, 0);
+    } else
+    if (strstart(filename, "telnet:", &p)) {
+        return qemu_chr_open_tcp(p, 1, 0);
+    } else
+    if (strstart(filename, "udp:", &p)) {
+        return qemu_chr_open_udp(p);
+    } else
+#ifndef _WIN32
+    if (strstart(filename, "unix:", &p)) {
+       return qemu_chr_open_tcp(p, 0, 1);
+    } else if (strstart(filename, "file:", &p)) {
+        return qemu_chr_open_file_out(p);
+    } else if (strstart(filename, "pipe:", &p)) {
+        return qemu_chr_open_pipe(p);
+    } else if (!strcmp(filename, "pty")) {
+        return qemu_chr_open_pty();
+    } else if (!strcmp(filename, "stdio")) {
+        return qemu_chr_open_stdio();
+    } else 
+#endif
+#if defined(__linux__)
+    if (strstart(filename, "/dev/parport", NULL)) {
+        return qemu_chr_open_pp(filename);
+    } else 
+    if (strstart(filename, "/dev/", NULL)) {
+        return qemu_chr_open_tty(filename);
+    } else 
+#endif
+#ifdef _WIN32
+    if (strstart(filename, "COM", NULL)) {
+        return qemu_chr_open_win(filename);
+    } else
+    if (strstart(filename, "pipe:", &p)) {
+        return qemu_chr_open_win_pipe(p);
+    } else
+    if (strstart(filename, "file:", &p)) {
+        return qemu_chr_open_win_file_out(p);
+    }
+#endif
+    {
+        return NULL;
+    }
+}
+
+void qemu_chr_close(CharDriverState *chr)
+{
+    if (chr->chr_close)
+        chr->chr_close(chr);
+}
+
+/***********************************************************/
+/* network device redirectors */
+
+void hex_dump(FILE *f, const uint8_t *buf, int size)
+{
+    int len, i, j, c;
+
+    for(i=0;i<size;i+=16) {
+        len = size - i;
+        if (len > 16)
+            len = 16;
+        fprintf(f, "%08x ", i);
+        for(j=0;j<16;j++) {
+            if (j < len)
+                fprintf(f, " %02x", buf[i+j]);
+            else
+                fprintf(f, "   ");
+        }
+        fprintf(f, " ");
+        for(j=0;j<len;j++) {
+            c = buf[i+j];
+            if (c < ' ' || c > '~')
+                c = '.';
+            fprintf(f, "%c", c);
+        }
+        fprintf(f, "\n");
+    }
+}
+
+static int parse_macaddr(uint8_t *macaddr, const char *p)
+{
+    int i;
+    for(i = 0; i < 6; i++) {
+        macaddr[i] = strtol(p, (char **)&p, 16);
+        if (i == 5) {
+            if (*p != '\0') 
+                return -1;
+        } else {
+            if (*p != ':') 
+                return -1;
+            p++;
+        }
+    }
+    return 0;
+}
+
+static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+{
+    const char *p, *p1;
+    int len;
+    p = *pp;
+    p1 = strchr(p, sep);
+    if (!p1)
+        return -1;
+    len = p1 - p;
+    p1++;
+    if (buf_size > 0) {
+        if (len > buf_size - 1)
+            len = buf_size - 1;
+        memcpy(buf, p, len);
+        buf[len] = '\0';
+    }
+    *pp = p1;
+    return 0;
+}
+
+int parse_host_src_port(struct sockaddr_in *haddr,
+                        struct sockaddr_in *saddr,
+                        const char *input_str)
+{
+    char *str = strdup(input_str);
+    char *host_str = str;
+    char *src_str;
+    char *ptr;
+
+    /*
+     * Chop off any extra arguments at the end of the string which
+     * would start with a comma, then fill in the src port information
+     * if it was provided else use the "any address" and "any port".
+     */
+    if ((ptr = strchr(str,',')))
+        *ptr = '\0';
+
+    if ((src_str = strchr(input_str,'@'))) {
+        *src_str = '\0';
+        src_str++;
+    }
+
+    if (parse_host_port(haddr, host_str) < 0)
+        goto fail;
+
+    if (!src_str || *src_str == '\0')
+        src_str = ":0";
+
+    if (parse_host_port(saddr, src_str) < 0)
+        goto fail;
+
+    free(str);
+    return(0);
+
+fail:
+    free(str);
+    return -1;
+}
+
+int parse_host_port(struct sockaddr_in *saddr, const char *str)
+{
+    char buf[512];
+    struct hostent *he;
+    const char *p, *r;
+    int port;
+
+    p = str;
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        return -1;
+    saddr->sin_family = AF_INET;
+    if (buf[0] == '\0') {
+        saddr->sin_addr.s_addr = 0;
+    } else {
+        if (isdigit(buf[0])) {
+            if (!inet_aton(buf, &saddr->sin_addr))
+                return -1;
+        } else {
+            if ((he = gethostbyname(buf)) == NULL)
+                return - 1;
+            saddr->sin_addr = *(struct in_addr *)he->h_addr;
+        }
+    }
+    port = strtol(p, (char **)&r, 0);
+    if (r == p)
+        return -1;
+    saddr->sin_port = htons(port);
+    return 0;
+}
+
+#ifndef _WIN32
+static int parse_unix_path(struct sockaddr_un *uaddr, const char *str)
+{
+    const char *p;
+    int len;
+
+    len = MIN(108, strlen(str));
+    p = strchr(str, ',');
+    if (p)
+       len = MIN(len, p - str);
+
+    memset(uaddr, 0, sizeof(*uaddr));
+
+    uaddr->sun_family = AF_UNIX;
+    memcpy(uaddr->sun_path, str, len);
+
+    return 0;
+}
+#endif
+
+/* find or alloc a new VLAN */
+VLANState *qemu_find_vlan(int id)
+{
+    VLANState **pvlan, *vlan;
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+        if (vlan->id == id)
+            return vlan;
+    }
+    vlan = qemu_mallocz(sizeof(VLANState));
+    if (!vlan)
+        return NULL;
+    vlan->id = id;
+    vlan->next = NULL;
+    pvlan = &first_vlan;
+    while (*pvlan != NULL)
+        pvlan = &(*pvlan)->next;
+    *pvlan = vlan;
+    return vlan;
+}
+
+VLANClientState *qemu_new_vlan_client(VLANState *vlan,
+                                      IOReadHandler *fd_read,
+                                      IOCanRWHandler *fd_can_read,
+                                      void *opaque)
+{
+    VLANClientState *vc, **pvc;
+    vc = qemu_mallocz(sizeof(VLANClientState));
+    if (!vc)
+        return NULL;
+    vc->fd_read = fd_read;
+    vc->fd_can_read = fd_can_read;
+    vc->opaque = opaque;
+    vc->vlan = vlan;
+
+    vc->next = NULL;
+    pvc = &vlan->first_client;
+    while (*pvc != NULL)
+        pvc = &(*pvc)->next;
+    *pvc = vc;
+    return vc;
+}
+
+int qemu_can_send_packet(VLANClientState *vc1)
+{
+    VLANState *vlan = vc1->vlan;
+    VLANClientState *vc;
+
+    for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+        if (vc != vc1) {
+            if (vc->fd_can_read && !vc->fd_can_read(vc->opaque))
+                return 0;
+        }
+    }
+    return 1;
+}
+
+void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
+{
+    VLANState *vlan = vc1->vlan;
+    VLANClientState *vc;
+
+#if 0
+    printf("vlan %d send:\n", vlan->id);
+    hex_dump(stdout, buf, size);
+#endif
+    for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+        if (vc != vc1) {
+            vc->fd_read(vc->opaque, buf, size);
+        }
+    }
+}
+
+#if defined(CONFIG_SLIRP)
+
+/* slirp network adapter */
 
 static int slirp_inited;
 static VLANClientState *slirp_vc;
 
 int slirp_can_output(void)
 {
-    return 1;
+    return !slirp_vc || qemu_can_send_packet(slirp_vc);
 }
 
 void slirp_output(const uint8_t *pkt, int pkt_len)
@@ -1894,6 +3005,8 @@ void slirp_output(const uint8_t *pkt, int pkt_len)
     printf("slirp output:\n");
     hex_dump(stdout, pkt, pkt_len);
 #endif
+    if (!slirp_vc)
+        return;
     qemu_send_packet(slirp_vc, pkt, pkt_len);
 }
 
@@ -1913,7 +3026,7 @@ static int net_slirp_init(VLANState *vlan)
         slirp_init();
     }
     slirp_vc = qemu_new_vlan_client(vlan, 
-                                    slirp_receive, NULL);
+                                    slirp_receive, NULL, NULL);
     snprintf(slirp_vc->info_str, sizeof(slirp_vc->info_str), "user redirector");
     return 0;
 }
@@ -2046,8 +3159,8 @@ void net_slirp_smb(const char *exported_dir)
     fclose(f);
     atexit(smb_exit);
 
-    snprintf(smb_cmdline, sizeof(smb_cmdline), "/usr/sbin/smbd -s %s",
-             smb_conf);
+    snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
+             SMBD_COMMAND, smb_conf);
     
     slirp_add_exec(0, smb_cmdline, 4, 139);
 }
@@ -2098,7 +3211,7 @@ static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
     if (!s)
         return NULL;
     s->fd = fd;
-    s->vc = qemu_new_vlan_client(vlan, tap_receive, s);
+    s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
     qemu_set_fd_handler(s->fd, tap_send, NULL, s);
     snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd);
     return s;
@@ -2124,6 +3237,12 @@ static int tap_open(char *ifname, int ifname_size)
     fcntl(fd, F_SETFL, O_NONBLOCK);
     return fd;
 }
+#elif defined(__sun__)
+static int tap_open(char *ifname, int ifname_size)
+{
+    fprintf(stderr, "warning: tap_open not yet implemented\n");
+    return -1;
+}
 #else
 static int tap_open(char *ifname, int ifname_size)
 {
@@ -2170,7 +3289,7 @@ static int net_tap_init(VLANState *vlan, const char *ifname1,
     if (fd < 0)
         return -1;
 
-    if (!setup_script)
+    if (!setup_script || !strcmp(setup_script, "no"))
         setup_script = "";
     if (setup_script[0] != '\0') {
         /* try to launch network init script */
@@ -2364,7 +3483,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
     socket_set_nonblock(fd);
     return fd;
 fail:
-    if (fd>=0) close(fd);
+    if (fd >= 0) 
+        closesocket(fd);
     return -1;
 }
 
@@ -2412,7 +3532,7 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, int fd,
         return NULL;
     s->fd = fd;
 
-    s->vc = qemu_new_vlan_client(vlan, net_socket_receive_dgram, s);
+    s->vc = qemu_new_vlan_client(vlan, net_socket_receive_dgram, NULL, s);
     qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
 
     /* mcast: save bound address as dst */
@@ -2440,7 +3560,7 @@ static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, int fd,
         return NULL;
     s->fd = fd;
     s->vc = qemu_new_vlan_client(vlan, 
-                                 net_socket_receive, s);
+                                 net_socket_receive, NULL, s);
     snprintf(s->vc->info_str, sizeof(s->vc->info_str),
              "socket: fd=%d", fd);
     if (is_connected) {
@@ -2492,7 +3612,7 @@ static void net_socket_accept(void *opaque)
     }
     s1 = net_socket_fd_init(s->vlan, fd, 1); 
     if (!s1) {
-        close(fd);
+        closesocket(fd);
     } else {
         snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
                  "socket: connection from %s:%d", 
@@ -2650,7 +3770,7 @@ static int get_param_value(char *buf, int buf_size,
     return 0;
 }
 
-int net_client_init(const char *str)
+static int net_client_init(const char *str)
 {
     const char *p;
     char *q;
@@ -2701,6 +3821,9 @@ int net_client_init(const char *str)
                 return -1;
             }
         }
+        if (get_param_value(buf, sizeof(buf), "model", p)) {
+            nd->model = strdup(buf);
+        }
         nd->vlan = vlan;
         nb_nics++;
         ret = 0;
@@ -2712,10 +3835,22 @@ int net_client_init(const char *str)
     } else
 #ifdef CONFIG_SLIRP
     if (!strcmp(device, "user")) {
+        if (get_param_value(buf, sizeof(buf), "hostname", p)) {
+            pstrcpy(slirp_hostname, sizeof(slirp_hostname), buf);
+        }
         ret = net_slirp_init(vlan);
     } else
 #endif
-#ifndef _WIN32
+#ifdef _WIN32
+    if (!strcmp(device, "tap")) {
+        char ifname[64];
+        if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+            fprintf(stderr, "tap: no interface name\n");
+            return -1;
+        }
+        ret = tap_win32_init(vlan, ifname);
+    } else
+#else
     if (!strcmp(device, "tap")) {
         char ifname[64];
         char setup_script[1024];
@@ -2726,7 +3861,9 @@ int net_client_init(const char *str)
             if (net_tap_fd_init(vlan, fd))
                 ret = 0;
         } else {
-            get_param_value(ifname, sizeof(ifname), "ifname", p);
+            if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+                ifname[0] = '\0';
+            }
             if (get_param_value(setup_script, sizeof(setup_script), "script", p) == 0) {
                 pstrcpy(setup_script, sizeof(setup_script), DEFAULT_NETWORK_SCRIPT);
             }
@@ -2774,47 +3911,78 @@ void do_info_network(void)
             term_printf("  %s\n", vc->info_str);
     }
 }
+
 /***********************************************************/
 /* USB devices */
 
+static USBPort *used_usb_ports;
+static USBPort *free_usb_ports;
+
+/* ??? Maybe change this to register a hub to keep track of the topology.  */
+void qemu_register_usb_port(USBPort *port, void *opaque, int index,
+                            usb_attachfn attach)
+{
+    port->opaque = opaque;
+    port->index = index;
+    port->attach = attach;
+    port->next = free_usb_ports;
+    free_usb_ports = port;
+}
+
 static int usb_device_add(const char *devname)
 {
     const char *p;
     USBDevice *dev;
-    int i;
+    USBPort *port;
 
-    if (!vm_usb_hub)
-        return -1;
-    for(i = 0;i < MAX_VM_USB_PORTS; i++) {
-        if (!vm_usb_ports[i]->dev)
-            break;
-    }
-    if (i == MAX_VM_USB_PORTS)
+    if (!free_usb_ports)
         return -1;
 
     if (strstart(devname, "host:", &p)) {
         dev = usb_host_device_open(p);
-        if (!dev)
-            return -1;
     } else if (!strcmp(devname, "mouse")) {
         dev = usb_mouse_init();
-        if (!dev)
-            return -1;
+    } else if (!strcmp(devname, "tablet")) {
+       dev = usb_tablet_init();
+    } else if (strstart(devname, "disk:", &p)) {
+        dev = usb_msd_init(p);
     } else {
         return -1;
     }
-    usb_attach(vm_usb_ports[i], dev);
+    if (!dev)
+        return -1;
+
+    /* Find a USB port to add the device to.  */
+    port = free_usb_ports;
+    if (!port->next) {
+        USBDevice *hub;
+
+        /* Create a new hub and chain it on.  */
+        free_usb_ports = NULL;
+        port->next = used_usb_ports;
+        used_usb_ports = port;
+
+        hub = usb_hub_init(VM_USB_HUB_SIZE);
+        usb_attach(port, hub);
+        port = free_usb_ports;
+    }
+
+    free_usb_ports = port->next;
+    port->next = used_usb_ports;
+    used_usb_ports = port;
+    usb_attach(port, dev);
     return 0;
 }
 
 static int usb_device_del(const char *devname)
 {
+    USBPort *port;
+    USBPort **lastp;
     USBDevice *dev;
-    int bus_num, addr, i;
+    int bus_num, addr;
     const char *p;
 
-    if (!vm_usb_hub)
+    if (!used_usb_ports)
         return -1;
 
     p = strchr(devname, '.');
@@ -2824,14 +3992,23 @@ static int usb_device_del(const char *devname)
     addr = strtoul(p + 1, NULL, 0);
     if (bus_num != 0)
         return -1;
-    for(i = 0;i < MAX_VM_USB_PORTS; i++) {
-        dev = vm_usb_ports[i]->dev;
-        if (dev && dev->addr == addr)
-            break;
+
+    lastp = &used_usb_ports;
+    port = used_usb_ports;
+    while (port && port->dev->addr != addr) {
+        lastp = &port->next;
+        port = port->next;
     }
-    if (i == MAX_VM_USB_PORTS)
+
+    if (!port)
         return -1;
-    usb_attach(vm_usb_ports[i], NULL);
+
+    dev = port->dev;
+    *lastp = port->next;
+    usb_attach(port, NULL);
+    dev->handle_destroy(dev);
+    port->next = free_usb_ports;
+    free_usb_ports = port;
     return 0;
 }
 
@@ -2854,35 +4031,34 @@ void do_usb_del(const char *devname)
 void usb_info(void)
 {
     USBDevice *dev;
-    int i;
+    USBPort *port;
     const char *speed_str;
 
-    if (!vm_usb_hub) {
+    if (!usb_enabled) {
         term_printf("USB support not enabled\n");
         return;
     }
 
-    for(i = 0; i < MAX_VM_USB_PORTS; i++) {
-        dev = vm_usb_ports[i]->dev;
-        if (dev) {
-            term_printf("Hub port %d:\n", i);
-            switch(dev->speed) {
-            case USB_SPEED_LOW: 
-                speed_str = "1.5"; 
-                break;
-            case USB_SPEED_FULL: 
-                speed_str = "12"; 
-                break;
-            case USB_SPEED_HIGH: 
-                speed_str = "480"; 
-                break;
-            default:
-                speed_str = "?"; 
-                break;
-            }
-            term_printf("  Device %d.%d, speed %s Mb/s\n", 
-                        0, dev->addr, speed_str);
+    for (port = used_usb_ports; port; port = port->next) {
+        dev = port->dev;
+        if (!dev)
+            continue;
+        switch(dev->speed) {
+        case USB_SPEED_LOW: 
+            speed_str = "1.5"; 
+            break;
+        case USB_SPEED_FULL: 
+            speed_str = "12"; 
+            break;
+        case USB_SPEED_HIGH: 
+            speed_str = "480"; 
+            break;
+        default:
+            speed_str = "?"; 
+            break;
         }
+        term_printf("  Device %d.%d, Speed %s Mb/s, Product %s\n", 
+                    0, dev->addr, speed_str, dev->devname);
     }
 }
 
@@ -2939,7 +4115,7 @@ static void dumb_resize(DisplayState *ds, int w, int h)
 
 static void dumb_refresh(DisplayState *ds)
 {
-    vga_update_display();
+    vga_hw_update();
 }
 
 void dumb_display_init(DisplayState *ds)
@@ -2952,20 +4128,6 @@ void dumb_display_init(DisplayState *ds)
     ds->dpy_refresh = dumb_refresh;
 }
 
-#if !defined(CONFIG_SOFTMMU)
-/***********************************************************/
-/* cpu signal handler */
-static void host_segv_handler(int host_signum, siginfo_t *info, 
-                              void *puc)
-{
-    if (cpu_signal_handler(host_signum, info, puc))
-        return;
-    if (stdio_nb_clients > 0)
-        term_exit();
-    abort();
-}
-#endif
-
 /***********************************************************/
 /* I/O handling */
 
@@ -3036,16 +4198,274 @@ int qemu_set_fd_handler(int fd,
 }
 
 /***********************************************************/
+/* Polling handling */
+
+typedef struct PollingEntry {
+    PollingFunc *func;
+    void *opaque;
+    struct PollingEntry *next;
+} PollingEntry;
+
+static PollingEntry *first_polling_entry;
+
+int qemu_add_polling_cb(PollingFunc *func, void *opaque)
+{
+    PollingEntry **ppe, *pe;
+    pe = qemu_mallocz(sizeof(PollingEntry));
+    if (!pe)
+        return -1;
+    pe->func = func;
+    pe->opaque = opaque;
+    for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next);
+    *ppe = pe;
+    return 0;
+}
+
+void qemu_del_polling_cb(PollingFunc *func, void *opaque)
+{
+    PollingEntry **ppe, *pe;
+    for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) {
+        pe = *ppe;
+        if (pe->func == func && pe->opaque == opaque) {
+            *ppe = pe->next;
+            qemu_free(pe);
+            break;
+        }
+    }
+}
+
+#ifdef _WIN32
+/***********************************************************/
+/* Wait objects support */
+typedef struct WaitObjects {
+    int num;
+    HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
+    WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1];
+    void *opaque[MAXIMUM_WAIT_OBJECTS + 1];
+} WaitObjects;
+
+static WaitObjects wait_objects = {0};
+    
+int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+    WaitObjects *w = &wait_objects;
+
+    if (w->num >= MAXIMUM_WAIT_OBJECTS)
+        return -1;
+    w->events[w->num] = handle;
+    w->func[w->num] = func;
+    w->opaque[w->num] = opaque;
+    w->num++;
+    return 0;
+}
+
+void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+    int i, found;
+    WaitObjects *w = &wait_objects;
+
+    found = 0;
+    for (i = 0; i < w->num; i++) {
+        if (w->events[i] == handle)
+            found = 1;
+        if (found) {
+            w->events[i] = w->events[i + 1];
+            w->func[i] = w->func[i + 1];
+            w->opaque[i] = w->opaque[i + 1];
+        }            
+    }
+    if (found)
+        w->num--;
+}
+#endif
+
+/***********************************************************/
 /* savevm/loadvm support */
 
+#define IO_BUF_SIZE 32768
+
+struct QEMUFile {
+    FILE *outfile;
+    BlockDriverState *bs;
+    int is_file;
+    int is_writable;
+    int64_t base_offset;
+    int64_t buf_offset; /* start of buffer when writing, end of buffer
+                           when reading */
+    int buf_index;
+    int buf_size; /* 0 when writing */
+    uint8_t buf[IO_BUF_SIZE];
+};
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode)
+{
+    QEMUFile *f;
+
+    f = qemu_mallocz(sizeof(QEMUFile));
+    if (!f)
+        return NULL;
+    if (!strcmp(mode, "wb")) {
+        f->is_writable = 1;
+    } else if (!strcmp(mode, "rb")) {
+        f->is_writable = 0;
+    } else {
+        goto fail;
+    }
+    f->outfile = fopen(filename, mode);
+    if (!f->outfile)
+        goto fail;
+    f->is_file = 1;
+    return f;
+ fail:
+    if (f->outfile)
+        fclose(f->outfile);
+    qemu_free(f);
+    return NULL;
+}
+
+QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable)
+{
+    QEMUFile *f;
+
+    f = qemu_mallocz(sizeof(QEMUFile));
+    if (!f)
+        return NULL;
+    f->is_file = 0;
+    f->bs = bs;
+    f->is_writable = is_writable;
+    f->base_offset = offset;
+    return f;
+}
+
+void qemu_fflush(QEMUFile *f)
+{
+    if (!f->is_writable)
+        return;
+    if (f->buf_index > 0) {
+        if (f->is_file) {
+            fseek(f->outfile, f->buf_offset, SEEK_SET);
+            fwrite(f->buf, 1, f->buf_index, f->outfile);
+        } else {
+            bdrv_pwrite(f->bs, f->base_offset + f->buf_offset, 
+                        f->buf, f->buf_index);
+        }
+        f->buf_offset += f->buf_index;
+        f->buf_index = 0;
+    }
+}
+
+static void qemu_fill_buffer(QEMUFile *f)
+{
+    int len;
+
+    if (f->is_writable)
+        return;
+    if (f->is_file) {
+        fseek(f->outfile, f->buf_offset, SEEK_SET);
+        len = fread(f->buf, 1, IO_BUF_SIZE, f->outfile);
+        if (len < 0)
+            len = 0;
+    } else {
+        len = bdrv_pread(f->bs, f->base_offset + f->buf_offset, 
+                         f->buf, IO_BUF_SIZE);
+        if (len < 0)
+            len = 0;
+    }
+    f->buf_index = 0;
+    f->buf_size = len;
+    f->buf_offset += len;
+}
+
+void qemu_fclose(QEMUFile *f)
+{
+    if (f->is_writable)
+        qemu_fflush(f);
+    if (f->is_file) {
+        fclose(f->outfile);
+    }
+    qemu_free(f);
+}
+
 void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
 {
-    fwrite(buf, 1, size, f);
+    int l;
+    while (size > 0) {
+        l = IO_BUF_SIZE - f->buf_index;
+        if (l > size)
+            l = size;
+        memcpy(f->buf + f->buf_index, buf, l);
+        f->buf_index += l;
+        buf += l;
+        size -= l;
+        if (f->buf_index >= IO_BUF_SIZE)
+            qemu_fflush(f);
+    }
 }
 
 void qemu_put_byte(QEMUFile *f, int v)
 {
-    fputc(v, f);
+    f->buf[f->buf_index++] = v;
+    if (f->buf_index >= IO_BUF_SIZE)
+        qemu_fflush(f);
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+{
+    int size, l;
+
+    size = size1;
+    while (size > 0) {
+        l = f->buf_size - f->buf_index;
+        if (l == 0) {
+            qemu_fill_buffer(f);
+            l = f->buf_size - f->buf_index;
+            if (l == 0)
+                break;
+        }
+        if (l > size)
+            l = size;
+        memcpy(buf, f->buf + f->buf_index, l);
+        f->buf_index += l;
+        buf += l;
+        size -= l;
+    }
+    return size1 - size;
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+    if (f->buf_index >= f->buf_size) {
+        qemu_fill_buffer(f);
+        if (f->buf_index >= f->buf_size)
+            return 0;
+    }
+    return f->buf[f->buf_index++];
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+    return f->buf_offset - f->buf_size + f->buf_index;
+}
+
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
+{
+    if (whence == SEEK_SET) {
+        /* nothing to do */
+    } else if (whence == SEEK_CUR) {
+        pos += qemu_ftell(f);
+    } else {
+        /* SEEK_END not supported */
+        return -1;
+    }
+    if (f->is_writable) {
+        qemu_fflush(f);
+        f->buf_offset = pos;
+    } else {
+        f->buf_offset = pos;
+        f->buf_index = 0;
+        f->buf_size = 0;
+    }
+    return pos;
 }
 
 void qemu_put_be16(QEMUFile *f, unsigned int v)
@@ -3068,21 +4488,6 @@ void qemu_put_be64(QEMUFile *f, uint64_t v)
     qemu_put_be32(f, v);
 }
 
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
-{
-    return fread(buf, 1, size, f);
-}
-
-int qemu_get_byte(QEMUFile *f)
-{
-    int v;
-    v = fgetc(f);
-    if (v == EOF)
-        return 0;
-    else
-        return v;
-}
-
 unsigned int qemu_get_be16(QEMUFile *f)
 {
     unsigned int v;
@@ -3109,18 +4514,6 @@ uint64_t qemu_get_be64(QEMUFile *f)
     return v;
 }
 
-int64_t qemu_ftell(QEMUFile *f)
-{
-    return ftell(f);
-}
-
-int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
-{
-    if (fseek(f, pos, whence) < 0)
-        return -1;
-    return ftell(f);
-}
-
 typedef struct SaveStateEntry {
     char idstr[256];
     int instance_id;
@@ -3162,25 +4555,18 @@ int register_savevm(const char *idstr,
 }
 
 #define QEMU_VM_FILE_MAGIC   0x5145564d
-#define QEMU_VM_FILE_VERSION 0x00000001
+#define QEMU_VM_FILE_VERSION 0x00000002
 
-int qemu_savevm(const char *filename)
+int qemu_savevm_state(QEMUFile *f)
 {
     SaveStateEntry *se;
-    QEMUFile *f;
-    int len, len_pos, cur_pos, saved_vm_running, ret;
-
-    saved_vm_running = vm_running;
-    vm_stop(0);
-
-    f = fopen(filename, "wb");
-    if (!f) {
-        ret = -1;
-        goto the_end;
-    }
+    int len, ret;
+    int64_t cur_pos, len_pos, total_len_pos;
 
     qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
     qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+    total_len_pos = qemu_ftell(f);
+    qemu_put_be64(f, 0); /* total size */
 
     for(se = first_se; se != NULL; se = se->next) {
         /* ID string */
@@ -3192,101 +4578,377 @@ int qemu_savevm(const char *filename)
         qemu_put_be32(f, se->version_id);
 
         /* record size: filled later */
-        len_pos = ftell(f);
+        len_pos = qemu_ftell(f);
         qemu_put_be32(f, 0);
         
         se->save_state(f, se->opaque);
 
-        /* fill record size */
-        cur_pos = ftell(f);
-        len = ftell(f) - len_pos - 4;
-        fseek(f, len_pos, SEEK_SET);
-        qemu_put_be32(f, len);
-        fseek(f, cur_pos, SEEK_SET);
+        /* fill record size */
+        cur_pos = qemu_ftell(f);
+        len = cur_pos - len_pos - 4;
+        qemu_fseek(f, len_pos, SEEK_SET);
+        qemu_put_be32(f, len);
+        qemu_fseek(f, cur_pos, SEEK_SET);
+    }
+    cur_pos = qemu_ftell(f);
+    qemu_fseek(f, total_len_pos, SEEK_SET);
+    qemu_put_be64(f, cur_pos - total_len_pos - 8);
+    qemu_fseek(f, cur_pos, SEEK_SET);
+
+    ret = 0;
+    return ret;
+}
+
+static SaveStateEntry *find_se(const char *idstr, int instance_id)
+{
+    SaveStateEntry *se;
+
+    for(se = first_se; se != NULL; se = se->next) {
+        if (!strcmp(se->idstr, idstr) && 
+            instance_id == se->instance_id)
+            return se;
+    }
+    return NULL;
+}
+
+int qemu_loadvm_state(QEMUFile *f)
+{
+    SaveStateEntry *se;
+    int len, ret, instance_id, record_len, version_id;
+    int64_t total_len, end_pos, cur_pos;
+    unsigned int v;
+    char idstr[256];
+    
+    v = qemu_get_be32(f);
+    if (v != QEMU_VM_FILE_MAGIC)
+        goto fail;
+    v = qemu_get_be32(f);
+    if (v != QEMU_VM_FILE_VERSION) {
+    fail:
+        ret = -1;
+        goto the_end;
+    }
+    total_len = qemu_get_be64(f);
+    end_pos = total_len + qemu_ftell(f);
+    for(;;) {
+        if (qemu_ftell(f) >= end_pos)
+            break;
+        len = qemu_get_byte(f);
+        qemu_get_buffer(f, idstr, len);
+        idstr[len] = '\0';
+        instance_id = qemu_get_be32(f);
+        version_id = qemu_get_be32(f);
+        record_len = qemu_get_be32(f);
+#if 0
+        printf("idstr=%s instance=0x%x version=%d len=%d\n", 
+               idstr, instance_id, version_id, record_len);
+#endif
+        cur_pos = qemu_ftell(f);
+        se = find_se(idstr, instance_id);
+        if (!se) {
+            fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", 
+                    instance_id, idstr);
+        } else {
+            ret = se->load_state(f, se->opaque, version_id);
+            if (ret < 0) {
+                fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", 
+                        instance_id, idstr);
+            }
+        }
+        /* always seek to exact end of record */
+        qemu_fseek(f, cur_pos + record_len, SEEK_SET);
+    }
+    ret = 0;
+ the_end:
+    return ret;
+}
+
+/* device can contain snapshots */
+static int bdrv_can_snapshot(BlockDriverState *bs)
+{
+    return (bs &&
+            !bdrv_is_removable(bs) &&
+            !bdrv_is_read_only(bs));
+}
+
+/* device must be snapshots in order to have a reliable snapshot */
+static int bdrv_has_snapshot(BlockDriverState *bs)
+{
+    return (bs &&
+            !bdrv_is_removable(bs) &&
+            !bdrv_is_read_only(bs));
+}
+
+static BlockDriverState *get_bs_snapshots(void)
+{
+    BlockDriverState *bs;
+    int i;
+
+    if (bs_snapshots)
+        return bs_snapshots;
+    for(i = 0; i <= MAX_DISKS; i++) {
+        bs = bs_table[i];
+        if (bdrv_can_snapshot(bs))
+            goto ok;
+    }
+    return NULL;
+ ok:
+    bs_snapshots = bs;
+    return bs;
+}
+
+static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
+                              const char *name)
+{
+    QEMUSnapshotInfo *sn_tab, *sn;
+    int nb_sns, i, ret;
+    
+    ret = -ENOENT;
+    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+    if (nb_sns < 0)
+        return ret;
+    for(i = 0; i < nb_sns; i++) {
+        sn = &sn_tab[i];
+        if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
+            *sn_info = *sn;
+            ret = 0;
+            break;
+        }
+    }
+    qemu_free(sn_tab);
+    return ret;
+}
+
+void do_savevm(const char *name)
+{
+    BlockDriverState *bs, *bs1;
+    QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
+    int must_delete, ret, i;
+    BlockDriverInfo bdi1, *bdi = &bdi1;
+    QEMUFile *f;
+    int saved_vm_running;
+#ifdef _WIN32
+    struct _timeb tb;
+#else
+    struct timeval tv;
+#endif
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No block device can accept snapshots\n");
+        return;
+    }
+
+    /* ??? Should this occur after vm_stop?  */
+    qemu_aio_flush();
+
+    saved_vm_running = vm_running;
+    vm_stop(0);
+    
+    must_delete = 0;
+    if (name) {
+        ret = bdrv_snapshot_find(bs, old_sn, name);
+        if (ret >= 0) {
+            must_delete = 1;
+        }
+    }
+    memset(sn, 0, sizeof(*sn));
+    if (must_delete) {
+        pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
+        pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
+    } else {
+        if (name)
+            pstrcpy(sn->name, sizeof(sn->name), name);
+    }
+
+    /* fill auxiliary fields */
+#ifdef _WIN32
+    _ftime(&tb);
+    sn->date_sec = tb.time;
+    sn->date_nsec = tb.millitm * 1000000;
+#else
+    gettimeofday(&tv, NULL);
+    sn->date_sec = tv.tv_sec;
+    sn->date_nsec = tv.tv_usec * 1000;
+#endif
+    sn->vm_clock_nsec = qemu_get_clock(vm_clock);
+    
+    if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
+        term_printf("Device %s does not support VM state snapshots\n",
+                    bdrv_get_device_name(bs));
+        goto the_end;
+    }
+    
+    /* save the VM state */
+    f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1);
+    if (!f) {
+        term_printf("Could not open VM state file\n");
+        goto the_end;
+    }
+    ret = qemu_savevm_state(f);
+    sn->vm_state_size = qemu_ftell(f);
+    qemu_fclose(f);
+    if (ret < 0) {
+        term_printf("Error %d while writing VM\n", ret);
+        goto the_end;
+    }
+    
+    /* create the snapshots */
+
+    for(i = 0; i < MAX_DISKS; i++) {
+        bs1 = bs_table[i];
+        if (bdrv_has_snapshot(bs1)) {
+            if (must_delete) {
+                ret = bdrv_snapshot_delete(bs1, old_sn->id_str);
+                if (ret < 0) {
+                    term_printf("Error while deleting snapshot on '%s'\n",
+                                bdrv_get_device_name(bs1));
+                }
+            }
+            ret = bdrv_snapshot_create(bs1, sn);
+            if (ret < 0) {
+                term_printf("Error while creating snapshot on '%s'\n",
+                            bdrv_get_device_name(bs1));
+            }
+        }
     }
 
-    fclose(f);
-    ret = 0;
  the_end:
     if (saved_vm_running)
         vm_start();
-    return ret;
-}
-
-static SaveStateEntry *find_se(const char *idstr, int instance_id)
-{
-    SaveStateEntry *se;
-
-    for(se = first_se; se != NULL; se = se->next) {
-        if (!strcmp(se->idstr, idstr) && 
-            instance_id == se->instance_id)
-            return se;
-    }
-    return NULL;
 }
 
-int qemu_loadvm(const char *filename)
+void do_loadvm(const char *name)
 {
-    SaveStateEntry *se;
+    BlockDriverState *bs, *bs1;
+    BlockDriverInfo bdi1, *bdi = &bdi1;
     QEMUFile *f;
-    int len, cur_pos, ret, instance_id, record_len, version_id;
+    int i, ret;
     int saved_vm_running;
-    unsigned int v;
-    char idstr[256];
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No block device supports snapshots\n");
+        return;
+    }
     
+    /* Flush all IO requests so they don't interfere with the new state.  */
+    qemu_aio_flush();
+
     saved_vm_running = vm_running;
     vm_stop(0);
 
-    f = fopen(filename, "rb");
+    for(i = 0; i <= MAX_DISKS; i++) {
+        bs1 = bs_table[i];
+        if (bdrv_has_snapshot(bs1)) {
+            ret = bdrv_snapshot_goto(bs1, name);
+            if (ret < 0) {
+                if (bs != bs1)
+                    term_printf("Warning: ");
+                switch(ret) {
+                case -ENOTSUP:
+                    term_printf("Snapshots not supported on device '%s'\n",
+                                bdrv_get_device_name(bs1));
+                    break;
+                case -ENOENT:
+                    term_printf("Could not find snapshot '%s' on device '%s'\n",
+                                name, bdrv_get_device_name(bs1));
+                    break;
+                default:
+                    term_printf("Error %d while activating snapshot on '%s'\n",
+                                ret, bdrv_get_device_name(bs1));
+                    break;
+                }
+                /* fatal on snapshot block device */
+                if (bs == bs1)
+                    goto the_end;
+            }
+        }
+    }
+
+    if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
+        term_printf("Device %s does not support VM state snapshots\n",
+                    bdrv_get_device_name(bs));
+        return;
+    }
+    
+    /* restore the VM state */
+    f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0);
     if (!f) {
-        ret = -1;
+        term_printf("Could not open VM state file\n");
         goto the_end;
     }
+    ret = qemu_loadvm_state(f);
+    qemu_fclose(f);
+    if (ret < 0) {
+        term_printf("Error %d while loading VM state\n", ret);
+    }
+ the_end:
+    if (saved_vm_running)
+        vm_start();
+}
 
-    v = qemu_get_be32(f);
-    if (v != QEMU_VM_FILE_MAGIC)
-        goto fail;
-    v = qemu_get_be32(f);
-    if (v != QEMU_VM_FILE_VERSION) {
-    fail:
-        fclose(f);
-        ret = -1;
-        goto the_end;
+void do_delvm(const char *name)
+{
+    BlockDriverState *bs, *bs1;
+    int i, ret;
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No block device supports snapshots\n");
+        return;
     }
-    for(;;) {
-        len = qemu_get_byte(f);
-        if (feof(f))
-            break;
-        qemu_get_buffer(f, idstr, len);
-        idstr[len] = '\0';
-        instance_id = qemu_get_be32(f);
-        version_id = qemu_get_be32(f);
-        record_len = qemu_get_be32(f);
-#if 0
-        printf("idstr=%s instance=0x%x version=%d len=%d\n", 
-               idstr, instance_id, version_id, record_len);
-#endif
-        cur_pos = ftell(f);
-        se = find_se(idstr, instance_id);
-        if (!se) {
-            fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", 
-                    instance_id, idstr);
-        } else {
-            ret = se->load_state(f, se->opaque, version_id);
+    
+    for(i = 0; i <= MAX_DISKS; i++) {
+        bs1 = bs_table[i];
+        if (bdrv_has_snapshot(bs1)) {
+            ret = bdrv_snapshot_delete(bs1, name);
             if (ret < 0) {
-                fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", 
-                        instance_id, idstr);
+                if (ret == -ENOTSUP)
+                    term_printf("Snapshots not supported on device '%s'\n",
+                                bdrv_get_device_name(bs1));
+                else
+                    term_printf("Error %d while deleting snapshot on '%s'\n",
+                                ret, bdrv_get_device_name(bs1));
             }
         }
-        /* always seek to exact end of record */
-        qemu_fseek(f, cur_pos + record_len, SEEK_SET);
     }
-    fclose(f);
-    ret = 0;
- the_end:
-    if (saved_vm_running)
-        vm_start();
-    return ret;
+}
+
+void do_info_snapshots(void)
+{
+    BlockDriverState *bs, *bs1;
+    QEMUSnapshotInfo *sn_tab, *sn;
+    int nb_sns, i;
+    char buf[256];
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No available block device supports snapshots\n");
+        return;
+    }
+    term_printf("Snapshot devices:");
+    for(i = 0; i <= MAX_DISKS; i++) {
+        bs1 = bs_table[i];
+        if (bdrv_has_snapshot(bs1)) {
+            if (bs == bs1)
+                term_printf(" %s", bdrv_get_device_name(bs1));
+        }
+    }
+    term_printf("\n");
+
+    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+    if (nb_sns < 0) {
+        term_printf("bdrv_snapshot_list: error %d\n", nb_sns);
+        return;
+    }
+    term_printf("Snapshot list (from %s):\n", bdrv_get_device_name(bs));
+    term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+    for(i = 0; i < nb_sns; i++) {
+        sn = &sn_tab[i];
+        term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+    }
+    qemu_free(sn_tab);
 }
 
 /***********************************************************/
@@ -3400,6 +5062,7 @@ void cpu_save(QEMUFile *f, void *opaque)
     qemu_put_be64s(f, &env->fmask);
     qemu_put_be64s(f, &env->kernelgsbase);
 #endif
+    qemu_put_be32s(f, &env->smbase);
 }
 
 #ifdef USE_X86LDOUBLE
@@ -3433,7 +5096,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     uint32_t hflags;
     uint16_t fpus, fpuc, fptag, fpregs_format;
 
-    if (version_id != 3)
+    if (version_id != 3 && version_id != 4)
         return -EINVAL;
     for(i = 0; i < CPU_NB_REGS; i++)
         qemu_get_betls(f, &env->regs[i]);
@@ -3536,6 +5199,8 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     qemu_get_be64s(f, &env->fmask);
     qemu_get_be64s(f, &env->kernelgsbase);
 #endif
+    if (version_id >= 4) 
+        qemu_get_be32s(f, &env->smbase);
 
     /* XXX: compute hflags from scratch, except for CPL and IIF */
     env->hflags = hflags;
@@ -3578,11 +5243,11 @@ void cpu_save(QEMUFile *f, void *opaque)
     /* FPU */
     for(i = 0; i < TARGET_FPREGS; i++) {
         union {
-            TARGET_FPREG_T f;
-            target_ulong i;
+            float32 f;
+            uint32_t i;
         } u;
         u.f = env->fpr[i];
-        qemu_put_betl(f, u.i);
+        qemu_put_be32(f, u.i);
     }
 
     qemu_put_betls(f, &env->pc);
@@ -3614,10 +5279,10 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     /* FPU */
     for(i = 0; i < TARGET_FPREGS; i++) {
         union {
-            TARGET_FPREG_T f;
-            target_ulong i;
+            float32 f;
+            uint32_t i;
         } u;
-        u.i = qemu_get_betl(f);
+        u.i = qemu_get_be32(f);
         env->fpr[i] = u.f;
     }
 
@@ -3640,88 +5305,355 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
-#elif defined(TARGET_ARM)
+#elif defined(TARGET_ARM)
+
+/* ??? Need to implement these.  */
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    return 0;
+}
+
+#else
+
+#warning No CPU save/restore functions
+
+#endif
+
+/***********************************************************/
+/* ram save/restore */
+
+static int ram_get_page(QEMUFile *f, uint8_t *buf, int len)
+{
+    int v;
+
+    v = qemu_get_byte(f);
+    switch(v) {
+    case 0:
+        if (qemu_get_buffer(f, buf, len) != len)
+            return -EIO;
+        break;
+    case 1:
+        v = qemu_get_byte(f);
+        memset(buf, v, len);
+        break;
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int ram_load_v1(QEMUFile *f, void *opaque)
+{
+    int i, ret;
+
+    if (qemu_get_be32(f) != phys_ram_size)
+        return -EINVAL;
+    for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) {
+        ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
+        if (ret)
+            return ret;
+    }
+    return 0;
+}
+
+#define BDRV_HASH_BLOCK_SIZE 1024
+#define IOBUF_SIZE 4096
+#define RAM_CBLOCK_MAGIC 0xfabe
+
+typedef struct RamCompressState {
+    z_stream zstream;
+    QEMUFile *f;
+    uint8_t buf[IOBUF_SIZE];
+} RamCompressState;
+
+static int ram_compress_open(RamCompressState *s, QEMUFile *f)
+{
+    int ret;
+    memset(s, 0, sizeof(*s));
+    s->f = f;
+    ret = deflateInit2(&s->zstream, 1,
+                       Z_DEFLATED, 15, 
+                       9, Z_DEFAULT_STRATEGY);
+    if (ret != Z_OK)
+        return -1;
+    s->zstream.avail_out = IOBUF_SIZE;
+    s->zstream.next_out = s->buf;
+    return 0;
+}
+
+static void ram_put_cblock(RamCompressState *s, const uint8_t *buf, int len)
+{
+    qemu_put_be16(s->f, RAM_CBLOCK_MAGIC);
+    qemu_put_be16(s->f, len);
+    qemu_put_buffer(s->f, buf, len);
+}
+
+static int ram_compress_buf(RamCompressState *s, const uint8_t *buf, int len)
+{
+    int ret;
+
+    s->zstream.avail_in = len;
+    s->zstream.next_in = (uint8_t *)buf;
+    while (s->zstream.avail_in > 0) {
+        ret = deflate(&s->zstream, Z_NO_FLUSH);
+        if (ret != Z_OK)
+            return -1;
+        if (s->zstream.avail_out == 0) {
+            ram_put_cblock(s, s->buf, IOBUF_SIZE);
+            s->zstream.avail_out = IOBUF_SIZE;
+            s->zstream.next_out = s->buf;
+        }
+    }
+    return 0;
+}
+
+static void ram_compress_close(RamCompressState *s)
+{
+    int len, ret;
+
+    /* compress last bytes */
+    for(;;) {
+        ret = deflate(&s->zstream, Z_FINISH);
+        if (ret == Z_OK || ret == Z_STREAM_END) {
+            len = IOBUF_SIZE - s->zstream.avail_out;
+            if (len > 0) {
+                ram_put_cblock(s, s->buf, len);
+            }
+            s->zstream.avail_out = IOBUF_SIZE;
+            s->zstream.next_out = s->buf;
+            if (ret == Z_STREAM_END)
+                break;
+        } else {
+            goto fail;
+        }
+    }
+fail:
+    deflateEnd(&s->zstream);
+}
+
+typedef struct RamDecompressState {
+    z_stream zstream;
+    QEMUFile *f;
+    uint8_t buf[IOBUF_SIZE];
+} RamDecompressState;
+
+static int ram_decompress_open(RamDecompressState *s, QEMUFile *f)
+{
+    int ret;
+    memset(s, 0, sizeof(*s));
+    s->f = f;
+    ret = inflateInit(&s->zstream);
+    if (ret != Z_OK)
+        return -1;
+    return 0;
+}
+
+static int ram_decompress_buf(RamDecompressState *s, uint8_t *buf, int len)
+{
+    int ret, clen;
+
+    s->zstream.avail_out = len;
+    s->zstream.next_out = buf;
+    while (s->zstream.avail_out > 0) {
+        if (s->zstream.avail_in == 0) {
+            if (qemu_get_be16(s->f) != RAM_CBLOCK_MAGIC)
+                return -1;
+            clen = qemu_get_be16(s->f);
+            if (clen > IOBUF_SIZE)
+                return -1;
+            qemu_get_buffer(s->f, s->buf, clen);
+            s->zstream.avail_in = clen;
+            s->zstream.next_in = s->buf;
+        }
+        ret = inflate(&s->zstream, Z_PARTIAL_FLUSH);
+        if (ret != Z_OK && ret != Z_STREAM_END) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void ram_decompress_close(RamDecompressState *s)
+{
+    inflateEnd(&s->zstream);
+}
+
+static void ram_save(QEMUFile *f, void *opaque)
+{
+    int i;
+    RamCompressState s1, *s = &s1;
+    uint8_t buf[10];
+    
+    qemu_put_be32(f, phys_ram_size);
+    if (ram_compress_open(s, f) < 0)
+        return;
+    for(i = 0; i < phys_ram_size; i+= BDRV_HASH_BLOCK_SIZE) {
+#if 0
+        if (tight_savevm_enabled) {
+            int64_t sector_num;
+            int j;
+
+            /* find if the memory block is available on a virtual
+               block device */
+            sector_num = -1;
+            for(j = 0; j < MAX_DISKS; j++) {
+                if (bs_table[j]) {
+                    sector_num = bdrv_hash_find(bs_table[j], 
+                                                phys_ram_base + i, BDRV_HASH_BLOCK_SIZE);
+                    if (sector_num >= 0)
+                        break;
+                }
+            }
+            if (j == MAX_DISKS)
+                goto normal_compress;
+            buf[0] = 1;
+            buf[1] = j;
+            cpu_to_be64wu((uint64_t *)(buf + 2), sector_num);
+            ram_compress_buf(s, buf, 10);
+        } else 
+#endif
+        {
+            //        normal_compress:
+            buf[0] = 0;
+            ram_compress_buf(s, buf, 1);
+            ram_compress_buf(s, phys_ram_base + i, BDRV_HASH_BLOCK_SIZE);
+        }
+    }
+    ram_compress_close(s);
+}
+
+static int ram_load(QEMUFile *f, void *opaque, int version_id)
+{
+    RamDecompressState s1, *s = &s1;
+    uint8_t buf[10];
+    int i;
+
+    if (version_id == 1)
+        return ram_load_v1(f, opaque);
+    if (version_id != 2)
+        return -EINVAL;
+    if (qemu_get_be32(f) != phys_ram_size)
+        return -EINVAL;
+    if (ram_decompress_open(s, f) < 0)
+        return -EINVAL;
+    for(i = 0; i < phys_ram_size; i+= BDRV_HASH_BLOCK_SIZE) {
+        if (ram_decompress_buf(s, buf, 1) < 0) {
+            fprintf(stderr, "Error while reading ram block header\n");
+            goto error;
+        }
+        if (buf[0] == 0) {
+            if (ram_decompress_buf(s, phys_ram_base + i, BDRV_HASH_BLOCK_SIZE) < 0) {
+                fprintf(stderr, "Error while reading ram block address=0x%08x", i);
+                goto error;
+            }
+        } else 
+#if 0
+        if (buf[0] == 1) {
+            int bs_index;
+            int64_t sector_num;
+
+            ram_decompress_buf(s, buf + 1, 9);
+            bs_index = buf[1];
+            sector_num = be64_to_cpupu((const uint64_t *)(buf + 2));
+            if (bs_index >= MAX_DISKS || bs_table[bs_index] == NULL) {
+                fprintf(stderr, "Invalid block device index %d\n", bs_index);
+                goto error;
+            }
+            if (bdrv_read(bs_table[bs_index], sector_num, phys_ram_base + i, 
+                          BDRV_HASH_BLOCK_SIZE / 512) < 0) {
+                fprintf(stderr, "Error while reading sector %d:%" PRId64 "\n", 
+                        bs_index, sector_num);
+                goto error;
+            }
+        } else 
+#endif
+        {
+        error:
+            printf("Error block header\n");
+            return -EINVAL;
+        }
+    }
+    ram_decompress_close(s);
+    return 0;
+}
+
+/***********************************************************/
+/* bottom halves (can be seen as timers which expire ASAP) */
 
-/* ??? Need to implement these.  */
-void cpu_save(QEMUFile *f, void *opaque)
-{
-}
+struct QEMUBH {
+    QEMUBHFunc *cb;
+    void *opaque;
+    int scheduled;
+    QEMUBH *next;
+};
 
-int cpu_load(QEMUFile *f, void *opaque, int version_id)
+static QEMUBH *first_bh = NULL;
+
+QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
 {
-    return 0;
+    QEMUBH *bh;
+    bh = qemu_mallocz(sizeof(QEMUBH));
+    if (!bh)
+        return NULL;
+    bh->cb = cb;
+    bh->opaque = opaque;
+    return bh;
 }
 
-#else
-
-#warning No CPU save/restore functions
-
-#endif
-
-/***********************************************************/
-/* ram save/restore */
-
-/* we just avoid storing empty pages */
-static void ram_put_page(QEMUFile *f, const uint8_t *buf, int len)
+int qemu_bh_poll(void)
 {
-    int i, v;
+    QEMUBH *bh, **pbh;
+    int ret;
 
-    v = buf[0];
-    for(i = 1; i < len; i++) {
-        if (buf[i] != v)
-            goto normal_save;
+    ret = 0;
+    for(;;) {
+        pbh = &first_bh;
+        bh = *pbh;
+        if (!bh)
+            break;
+        ret = 1;
+        *pbh = bh->next;
+        bh->scheduled = 0;
+        bh->cb(bh->opaque);
     }
-    qemu_put_byte(f, 1);
-    qemu_put_byte(f, v);
-    return;
- normal_save:
-    qemu_put_byte(f, 0); 
-    qemu_put_buffer(f, buf, len);
+    return ret;
 }
 
-static int ram_get_page(QEMUFile *f, uint8_t *buf, int len)
+void qemu_bh_schedule(QEMUBH *bh)
 {
-    int v;
+    CPUState *env = cpu_single_env;
+    if (bh->scheduled)
+        return;
+    bh->scheduled = 1;
+    bh->next = first_bh;
+    first_bh = bh;
 
-    v = qemu_get_byte(f);
-    switch(v) {
-    case 0:
-        if (qemu_get_buffer(f, buf, len) != len)
-            return -EIO;
-        break;
-    case 1:
-        v = qemu_get_byte(f);
-        memset(buf, v, len);
-        break;
-    default:
-        return -EINVAL;
+    /* stop the currently executing CPU to execute the BH ASAP */
+    if (env) {
+        cpu_interrupt(env, CPU_INTERRUPT_EXIT);
     }
-    return 0;
 }
 
-static void ram_save(QEMUFile *f, void *opaque)
+void qemu_bh_cancel(QEMUBH *bh)
 {
-    int i;
-    qemu_put_be32(f, phys_ram_size);
-    for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) {
-        ram_put_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
+    QEMUBH **pbh;
+    if (bh->scheduled) {
+        pbh = &first_bh;
+        while (*pbh != bh)
+            pbh = &(*pbh)->next;
+        *pbh = bh->next;
+        bh->scheduled = 0;
     }
 }
 
-static int ram_load(QEMUFile *f, void *opaque, int version_id)
+void qemu_bh_delete(QEMUBH *bh)
 {
-    int i, ret;
-
-    if (version_id != 1)
-        return -EINVAL;
-    if (qemu_get_be32(f) != phys_ram_size)
-        return -EINVAL;
-    for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) {
-        ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
-        if (ret)
-            return ret;
-    }
-    return 0;
+    qemu_bh_cancel(bh);
+    qemu_free(bh);
 }
 
 /***********************************************************/
@@ -3864,7 +5796,7 @@ void qemu_register_reset(QEMUResetHandler *func, void *opaque)
     *pre = re;
 }
 
-void qemu_system_reset(void)
+static void qemu_system_reset(void)
 {
     QEMUResetEntry *re;
 
@@ -3876,7 +5808,11 @@ void qemu_system_reset(void)
 
 void qemu_system_reset_request(void)
 {
-    reset_requested = 1;
+    if (no_reboot) {
+        shutdown_requested = 1;
+    } else {
+        reset_requested = 1;
+    }
     if (cpu_single_env)
         cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
 }
@@ -3898,21 +5834,39 @@ void qemu_system_powerdown_request(void)
 void main_loop_wait(int timeout)
 {
     IOHandlerRecord *ioh, *ioh_next;
-    fd_set rfds, wfds;
+    fd_set rfds, wfds, xfds;
     int ret, nfds;
     struct timeval tv;
+    PollingEntry *pe;
+
 
+    /* XXX: need to suppress polling by better using win32 events */
+    ret = 0;
+    for(pe = first_polling_entry; pe != NULL; pe = pe->next) {
+        ret |= pe->func(pe->opaque);
+    }
 #ifdef _WIN32
-    /* XXX: see how to merge it with the select. The constraint is
-       that the select must be interrupted by the timer */
-    if (timeout > 0)
-        Sleep(timeout);
+    if (ret == 0 && timeout > 0) {
+        int err;
+        WaitObjects *w = &wait_objects;
+        
+        ret = WaitForMultipleObjects(w->num, w->events, FALSE, timeout);
+        if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) {
+            if (w->func[ret - WAIT_OBJECT_0])
+                w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]);
+        } else if (ret == WAIT_TIMEOUT) {
+        } else {
+            err = GetLastError();
+            fprintf(stderr, "Wait error %d %d\n", ret, err);
+        }
+    }
 #endif
     /* poll any events */
     /* XXX: separate device handlers from system ones */
     nfds = -1;
     FD_ZERO(&rfds);
     FD_ZERO(&wfds);
+    FD_ZERO(&xfds);
     for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) {
         if (ioh->fd_read &&
             (!ioh->fd_read_poll ||
@@ -3934,7 +5888,12 @@ void main_loop_wait(int timeout)
 #else
     tv.tv_usec = timeout * 1000;
 #endif
-    ret = select(nfds + 1, &rfds, &wfds, NULL, &tv);
+#if defined(CONFIG_SLIRP)
+    if (slirp_inited) {
+        slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+    }
+#endif
+    ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
     if (ret > 0) {
         /* XXX: better handling of removal */
         for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) {
@@ -3947,27 +5906,18 @@ void main_loop_wait(int timeout)
             }
         }
     }
-
 #if defined(CONFIG_SLIRP)
-    /* XXX: merge with the previous select() */
     if (slirp_inited) {
-        fd_set rfds, wfds, xfds;
-        int nfds;
-        struct timeval tv;
-        
-        nfds = -1;
-        FD_ZERO(&rfds);
-        FD_ZERO(&wfds);
-        FD_ZERO(&xfds);
-        slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
-        tv.tv_sec = 0;
-        tv.tv_usec = 0;
-        ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
-        if (ret >= 0) {
-            slirp_select_poll(&rfds, &wfds, &xfds);
+        if (ret < 0) {
+            FD_ZERO(&rfds);
+            FD_ZERO(&wfds);
+            FD_ZERO(&xfds);
         }
+        slirp_select_poll(&rfds, &wfds, &xfds);
     }
 #endif
+    qemu_aio_poll();
+    qemu_bh_poll();
 
     if (vm_running) {
         qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL], 
@@ -3986,6 +5936,9 @@ static CPUState *cur_cpu;
 int main_loop(void)
 {
     int ret, timeout;
+#ifdef CONFIG_PROFILER
+    int64_t ti;
+#endif
     CPUState *env;
 
     cur_cpu = first_cpu;
@@ -3998,7 +5951,13 @@ int main_loop(void)
                 env = env->next_cpu;
                 if (!env)
                     env = first_cpu;
+#ifdef CONFIG_PROFILER
+                ti = profile_getclock();
+#endif
                 ret = cpu_exec(env);
+#ifdef CONFIG_PROFILER
+                qemu_time += profile_getclock() - ti;
+#endif
                 if (ret != EXCP_HALTED)
                     break;
                 /* all CPUs are halted ? */
@@ -4035,7 +5994,13 @@ int main_loop(void)
         } else {
             timeout = 10;
         }
+#ifdef CONFIG_PROFILER
+        ti = profile_getclock();
+#endif
         main_loop_wait(timeout);
+#ifdef CONFIG_PROFILER
+        dev_time += profile_getclock() - ti;
+#endif
     }
     cpu_disable_ticks();
     return ret;
@@ -4043,7 +6008,7 @@ int main_loop(void)
 
 void help(void)
 {
-    printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2005 Fabrice Bellard\n"
+    printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2006 Fabrice Bellard\n"
            "usage: %s [options] [disk_image]\n"
            "\n"
            "'disk_image' is a raw hard image image for IDE hard disk 0\n"
@@ -4054,13 +6019,19 @@ void help(void)
            "-hda/-hdb file  use 'file' as IDE hard disk 0/1 image\n"
            "-hdc/-hdd file  use 'file' as IDE hard disk 2/3 image\n"
            "-cdrom file     use 'file' as IDE cdrom image (cdrom is ide1 master)\n"
-           "-boot [a|c|d]   boot on floppy (a), hard disk (c) or CD-ROM (d)\n"
-          "-snapshot       write to temporary files instead of disk image files\n"
+           "-boot [a|c|d|n] boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)\n"
+           "-snapshot       write to temporary files instead of disk image files\n"
+#ifdef CONFIG_SDL
+           "-no-quit        disable SDL window close capability\n"
+#endif
+#ifdef TARGET_I386
+           "-no-fd-bootchk  disable boot signature checking for floppy disks\n"
+#endif
            "-m megs         set virtual RAM size to megs MB [default=%d]\n"
            "-smp n          set the number of CPUs to 'n' [default=1]\n"
            "-nographic      disable graphical output and redirect serial I/Os to console\n"
 #ifndef _WIN32
-          "-k language     use keyboard layout (for example \"fr\" for French)\n"
+           "-k language     use keyboard layout (for example \"fr\" for French)\n"
 #endif
 #ifdef HAS_AUDIO
            "-audio-help     print list of audio drivers and their options\n"
@@ -4081,22 +6052,27 @@ void help(void)
 #endif
            "\n"
            "Network options:\n"
-           "-net nic[,vlan=n][,macaddr=addr]\n"
+           "-net nic[,vlan=n][,macaddr=addr][,model=type]\n"
            "                create a new Network Interface Card and connect it to VLAN 'n'\n"
 #ifdef CONFIG_SLIRP
-           "-net user[,vlan=n]\n"
-           "                connect the user mode network stack to VLAN 'n'\n"
+           "-net user[,vlan=n][,hostname=host]\n"
+           "                connect the user mode network stack to VLAN 'n' and send\n"
+           "                hostname 'host' to DHCP clients\n"
 #endif
-#ifndef _WIN32
+#ifdef _WIN32
+           "-net tap[,vlan=n],ifname=name\n"
+           "                connect the host TAP network interface to VLAN 'n'\n"
+#else
            "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file]\n"
            "                connect the host TAP network interface to VLAN 'n' and use\n"
            "                the network script 'file' (default=%s);\n"
+           "                use 'script=no' to disable script execution;\n"
            "                use 'fd=h' to connect to an already opened TAP interface\n"
+#endif
            "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n"
            "                connect the vlan 'n' to another VLAN using a socket connection\n"
            "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n"
            "                connect the vlan 'n' to multicast maddr and port\n"
-#endif
            "-net none       use it alone to have zero network devices; if no -net option\n"
            "                is provided, the default is '-net nic -net user'\n"
            "\n"
@@ -4125,8 +6101,9 @@ void help(void)
            "-d item1,...    output log to %s (use -d ? for a list of log items)\n"
            "-hdachs c,h,s[,t]  force hard disk 0 physical geometry and the optional BIOS\n"
            "                translation (t=none or lba) (usually qemu can guess them)\n"
-           "-L path         set the directory for the BIOS and VGA BIOS\n"
+           "-L path         set the directory for the BIOS, VGA BIOS and keymaps\n"
 #ifdef USE_KQEMU
+           "-kernel-kqemu   enable KQEMU full virtualization (default is user mode only)\n"
            "-no-kqemu       disable KQEMU kernel module usage\n"
 #endif
 #ifdef USE_CODE_COPY
@@ -4135,8 +6112,15 @@ void help(void)
 #ifdef TARGET_I386
            "-std-vga        simulate a standard VGA card with VESA Bochs Extensions\n"
            "                (default is CL-GD5446 PCI VGA)\n"
+           "-no-acpi        disable ACPI\n"
 #endif
+           "-no-reboot      exit instead of rebooting\n"
            "-loadvm file    start right away with a saved state (loadvm in monitor)\n"
+          "-vnc display    start a VNC server on display\n"
+#ifndef _WIN32
+          "-daemonize      daemonize QEMU after initializing\n"
+#endif
+          "-option-rom rom load a file, rom, into the option ROM space\n"
            "\n"
            "During emulation, the following keys are useful:\n"
            "ctrl-alt-f      toggle full screen\n"
@@ -4145,23 +6129,13 @@ void help(void)
            "\n"
            "When using -nographic, press 'ctrl-a h' to get some help.\n"
            ,
-#ifdef CONFIG_SOFTMMU
            "qemu",
-#else
-           "qemu-fast",
-#endif
            DEFAULT_RAM_SIZE,
 #ifndef _WIN32
            DEFAULT_NETWORK_SCRIPT,
 #endif
            DEFAULT_GDBSTUB_PORT,
            "/tmp/qemu.log");
-#ifndef CONFIG_SOFTMMU
-    printf("\n"
-           "NOTE: this version of QEMU is faster but it needs slightly patched OSes to\n"
-           "work. Please use the 'qemu' executable to have a more accurate (but slower)\n"
-           "PC emulation.\n");
-#endif
     exit(1);
 }
 
@@ -4180,6 +6154,9 @@ enum {
     QEMU_OPTION_cdrom,
     QEMU_OPTION_boot,
     QEMU_OPTION_snapshot,
+#ifdef TARGET_I386
+    QEMU_OPTION_no_fd_bootchk,
+#endif
     QEMU_OPTION_m,
     QEMU_OPTION_nographic,
 #ifdef HAS_AUDIO
@@ -4213,12 +6190,20 @@ enum {
     QEMU_OPTION_parallel,
     QEMU_OPTION_loadvm,
     QEMU_OPTION_full_screen,
+    QEMU_OPTION_no_quit,
     QEMU_OPTION_pidfile,
     QEMU_OPTION_no_kqemu,
+    QEMU_OPTION_kernel_kqemu,
     QEMU_OPTION_win2k_hack,
     QEMU_OPTION_usb,
     QEMU_OPTION_usbdevice,
     QEMU_OPTION_smp,
+    QEMU_OPTION_vnc,
+    QEMU_OPTION_no_acpi,
+    QEMU_OPTION_no_reboot,
+    QEMU_OPTION_daemonize,
+    QEMU_OPTION_option_rom,
+    QEMU_OPTION_semihosting
 };
 
 typedef struct QEMUOption {
@@ -4229,6 +6214,7 @@ typedef struct QEMUOption {
 
 const QEMUOption qemu_options[] = {
     { "h", 0, QEMU_OPTION_h },
+    { "help", 0, QEMU_OPTION_h },
 
     { "M", HAS_ARG, QEMU_OPTION_M },
     { "fda", HAS_ARG, QEMU_OPTION_fda },
@@ -4240,6 +6226,9 @@ const QEMUOption qemu_options[] = {
     { "cdrom", HAS_ARG, QEMU_OPTION_cdrom },
     { "boot", HAS_ARG, QEMU_OPTION_boot },
     { "snapshot", 0, QEMU_OPTION_snapshot },
+#ifdef TARGET_I386
+    { "no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk },
+#endif
     { "m", HAS_ARG, QEMU_OPTION_m },
     { "nographic", 0, QEMU_OPTION_nographic },
     { "k", HAS_ARG, QEMU_OPTION_k },
@@ -4270,6 +6259,7 @@ const QEMUOption qemu_options[] = {
     { "no-code-copy", 0, QEMU_OPTION_no_code_copy },
 #ifdef USE_KQEMU
     { "no-kqemu", 0, QEMU_OPTION_no_kqemu },
+    { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu },
 #endif
 #if defined(TARGET_PPC) || defined(TARGET_SPARC)
     { "g", 1, QEMU_OPTION_g },
@@ -4281,14 +6271,25 @@ const QEMUOption qemu_options[] = {
     { "parallel", 1, QEMU_OPTION_parallel },
     { "loadvm", HAS_ARG, QEMU_OPTION_loadvm },
     { "full-screen", 0, QEMU_OPTION_full_screen },
+#ifdef CONFIG_SDL
+    { "no-quit", 0, QEMU_OPTION_no_quit },
+#endif
     { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
     { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
     { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
     { "smp", HAS_ARG, QEMU_OPTION_smp },
-    
+    { "vnc", HAS_ARG, QEMU_OPTION_vnc },
+
     /* temporary options */
     { "usb", 0, QEMU_OPTION_usb },
     { "cirrusvga", 0, QEMU_OPTION_cirrusvga },
+    { "no-acpi", 0, QEMU_OPTION_no_acpi },
+    { "no-reboot", 0, QEMU_OPTION_no_reboot },
+    { "daemonize", 0, QEMU_OPTION_daemonize },
+    { "option-rom", HAS_ARG, QEMU_OPTION_option_rom },
+#if defined(TARGET_ARM)
+    { "semihosting", 0, QEMU_OPTION_semihosting },
+#endif
     { NULL },
 };
 
@@ -4350,6 +6351,7 @@ void register_machines(void)
     qemu_register_machine(&prep_machine);
 #elif defined(TARGET_MIPS)
     qemu_register_machine(&mips_machine);
+    qemu_register_machine(&mips_malta_machine);
 #elif defined(TARGET_SPARC)
 #ifdef TARGET_SPARC64
     qemu_register_machine(&sun4u_machine);
@@ -4357,7 +6359,13 @@ void register_machines(void)
     qemu_register_machine(&sun4m_machine);
 #endif
 #elif defined(TARGET_ARM)
-    qemu_register_machine(&integratorcp_machine);
+    qemu_register_machine(&integratorcp926_machine);
+    qemu_register_machine(&integratorcp1026_machine);
+    qemu_register_machine(&versatilepb_machine);
+    qemu_register_machine(&versatileab_machine);
+    qemu_register_machine(&realview_machine);
+#elif defined(TARGET_SH4)
+    qemu_register_machine(&shix_machine);
 #else
 #error unsupported CPU
 #endif
@@ -4365,6 +6373,15 @@ void register_machines(void)
 
 #ifdef HAS_AUDIO
 struct soundhw soundhw[] = {
+#ifdef TARGET_I386
+    {
+        "pcspk",
+        "PC speaker",
+        0,
+        1,
+        { .init_isa = pcspk_audio_init }
+    },
+#endif
     {
         "sb16",
         "Creative Sound Blaster 16",
@@ -4467,12 +6484,21 @@ static void select_soundhw (const char *optarg)
 }
 #endif
 
+#ifdef _WIN32
+static BOOL WINAPI qemu_ctrl_handler(DWORD type)
+{
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+#endif
+
 #define MAX_NET_CLIENTS 32
 
 int main(int argc, char **argv)
 {
 #ifdef CONFIG_GDBSTUB
-    int use_gdbstub, gdbstub_port;
+    int use_gdbstub;
+    char gdbstub_port_name[128];
 #endif
     int i, cdrom_index;
     int snapshot, linux_boot;
@@ -4481,7 +6507,6 @@ int main(int argc, char **argv)
     const char *kernel_filename, *kernel_cmdline;
     DisplayState *ds = &display_state;
     int cyls, heads, secs, translation;
-    int start_emulation = 1;
     char net_clients[MAX_NET_CLIENTS][256];
     int nb_net_clients;
     int optind;
@@ -4494,14 +6519,41 @@ int main(int argc, char **argv)
     int parallel_device_index;
     const char *loadvm = NULL;
     QEMUMachine *machine;
-    char usb_devices[MAX_VM_USB_PORTS][128];
+    char usb_devices[MAX_USB_CMDLINE][128];
     int usb_devices_index;
+    int fds[2];
 
     LIST_INIT (&vm_change_state_head);
-#if !defined(CONFIG_SOFTMMU)
-    /* we never want that malloc() uses mmap() */
-    mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
+#ifndef _WIN32
+    {
+        struct sigaction act;
+        sigfillset(&act.sa_mask);
+        act.sa_flags = 0;
+        act.sa_handler = SIG_IGN;
+        sigaction(SIGPIPE, &act, NULL);
+    }
+#else
+    SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE);
+    /* Note: cpu_interrupt() is currently not SMP safe, so we force
+       QEMU to run on a single CPU */
+    {
+        HANDLE h;
+        DWORD mask, smask;
+        int i;
+        h = GetCurrentProcess();
+        if (GetProcessAffinityMask(h, &mask, &smask)) {
+            for(i = 0; i < 32; i++) {
+                if (mask & (1 << i))
+                    break;
+            }
+            if (i != 32) {
+                mask = 1 << i;
+                SetProcessAffinityMask(h, mask);
+            }
+        }
+    }
 #endif
+
     register_machines();
     machine = first_machine;
     initrd_filename = NULL;
@@ -4514,7 +6566,7 @@ int main(int argc, char **argv)
     bios_size = BIOS_SIZE;
 #ifdef CONFIG_GDBSTUB
     use_gdbstub = 0;
-    gdbstub_port = DEFAULT_GDBSTUB_PORT;
+    sprintf(gdbstub_port_name, "%d", DEFAULT_GDBSTUB_PORT);
 #endif
     snapshot = 0;
     nographic = 0;
@@ -4557,6 +6609,9 @@ int main(int argc, char **argv)
             const QEMUOption *popt;
 
             optind++;
+            /* Treat --foo the same as -foo.  */
+            if (r[1] == '-')
+                r++;
             popt = qemu_options;
             for(;;) {
                 if (!popt->name) {
@@ -4666,7 +6721,7 @@ int main(int argc, char **argv)
             case QEMU_OPTION_boot:
                 boot_device = optarg[0];
                 if (boot_device != 'a' && 
-#ifdef TARGET_SPARC
+#if defined(TARGET_SPARC) || defined(TARGET_I386)
                    // Network boot
                    boot_device != 'n' &&
 #endif
@@ -4681,6 +6736,11 @@ int main(int argc, char **argv)
             case QEMU_OPTION_fdb:
                 fd_filename[1] = optarg;
                 break;
+#ifdef TARGET_I386
+            case QEMU_OPTION_no_fd_bootchk:
+                fd_bootchk = 0;
+                break;
+#endif
             case QEMU_OPTION_no_code_copy:
                 code_copy_enabled = 0;
                 break;
@@ -4750,14 +6810,14 @@ int main(int argc, char **argv)
                 use_gdbstub = 1;
                 break;
             case QEMU_OPTION_p:
-                gdbstub_port = atoi(optarg);
+                pstrcpy(gdbstub_port_name, sizeof(gdbstub_port_name), optarg);
                 break;
 #endif
             case QEMU_OPTION_L:
                 bios_dir = optarg;
                 break;
             case QEMU_OPTION_S:
-                start_emulation = 0;
+                autostart = 0;
                 break;
            case QEMU_OPTION_k:
                keyboard_layout = optarg;
@@ -4832,6 +6892,11 @@ int main(int argc, char **argv)
             case QEMU_OPTION_full_screen:
                 full_screen = 1;
                 break;
+#ifdef CONFIG_SDL
+            case QEMU_OPTION_no_quit:
+                no_quit = 1;
+                break;
+#endif
             case QEMU_OPTION_pidfile:
                 create_pidfile(optarg);
                 break;
@@ -4844,13 +6909,16 @@ int main(int argc, char **argv)
             case QEMU_OPTION_no_kqemu:
                 kqemu_allowed = 0;
                 break;
+            case QEMU_OPTION_kernel_kqemu:
+                kqemu_allowed = 2;
+                break;
 #endif
             case QEMU_OPTION_usb:
                 usb_enabled = 1;
                 break;
             case QEMU_OPTION_usbdevice:
                 usb_enabled = 1;
-                if (usb_devices_index >= MAX_VM_USB_PORTS) {
+                if (usb_devices_index >= MAX_USB_CMDLINE) {
                     fprintf(stderr, "Too many USB devices\n");
                     exit(1);
                 }
@@ -4866,23 +6934,94 @@ int main(int argc, char **argv)
                     exit(1);
                 }
                 break;
+           case QEMU_OPTION_vnc:
+               vnc_display = optarg;
+               break;
+            case QEMU_OPTION_no_acpi:
+                acpi_enabled = 0;
+                break;
+            case QEMU_OPTION_no_reboot:
+                no_reboot = 1;
+                break;
+           case QEMU_OPTION_daemonize:
+               daemonize = 1;
+               break;
+           case QEMU_OPTION_option_rom:
+               if (nb_option_roms >= MAX_OPTION_ROMS) {
+                   fprintf(stderr, "Too many option ROMs\n");
+                   exit(1);
+               }
+               option_rom[nb_option_roms] = optarg;
+               nb_option_roms++;
+               break;
+            case QEMU_OPTION_semihosting:
+                semihosting_enabled = 1;
+                break;
             }
         }
     }
 
+#ifndef _WIN32
+    if (daemonize && !nographic && vnc_display == NULL) {
+       fprintf(stderr, "Can only daemonize if using -nographic or -vnc\n");
+       daemonize = 0;
+    }
+
+    if (daemonize) {
+       pid_t pid;
+
+       if (pipe(fds) == -1)
+           exit(1);
+
+       pid = fork();
+       if (pid > 0) {
+           uint8_t status;
+           ssize_t len;
+
+           close(fds[1]);
+
+       again:
+           len = read(fds[0], &status, 1);
+           if (len == -1 && (errno == EINTR))
+               goto again;
+           
+           if (len != 1 || status != 0)
+               exit(1);
+           else
+               exit(0);
+       } else if (pid < 0)
+           exit(1);
+
+       setsid();
+
+       pid = fork();
+       if (pid > 0)
+           exit(0);
+       else if (pid < 0)
+           exit(1);
+
+       umask(027);
+       chdir("/");
+
+        signal(SIGTSTP, SIG_IGN);
+        signal(SIGTTOU, SIG_IGN);
+        signal(SIGTTIN, SIG_IGN);
+    }
+#endif
+
 #ifdef USE_KQEMU
     if (smp_cpus > 1)
         kqemu_allowed = 0;
 #endif
     linux_boot = (kernel_filename != NULL);
-        
-    if (!linux_boot && 
+
+    if (!linux_boot &&
         hd_filename[0] == '\0' && 
         (cdrom_index >= 0 && hd_filename[cdrom_index] == '\0') &&
         fd_filename[0] == '\0')
         help();
-    
-    /* boot to cd by default if no hard disk */
+
+    /* boot to floppy or the default cd if no hard disk defined yet */
     if (hd_filename[0] == '\0' && boot_device == 'c') {
         if (fd_filename[0] != '\0')
             boot_device = 'a';
@@ -4890,16 +7029,12 @@ int main(int argc, char **argv)
             boot_device = 'd';
     }
 
-#if !defined(CONFIG_SOFTMMU)
-    /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
-    {
-        static uint8_t stdout_buf[4096];
-        setvbuf(stdout, stdout_buf, _IOLBF, sizeof(stdout_buf));
-    }
-#else
     setvbuf(stdout, NULL, _IOLBF, 0);
-#endif
     
+    init_timers();
+    init_timer_alarm();
+    qemu_aio_init();
+
 #ifdef _WIN32
     socket_init();
 #endif
@@ -4919,48 +7054,45 @@ int main(int argc, char **argv)
             exit(1);
     }
 
+#ifdef TARGET_I386
+    if (boot_device == 'n') {
+       for (i = 0; i < nb_nics; i++) {
+           const char *model = nd_table[i].model;
+           char buf[1024];
+           if (model == NULL)
+               model = "ne2k_pci";
+           snprintf(buf, sizeof(buf), "%s/pxe-%s.bin", bios_dir, model);
+           if (get_image_size(buf) > 0) {
+               option_rom[nb_option_roms] = strdup(buf);
+               nb_option_roms++;
+               break;
+           }
+       }
+       if (i == nb_nics) {
+           fprintf(stderr, "No valid PXE rom found for network device\n");
+           exit(1);
+       }
+       boot_device = 'c'; /* to prevent confusion by the BIOS */
+    }
+#endif
+
     /* init the memory */
     phys_ram_size = ram_size + vga_ram_size + bios_size;
 
-#ifdef CONFIG_SOFTMMU
+    for (i = 0; i < nb_option_roms; i++) {
+       int ret = get_image_size(option_rom[i]);
+       if (ret == -1) {
+           fprintf(stderr, "Could not load option rom '%s'\n", option_rom[i]);
+           exit(1);
+       }
+       phys_ram_size += ret;
+    }
+
     phys_ram_base = qemu_vmalloc(phys_ram_size);
     if (!phys_ram_base) {
         fprintf(stderr, "Could not allocate physical memory\n");
         exit(1);
     }
-#else
-    /* as we must map the same page at several addresses, we must use
-       a fd */
-    {
-        const char *tmpdir;
-
-        tmpdir = getenv("QEMU_TMPDIR");
-        if (!tmpdir)
-            tmpdir = "/tmp";
-        snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/vlXXXXXX", tmpdir);
-        if (mkstemp(phys_ram_file) < 0) {
-            fprintf(stderr, "Could not create temporary memory file '%s'\n", 
-                    phys_ram_file);
-            exit(1);
-        }
-        phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
-        if (phys_ram_fd < 0) {
-            fprintf(stderr, "Could not open temporary memory file '%s'\n", 
-                    phys_ram_file);
-            exit(1);
-        }
-        ftruncate(phys_ram_fd, phys_ram_size);
-        unlink(phys_ram_file);
-        phys_ram_base = mmap(get_mmap_addr(phys_ram_size), 
-                             phys_ram_size, 
-                             PROT_WRITE | PROT_READ, MAP_SHARED | MAP_FIXED, 
-                             phys_ram_fd, 0);
-        if (phys_ram_base == MAP_FAILED) {
-            fprintf(stderr, "Could not map physical memory\n");
-            exit(1);
-        }
-    }
-#endif
 
     /* we always create the cdrom drive, even if no disk is there */
     bdrv_init();
@@ -4977,7 +7109,7 @@ int main(int argc, char **argv)
                 snprintf(buf, sizeof(buf), "hd%c", i + 'a');
                 bs_table[i] = bdrv_new(buf);
             }
-            if (bdrv_open(bs_table[i], hd_filename[i], snapshot) < 0) {
+            if (bdrv_open(bs_table[i], hd_filename[i], snapshot ? BDRV_O_SNAPSHOT : 0) < 0) {
                 fprintf(stderr, "qemu: could not open hard disk image '%s'\n",
                         hd_filename[i]);
                 exit(1);
@@ -5002,7 +7134,8 @@ int main(int argc, char **argv)
                 bdrv_set_type_hint(fd_table[i], BDRV_TYPE_FLOPPY);
             }
             if (fd_filename[i] != '\0') {
-                if (bdrv_open(fd_table[i], fd_filename[i], snapshot) < 0) {
+                if (bdrv_open(fd_table[i], fd_filename[i],
+                              snapshot ? BDRV_O_SNAPSHOT : 0) < 0) {
                     fprintf(stderr, "qemu: could not open floppy disk image '%s'\n",
                             fd_filename[i]);
                     exit(1);
@@ -5011,26 +7144,16 @@ int main(int argc, char **argv)
         }
     }
 
-    /* init USB devices */
-    if (usb_enabled) {
-        vm_usb_hub = usb_hub_init(vm_usb_ports, MAX_VM_USB_PORTS);
-        for(i = 0; i < usb_devices_index; i++) {
-            if (usb_device_add(usb_devices[i]) < 0) {
-                fprintf(stderr, "Warning: could not add USB device %s\n",
-                        usb_devices[i]);
-            }
-        }
-    }
-
-    register_savevm("timer", 0, 1, timer_save, timer_load, NULL);
-    register_savevm("ram", 0, 1, ram_save, ram_load, NULL);
+    register_savevm("timer", 0, 2, timer_save, timer_load, NULL);
+    register_savevm("ram", 0, 2, ram_save, ram_load, NULL);
 
     init_ioports();
-    cpu_calibrate_ticks();
 
     /* terminal init */
     if (nographic) {
         dumb_display_init(ds);
+    } else if (vnc_display != NULL) {
+       vnc_display_init(ds, vnc_display);
     } else {
 #if defined(CONFIG_SDL)
         sdl_display_init(ds, full_screen);
@@ -5041,8 +7164,6 @@ int main(int argc, char **argv)
 #endif
     }
 
-    vga_console = graphic_console_init(ds);
-    
     monitor_hd = qemu_chr_open(monitor_device);
     if (!monitor_hd) {
         fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
@@ -5051,104 +7172,102 @@ int main(int argc, char **argv)
     monitor_init(monitor_hd, !nographic);
 
     for(i = 0; i < MAX_SERIAL_PORTS; i++) {
-        if (serial_devices[i][0] != '\0') {
-            serial_hds[i] = qemu_chr_open(serial_devices[i]);
+        const char *devname = serial_devices[i];
+        if (devname[0] != '\0' && strcmp(devname, "none")) {
+            serial_hds[i] = qemu_chr_open(devname);
             if (!serial_hds[i]) {
                 fprintf(stderr, "qemu: could not open serial device '%s'\n", 
-                        serial_devices[i]);
+                        devname);
                 exit(1);
             }
-            if (!strcmp(serial_devices[i], "vc"))
-                qemu_chr_printf(serial_hds[i], "serial%d console\n", i);
+            if (!strcmp(devname, "vc"))
+                qemu_chr_printf(serial_hds[i], "serial%d console\r\n", i);
         }
     }
 
     for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
-        if (parallel_devices[i][0] != '\0') {
-            parallel_hds[i] = qemu_chr_open(parallel_devices[i]);
+        const char *devname = parallel_devices[i];
+        if (devname[0] != '\0' && strcmp(devname, "none")) {
+            parallel_hds[i] = qemu_chr_open(devname);
             if (!parallel_hds[i]) {
                 fprintf(stderr, "qemu: could not open parallel device '%s'\n", 
-                        parallel_devices[i]);
+                        devname);
                 exit(1);
             }
-            if (!strcmp(parallel_devices[i], "vc"))
-                qemu_chr_printf(parallel_hds[i], "parallel%d console\n", i);
-        }
-    }
-
-    /* setup cpu signal handlers for MMU / self modifying code handling */
-#if !defined(CONFIG_SOFTMMU)
-    
-#if defined (TARGET_I386) && defined(USE_CODE_COPY)
-    {
-        stack_t stk;
-        signal_stack = memalign(16, SIGNAL_STACK_SIZE);
-        stk.ss_sp = signal_stack;
-        stk.ss_size = SIGNAL_STACK_SIZE;
-        stk.ss_flags = 0;
-
-        if (sigaltstack(&stk, NULL) < 0) {
-            perror("sigaltstack");
-            exit(1);
+            if (!strcmp(devname, "vc"))
+                qemu_chr_printf(parallel_hds[i], "parallel%d console\r\n", i);
         }
     }
-#endif
-    {
-        struct sigaction act;
-        
-        sigfillset(&act.sa_mask);
-        act.sa_flags = SA_SIGINFO;
-#if defined (TARGET_I386) && defined(USE_CODE_COPY)
-        act.sa_flags |= SA_ONSTACK;
-#endif
-        act.sa_sigaction = host_segv_handler;
-        sigaction(SIGSEGV, &act, NULL);
-        sigaction(SIGBUS, &act, NULL);
-#if defined (TARGET_I386) && defined(USE_CODE_COPY)
-        sigaction(SIGFPE, &act, NULL);
-#endif
-    }
-#endif
-
-#ifndef _WIN32
-    {
-        struct sigaction act;
-        sigfillset(&act.sa_mask);
-        act.sa_flags = 0;
-        act.sa_handler = SIG_IGN;
-        sigaction(SIGPIPE, &act, NULL);
-    }
-#endif
-    init_timers();
 
     machine->init(ram_size, vga_ram_size, boot_device,
                   ds, fd_filename, snapshot,
                   kernel_filename, kernel_cmdline, initrd_filename);
 
+    /* init USB devices */
+    if (usb_enabled) {
+        for(i = 0; i < usb_devices_index; i++) {
+            if (usb_device_add(usb_devices[i]) < 0) {
+                fprintf(stderr, "Warning: could not add USB device %s\n",
+                        usb_devices[i]);
+            }
+        }
+    }
+
     gui_timer = qemu_new_timer(rt_clock, gui_update, NULL);
     qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock));
 
 #ifdef CONFIG_GDBSTUB
     if (use_gdbstub) {
-        if (gdbserver_start(gdbstub_port) < 0) {
-            fprintf(stderr, "Could not open gdbserver socket on port %d\n", 
-                    gdbstub_port);
+        CharDriverState *chr;
+        int port;
+
+        port = atoi(gdbstub_port_name);
+        if (port != 0)
+            sprintf(gdbstub_port_name, "tcp::%d,nowait,nodelay,server", port);
+        chr = qemu_chr_open(gdbstub_port_name);
+        if (!chr) {
+            fprintf(stderr, "qemu: could not open gdbstub device '%s'\n",
+                    gdbstub_port_name);
             exit(1);
-        } else {
-            printf("Waiting gdb connection on port %d\n", gdbstub_port);
         }
+        gdbserver_start(chr);
     } else 
 #endif
     if (loadvm)
-        qemu_loadvm(loadvm);
+        do_loadvm(loadvm);
 
     {
         /* XXX: simplify init */
         read_passwords();
-        if (start_emulation) {
+        if (autostart) {
             vm_start();
         }
     }
+
+    if (daemonize) {
+       uint8_t status = 0;
+       ssize_t len;
+       int fd;
+
+    again1:
+       len = write(fds[1], &status, 1);
+       if (len == -1 && (errno == EINTR))
+           goto again1;
+
+       if (len != 1)
+           exit(1);
+
+       fd = open("/dev/null", O_RDWR);
+       if (fd == -1)
+           exit(1);
+
+       dup2(fd, 0);
+       dup2(fd, 1);
+       dup2(fd, 2);
+
+       close(fd);
+    }
+
     main_loop();
     quit_timers();
     return 0;