[Spice-commits] 61 commits - Makefile.objs Makefile.target block-migration.c block/nbd.c block/qed-check.c block/qed.h configure cpu-all.h darwin-user/commpage.c exec.c fsdev/file-op-9p.h fsdev/qemu-fsdev.h hw/9pfs hw/file-op-9p.h hw/ide hw/ioapic.c hw/kvmclock.c hw/milkymist-hw.h hw/milkymist-minimac.c hw/milkymist-minimac2.c hw/milkymist-sysctl.c hw/milkymist-vgafb.c hw/milkymist.c hw/pflash_cfi02.c hw/qdev-properties.c hw/rtl8139.c hw/virtio-9p-debug.c hw/virtio-9p-debug.h hw/virtio-9p-local.c hw/virtio-9p-posix-acl.c hw/virtio-9p-xattr-user.c hw/virtio-9p-xattr.c hw/virtio-9p-xattr.h hw/virtio-9p.c hw/virtio-9p.h hw/virtio-console.c hw/virtio-serial-bus.c kvm-all.c linux-user/strace.c linux-user/syscall.c qemu-char.c qemu-char.h qemu-img.c qemu-progress.c qemu-timer.c qemu-timer.h spice-qemu-char.c target-arm/translate.c target-i386/cpuid.c target-i386/kvm.c target-lm32/helper.c target-lm32/translate.c trace-events vl.c

Gerd Hoffmann kraxel at kemper.freedesktop.org
Tue May 3 07:23:23 PDT 2011


 Makefile.objs                  |   12 
 Makefile.target                |    8 
 block-migration.c              |   23 
 block/nbd.c                    |    4 
 block/qed-check.c              |    4 
 block/qed.h                    |    2 
 configure                      |    2 
 cpu-all.h                      |   14 
 darwin-user/commpage.c         |    2 
 exec.c                         |   18 
 fsdev/file-op-9p.h             |  107 +
 fsdev/qemu-fsdev.h             |    2 
 hw/9pfs/virtio-9p-debug.c      |  645 +++++++
 hw/9pfs/virtio-9p-debug.h      |    6 
 hw/9pfs/virtio-9p-local.c      |  563 ++++++
 hw/9pfs/virtio-9p-posix-acl.c  |  151 +
 hw/9pfs/virtio-9p-xattr-user.c |  109 +
 hw/9pfs/virtio-9p-xattr.c      |  159 +
 hw/9pfs/virtio-9p-xattr.h      |  102 +
 hw/9pfs/virtio-9p.c            | 3747 +++++++++++++++++++++++++++++++++++++++++
 hw/9pfs/virtio-9p.h            |  507 +++++
 hw/file-op-9p.h                |  107 -
 hw/ide/atapi.c                 | 1138 ++++++++++++
 hw/ide/core.c                  | 1067 -----------
 hw/ide/internal.h              |   10 
 hw/ioapic.c                    |    5 
 hw/kvmclock.c                  |    6 
 hw/milkymist-hw.h              |   20 
 hw/milkymist-minimac.c         |  568 ------
 hw/milkymist-minimac2.c        |  539 +++++
 hw/milkymist-sysctl.c          |   26 
 hw/milkymist-vgafb.c           |    3 
 hw/milkymist.c                 |    2 
 hw/pflash_cfi02.c              |   12 
 hw/qdev-properties.c           |    4 
 hw/rtl8139.c                   |    3 
 hw/virtio-9p-debug.c           |  645 -------
 hw/virtio-9p-debug.h           |    6 
 hw/virtio-9p-local.c           |  563 ------
 hw/virtio-9p-posix-acl.c       |  140 -
 hw/virtio-9p-xattr-user.c      |  109 -
 hw/virtio-9p-xattr.c           |  159 -
 hw/virtio-9p-xattr.h           |  102 -
 hw/virtio-9p.c                 | 3741 ----------------------------------------
 hw/virtio-9p.h                 |  507 -----
 hw/virtio-console.c            |   18 
 hw/virtio-serial-bus.c         |   23 
 kvm-all.c                      |   30 
 linux-user/strace.c            |   12 
 linux-user/syscall.c           |   13 
 qemu-char.c                    |   24 
 qemu-char.h                    |    6 
 qemu-img.c                     |   15 
 qemu-progress.c                |   64 
 qemu-timer.c                   |  155 +
 qemu-timer.h                   |    1 
 spice-qemu-char.c              |   14 
 target-arm/translate.c         |   27 
 target-i386/cpuid.c            |   16 
 target-i386/kvm.c              |   80 
 target-lm32/helper.c           |    6 
 target-lm32/translate.c        |   26 
 trace-events                   |   23 
 vl.c                           |   56 
 64 files changed, 8325 insertions(+), 7953 deletions(-)

New commits:
commit d2d979c628e4b2c4a3cb71a31841875795c79043
Author: Nick Thomas <nick at bytemark.co.uk>
Date:   Thu Apr 28 16:20:01 2011 +0100

    NBD: Avoid leaking a couple of strings when the NBD device is closed
    
    Signed-off-by: Nick Thomas <nick at bytemark.co.uk>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/block/nbd.c b/block/nbd.c
index 1d6b225..7a52f62 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -239,6 +239,10 @@ static int nbd_write(BlockDriverState *bs, int64_t sector_num,
 
 static void nbd_close(BlockDriverState *bs)
 {
+    BDRVNBDState *s = bs->opaque;
+    qemu_free(s->export_name);
+    qemu_free(s->host_spec);
+
     nbd_teardown_connection(bs);
 }
 
commit 2ab3cb8c0ae79c96f38f6bfd35620cc18ddba19f
Author: Jes Sorensen <Jes.Sorensen at redhat.com>
Date:   Thu Apr 28 13:58:30 2011 +0200

    qemu-progress.c: printf isn't signal safe
    
    Change the signal handling to indicate a signal is pending, rather
    then printing directly from the signal handler.
    
    In addition make the signal prints go to stderr, rather than stdout.
    
    Signed-off-by: Jes Sorensen <Jes.Sorensen at redhat.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/qemu-progress.c b/qemu-progress.c
index e1feb89..a4894c0 100644
--- a/qemu-progress.c
+++ b/qemu-progress.c
@@ -37,6 +37,7 @@ struct progress_state {
 };
 
 static struct progress_state state;
+static volatile sig_atomic_t print_pending;
 
 /*
  * Simple progress print function.
@@ -63,12 +64,16 @@ static void progress_simple_init(void)
 #ifdef CONFIG_POSIX
 static void sigusr_print(int signal)
 {
-    printf("    (%3.2f/100%%)\n", state.current);
+    print_pending = 1;
 }
 #endif
 
 static void progress_dummy_print(void)
 {
+    if (print_pending) {
+        fprintf(stderr, "    (%3.2f/100%%)\n", state.current);
+        print_pending = 0;
+    }
 }
 
 static void progress_dummy_end(void)
commit ab71982716928577826b9aa7fc9a5102bfce5f0e
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Apr 28 16:34:39 2011 +0300

    ide/atapi: fix set but unused
    
    Signed-off-by: Alon Levy <alevy at redhat.com>
    Acked-by: Stefan Weil <weil at mail.berlios.de>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 58febc0..fe2fb0b 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -1080,17 +1080,15 @@ static const struct {
 
 void ide_atapi_cmd(IDEState *s)
 {
-    const uint8_t *packet;
     uint8_t *buf;
 
-    packet = s->io_buffer;
     buf = s->io_buffer;
 #ifdef DEBUG_IDE_ATAPI
     {
         int i;
         printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8));
         for(i = 0; i < ATAPI_PACKET_SIZE; i++) {
-            printf(" %02x", packet[i]);
+            printf(" %02x", buf[i]);
         }
         printf("\n");
     }
commit 4a737d14d0788412eead23330b554eb75900af04
Author: Amit Shah <amit.shah at redhat.com>
Date:   Thu Apr 28 20:04:41 2011 +0530

    atapi: Explain why we need a 'media not present' state
    
    After the re-org of the atapi code, it might not be intuitive for a
    reader of the code to understand why we're inserting a 'media not
    present' state between cd changes.
    
    Signed-off-by: Amit Shah <amit.shah at redhat.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 86b18d8..58febc0 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -1106,7 +1106,13 @@ void ide_atapi_cmd(IDEState *s)
         ide_atapi_cmd_check_status(s);
         return;
     }
-
+    /*
+     * When a CD gets changed, we have to report an ejected state and
+     * then a loaded state to guests so that they detect tray
+     * open/close and media change events.  Guests that do not use
+     * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close
+     * states rely on this behavior.
+     */
     if (bdrv_is_inserted(s->bs) && s->cdrom_changed) {
         ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
 
commit a7acf552e2cfb42ea1b27966b7f318eca2cc478a
Author: Amit Shah <amit.shah at redhat.com>
Date:   Thu Apr 28 20:04:40 2011 +0530

    atapi: Move comment to proper place
    
    Move misplaced comment for media_is_dvd()
    
    Signed-off-by: Amit Shah <amit.shah at redhat.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 690a0ab..86b18d8 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -71,12 +71,12 @@ static void lba_to_msf(uint8_t *buf, int lba)
     buf[2] = lba % 75;
 }
 
-/* XXX: DVDs that could fit on a CD will be reported as a CD */
 static inline int media_present(IDEState *s)
 {
     return (s->nb_sectors > 0);
 }
 
+/* XXX: DVDs that could fit on a CD will be reported as a CD */
 static inline int media_is_dvd(IDEState *s)
 {
     return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS);
commit e80fec7feb62c741df5360c1841ca49e4087c1cd
Author: Kevin Wolf <kwolf at redhat.com>
Date:   Fri Apr 29 10:58:12 2011 +0200

    qemu-img resize: Fix option parsing
    
    For shrinking images, you're supposed to use a negative size. However, the
    leading minus makes getopt think that it's an option and so you get the help
    text if you don't use -- like in 'qemu-img resize test.img -- -1G'.
    
    This patch handles the size first and removes it from the argument list so that
    getopt won't even try to interpret it and you don't need -- any more.
    
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/qemu-img.c b/qemu-img.c
index ed5ba91..e825123 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1442,6 +1442,16 @@ static int img_resize(int argc, char **argv)
         { NULL }
     };
 
+    /* Remove size from argv manually so that negative numbers are not treated
+     * as options by getopt. */
+    if (argc < 3) {
+        help();
+        return 1;
+    }
+
+    size = argv[--argc];
+
+    /* Parse getopt arguments */
     fmt = NULL;
     for(;;) {
         c = getopt(argc, argv, "f:h");
@@ -1458,11 +1468,10 @@ static int img_resize(int argc, char **argv)
             break;
         }
     }
-    if (optind + 1 >= argc) {
+    if (optind >= argc) {
         help();
     }
     filename = argv[optind++];
-    size = argv[optind++];
 
     /* Choose grow, shrink, or absolute resize mode */
     switch (size[0]) {
commit 57aa265d462a64a06268be26d49020729cff56c1
Author: Michael Walle <michael at walle.cc>
Date:   Wed Apr 13 00:29:36 2011 +0200

    lm32: add Milkymist Minimac2 support
    
    This patch adds support for Milkymist's minimal Ethernet MAC v2. It
    superseds minimac1.
    
    Signed-off-by: Michael Walle <michael at walle.cc>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/Makefile.target b/Makefile.target
index 2501c63..21f864a 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -271,7 +271,7 @@ obj-lm32-y += lm32_sys.o
 obj-lm32-y += milkymist-ac97.o
 obj-lm32-y += milkymist-hpdmc.o
 obj-lm32-y += milkymist-memcard.o
-obj-lm32-y += milkymist-minimac.o
+obj-lm32-y += milkymist-minimac2.o
 obj-lm32-y += milkymist-pfpu.o
 obj-lm32-y += milkymist-softusb.o
 obj-lm32-y += milkymist-sysctl.o
diff --git a/hw/milkymist-hw.h b/hw/milkymist-hw.h
index 15acdbc..20de68e 100644
--- a/hw/milkymist-hw.h
+++ b/hw/milkymist-hw.h
@@ -1,6 +1,9 @@
 #ifndef QEMU_HW_MILKYMIST_H
 #define QEMU_HW_MILKYMIST_H
 
+#include "qdev.h"
+#include "qdev-addr.h"
+
 static inline DeviceState *milkymist_uart_create(target_phys_addr_t base,
         qemu_irq rx_irq, qemu_irq tx_irq)
 {
@@ -183,6 +186,23 @@ static inline DeviceState *milkymist_minimac_create(target_phys_addr_t base,
     return dev;
 }
 
+static inline DeviceState *milkymist_minimac2_create(target_phys_addr_t base,
+        target_phys_addr_t buffers_base, qemu_irq rx_irq, qemu_irq tx_irq)
+{
+    DeviceState *dev;
+
+    qemu_check_nic_model(&nd_table[0], "minimac2");
+    dev = qdev_create(NULL, "milkymist-minimac2");
+    qdev_prop_set_taddr(dev, "buffers_base", buffers_base);
+    qdev_set_nic_properties(dev, &nd_table[0]);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq);
+    sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq);
+
+    return dev;
+}
+
 static inline DeviceState *milkymist_softusb_create(target_phys_addr_t base,
         qemu_irq irq, uint32_t pmem_base, uint32_t pmem_size,
         uint32_t dmem_base, uint32_t dmem_size)
diff --git a/hw/milkymist-minimac.c b/hw/milkymist-minimac.c
deleted file mode 100644
index b07f18d..0000000
--- a/hw/milkymist-minimac.c
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- *  QEMU model of the Milkymist minimac block.
- *
- *  Copyright (c) 2010 Michael Walle <michael at walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Specification available at:
- *   http://www.milkymist.org/socdoc/minimac.pdf
- *
- */
-
-#include "hw.h"
-#include "sysbus.h"
-#include "trace.h"
-#include "net.h"
-#include "qemu-error.h"
-
-#include <zlib.h>
-
-enum {
-    R_SETUP = 0,
-    R_MDIO,
-    R_STATE0,
-    R_ADDR0,
-    R_COUNT0,
-    R_STATE1,
-    R_ADDR1,
-    R_COUNT1,
-    R_STATE2,
-    R_ADDR2,
-    R_COUNT2,
-    R_STATE3,
-    R_ADDR3,
-    R_COUNT3,
-    R_TXADDR,
-    R_TXCOUNT,
-    R_MAX
-};
-
-enum {
-    SETUP_RX_RST = (1<<0),
-    SETUP_TX_RST = (1<<2),
-};
-
-enum {
-    MDIO_DO  = (1<<0),
-    MDIO_DI  = (1<<1),
-    MDIO_OE  = (1<<2),
-    MDIO_CLK = (1<<3),
-};
-
-enum {
-    STATE_EMPTY   = 0,
-    STATE_LOADED  = 1,
-    STATE_PENDING = 2,
-};
-
-enum {
-    MDIO_OP_WRITE = 1,
-    MDIO_OP_READ  = 2,
-};
-
-enum mdio_state {
-    MDIO_STATE_IDLE,
-    MDIO_STATE_READING,
-    MDIO_STATE_WRITING,
-};
-
-enum {
-    R_PHY_ID1  = 2,
-    R_PHY_ID2  = 3,
-    R_PHY_MAX  = 32
-};
-
-#define MINIMAC_MTU 1530
-
-struct MilkymistMinimacMdioState {
-    int last_clk;
-    int count;
-    uint32_t data;
-    uint16_t data_out;
-    int state;
-
-    uint8_t phy_addr;
-    uint8_t reg_addr;
-};
-typedef struct MilkymistMinimacMdioState MilkymistMinimacMdioState;
-
-struct MilkymistMinimacState {
-    SysBusDevice busdev;
-    NICState *nic;
-    NICConf conf;
-    char *phy_model;
-
-    qemu_irq rx_irq;
-    qemu_irq tx_irq;
-
-    uint32_t regs[R_MAX];
-
-    MilkymistMinimacMdioState mdio;
-
-    uint16_t phy_regs[R_PHY_MAX];
-};
-typedef struct MilkymistMinimacState MilkymistMinimacState;
-
-static const uint8_t preamble_sfd[] = {
-        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5
-};
-
-static void minimac_mdio_write_reg(MilkymistMinimacState *s,
-        uint8_t phy_addr, uint8_t reg_addr, uint16_t value)
-{
-    trace_milkymist_minimac_mdio_write(phy_addr, reg_addr, value);
-
-    /* nop */
-}
-
-static uint16_t minimac_mdio_read_reg(MilkymistMinimacState *s,
-        uint8_t phy_addr, uint8_t reg_addr)
-{
-    uint16_t r = s->phy_regs[reg_addr];
-
-    trace_milkymist_minimac_mdio_read(phy_addr, reg_addr, r);
-
-    return r;
-}
-
-static void minimac_update_mdio(MilkymistMinimacState *s)
-{
-    MilkymistMinimacMdioState *m = &s->mdio;
-
-    /* detect rising clk edge */
-    if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) {
-        /* shift data in */
-        int bit = ((s->regs[R_MDIO] & MDIO_DO)
-                   && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0;
-        m->data = (m->data << 1) | bit;
-
-        /* check for sync */
-        if (m->data == 0xffffffff) {
-            m->count = 32;
-        }
-
-        if (m->count == 16) {
-            uint8_t start = (m->data >> 14) & 0x3;
-            uint8_t op = (m->data >> 12) & 0x3;
-            uint8_t ta = (m->data) & 0x3;
-
-            if (start == 1 && op == MDIO_OP_WRITE && ta == 2) {
-                m->state = MDIO_STATE_WRITING;
-            } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) {
-                m->state = MDIO_STATE_READING;
-            } else {
-                m->state = MDIO_STATE_IDLE;
-            }
-
-            if (m->state != MDIO_STATE_IDLE) {
-                m->phy_addr = (m->data >> 7) & 0x1f;
-                m->reg_addr = (m->data >> 2) & 0x1f;
-            }
-
-            if (m->state == MDIO_STATE_READING) {
-                m->data_out = minimac_mdio_read_reg(s, m->phy_addr,
-                        m->reg_addr);
-            }
-        }
-
-        if (m->count < 16 && m->state == MDIO_STATE_READING) {
-            int bit = (m->data_out & 0x8000) ? 1 : 0;
-            m->data_out <<= 1;
-
-            if (bit) {
-                s->regs[R_MDIO] |= MDIO_DI;
-            } else {
-                s->regs[R_MDIO] &= ~MDIO_DI;
-            }
-        }
-
-        if (m->count == 0 && m->state) {
-            if (m->state == MDIO_STATE_WRITING) {
-                uint16_t data = m->data & 0xffff;
-                minimac_mdio_write_reg(s, m->phy_addr, m->reg_addr, data);
-            }
-            m->state = MDIO_STATE_IDLE;
-        }
-        m->count--;
-    }
-
-    m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0;
-}
-
-static size_t assemble_frame(uint8_t *buf, size_t size,
-        const uint8_t *payload, size_t payload_size)
-{
-    uint32_t crc;
-
-    if (size < payload_size + 12) {
-        error_report("milkymist_minimac: received too big ethernet frame");
-        return 0;
-    }
-
-    /* prepend preamble and sfd */
-    memcpy(buf, preamble_sfd, 8);
-
-    /* now copy the payload */
-    memcpy(buf + 8, payload, payload_size);
-
-    /* pad frame if needed */
-    if (payload_size < 60) {
-        memset(buf + payload_size + 8, 0, 60 - payload_size);
-        payload_size = 60;
-    }
-
-    /* append fcs */
-    crc = cpu_to_le32(crc32(0, buf + 8, payload_size));
-    memcpy(buf + payload_size + 8, &crc, 4);
-
-    return payload_size + 12;
-}
-
-static void minimac_tx(MilkymistMinimacState *s)
-{
-    uint8_t buf[MINIMAC_MTU];
-    uint32_t txcount = s->regs[R_TXCOUNT];
-
-    /* do nothing if transmission logic is in reset */
-    if (s->regs[R_SETUP] & SETUP_TX_RST) {
-        return;
-    }
-
-    if (txcount < 64) {
-        error_report("milkymist_minimac: ethernet frame too small (%u < %u)\n",
-                txcount, 64);
-        return;
-    }
-
-    if (txcount > MINIMAC_MTU) {
-        error_report("milkymist_minimac: MTU exceeded (%u > %u)\n",
-                txcount, MINIMAC_MTU);
-        return;
-    }
-
-    /* dma */
-    cpu_physical_memory_read(s->regs[R_TXADDR], buf, txcount);
-
-    if (memcmp(buf, preamble_sfd, 8) != 0) {
-        error_report("milkymist_minimac: frame doesn't contain the preamble "
-                "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)\n",
-                buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
-        return;
-    }
-
-    trace_milkymist_minimac_tx_frame(txcount - 12);
-
-    /* send packet, skipping preamble and sfd */
-    qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12);
-
-    s->regs[R_TXCOUNT] = 0;
-
-    trace_milkymist_minimac_pulse_irq_tx();
-    qemu_irq_pulse(s->tx_irq);
-}
-
-static ssize_t minimac_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
-{
-    MilkymistMinimacState *s = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    uint32_t r_addr;
-    uint32_t r_count;
-    uint32_t r_state;
-
-    uint8_t frame_buf[MINIMAC_MTU];
-    size_t frame_size;
-
-    trace_milkymist_minimac_rx_frame(buf, size);
-
-    /* discard frames if nic is in reset */
-    if (s->regs[R_SETUP] & SETUP_RX_RST) {
-        return size;
-    }
-
-    /* choose appropriate slot */
-    if (s->regs[R_STATE0] == STATE_LOADED) {
-        r_addr = R_ADDR0;
-        r_count = R_COUNT0;
-        r_state = R_STATE0;
-    } else if (s->regs[R_STATE1] == STATE_LOADED) {
-        r_addr = R_ADDR1;
-        r_count = R_COUNT1;
-        r_state = R_STATE1;
-    } else if (s->regs[R_STATE2] == STATE_LOADED) {
-        r_addr = R_ADDR2;
-        r_count = R_COUNT2;
-        r_state = R_STATE2;
-    } else if (s->regs[R_STATE3] == STATE_LOADED) {
-        r_addr = R_ADDR3;
-        r_count = R_COUNT3;
-        r_state = R_STATE3;
-    } else {
-        trace_milkymist_minimac_drop_rx_frame(buf);
-        return size;
-    }
-
-    /* assemble frame */
-    frame_size = assemble_frame(frame_buf, sizeof(frame_buf), buf, size);
-
-    if (frame_size == 0) {
-        return size;
-    }
-
-    trace_milkymist_minimac_rx_transfer(buf, frame_size);
-
-    /* do dma */
-    cpu_physical_memory_write(s->regs[r_addr], frame_buf, frame_size);
-
-    /* update slot */
-    s->regs[r_count] = frame_size;
-    s->regs[r_state] = STATE_PENDING;
-
-    trace_milkymist_minimac_pulse_irq_rx();
-    qemu_irq_pulse(s->rx_irq);
-
-    return size;
-}
-
-static uint32_t
-minimac_read(void *opaque, target_phys_addr_t addr)
-{
-    MilkymistMinimacState *s = opaque;
-    uint32_t r = 0;
-
-    addr >>= 2;
-    switch (addr) {
-    case R_SETUP:
-    case R_MDIO:
-    case R_STATE0:
-    case R_ADDR0:
-    case R_COUNT0:
-    case R_STATE1:
-    case R_ADDR1:
-    case R_COUNT1:
-    case R_STATE2:
-    case R_ADDR2:
-    case R_COUNT2:
-    case R_STATE3:
-    case R_ADDR3:
-    case R_COUNT3:
-    case R_TXADDR:
-    case R_TXCOUNT:
-        r = s->regs[addr];
-        break;
-
-    default:
-        error_report("milkymist_minimac: read access to unknown register 0x"
-                TARGET_FMT_plx, addr << 2);
-        break;
-    }
-
-    trace_milkymist_minimac_memory_read(addr << 2, r);
-
-    return r;
-}
-
-static void
-minimac_write(void *opaque, target_phys_addr_t addr, uint32_t value)
-{
-    MilkymistMinimacState *s = opaque;
-
-    trace_milkymist_minimac_memory_read(addr, value);
-
-    addr >>= 2;
-    switch (addr) {
-    case R_MDIO:
-    {
-        /* MDIO_DI is read only */
-        int mdio_di = (s->regs[R_MDIO] & MDIO_DI);
-        s->regs[R_MDIO] = value;
-        if (mdio_di) {
-            s->regs[R_MDIO] |= mdio_di;
-        } else {
-            s->regs[R_MDIO] &= ~mdio_di;
-        }
-
-        minimac_update_mdio(s);
-    } break;
-    case R_TXCOUNT:
-        s->regs[addr] = value;
-        if (value > 0) {
-            minimac_tx(s);
-        }
-        break;
-    case R_SETUP:
-    case R_STATE0:
-    case R_ADDR0:
-    case R_COUNT0:
-    case R_STATE1:
-    case R_ADDR1:
-    case R_COUNT1:
-    case R_STATE2:
-    case R_ADDR2:
-    case R_COUNT2:
-    case R_STATE3:
-    case R_ADDR3:
-    case R_COUNT3:
-    case R_TXADDR:
-        s->regs[addr] = value;
-        break;
-
-    default:
-        error_report("milkymist_minimac: write access to unknown register 0x"
-                TARGET_FMT_plx, addr << 2);
-        break;
-    }
-}
-
-static CPUReadMemoryFunc * const minimac_read_fn[] = {
-    NULL,
-    NULL,
-    &minimac_read,
-};
-
-static CPUWriteMemoryFunc * const minimac_write_fn[] = {
-    NULL,
-    NULL,
-    &minimac_write,
-};
-
-static int minimac_can_rx(VLANClientState *nc)
-{
-    MilkymistMinimacState *s = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    /* discard frames if nic is in reset */
-    if (s->regs[R_SETUP] & SETUP_RX_RST) {
-        return 1;
-    }
-
-    if (s->regs[R_STATE0] == STATE_LOADED) {
-        return 1;
-    }
-    if (s->regs[R_STATE1] == STATE_LOADED) {
-        return 1;
-    }
-    if (s->regs[R_STATE2] == STATE_LOADED) {
-        return 1;
-    }
-    if (s->regs[R_STATE3] == STATE_LOADED) {
-        return 1;
-    }
-
-    return 0;
-}
-
-static void minimac_cleanup(VLANClientState *nc)
-{
-    MilkymistMinimacState *s = DO_UPCAST(NICState, nc, nc)->opaque;
-
-    s->nic = NULL;
-}
-
-static void milkymist_minimac_reset(DeviceState *d)
-{
-    MilkymistMinimacState *s =
-            container_of(d, MilkymistMinimacState, busdev.qdev);
-    int i;
-
-    for (i = 0; i < R_MAX; i++) {
-        s->regs[i] = 0;
-    }
-    for (i = 0; i < R_PHY_MAX; i++) {
-        s->phy_regs[i] = 0;
-    }
-
-    /* defaults */
-    s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */
-    s->phy_regs[R_PHY_ID2] = 0x161a;
-}
-
-static NetClientInfo net_milkymist_minimac_info = {
-    .type = NET_CLIENT_TYPE_NIC,
-    .size = sizeof(NICState),
-    .can_receive = minimac_can_rx,
-    .receive = minimac_rx,
-    .cleanup = minimac_cleanup,
-};
-
-static int milkymist_minimac_init(SysBusDevice *dev)
-{
-    MilkymistMinimacState *s = FROM_SYSBUS(typeof(*s), dev);
-    int regs;
-
-    sysbus_init_irq(dev, &s->rx_irq);
-    sysbus_init_irq(dev, &s->tx_irq);
-
-    regs = cpu_register_io_memory(minimac_read_fn, minimac_write_fn, s,
-            DEVICE_NATIVE_ENDIAN);
-    sysbus_init_mmio(dev, R_MAX * 4, regs);
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_milkymist_minimac_info, &s->conf,
-                          dev->qdev.info->name, dev->qdev.id, s);
-    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_milkymist_minimac_mdio = {
-    .name = "milkymist_minimac_mdio",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_INT32(last_clk, MilkymistMinimacMdioState),
-        VMSTATE_INT32(count, MilkymistMinimacMdioState),
-        VMSTATE_UINT32(data, MilkymistMinimacMdioState),
-        VMSTATE_UINT16(data_out, MilkymistMinimacMdioState),
-        VMSTATE_INT32(state, MilkymistMinimacMdioState),
-        VMSTATE_UINT8(phy_addr, MilkymistMinimacMdioState),
-        VMSTATE_UINT8(reg_addr, MilkymistMinimacMdioState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_milkymist_minimac = {
-    .name = "milkymist-minimac",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32_ARRAY(regs, MilkymistMinimacState, R_MAX),
-        VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimacState, R_PHY_MAX),
-        VMSTATE_STRUCT(mdio, MilkymistMinimacState, 0,
-                vmstate_milkymist_minimac_mdio, MilkymistMinimacMdioState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static SysBusDeviceInfo milkymist_minimac_info = {
-    .init = milkymist_minimac_init,
-    .qdev.name  = "milkymist-minimac",
-    .qdev.size  = sizeof(MilkymistMinimacState),
-    .qdev.vmsd  = &vmstate_milkymist_minimac,
-    .qdev.reset = milkymist_minimac_reset,
-    .qdev.props = (Property[]) {
-        DEFINE_NIC_PROPERTIES(MilkymistMinimacState, conf),
-        DEFINE_PROP_STRING("phy_model", MilkymistMinimacState, phy_model),
-        DEFINE_PROP_END_OF_LIST(),
-    }
-};
-
-static void milkymist_minimac_register(void)
-{
-    sysbus_register_withprop(&milkymist_minimac_info);
-}
-
-device_init(milkymist_minimac_register)
diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c
new file mode 100644
index 0000000..c4e2818
--- /dev/null
+++ b/hw/milkymist-minimac2.c
@@ -0,0 +1,539 @@
+/*
+ *  QEMU model of the Milkymist minimac2 block.
+ *
+ *  Copyright (c) 2011 Michael Walle <michael at walle.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Specification available at:
+ *   not available yet
+ *
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+#include "trace.h"
+#include "net.h"
+#include "qemu-error.h"
+#include "qdev-addr.h"
+
+#include <zlib.h>
+
+enum {
+    R_SETUP = 0,
+    R_MDIO,
+    R_STATE0,
+    R_COUNT0,
+    R_STATE1,
+    R_COUNT1,
+    R_TXCOUNT,
+    R_MAX
+};
+
+enum {
+    SETUP_PHY_RST = (1<<0),
+};
+
+enum {
+    MDIO_DO  = (1<<0),
+    MDIO_DI  = (1<<1),
+    MDIO_OE  = (1<<2),
+    MDIO_CLK = (1<<3),
+};
+
+enum {
+    STATE_EMPTY   = 0,
+    STATE_LOADED  = 1,
+    STATE_PENDING = 2,
+};
+
+enum {
+    MDIO_OP_WRITE = 1,
+    MDIO_OP_READ  = 2,
+};
+
+enum mdio_state {
+    MDIO_STATE_IDLE,
+    MDIO_STATE_READING,
+    MDIO_STATE_WRITING,
+};
+
+enum {
+    R_PHY_ID1  = 2,
+    R_PHY_ID2  = 3,
+    R_PHY_MAX  = 32
+};
+
+#define MINIMAC2_MTU 1530
+#define MINIMAC2_BUFFER_SIZE 2048
+
+struct MilkymistMinimac2MdioState {
+    int last_clk;
+    int count;
+    uint32_t data;
+    uint16_t data_out;
+    int state;
+
+    uint8_t phy_addr;
+    uint8_t reg_addr;
+};
+typedef struct MilkymistMinimac2MdioState MilkymistMinimac2MdioState;
+
+struct MilkymistMinimac2State {
+    SysBusDevice busdev;
+    NICState *nic;
+    NICConf conf;
+    char *phy_model;
+    target_phys_addr_t buffers_base;
+
+    qemu_irq rx_irq;
+    qemu_irq tx_irq;
+
+    uint32_t regs[R_MAX];
+
+    MilkymistMinimac2MdioState mdio;
+
+    uint16_t phy_regs[R_PHY_MAX];
+
+    uint8_t *rx0_buf;
+    uint8_t *rx1_buf;
+    uint8_t *tx_buf;
+};
+typedef struct MilkymistMinimac2State MilkymistMinimac2State;
+
+static const uint8_t preamble_sfd[] = {
+        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5
+};
+
+static void minimac2_mdio_write_reg(MilkymistMinimac2State *s,
+        uint8_t phy_addr, uint8_t reg_addr, uint16_t value)
+{
+    trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value);
+
+    /* nop */
+}
+
+static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s,
+        uint8_t phy_addr, uint8_t reg_addr)
+{
+    uint16_t r = s->phy_regs[reg_addr];
+
+    trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r);
+
+    return r;
+}
+
+static void minimac2_update_mdio(MilkymistMinimac2State *s)
+{
+    MilkymistMinimac2MdioState *m = &s->mdio;
+
+    /* detect rising clk edge */
+    if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) {
+        /* shift data in */
+        int bit = ((s->regs[R_MDIO] & MDIO_DO)
+                   && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0;
+        m->data = (m->data << 1) | bit;
+
+        /* check for sync */
+        if (m->data == 0xffffffff) {
+            m->count = 32;
+        }
+
+        if (m->count == 16) {
+            uint8_t start = (m->data >> 14) & 0x3;
+            uint8_t op = (m->data >> 12) & 0x3;
+            uint8_t ta = (m->data) & 0x3;
+
+            if (start == 1 && op == MDIO_OP_WRITE && ta == 2) {
+                m->state = MDIO_STATE_WRITING;
+            } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) {
+                m->state = MDIO_STATE_READING;
+            } else {
+                m->state = MDIO_STATE_IDLE;
+            }
+
+            if (m->state != MDIO_STATE_IDLE) {
+                m->phy_addr = (m->data >> 7) & 0x1f;
+                m->reg_addr = (m->data >> 2) & 0x1f;
+            }
+
+            if (m->state == MDIO_STATE_READING) {
+                m->data_out = minimac2_mdio_read_reg(s, m->phy_addr,
+                        m->reg_addr);
+            }
+        }
+
+        if (m->count < 16 && m->state == MDIO_STATE_READING) {
+            int bit = (m->data_out & 0x8000) ? 1 : 0;
+            m->data_out <<= 1;
+
+            if (bit) {
+                s->regs[R_MDIO] |= MDIO_DI;
+            } else {
+                s->regs[R_MDIO] &= ~MDIO_DI;
+            }
+        }
+
+        if (m->count == 0 && m->state) {
+            if (m->state == MDIO_STATE_WRITING) {
+                uint16_t data = m->data & 0xffff;
+                minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data);
+            }
+            m->state = MDIO_STATE_IDLE;
+        }
+        m->count--;
+    }
+
+    m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0;
+}
+
+static size_t assemble_frame(uint8_t *buf, size_t size,
+        const uint8_t *payload, size_t payload_size)
+{
+    uint32_t crc;
+
+    if (size < payload_size + 12) {
+        error_report("milkymist_minimac2: received too big ethernet frame");
+        return 0;
+    }
+
+    /* prepend preamble and sfd */
+    memcpy(buf, preamble_sfd, 8);
+
+    /* now copy the payload */
+    memcpy(buf + 8, payload, payload_size);
+
+    /* pad frame if needed */
+    if (payload_size < 60) {
+        memset(buf + payload_size + 8, 0, 60 - payload_size);
+        payload_size = 60;
+    }
+
+    /* append fcs */
+    crc = cpu_to_le32(crc32(0, buf + 8, payload_size));
+    memcpy(buf + payload_size + 8, &crc, 4);
+
+    return payload_size + 12;
+}
+
+static void minimac2_tx(MilkymistMinimac2State *s)
+{
+    uint32_t txcount = s->regs[R_TXCOUNT];
+    uint8_t *buf = s->tx_buf;
+
+    if (txcount < 64) {
+        error_report("milkymist_minimac2: ethernet frame too small (%u < %u)\n",
+                txcount, 64);
+        goto err;
+    }
+
+    if (txcount > MINIMAC2_MTU) {
+        error_report("milkymist_minimac2: MTU exceeded (%u > %u)\n",
+                txcount, MINIMAC2_MTU);
+        goto err;
+    }
+
+    if (memcmp(buf, preamble_sfd, 8) != 0) {
+        error_report("milkymist_minimac2: frame doesn't contain the preamble "
+                "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)\n",
+                buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+        goto err;
+    }
+
+    trace_milkymist_minimac2_tx_frame(txcount - 12);
+
+    /* send packet, skipping preamble and sfd */
+    qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12);
+
+    s->regs[R_TXCOUNT] = 0;
+
+err:
+    trace_milkymist_minimac2_pulse_irq_tx();
+    qemu_irq_pulse(s->tx_irq);
+}
+
+static void update_rx_interrupt(MilkymistMinimac2State *s)
+{
+    if (s->regs[R_STATE0] == STATE_PENDING
+            || s->regs[R_STATE1] == STATE_PENDING) {
+        trace_milkymist_minimac2_raise_irq_rx();
+        qemu_irq_raise(s->rx_irq);
+    } else {
+        trace_milkymist_minimac2_lower_irq_rx();
+        qemu_irq_lower(s->rx_irq);
+    }
+}
+
+static ssize_t minimac2_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+    MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    uint32_t r_count;
+    uint32_t r_state;
+    uint8_t *rx_buf;
+
+    size_t frame_size;
+
+    trace_milkymist_minimac2_rx_frame(buf, size);
+
+    /* choose appropriate slot */
+    if (s->regs[R_STATE0] == STATE_LOADED) {
+        r_count = R_COUNT0;
+        r_state = R_STATE0;
+        rx_buf = s->rx0_buf;
+    } else if (s->regs[R_STATE1] == STATE_LOADED) {
+        r_count = R_COUNT1;
+        r_state = R_STATE1;
+        rx_buf = s->rx1_buf;
+    } else {
+        trace_milkymist_minimac2_drop_rx_frame(buf);
+        return size;
+    }
+
+    /* assemble frame */
+    frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size);
+
+    if (frame_size == 0) {
+        return size;
+    }
+
+    trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size);
+
+    /* update slot */
+    s->regs[r_count] = frame_size;
+    s->regs[r_state] = STATE_PENDING;
+
+    update_rx_interrupt(s);
+
+    return size;
+}
+
+static uint32_t
+minimac2_read(void *opaque, target_phys_addr_t addr)
+{
+    MilkymistMinimac2State *s = opaque;
+    uint32_t r = 0;
+
+    addr >>= 2;
+    switch (addr) {
+    case R_SETUP:
+    case R_MDIO:
+    case R_STATE0:
+    case R_COUNT0:
+    case R_STATE1:
+    case R_COUNT1:
+    case R_TXCOUNT:
+        r = s->regs[addr];
+        break;
+
+    default:
+        error_report("milkymist_minimac2: read access to unknown register 0x"
+                TARGET_FMT_plx, addr << 2);
+        break;
+    }
+
+    trace_milkymist_minimac2_memory_read(addr << 2, r);
+
+    return r;
+}
+
+static void
+minimac2_write(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+    MilkymistMinimac2State *s = opaque;
+
+    trace_milkymist_minimac2_memory_read(addr, value);
+
+    addr >>= 2;
+    switch (addr) {
+    case R_MDIO:
+    {
+        /* MDIO_DI is read only */
+        int mdio_di = (s->regs[R_MDIO] & MDIO_DI);
+        s->regs[R_MDIO] = value;
+        if (mdio_di) {
+            s->regs[R_MDIO] |= mdio_di;
+        } else {
+            s->regs[R_MDIO] &= ~mdio_di;
+        }
+
+        minimac2_update_mdio(s);
+    } break;
+    case R_TXCOUNT:
+        s->regs[addr] = value;
+        if (value > 0) {
+            minimac2_tx(s);
+        }
+        break;
+    case R_STATE0:
+    case R_STATE1:
+        s->regs[addr] = value;
+        update_rx_interrupt(s);
+        break;
+    case R_SETUP:
+    case R_COUNT0:
+    case R_COUNT1:
+        s->regs[addr] = value;
+        break;
+
+    default:
+        error_report("milkymist_minimac2: write access to unknown register 0x"
+                TARGET_FMT_plx, addr << 2);
+        break;
+    }
+}
+
+static CPUReadMemoryFunc * const minimac2_read_fn[] = {
+    NULL,
+    NULL,
+    &minimac2_read,
+};
+
+static CPUWriteMemoryFunc * const minimac2_write_fn[] = {
+    NULL,
+    NULL,
+    &minimac2_write,
+};
+
+static int minimac2_can_rx(VLANClientState *nc)
+{
+    MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    if (s->regs[R_STATE0] == STATE_LOADED) {
+        return 1;
+    }
+    if (s->regs[R_STATE1] == STATE_LOADED) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static void minimac2_cleanup(VLANClientState *nc)
+{
+    MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+    s->nic = NULL;
+}
+
+static void milkymist_minimac2_reset(DeviceState *d)
+{
+    MilkymistMinimac2State *s =
+            container_of(d, MilkymistMinimac2State, busdev.qdev);
+    int i;
+
+    for (i = 0; i < R_MAX; i++) {
+        s->regs[i] = 0;
+    }
+    for (i = 0; i < R_PHY_MAX; i++) {
+        s->phy_regs[i] = 0;
+    }
+
+    /* defaults */
+    s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */
+    s->phy_regs[R_PHY_ID2] = 0x161a;
+}
+
+static NetClientInfo net_milkymist_minimac2_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = minimac2_can_rx,
+    .receive = minimac2_rx,
+    .cleanup = minimac2_cleanup,
+};
+
+static int milkymist_minimac2_init(SysBusDevice *dev)
+{
+    MilkymistMinimac2State *s = FROM_SYSBUS(typeof(*s), dev);
+    int regs;
+    ram_addr_t buffers;
+    size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE);
+
+    sysbus_init_irq(dev, &s->rx_irq);
+    sysbus_init_irq(dev, &s->tx_irq);
+
+    regs = cpu_register_io_memory(minimac2_read_fn, minimac2_write_fn, s,
+            DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, R_MAX * 4, regs);
+
+    /* register buffers memory */
+    buffers = qemu_ram_alloc(NULL, "milkymist_minimac2.buffers", buffers_size);
+    s->rx0_buf = qemu_get_ram_ptr(buffers);
+    s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
+    s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE;
+
+    cpu_register_physical_memory(s->buffers_base, buffers_size,
+            buffers | IO_MEM_RAM);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf,
+                          dev->qdev.info->name, dev->qdev.id, s);
+    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_milkymist_minimac2_mdio = {
+    .name = "milkymist-minimac2-mdio",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState),
+        VMSTATE_INT32(count, MilkymistMinimac2MdioState),
+        VMSTATE_UINT32(data, MilkymistMinimac2MdioState),
+        VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState),
+        VMSTATE_INT32(state, MilkymistMinimac2MdioState),
+        VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState),
+        VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_milkymist_minimac2 = {
+    .name = "milkymist-minimac2",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX),
+        VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX),
+        VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0,
+                vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static SysBusDeviceInfo milkymist_minimac2_info = {
+    .init = milkymist_minimac2_init,
+    .qdev.name  = "milkymist-minimac2",
+    .qdev.size  = sizeof(MilkymistMinimac2State),
+    .qdev.vmsd  = &vmstate_milkymist_minimac2,
+    .qdev.reset = milkymist_minimac2_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_TADDR("buffers_base", MilkymistMinimac2State,
+                buffers_base, 0),
+        DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf),
+        DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void milkymist_minimac2_register(void)
+{
+    sysbus_register_withprop(&milkymist_minimac2_info);
+}
+
+device_init(milkymist_minimac2_register)
diff --git a/hw/milkymist.c b/hw/milkymist.c
index 8defad8..7879840 100644
--- a/hw/milkymist.c
+++ b/hw/milkymist.c
@@ -156,7 +156,7 @@ milkymist_init(ram_addr_t ram_size_not_used,
     milkymist_ac97_create(0x60005000, irq[5], irq[6], irq[7], irq[8]);
     milkymist_pfpu_create(0x60006000, irq[9]);
     milkymist_tmu2_create(0x60007000, irq[10]);
-    milkymist_minimac_create(0x60008000, irq[11], irq[12]);
+    milkymist_minimac2_create(0x60008000, 0x30000000, irq[11], irq[12]);
     milkymist_softusb_create(0x6000f000, irq[17],
             0x20000000, 0x1000, 0x20020000, 0x2000);
 
diff --git a/trace-events b/trace-events
index 77c96a5..4f965e2 100644
--- a/trace-events
+++ b/trace-events
@@ -308,17 +308,18 @@ disable milkymist_hpdmc_memory_write(uint32_t addr, uint32_t value) "addr=%08x v
 disable milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
 disable milkymist_memcard_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
 
-# hw/milkymist-minimac.c
-disable milkymist_minimac_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
-disable milkymist_minimac_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
-disable milkymist_minimac_mdio_write(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x"
-disable milkymist_minimac_mdio_read(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x"
-disable milkymist_minimac_tx_frame(uint32_t length) "length %u"
-disable milkymist_minimac_rx_frame(const void *buf, uint32_t length) "buf %p length %u"
-disable milkymist_minimac_drop_rx_frame(const void *buf) "buf %p"
-disable milkymist_minimac_rx_transfer(const void *buf, uint32_t length) "buf %p length %d"
-disable milkymist_minimac_pulse_irq_rx(void) "Pulse IRQ RX"
-disable milkymist_minimac_pulse_irq_tx(void) "Pulse IRQ TX"
+# hw/milkymist-minimac2.c
+disable milkymist_minimac2_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
+disable milkymist_minimac2_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
+disable milkymist_minimac2_mdio_write(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x"
+disable milkymist_minimac2_mdio_read(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x"
+disable milkymist_minimac2_tx_frame(uint32_t length) "length %u"
+disable milkymist_minimac2_rx_frame(const void *buf, uint32_t length) "buf %p length %u"
+disable milkymist_minimac2_drop_rx_frame(const void *buf) "buf %p"
+disable milkymist_minimac2_rx_transfer(const void *buf, uint32_t length) "buf %p length %d"
+disable milkymist_minimac2_raise_irq_rx(void) "Raise IRQ RX"
+disable milkymist_minimac2_lower_irq_rx(void) "Lower IRQ RX"
+disable milkymist_minimac2_pulse_irq_tx(void) "Pulse IRQ TX"
 
 # hw/milkymist-pfpu.c
 disable milkymist_pfpu_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
commit f3172a0e2e7bd983cada19f11d9bb59400e0dd3d
Author: Michael Walle <michael at walle.cc>
Date:   Wed Apr 13 00:29:35 2011 +0200

    milkymist-sysctl: fix timers
    
    Prevent timers from firing right after starting.
    
    Signed-off-by: Michael Walle <michael at walle.cc>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/milkymist-sysctl.c b/hw/milkymist-sysctl.c
index eaea543..6bd0cb9 100644
--- a/hw/milkymist-sysctl.c
+++ b/hw/milkymist-sysctl.c
@@ -140,24 +140,8 @@ static void sysctl_write(void *opaque, target_phys_addr_t addr, uint32_t value)
     case R_GPIO_OUT:
     case R_GPIO_INTEN:
     case R_TIMER0_COUNTER:
-        if (value > s->regs[R_TIMER0_COUNTER]) {
-            value = s->regs[R_TIMER0_COUNTER];
-            error_report("milkymist_sysctl: timer0: trying to write a "
-                    "value greater than the limit. Clipping.");
-        }
-        /* milkymist timer counts up */
-        value = s->regs[R_TIMER0_COUNTER] - value;
-        ptimer_set_count(s->ptimer0, value);
-        break;
     case R_TIMER1_COUNTER:
-        if (value > s->regs[R_TIMER1_COUNTER]) {
-            value = s->regs[R_TIMER1_COUNTER];
-            error_report("milkymist_sysctl: timer1: trying to write a "
-                    "value greater than the limit. Clipping.");
-        }
-        /* milkymist timer counts up */
-        value = s->regs[R_TIMER1_COUNTER] - value;
-        ptimer_set_count(s->ptimer1, value);
+        s->regs[addr] = value;
         break;
     case R_TIMER0_COMPARE:
         ptimer_set_limit(s->ptimer0, value, 0);
@@ -170,10 +154,12 @@ static void sysctl_write(void *opaque, target_phys_addr_t addr, uint32_t value)
     case R_TIMER0_CONTROL:
         s->regs[addr] = value;
         if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) {
-            trace_milkymist_sysctl_start_timer1();
+            trace_milkymist_sysctl_start_timer0();
+            ptimer_set_count(s->ptimer0,
+                    s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]);
             ptimer_run(s->ptimer0, 0);
         } else {
-            trace_milkymist_sysctl_stop_timer1();
+            trace_milkymist_sysctl_stop_timer0();
             ptimer_stop(s->ptimer0);
         }
         break;
@@ -181,6 +167,8 @@ static void sysctl_write(void *opaque, target_phys_addr_t addr, uint32_t value)
         s->regs[addr] = value;
         if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) {
             trace_milkymist_sysctl_start_timer1();
+            ptimer_set_count(s->ptimer1,
+                    s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]);
             ptimer_run(s->ptimer1, 0);
         } else {
             trace_milkymist_sysctl_stop_timer1();
commit c07050ddb9659805068b2f1a3686c6f096dd11f9
Author: Michael Walle <michael at walle.cc>
Date:   Wed Apr 13 00:29:34 2011 +0200

    milkymist-vgafb: fix console resizing
    
    After enabling the framebuffer, ensure that the console is resized.
    
    Signed-off-by: Michael Walle <michael at walle.cc>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/milkymist-vgafb.c b/hw/milkymist-vgafb.c
index 8922731..2e55e42 100644
--- a/hw/milkymist-vgafb.c
+++ b/hw/milkymist-vgafb.c
@@ -199,6 +199,9 @@ vgafb_write(void *opaque, target_phys_addr_t addr, uint32_t value)
     addr >>= 2;
     switch (addr) {
     case R_CTRL:
+        s->regs[addr] = value;
+        vgafb_resize(s);
+        break;
     case R_HSYNC_START:
     case R_HSYNC_END:
     case R_HSCAN:
commit ecbe1de82362e73c2b1111770c4a91b675a6fca2
Author: Michael Walle <michael at walle.cc>
Date:   Wed Apr 13 00:29:33 2011 +0200

    lm32: fix exception handling
    
    Global interrupt enable bit is already saved within the exception handler
    helper routine. Thus remove extra code in translation routines.
    
    Additionally, debug exceptions has always DEBA as base address.
    
    Signed-off-by: Michael Walle <michael at walle.cc>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/target-lm32/helper.c b/target-lm32/helper.c
index 318e2cf..4f3e7e0 100644
--- a/target-lm32/helper.c
+++ b/target-lm32/helper.c
@@ -76,11 +76,7 @@ void do_interrupt(CPUState *env)
         env->regs[R_BA] = env->pc;
         env->ie |= (env->ie & IE_IE) ? IE_BIE : 0;
         env->ie &= ~IE_IE;
-        if (env->dc & DC_RE) {
-            env->pc = env->deba + (env->exception_index * 32);
-        } else {
-            env->pc = env->eba + (env->exception_index * 32);
-        }
+        env->pc = env->deba + (env->exception_index * 32);
         log_cpu_state_mask(CPU_LOG_INT, env, 0);
         break;
     default:
diff --git a/target-lm32/translate.c b/target-lm32/translate.c
index 51b4f5a..bcd52fe 100644
--- a/target-lm32/translate.c
+++ b/target-lm32/translate.c
@@ -598,36 +598,10 @@ static void dec_scall(DisasContext *dc)
     t0 = tcg_temp_new();
     l1 = gen_new_label();
 
-    /* save IE.IE */
-    tcg_gen_andi_tl(t0, cpu_ie, IE_IE);
-
-    /* IE.IE = 0 */
-    tcg_gen_andi_tl(cpu_ie, cpu_ie, ~IE_IE);
-
     if (dc->imm5 == 7) {
-        /* IE.EIE = IE.IE */
-        tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_EIE);
-        tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_IE, l1);
-        tcg_gen_andi_tl(cpu_ie, cpu_ie, ~IE_EIE);
-        gen_set_label(l1);
-
-        /* gpr[ea] = PC */
-        tcg_gen_movi_tl(cpu_R[R_EA], dc->pc);
-        tcg_temp_free(t0);
-
         tcg_gen_movi_tl(cpu_pc, dc->pc);
         t_gen_raise_exception(dc, EXCP_SYSTEMCALL);
     } else {
-        /* IE.BIE = IE.IE */
-        tcg_gen_ori_tl(cpu_ie, cpu_ie, IE_BIE);
-        tcg_gen_brcondi_tl(TCG_COND_EQ, t0, IE_IE, l1);
-        tcg_gen_andi_tl(cpu_ie, cpu_ie, ~IE_BIE);
-        gen_set_label(l1);
-
-        /* gpr[ba] = PC */
-        tcg_gen_movi_tl(cpu_R[R_BA], dc->pc);
-        tcg_temp_free(t0);
-
         tcg_gen_movi_tl(cpu_pc, dc->pc);
         t_gen_raise_exception(dc, EXCP_BREAKPOINT);
     }
commit 4a043713b34af8947a4e8f40a9f4f43d7a6d2ae9
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Mon May 2 09:54:04 2011 +0200

    kvm: use qemu_free consistently
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
    Signed-off-by: Marcelo Tosatti <mtosatti at redhat.com>

diff --git a/kvm-all.c b/kvm-all.c
index 8b85029..d92c20e 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -1191,7 +1191,7 @@ int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
         bp->use_count = 1;
         err = kvm_arch_insert_sw_breakpoint(current_env, bp);
         if (err) {
-            free(bp);
+            qemu_free(bp);
             return err;
         }
 
@@ -1315,7 +1315,7 @@ int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset)
     sigmask->len = 8;
     memcpy(sigmask->sigset, sigset, sizeof(*sigset));
     r = kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, sigmask);
-    free(sigmask);
+    qemu_free(sigmask);
 
     return r;
 }
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 485572f..faedc6c 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -572,7 +572,7 @@ static int kvm_get_supported_msrs(KVMState *s)
             }
         }
 
-        free(kvm_msr_list);
+        qemu_free(kvm_msr_list);
     }
 
     return ret;
commit 51b0c6065aa6e47a47094d73e24be298a4a7f3a1
Author: Michael Tokarev <mjt at tls.msk.ru>
Date:   Tue Apr 26 20:13:49 2011 +0400

    fix crash in migration, 32-bit userspace on 64-bit host
    
    This change fixes a long-standing immediate crash (memory corruption
    and abort in glibc malloc code) in migration on 32bits.
    
    The bug is present since this commit:
    
      commit 692d9aca97b865b0f7903565274a52606910f129
      Author: Bruce Rogers <brogers at novell.com>
      Date:   Wed Sep 23 16:13:18 2009 -0600
    
        qemu-kvm: allocate correct size for dirty bitmap
    
        The dirty bitmap copied out to userspace is stored in a long array,
        and gets copied out to userspace accordingly.  This patch accounts
        for that correctly.  Currently I'm seeing kvm crashing due to writing
        beyond the end of the alloc'd dirty bitmap memory, because the buffer
        has the wrong size.
    
        Signed-off-by: Bruce Rogers
        Signed-off-by: Marcelo Tosatti <mtosatti at redhat.com>
    
     --- a/qemu-kvm.c
     +++ b/qemu-kvm.c
     @@ int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr,
     -            buf = qemu_malloc((slots[i].len / 4096 + 7) / 8 + 2);
     +            buf = qemu_malloc(BITMAP_SIZE(slots[i].len));
                 r = kvm_get_map(kvm, KVM_GET_DIRTY_LOG, i, buf);
    
    BITMAP_SIZE is now open-coded in that function, like this:
    
     size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), HOST_LONG_BITS) / 8;
    
    The problem is that HOST_LONG_BITS in 32bit userspace is 32
    but it's 64 in 64bit kernel.  So userspace aligns this to
    32, and kernel to 64, but since no length is passed from
    userspace to kernel on ioctl, kernel uses its size calculation
    and copies 4 extra bytes to userspace, corrupting memory.
    
    Here's how it looks like during migrate execution:
    
    our=20, kern=24
    our=4, kern=8
    ...
    our=4, kern=8
    our=4064, kern=4064
    our=512, kern=512
    our=4, kern=8
    our=20, kern=24
    our=4, kern=8
    ...
    our=4, kern=8
    our=4064, kern=4064
    *** glibc detected *** ./x86_64-softmmu/qemu-system-x86_64: realloc(): invalid next size: 0x08f20528 ***
    
    (our is userspace size above, kern is the size as calculated
    by the kernel).
    
    Fix this by always aligning to 64 in a hope that no platform will
    have sizeof(long)>8 any time soon, and add a comment describing it
    all.  It's a small price to pay for bad kernel design.
    
    Alternatively it's possible to fix that in the kernel by using
    different size calculation depending on the current process.
    But this becomes quite ugly.
    
    Special thanks goes to Stefan Hajnoczi for spotting the fundamental
    cause of the issue, and to Alexander Graf for his support in #qemu.
    
    Signed-off-by: Michael Tokarev <mjt at tls.msk.ru>
    CC: Bruce Rogers <brogers at novell.com>
    Signed-off-by: Avi Kivity <avi at redhat.com>

diff --git a/kvm-all.c b/kvm-all.c
index fd1fbfe..8b85029 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -373,7 +373,20 @@ static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
             break;
         }
 
-        size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), HOST_LONG_BITS) / 8;
+        /* XXX bad kernel interface alert
+         * For dirty bitmap, kernel allocates array of size aligned to
+         * bits-per-long.  But for case when the kernel is 64bits and
+         * the userspace is 32bits, userspace can't align to the same
+         * bits-per-long, since sizeof(long) is different between kernel
+         * and user space.  This way, userspace will provide buffer which
+         * may be 4 bytes less than the kernel will use, resulting in
+         * userspace memory corruption (which is not detectable by valgrind
+         * too, in most cases).
+         * So for now, let's align to 64 instead of HOST_LONG_BITS here, in
+         * a hope that sizeof(long) wont become >8 any time soon.
+         */
+        size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
+                     /*HOST_LONG_BITS*/ 64) / 8;
         if (!d.dirty_bitmap) {
             d.dirty_bitmap = qemu_malloc(size);
         } else if (size > allocated_size) {
commit aa7f74d1199020a29c677bc80518df5267bfe73f
Author: Jan Kiszka <jan.kiszka at siemens.com>
Date:   Wed Apr 13 01:32:56 2011 +0200

    kvm: Install specialized interrupt handler
    
    KVM only requires to set the raised IRQ in CPUState and to kick the
    receiving vcpu if it is remote. Installing a specialized handler allows
    potential future changes to the TCG code path without risking KVM side
    effects.
    
    Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
    Signed-off-by: Marcelo Tosatti <mtosatti at redhat.com>

diff --git a/kvm-all.c b/kvm-all.c
index 1d7e8ea..fd1fbfe 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -651,6 +651,15 @@ static CPUPhysMemoryClient kvm_cpu_phys_memory_client = {
     .log_stop = kvm_log_stop,
 };
 
+static void kvm_handle_interrupt(CPUState *env, int mask)
+{
+    env->interrupt_request |= mask;
+
+    if (!qemu_cpu_is_self(env)) {
+        qemu_cpu_kick(env);
+    }
+}
+
 int kvm_init(void)
 {
     static const char upgrade_note[] =
@@ -759,6 +768,8 @@ int kvm_init(void)
 
     s->many_ioeventfds = kvm_check_many_ioeventfds();
 
+    cpu_interrupt_handler = kvm_handle_interrupt;
+
     return 0;
 
 err:
commit ec6959d0466fb240fe4d94d5f525eebf9ba18b84
Author: Jan Kiszka <jan.kiszka at siemens.com>
Date:   Wed Apr 13 01:32:56 2011 +0200

    Redirect cpu_interrupt to callback handler
    
    This allows to override the interrupt handling of QEMU in system mode.
    KVM will make use of it to set a specialized handler.
    
    Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
    Signed-off-by: Marcelo Tosatti <mtosatti at redhat.com>

diff --git a/cpu-all.h b/cpu-all.h
index 0bae6df..88126ea 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -799,7 +799,19 @@ extern CPUState *cpu_single_env;
 #define CPU_INTERRUPT_SIPI   0x800 /* SIPI pending. */
 #define CPU_INTERRUPT_MCE    0x1000 /* (x86 only) MCE pending. */
 
-void cpu_interrupt(CPUState *s, int mask);
+#ifndef CONFIG_USER_ONLY
+typedef void (*CPUInterruptHandler)(CPUState *, int);
+
+extern CPUInterruptHandler cpu_interrupt_handler;
+
+static inline void cpu_interrupt(CPUState *s, int mask)
+{
+    cpu_interrupt_handler(s, mask);
+}
+#else /* USER_ONLY */
+void cpu_interrupt(CPUState *env, int mask);
+#endif /* USER_ONLY */
+
 void cpu_reset_interrupt(CPUState *env, int mask);
 
 void cpu_exit(CPUState *s);
diff --git a/exec.c b/exec.c
index d6d8a89..a718d74 100644
--- a/exec.c
+++ b/exec.c
@@ -1631,7 +1631,7 @@ static void cpu_unlink_tb(CPUState *env)
 
 #ifndef CONFIG_USER_ONLY
 /* mask must never be zero, except for A20 change call */
-void cpu_interrupt(CPUState *env, int mask)
+static void tcg_handle_interrupt(CPUState *env, int mask)
 {
     int old_mask;
 
@@ -1658,6 +1658,8 @@ void cpu_interrupt(CPUState *env, int mask)
     }
 }
 
+CPUInterruptHandler cpu_interrupt_handler = tcg_handle_interrupt;
+
 #else /* CONFIG_USER_ONLY */
 
 void cpu_interrupt(CPUState *env, int mask)
commit 97ffbd8d9d54736dd73227e5330c7f5cdc2d7a96
Author: Jan Kiszka <jan.kiszka at siemens.com>
Date:   Wed Apr 13 01:32:56 2011 +0200

    Break up user and system cpu_interrupt implementations
    
    Both have only two lines in common, and we will convert the system
    service into a callback which is of no use for user mode operation.
    
    Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
    CC: Riku Voipio <riku.voipio at iki.fi>
    Signed-off-by: Marcelo Tosatti <mtosatti at redhat.com>

diff --git a/exec.c b/exec.c
index c3dc68a..d6d8a89 100644
--- a/exec.c
+++ b/exec.c
@@ -1629,6 +1629,7 @@ static void cpu_unlink_tb(CPUState *env)
     spin_unlock(&interrupt_lock);
 }
 
+#ifndef CONFIG_USER_ONLY
 /* mask must never be zero, except for A20 change call */
 void cpu_interrupt(CPUState *env, int mask)
 {
@@ -1637,7 +1638,6 @@ void cpu_interrupt(CPUState *env, int mask)
     old_mask = env->interrupt_request;
     env->interrupt_request |= mask;
 
-#ifndef CONFIG_USER_ONLY
     /*
      * If called from iothread context, wake the target cpu in
      * case its halted.
@@ -1646,21 +1646,27 @@ void cpu_interrupt(CPUState *env, int mask)
         qemu_cpu_kick(env);
         return;
     }
-#endif
 
     if (use_icount) {
         env->icount_decr.u16.high = 0xffff;
-#ifndef CONFIG_USER_ONLY
         if (!can_do_io(env)
             && (mask & ~old_mask) != 0) {
             cpu_abort(env, "Raised interrupt while not in I/O function");
         }
-#endif
     } else {
         cpu_unlink_tb(env);
     }
 }
 
+#else /* CONFIG_USER_ONLY */
+
+void cpu_interrupt(CPUState *env, int mask)
+{
+    env->interrupt_request |= mask;
+    cpu_unlink_tb(env);
+}
+#endif /* CONFIG_USER_ONLY */
+
 void cpu_reset_interrupt(CPUState *env, int mask)
 {
     env->interrupt_request &= ~mask;
commit 450fb75c478aa4134bc1e6b1655791c0a39ad141
Author: Glauber Costa <glommer at redhat.com>
Date:   Thu Mar 17 19:42:07 2011 -0300

    kvm: create kvmclock when one of the flags are present
    
    kvmclock presence can be signalled by two different flags. So for
    device creation, we have to test for both.
    
    Signed-off-by: Glauber Costa <glommer at redhat.com>
    Signed-off-by: Avi Kivity <avi at redhat.com>

diff --git a/hw/kvmclock.c b/hw/kvmclock.c
index b6ceddf..004c4ad 100644
--- a/hw/kvmclock.c
+++ b/hw/kvmclock.c
@@ -103,7 +103,11 @@ static SysBusDeviceInfo kvmclock_info = {
 void kvmclock_create(void)
 {
     if (kvm_enabled() &&
-        first_cpu->cpuid_kvm_features & (1ULL << KVM_FEATURE_CLOCKSOURCE)) {
+        first_cpu->cpuid_kvm_features & ((1ULL << KVM_FEATURE_CLOCKSOURCE)
+#ifdef KVM_FEATURE_CLOCKSOURCE2
+        || (1ULL << KVM_FEATURE_CLOCKSOURCE2)
+#endif
+    )) {
         sysbus_create_simple("kvmclock", -1, NULL);
     }
 }
commit 642258c6c7f386165bc7e79dcd42040fd77df01e
Author: Glauber Costa <glommer at redhat.com>
Date:   Thu Mar 17 19:42:06 2011 -0300

    kvm: add kvmclock to its second bit
    
    We have two bits that can represent kvmclock in cpuid.
    They signal the guest which msr set to use. When we tweak flags
    involving this value - specially when we use "-", we have to act on both.
    
    Signed-off-by: Glauber Costa <glommer at redhat.com>
    Signed-off-by: Avi Kivity <avi at redhat.com>

diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c
index 0ac592f..e479a4d 100644
--- a/target-i386/cpuid.c
+++ b/target-i386/cpuid.c
@@ -73,7 +73,7 @@ static const char *ext3_feature_name[] = {
 };
 
 static const char *kvm_feature_name[] = {
-    "kvmclock", "kvm_nopiodelay", "kvm_mmu", NULL, "kvm_asyncpf", NULL, NULL, NULL,
+    "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", "kvm_asyncpf", NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
commit e41e0fc61ae776b9235380fe9570af31ea7bbc86
Author: Jan Kiszka <jan.kiszka at siemens.com>
Date:   Tue Apr 19 13:06:06 2011 +0200

    x86: Allow multiple cpu feature matches of lookup_feature
    
    kvmclock is represented by two feature bits. Therefore, lookup_feature
    needs to continue its search even after the first match. Enhance it
    accordingly and switch to a bool return type at this chance.
    
    Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
    Signed-off-by: Marcelo Tosatti <mtosatti at redhat.com>

diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c
index 814d13e..0ac592f 100644
--- a/target-i386/cpuid.c
+++ b/target-i386/cpuid.c
@@ -182,20 +182,22 @@ static int altcmp(const char *s, const char *e, const char *altstr)
 }
 
 /* search featureset for flag *[s..e), if found set corresponding bit in
- * *pval and return success, otherwise return zero
+ * *pval and return true, otherwise return false
  */
-static int lookup_feature(uint32_t *pval, const char *s, const char *e,
-    const char **featureset)
+static bool lookup_feature(uint32_t *pval, const char *s, const char *e,
+                           const char **featureset)
 {
     uint32_t mask;
     const char **ppc;
+    bool found = false;
 
-    for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc)
+    for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc) {
         if (*ppc && !altcmp(s, e, *ppc)) {
             *pval |= mask;
-            break;
+            found = true;
         }
-    return (mask ? 1 : 0);
+    }
+    return found;
 }
 
 static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features,
commit 0c31b744f606d1c19b698041480eb195b8801747
Author: Glauber Costa <glommer at redhat.com>
Date:   Thu Mar 17 19:42:05 2011 -0300

    kvm: use kernel-provided para_features instead of statically coming up with new capabilities
    
    Use the features provided by KVM_GET_SUPPORTED_CPUID directly to
    mask out features from guest-visible cpuid.
    
    The old get_para_features() mechanism is kept for older kernels that do not implement it.
    
    Signed-off-by: Glauber Costa <glommer at redhat.com>
    Signed-off-by: Avi Kivity <avi at redhat.com>

diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index a13599d..485572f 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -92,6 +92,35 @@ static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max)
     return cpuid;
 }
 
+#ifdef CONFIG_KVM_PARA
+struct kvm_para_features {
+    int cap;
+    int feature;
+} para_features[] = {
+    { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE },
+    { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY },
+    { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP },
+#ifdef KVM_CAP_ASYNC_PF
+    { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF },
+#endif
+    { -1, -1 }
+};
+
+static int get_para_features(CPUState *env)
+{
+    int i, features = 0;
+
+    for (i = 0; i < ARRAY_SIZE(para_features) - 1; i++) {
+        if (kvm_check_extension(env->kvm_state, para_features[i].cap)) {
+            features |= (1 << para_features[i].feature);
+        }
+    }
+
+    return features;
+}
+#endif
+
+
 uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
                                       uint32_t index, int reg)
 {
@@ -99,6 +128,9 @@ uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
     int i, max;
     uint32_t ret = 0;
     uint32_t cpuid_1_edx;
+#ifdef CONFIG_KVM_PARA
+    int has_kvm_features = 0;
+#endif
 
     max = 1;
     while ((cpuid = try_get_cpuid(env->kvm_state, max)) == NULL) {
@@ -108,6 +140,11 @@ uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
     for (i = 0; i < cpuid->nent; ++i) {
         if (cpuid->entries[i].function == function &&
             cpuid->entries[i].index == index) {
+#ifdef CONFIG_KVM_PARA
+            if (cpuid->entries[i].function == KVM_CPUID_FEATURES) {
+                has_kvm_features = 1;
+            }
+#endif
             switch (reg) {
             case R_EAX:
                 ret = cpuid->entries[i].eax;
@@ -140,38 +177,15 @@ uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
 
     qemu_free(cpuid);
 
-    return ret;
-}
-
 #ifdef CONFIG_KVM_PARA
-struct kvm_para_features {
-    int cap;
-    int feature;
-} para_features[] = {
-    { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE },
-    { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY },
-    { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP },
-#ifdef KVM_CAP_ASYNC_PF
-    { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF },
-#endif
-    { -1, -1 }
-};
-
-static int get_para_features(CPUState *env)
-{
-    int i, features = 0;
-
-    for (i = 0; i < ARRAY_SIZE(para_features) - 1; i++) {
-        if (kvm_check_extension(env->kvm_state, para_features[i].cap)) {
-            features |= (1 << para_features[i].feature);
-        }
+    /* fallback for older kernels */
+    if (!has_kvm_features && (function == KVM_CPUID_FEATURES)) {
+        ret = get_para_features(env);
     }
-#ifdef KVM_CAP_ASYNC_PF
-    has_msr_async_pf_en = features & (1 << KVM_FEATURE_ASYNC_PF);
 #endif
-    return features;
+
+    return ret;
 }
-#endif /* CONFIG_KVM_PARA */
 
 typedef struct HWPoisonPage {
     ram_addr_t ram_addr;
@@ -397,7 +411,13 @@ int kvm_arch_init_vcpu(CPUState *env)
     c = &cpuid_data.entries[cpuid_i++];
     memset(c, 0, sizeof(*c));
     c->function = KVM_CPUID_FEATURES;
-    c->eax = env->cpuid_kvm_features & get_para_features(env);
+    c->eax = env->cpuid_kvm_features & kvm_arch_get_supported_cpuid(env,
+                                                KVM_CPUID_FEATURES, 0, R_EAX);
+
+#ifdef KVM_CAP_ASYNC_PF
+    has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF);
+#endif
+
 #endif
 
     cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
commit cd18f05e248bb916028021634058da06a4657e26
Author: Mike McCormack <mj.mccormack at samsung.com>
Date:   Mon Apr 18 14:43:36 2011 +0900

    Don't zero out buffer in sched_getaffinity
    
    The kernel doesn't fill the buffer provided to sched_getaffinity
    with zero bytes, so neither should QEMU.
    
    Signed-off-by: Mike McCormack <mj.mccormack at samsung.com>
    Reviewed-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
    Signed-off-by: Riku Voipio <riku.voipio at iki.fi>

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 5b7b8e2..279cef3 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -6500,20 +6500,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             ret = get_errno(sys_sched_getaffinity(arg1, mask_size, mask));
 
             if (!is_error(ret)) {
-                if (arg2 > ret) {
-                    /* Zero out any extra space kernel didn't fill */
-                    unsigned long zero = arg2 - ret;
-                    p = alloca(zero);
-                    memset(p, 0, zero);
-                    if (copy_to_user(arg3 + ret, p, zero)) {
-                        goto efault;
-                    }
-                    arg2 = ret;
-                }
-                if (copy_to_user(arg3, mask, arg2)) {
+                if (copy_to_user(arg3, mask, ret)) {
                     goto efault;
                 }
-                ret = arg2;
             }
         }
         break;
commit e95d3bf04d8a54af43bb8db3b8eb64d68c9f6927
Author: Mike McCormack <mj.mccormack at samsung.com>
Date:   Tue Apr 12 11:41:00 2011 +0900

    Fix buffer overrun in sched_getaffinity
    
    Zeroing of the cpu array should start from &cpus[kernel_ret]
    not &cpus[num_zeros_to_fill].
    
    This fixes a crash in EFL's edje_cc running under qemu-arm.
    
    Signed-off-by: Mike McCormack <mj.mccormack at samsung.com>
    Reviewed-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
    Acked-by: Mike Frysinger <vapier at gentoo.org>
    Signed-off-by: Riku Voipio <riku.voipio at iki.fi>

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index e969d1b..5b7b8e2 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -6505,7 +6505,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
                     unsigned long zero = arg2 - ret;
                     p = alloca(zero);
                     memset(p, 0, zero);
-                    if (copy_to_user(arg3 + zero, p, zero)) {
+                    if (copy_to_user(arg3 + ret, p, zero)) {
                         goto efault;
                     }
                     arg2 = ret;
commit 6f11f013a5ef88c42ce2e9060bbaafb39676ca39
Author: Stefan Weil <weil at mail.berlios.de>
Date:   Wed Apr 27 10:44:38 2011 +0200

    linux-user: Fix compilation for "old" linux versions
    
    Debian Lenny and other installations with older linux versions
    failed to compile linux-user because some CLONE_xxx macros are
    undefined.
    
    Signed-off-by: Stefan Weil <weil at mail.berlios.de>
    Signed-off-by: Riku Voipio <riku.voipio at iki.fi>

diff --git a/linux-user/strace.c b/linux-user/strace.c
index 5d9bb08..fe9326a 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -477,12 +477,24 @@ UNUSED static struct flags clone_flags[] = {
     FLAG_GENERIC(CLONE_DETACHED),
     FLAG_GENERIC(CLONE_UNTRACED),
     FLAG_GENERIC(CLONE_CHILD_SETTID),
+#if defined(CLONE_NEWUTS)
     FLAG_GENERIC(CLONE_NEWUTS),
+#endif
+#if defined(CLONE_NEWIPC)
     FLAG_GENERIC(CLONE_NEWIPC),
+#endif
+#if defined(CLONE_NEWUSER)
     FLAG_GENERIC(CLONE_NEWUSER),
+#endif
+#if defined(CLONE_NEWPID)
     FLAG_GENERIC(CLONE_NEWPID),
+#endif
+#if defined(CLONE_NEWNET)
     FLAG_GENERIC(CLONE_NEWNET),
+#endif
+#if defined(CLONE_IO)
     FLAG_GENERIC(CLONE_IO),
+#endif
     FLAG_END,
 };
 
commit 08ab2ccb08372a52ee1c597acf640cadb9089a3a
Merge: 642cfd4... 2f9cba0...
Author: Blue Swirl <blauwirbel at gmail.com>
Date:   Fri Apr 29 20:01:51 2011 +0000

    Merge branch 'patches' of git://qemu.weilnetz.de/git/qemu
    
    * 'patches' of git://qemu.weilnetz.de/git/qemu:
      qemu-timer: Fix timers for w32
      qemu-timer: Avoid type casts
      qemu-timer: Remove unneeded include statement (w32)
      qemu-timer: Add and use new function qemu_timer_expired_ns

commit 642cfd4d31241c0fc65c520cb1e703659af66236
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Thu Apr 28 12:40:54 2011 -0500

    virtfs: fix build due from rename
    
    The latest virtfs pull broke the cris-softmmu target.
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

diff --git a/Makefile.objs b/Makefile.objs
index 0cbff4d..9d8851e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -285,11 +285,11 @@ sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
 adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
 hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
 
-9pfs-nested-$(CONFIG_VIRTFS) = virtio-9p-debug.o
+9pfs-nested-$(CONFIG_REALLY_VIRTFS) = virtio-9p-debug.o
 9pfs-nested-$(CONFIG_VIRTFS) +=  virtio-9p-local.o virtio-9p-xattr.o
 9pfs-nested-$(CONFIG_VIRTFS) +=   virtio-9p-xattr-user.o virtio-9p-posix-acl.o
 
-hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
+hw-obj-$(CONFIG_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
 $(addprefix 9pfs/, $(9pfs-nested-y)): CFLAGS +=  -I$(SRC_PATH)/hw/
 
 
commit 71ef18e1f2291b47ed7bc6dd65a840d4fc42d395
Merge: e77976a... 5c1c9bb...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Thu Apr 28 08:37:54 2011 -0500

    Merge remote-tracking branch 'amitshah/for-anthony' into staging

commit e77976a247a9fcefe95cf97b25bf01dbe574e66b
Merge: a7d3970... e14ea47...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Thu Apr 28 08:25:45 2011 -0500

    Merge remote-tracking branch 'jvrao/for-anthony' into staging

commit 5c1c9bb24b20fb5844d01ac67d51c26941db5af1
Author: Alexey Kardashevskiy <aik at ozlabs.ru>
Date:   Tue Apr 19 12:03:46 2011 +1000

    virtio-serial: Fix endianness bug in the config space
    
    The virtio serial specification requres that the values in the config
    space are encoded in native endian of the guest.
    
    The qemu virtio-serial code did not do conversion to the guest endian
    format what caused problems when host and guest use different format.
    
    This patch corrects the qemu side, correctly doing host-native <->
    guest-native conversions when accessing the config space. This won't
    break any setups that aren't already broken, and fixes the case
    of different host and guest endianness.
    
    Signed-off-by: Alexey Kardashevskiy <aik at ozlabs.ru>
    Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
    Reviewed-by: Juan Quintela <quintela at redhat.com>
    Signed-off-by: Amit Shah <amit.shah at redhat.com>

diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 6227379..f10d48f 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -494,7 +494,7 @@ static void virtio_serial_save(QEMUFile *f, void *opaque)
     VirtIOSerial *s = opaque;
     VirtIOSerialPort *port;
     uint32_t nr_active_ports;
-    unsigned int i;
+    unsigned int i, max_nr_ports;
 
     /* The virtio device */
     virtio_save(&s->vdev, f);
@@ -506,8 +506,8 @@ static void virtio_serial_save(QEMUFile *f, void *opaque)
     qemu_put_be32s(f, &s->config.max_nr_ports);
 
     /* The ports map */
-
-    for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) {
+    max_nr_ports = tswap32(s->config.max_nr_ports);
+    for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
         qemu_put_be32s(f, &s->ports_map[i]);
     }
 
@@ -568,7 +568,8 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id)
     qemu_get_be16s(f, &s->config.rows);
 
     qemu_get_be32s(f, &max_nr_ports);
-    if (max_nr_ports > s->config.max_nr_ports) {
+    tswap32s(&max_nr_ports);
+    if (max_nr_ports > tswap32(s->config.max_nr_ports)) {
         /* Source could have had more ports than us. Fail migration. */
         return -EINVAL;
     }
@@ -670,9 +671,10 @@ static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
 /* This function is only used if a port id is not provided by the user */
 static uint32_t find_free_port_id(VirtIOSerial *vser)
 {
-    unsigned int i;
+    unsigned int i, max_nr_ports;
 
-    for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) {
+    max_nr_ports = tswap32(vser->config.max_nr_ports);
+    for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
         uint32_t map, bit;
 
         map = vser->ports_map[i];
@@ -720,7 +722,7 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
     VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev);
     VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, base);
     VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus);
-    int ret;
+    int ret, max_nr_ports;
     bool plugging_port0;
 
     port->vser = bus->vser;
@@ -750,9 +752,10 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
         }
     }
 
-    if (port->id >= port->vser->config.max_nr_ports) {
+    max_nr_ports = tswap32(port->vser->config.max_nr_ports);
+    if (port->id >= max_nr_ports) {
         error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n",
-                     port->vser->config.max_nr_ports - 1);
+                     max_nr_ports - 1);
         return -1;
     }
 
@@ -863,7 +866,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf)
         vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
     }
 
-    vser->config.max_nr_ports = conf->max_virtserial_ports;
+    vser->config.max_nr_ports = tswap32(conf->max_virtserial_ports);
     vser->ports_map = qemu_mallocz(((conf->max_virtserial_ports + 31) / 32)
         * sizeof(vser->ports_map[0]));
     /*
commit da7d998bbb80f141ed5743418a4dfa5c1409e75f
Author: Amit Shah <amit.shah at redhat.com>
Date:   Mon Apr 25 15:18:22 2011 +0530

    char: Detect chardev release by NULL handlers as well as NULL opaque
    
    Juan says he prefers these extra checks to ensure a user of a chardev is
    releasing it.
    
    Requested-by: Juan Quintela <quintela at redhat.com>
    Signed-off-by: Amit Shah <amit.shah at redhat.com>

diff --git a/qemu-char.c b/qemu-char.c
index eaf6571..5e04a20 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -197,7 +197,7 @@ void qemu_chr_add_handlers(CharDriverState *s,
                            IOEventHandler *fd_event,
                            void *opaque)
 {
-    if (!opaque) {
+    if (!opaque && !fd_can_read && !fd_read && !fd_event) {
         /* chr driver being released. */
         ++s->avail_connections;
     }
commit d5b27167e17e0d9393d6364703cc68e7f018023c
Author: Kusanagi Kouichi <slash at ac.auone-net.jp>
Date:   Tue Apr 26 19:19:26 2011 +0900

    char: Allow devices to use a single multiplexed chardev.
    
    This fixes regression caused by commit
    2d6c1ef40f3678ab47a4d14fb5dadaa486bfcda6
    ("char: Prevent multiple devices opening same chardev"):
    
    -nodefaults -nographic -chardev stdio,id=stdio,mux=on,signal=off \
     -mon stdio -device virtio-serial-pci \
     -device virtconsole,chardev=stdio -device isa-serial,chardev=stdio
    
    fails with:
    
    qemu-system-x86_64: -device isa-serial,chardev=stdio: Property 'isa-serial.chardev' can't take value 'stdio', it's in use
    
    Signed-off-by: Kusanagi Kouichi <slash at ac.auone-net.jp>
    Signed-off-by: Amit Shah <amit.shah at redhat.com>

diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index 1088a26..eff2d24 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -354,10 +354,10 @@ static int parse_chr(DeviceState *dev, Property *prop, const char *str)
     if (*ptr == NULL) {
         return -ENOENT;
     }
-    if ((*ptr)->assigned) {
+    if ((*ptr)->avail_connections < 1) {
         return -EEXIST;
     }
-    (*ptr)->assigned = 1;
+    --(*ptr)->avail_connections;
     return 0;
 }
 
diff --git a/qemu-char.c b/qemu-char.c
index 710d98f..eaf6571 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -199,7 +199,7 @@ void qemu_chr_add_handlers(CharDriverState *s,
 {
     if (!opaque) {
         /* chr driver being released. */
-        s->assigned = 0;
+        ++s->avail_connections;
     }
     s->chr_can_read = fd_can_read;
     s->chr_read = fd_read;
@@ -2547,7 +2547,10 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
         snprintf(base->label, len, "%s-base", qemu_opts_id(opts));
         chr = qemu_chr_open_mux(base);
         chr->filename = base->filename;
+        chr->avail_connections = MAX_MUX;
         QTAILQ_INSERT_TAIL(&chardevs, chr, next);
+    } else {
+        chr->avail_connections = 1;
     }
     chr->label = qemu_strdup(qemu_opts_id(opts));
     return chr;
diff --git a/qemu-char.h b/qemu-char.h
index 2f8512e..892c6da 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -72,7 +72,7 @@ struct CharDriverState {
     char *label;
     char *filename;
     int opened;
-    int assigned; /* chardev assigned to a device */
+    int avail_connections;
     QTAILQ_ENTRY(CharDriverState) next;
 };
 
commit cd8f7df2891891c3a6c346892545c4407be6699f
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Mar 24 11:12:04 2011 +0100

    spice-chardev: listen to frontend guest open / close
    
    Note the vmc_register_interface() in spice_chr_write is left in place
    in case someone uses spice-chardev with a frontend which does not have
    guest open / close notification.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Reviewed-by: Alon Levy <alevy at redhat.com>
    Signed-off-by: Amit Shah <amit.shah at redhat.com>

diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 517f337..fa15a71 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -131,6 +131,18 @@ static void spice_chr_close(struct CharDriverState *chr)
     qemu_free(s);
 }
 
+static void spice_chr_guest_open(struct CharDriverState *chr)
+{
+    SpiceCharDriver *s = chr->opaque;
+    vmc_register_interface(s);
+}
+
+static void spice_chr_guest_close(struct CharDriverState *chr)
+{
+    SpiceCharDriver *s = chr->opaque;
+    vmc_unregister_interface(s);
+}
+
 static void print_allowed_subtypes(void)
 {
     const char** psubtype;
@@ -183,6 +195,8 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
     chr->opaque = s;
     chr->chr_write = spice_chr_write;
     chr->chr_close = spice_chr_close;
+    chr->chr_guest_open = spice_chr_guest_open;
+    chr->chr_guest_close = spice_chr_guest_close;
 
     qemu_chr_generic_open(chr);
 
commit 0b6d2266e3ee079c0eab4a5d2facacdcd36a329c
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Mar 24 11:12:03 2011 +0100

    virtio-console: notify backend of guest open / close
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Reviewed-by: Alon Levy <alevy at redhat.com>
    Signed-off-by: Amit Shah <amit.shah at redhat.com>

diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index 6b5237b..de539c4 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -28,6 +28,22 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
     return qemu_chr_write(vcon->chr, buf, len);
 }
 
+/* Callback function that's called when the guest opens the port */
+static void guest_open(VirtIOSerialPort *port)
+{
+    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+    qemu_chr_guest_open(vcon->chr);
+}
+
+/* Callback function that's called when the guest closes the port */
+static void guest_close(VirtIOSerialPort *port)
+{
+    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+    qemu_chr_guest_close(vcon->chr);
+}
+
 /* Readiness of the guest to accept data on a port */
 static int chr_can_read(void *opaque)
 {
@@ -64,6 +80,8 @@ static int generic_port_init(VirtConsole *vcon, VirtIOSerialPort *port)
         qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
                               vcon);
         vcon->port.info->have_data = flush_buf;
+        vcon->port.info->guest_open = guest_open;
+        vcon->port.info->guest_close = guest_close;
     }
     return 0;
 }
commit 7c32c4feebd962960fb160291a426b983e0ae668
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Mar 24 11:12:02 2011 +0100

    chardev: Allow frontends to notify backends of guest open / close
    
    Some frontends know when the guest has opened the "channel" and is actively
    listening to it, for example virtio-serial. This patch adds 2 new qemu-chardev
    functions which can be used by frontends to signal guest open / close, and
    allows interested backends to listen to this.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Reviewed-by: Alon Levy <alevy at redhat.com>
    Signed-off-by: Amit Shah <amit.shah at redhat.com>

diff --git a/qemu-char.c b/qemu-char.c
index 03858d4..710d98f 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -480,6 +480,9 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
     chr->chr_write = mux_chr_write;
     chr->chr_update_read_handler = mux_chr_update_read_handler;
     chr->chr_accept_input = mux_chr_accept_input;
+    /* Frontend guest-open / -close notification is not support with muxes */
+    chr->chr_guest_open = NULL;
+    chr->chr_guest_close = NULL;
 
     /* Muxes are always open on creation */
     qemu_chr_generic_open(chr);
@@ -2579,6 +2582,20 @@ void qemu_chr_set_echo(struct CharDriverState *chr, bool echo)
     }
 }
 
+void qemu_chr_guest_open(struct CharDriverState *chr)
+{
+    if (chr->chr_guest_open) {
+        chr->chr_guest_open(chr);
+    }
+}
+
+void qemu_chr_guest_close(struct CharDriverState *chr)
+{
+    if (chr->chr_guest_close) {
+        chr->chr_guest_close(chr);
+    }
+}
+
 void qemu_chr_close(CharDriverState *chr)
 {
     QTAILQ_REMOVE(&chardevs, chr, next);
diff --git a/qemu-char.h b/qemu-char.h
index fb96eef..2f8512e 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -65,6 +65,8 @@ struct CharDriverState {
     void (*chr_close)(struct CharDriverState *chr);
     void (*chr_accept_input)(struct CharDriverState *chr);
     void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
+    void (*chr_guest_open)(struct CharDriverState *chr);
+    void (*chr_guest_close)(struct CharDriverState *chr);
     void *opaque;
     QEMUBH *bh;
     char *label;
@@ -79,6 +81,8 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
                                     void (*init)(struct CharDriverState *s));
 CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s));
 void qemu_chr_set_echo(struct CharDriverState *chr, bool echo);
+void qemu_chr_guest_open(struct CharDriverState *chr);
+void qemu_chr_guest_close(struct CharDriverState *chr);
 void qemu_chr_close(CharDriverState *chr);
 void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
     GCC_FMT_ATTR(2, 3);
commit a7d3970d0635ebce1412736e7aaf11d387919dc8
Author: Peter Maydell <peter.maydell at linaro.org>
Date:   Tue Apr 26 18:17:20 2011 +0100

    target-arm: Don't update base register on abort in Thumb T1 LDM
    
    Make sure the base register isn't updated if it is in the load list
    for a Thumb LDM (T1 encoding) which aborts partway through the load.
    
    Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
    Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>

diff --git a/target-arm/translate.c b/target-arm/translate.c
index d8da514..a1af436 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -9454,7 +9454,10 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
         break;
 
     case 12:
+    {
         /* load/store multiple */
+        TCGv loaded_var;
+        TCGV_UNUSED(loaded_var);
         rn = (insn >> 8) & 0x7;
         addr = load_reg(s, rn);
         for (i = 0; i < 8; i++) {
@@ -9462,7 +9465,11 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
                 if (insn & (1 << 11)) {
                     /* load */
                     tmp = gen_ld32(addr, IS_USER(s));
-                    store_reg(s, i, tmp);
+                    if (i == rn) {
+                        loaded_var = tmp;
+                    } else {
+                        store_reg(s, i, tmp);
+                    }
                 } else {
                     /* store */
                     tmp = load_reg(s, i);
@@ -9472,14 +9479,18 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s)
                 tcg_gen_addi_i32(addr, addr, 4);
             }
         }
-        /* Base register writeback.  */
         if ((insn & (1 << rn)) == 0) {
+            /* base reg not in list: base register writeback */
             store_reg(s, rn, addr);
         } else {
+            /* base reg in list: if load, complete it now */
+            if (insn & (1 << 11)) {
+                store_reg(s, rn, loaded_var);
+            }
             tcg_temp_free_i32(addr);
         }
         break;
-
+    }
     case 13:
         /* conditional branch or swi */
         cond = (insn >> 8) & 0xf;
commit 5856d44eb592e05bb266fb2c7db42926faa22144
Author: YuYeon Oh <yuyeon.oh at samsung.com>
Date:   Mon Apr 25 01:23:58 2011 +0000

    target-arm: fix LDMIA bug on page boundary
    target-arm: fix LDMIA bug on page boundary
    
    When consecutive memory locations are on page boundary, a base register may be
    loaded before page fault occurs. After page fault handling, it losts the memory
    location information. To solve this problem, loading a base register has to put back.
    
    Signed-off-by: Yuyeon Oh <yuyeon.oh at samsung.com>
    Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
    Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>

diff --git a/target-arm/translate.c b/target-arm/translate.c
index 8b309d4..d8da514 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -8016,7 +8016,8 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                     }
                 }
             } else {
-                int i;
+                int i, loaded_base = 0;
+                TCGv loaded_var;
                 /* Load/store multiple.  */
                 addr = load_reg(s, rn);
                 offset = 0;
@@ -8028,6 +8029,7 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                     tcg_gen_addi_i32(addr, addr, -offset);
                 }
 
+                TCGV_UNUSED(loaded_var);
                 for (i = 0; i < 16; i++) {
                     if ((insn & (1 << i)) == 0)
                         continue;
@@ -8036,6 +8038,9 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                         tmp = gen_ld32(addr, IS_USER(s));
                         if (i == 15) {
                             gen_bx(s, tmp);
+                        } else if (i == rn) {
+                            loaded_var = tmp;
+                            loaded_base = 1;
                         } else {
                             store_reg(s, i, tmp);
                         }
@@ -8046,6 +8051,9 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
                     }
                     tcg_gen_addi_i32(addr, addr, 4);
                 }
+                if (loaded_base) {
+                    store_reg(s, rn, loaded_var);
+                }
                 if (insn & (1 << 21)) {
                     /* Base register writeback.  */
                     if (insn & (1 << 24)) {
commit 47f7be394aa7baf7855fe78f56b8ba4c69bf75d9
Author: Jan Kiszka <jan.kiszka at web.de>
Date:   Sat Apr 9 13:18:59 2011 +0200

    ioapic: Do not set irr for masked edge IRQs
    
    So far we set IRR for edge IRQs even if the pin is masked. If the guest
    later on unmasks and switches the pin to level-triggered mode, irr will
    remain set, causing an IRQ storm. The point is that setting IRR is not
    correct in this case according to the spec, and avoiding this resolves
    the issue.
    
    Reported-and-tested-by: Isaku Yamahata <yamahata at valinux.co.jp>
    Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
    Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>

diff --git a/hw/ioapic.c b/hw/ioapic.c
index 569327d..6c26e82 100644
--- a/hw/ioapic.c
+++ b/hw/ioapic.c
@@ -160,8 +160,9 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
                 s->irr &= ~mask;
             }
         } else {
-            /* edge triggered */
-            if (level) {
+            /* According to the 82093AA manual, we must ignore edge requests
+             * if the input pin is masked. */
+            if (level && !(entry & IOAPIC_LVT_MASKED)) {
                 s->irr |= mask;
                 ioapic_service(s);
             }
commit e14ea479b37d6c6665fae55ea0eb713c4b2d3376
Author: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Date:   Wed Mar 16 08:31:43 2011 +0000

    vl.c: Replace -virtfs string manipulation with QemuOpts
    
    The -virtfs option creates an fsdev representing the pass-through file
    system and a guest-visible virtio-9p-pci device that can access this
    file system.  This patch replaces the string manipulation used to build
    and reparse option lists with direct QemuOpts calls.  Removing the
    string manipulation code makes it easier to maintain and less error
    prone.
    
    An error message is also updated to use "mount_tag" instead of
    "mnt_tag".
    
    Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
    Signed-off-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>

diff --git a/vl.c b/vl.c
index b46ee66..6b9a2f6 100644
--- a/vl.c
+++ b/vl.c
@@ -2447,9 +2447,8 @@ int main(int argc, char **argv, char **envp)
                 }
                 break;
             case QEMU_OPTION_virtfs: {
-                char *arg_fsdev = NULL;
-                char *arg_9p = NULL;
-                int len = 0;
+                QemuOpts *fsdev;
+                QemuOpts *device;
 
                 olist = qemu_find_opts("virtfs");
                 if (!olist) {
@@ -2468,45 +2467,28 @@ int main(int argc, char **argv, char **envp)
                         qemu_opt_get(opts, "security_model") == NULL) {
                     fprintf(stderr, "Usage: -virtfs fstype,path=/share_path/,"
                             "security_model=[mapped|passthrough|none],"
-                            "mnt_tag=tag.\n");
+                            "mount_tag=tag.\n");
                     exit(1);
                 }
 
-                len = strlen(",id=,path=,security_model=");
-                len += strlen(qemu_opt_get(opts, "fstype"));
-                len += strlen(qemu_opt_get(opts, "mount_tag"));
-                len += strlen(qemu_opt_get(opts, "path"));
-                len += strlen(qemu_opt_get(opts, "security_model"));
-                arg_fsdev = qemu_malloc((len + 1) * sizeof(*arg_fsdev));
-
-                snprintf(arg_fsdev, (len + 1) * sizeof(*arg_fsdev),
-                         "%s,id=%s,path=%s,security_model=%s",
-                         qemu_opt_get(opts, "fstype"),
-                         qemu_opt_get(opts, "mount_tag"),
-                         qemu_opt_get(opts, "path"),
-                         qemu_opt_get(opts, "security_model"));
-
-                len = strlen("virtio-9p,fsdev=,mount_tag=");
-                len += 2*strlen(qemu_opt_get(opts, "mount_tag"));
-                arg_9p = qemu_malloc((len + 1) * sizeof(*arg_9p));
-
-                snprintf(arg_9p, (len + 1) * sizeof(*arg_9p),
-                         "virtio-9p,fsdev=%s,mount_tag=%s",
-                         qemu_opt_get(opts, "mount_tag"),
-                         qemu_opt_get(opts, "mount_tag"));
-
-                if (!qemu_opts_parse(qemu_find_opts("fsdev"), arg_fsdev, 1)) {
-                    fprintf(stderr, "parse error [fsdev]: %s\n", optarg);
+                fsdev = qemu_opts_create(qemu_find_opts("fsdev"),
+                                         qemu_opt_get(opts, "mount_tag"), 1);
+                if (!fsdev) {
+                    fprintf(stderr, "duplicate fsdev id: %s\n",
+                            qemu_opt_get(opts, "mount_tag"));
                     exit(1);
                 }
-
-                if (!qemu_opts_parse(qemu_find_opts("device"), arg_9p, 1)) {
-                    fprintf(stderr, "parse error [device]: %s\n", optarg);
-                    exit(1);
-                }
-
-                qemu_free(arg_fsdev);
-                qemu_free(arg_9p);
+                qemu_opt_set(fsdev, "fstype", qemu_opt_get(opts, "fstype"));
+                qemu_opt_set(fsdev, "path", qemu_opt_get(opts, "path"));
+                qemu_opt_set(fsdev, "security_model",
+                             qemu_opt_get(opts, "security_model"));
+
+                device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+                qemu_opt_set(device, "driver", "virtio-9p-pci");
+                qemu_opt_set(device, "fsdev",
+                             qemu_opt_get(opts, "mount_tag"));
+                qemu_opt_set(device, "mount_tag",
+                             qemu_opt_get(opts, "mount_tag"));
                 break;
             }
             case QEMU_OPTION_serial:
commit 4f8dee2dec9c6d590c8a7844b2824935542ca122
Author: Harsh Prateek Bora <harsh at linux.vnet.ibm.com>
Date:   Thu Apr 14 14:54:40 2011 +0530

    v9fs_walk: As per 9p2000 RFC, MAXWELEM >= nwnames >= 0.
    
    The nwnames field in TWALK message is assumed to be >=0 and <= MAXWELEM
    which is defined as macro P9_MAXWELEM (16) in virtio-9p.h as per 9p2000
    RFC. Appropriate changes are required in V9fsWalkState and v9fs_walk.
    
    Signed-off-by: Harsh Prateek Bora <harsh at linux.vnet.ibm.com>
    Reviewed-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
    Signed-off-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>

diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index ca39457..b5fc52b 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -1482,7 +1482,7 @@ static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
 {
     complete_pdu(s, vs->pdu, err);
 
-    if (vs->nwnames) {
+    if (vs->nwnames && vs->nwnames <= P9_MAXWELEM) {
         for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) {
             v9fs_string_free(&vs->wnames[vs->name_idx]);
         }
@@ -1578,7 +1578,7 @@ static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
     vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid,
                                             &newfid, &vs->nwnames);
 
-    if (vs->nwnames) {
+    if (vs->nwnames && vs->nwnames <= P9_MAXWELEM) {
         vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames);
 
         vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames);
@@ -1587,6 +1587,9 @@ static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
             vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s",
                                             &vs->wnames[i]);
         }
+    } else if (vs->nwnames > P9_MAXWELEM) {
+        err = -EINVAL;
+        goto out;
     }
 
     vs->fidp = lookup_fid(s, fid);
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
index 95e4977..622928f 100644
--- a/hw/9pfs/virtio-9p.h
+++ b/hw/9pfs/virtio-9p.h
@@ -282,7 +282,7 @@ typedef struct V9fsStatStateDotl {
 typedef struct V9fsWalkState {
     V9fsPDU *pdu;
     size_t offset;
-    int16_t nwnames;
+    uint16_t nwnames;
     int name_idx;
     V9fsQID *qids;
     V9fsFidState *fidp;
commit f35bde2f8fb55541d4d7ddca50d64ce5a6ef384c
Author: Harsh Prateek Bora <harsh at linux.vnet.ibm.com>
Date:   Wed Feb 2 10:20:33 2011 +0530

    hw/virtio-9p-local.c: Remove unnecessary null char in symlink file
    
    This patch removes the addition of null char in symlink file
    which is being appended to file in case of mapped security model.
    Without this patch, the extra null char causes LTP testcase lstat03
    to fail and hence this fix is required.
    
    Signed-off-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>

diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c
index a8e7525..0a015de 100644
--- a/hw/9pfs/virtio-9p-local.c
+++ b/hw/9pfs/virtio-9p-local.c
@@ -370,7 +370,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
             return fd;
         }
         /* Write the oldpath (target) to the file. */
-        oldpath_size = strlen(oldpath) + 1;
+        oldpath_size = strlen(oldpath);
         do {
             write_size = write(fd, (void *)oldpath, oldpath_size);
         } while (write_size == -1 && errno == EINTR);
commit 1d810aeb4eda548e8a875db9e364732b8765d894
Author: M. Mohan Kumar <mohan at in.ibm.com>
Date:   Tue Feb 1 14:21:41 2011 +0530

    virtio-9p: Bugfix to send correct iounit
    
    LCREATE function packs address of iounit in the pdu, fix that to send
    actual iounit itself.
    
    Signed-off-by: M. Mohan Kumar <mohan at in.ibm.com>
    Acked-by: Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
    Signed-off-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>

diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index 18968c2..ca39457 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -1771,7 +1771,7 @@ static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
         v9fs_string_copy(&vs->fidp->path, &vs->fullname);
         stat_to_qid(&vs->stbuf, &vs->qid);
         vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
-                &vs->iounit);
+                vs->iounit);
         err = vs->offset;
     } else {
         vs->fidp->fid_type = P9_FID_NONE;
commit a09947617cc3d0035f485d9804cd26c5a895b683
Author: Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
Date:   Wed Apr 27 12:26:43 2011 +0530

    virtio-9p: removexattr on default acl should return 0
    
    If we don't have default acl, removexattr on default acl
    should return 0
    
    Signed-off-by: Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
    Signed-off-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>

diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c
index e4e0777..575abe8 100644
--- a/hw/9pfs/virtio-9p-posix-acl.c
+++ b/hw/9pfs/virtio-9p-posix-acl.c
@@ -60,7 +60,7 @@ static int mp_pacl_removexattr(FsContext *ctx,
     ret  = lremovexattr(rpath(ctx, path), MAP_ACL_ACCESS);
     if (ret == -1 && errno == ENODATA) {
         /*
-         * We don't get ENODATA error when trying to remote a
+         * We don't get ENODATA error when trying to remove a
          * posix acl that is not present. So don't throw the error
          * even in case of mapped security model
          */
@@ -103,7 +103,18 @@ static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
 static int mp_dacl_removexattr(FsContext *ctx,
                                const char *path, const char *name)
 {
-    return lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT);
+    int ret;
+    ret  = lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT);
+    if (ret == -1 && errno == ENODATA) {
+        /*
+         * We don't get ENODATA error when trying to remove a
+         * posix acl that is not present. So don't throw the error
+         * even in case of mapped security model
+         */
+        errno = 0;
+        ret = 0;
+    }
+    return ret;
 }
 
 
commit 39792515185350a21ec84b9eb65aafd0bb65525c
Author: Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
Date:   Wed Apr 27 12:25:46 2011 +0530

    virtio-9p: Print the pdu details on return
    
    Signed-off-by: Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
    Signed-off-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>

diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index 7e29535..18968c2 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -596,7 +596,10 @@ static V9fsPDU *alloc_pdu(V9fsState *s)
 static void free_pdu(V9fsState *s, V9fsPDU *pdu)
 {
     if (pdu) {
-	QLIST_INSERT_HEAD(&s->free_list, pdu, next);
+        if (debug_9p_pdu) {
+            pprint_pdu(pdu);
+        }
+        QLIST_INSERT_HEAD(&s->free_list, pdu, next);
     }
 }
 
commit 353ac78d495ef976242abd868f68d78420861c2c
Author: Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
Date:   Fri Jan 28 18:09:08 2011 +0530

    virtio-9p: move 9p files around
    
    Now that we start adding more files related to 9pfs
    it make sense to move them to a separate directory
    
    Signed-off-by: Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
    Signed-off-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>

diff --git a/Makefile.objs b/Makefile.objs
index 1b44695..0cbff4d 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -285,9 +285,13 @@ sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
 adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
 hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
 
-hw-obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p-debug.o
-hw-obj-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o
-hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
+9pfs-nested-$(CONFIG_VIRTFS) = virtio-9p-debug.o
+9pfs-nested-$(CONFIG_VIRTFS) +=  virtio-9p-local.o virtio-9p-xattr.o
+9pfs-nested-$(CONFIG_VIRTFS) +=   virtio-9p-xattr-user.o virtio-9p-posix-acl.o
+
+hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
+$(addprefix 9pfs/, $(9pfs-nested-y)): CFLAGS +=  -I$(SRC_PATH)/hw/
+
 
 ######################################################################
 # libdis
diff --git a/Makefile.target b/Makefile.target
index 0e0ef36..2501c63 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -194,7 +194,7 @@ obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial
 obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
 obj-y += vhost_net.o
 obj-$(CONFIG_VHOST_NET) += vhost.o
-obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p.o
+obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p.o
 obj-y += rwhandler.o
 obj-$(CONFIG_KVM) += kvm.o kvm-all.o
 obj-$(CONFIG_NO_KVM) += kvm-stub.o
@@ -401,9 +401,11 @@ hmp-commands.h: $(SRC_PATH)/hmp-commands.hx
 qmp-commands.h: $(SRC_PATH)/qmp-commands.hx
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@")
 
+9pfs/virtio-9p.o: CFLAGS +=  -I$(SRC_PATH)/hw/
+
 clean:
 	rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
-	rm -f *.d */*.d tcg/*.o ide/*.o
+	rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o
 	rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c
 ifdef CONFIG_SYSTEMTAP_TRACE
 	rm -f *.stp
diff --git a/configure b/configure
index 35f7e8b..6f75e2e 100755
--- a/configure
+++ b/configure
@@ -3062,6 +3062,7 @@ mkdir -p $target_dir
 mkdir -p $target_dir/fpu
 mkdir -p $target_dir/tcg
 mkdir -p $target_dir/ide
+mkdir -p $target_dir/9pfs
 if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" -o "$target" = "arm-bsd-user" -o "$target" = "armeb-bsd-user" ; then
   mkdir -p $target_dir/nwfpe
 fi
@@ -3488,6 +3489,7 @@ for hwlib in 32 64; do
   mkdir -p $d
   mkdir -p $d/ide
   symlink $source_path/Makefile.hw $d/Makefile
+  mkdir -p $d/9pfs
   echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak
 done
 
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
new file mode 100644
index 0000000..126e60e
--- /dev/null
+++ b/fsdev/file-op-9p.h
@@ -0,0 +1,107 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ *  Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef _FILEOP_H
+#define _FILEOP_H
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vfs.h>
+#define SM_LOCAL_MODE_BITS    0600
+#define SM_LOCAL_DIR_MODE_BITS    0700
+
+typedef enum
+{
+    /*
+     * Server will try to set uid/gid.
+     * On failure ignore the error.
+     */
+    SM_NONE = 0,
+    /*
+     * uid/gid set on fileserver files
+     */
+    SM_PASSTHROUGH = 1,
+    /*
+     * uid/gid part of xattr
+     */
+    SM_MAPPED,
+} SecModel;
+
+typedef struct FsCred
+{
+    uid_t   fc_uid;
+    gid_t   fc_gid;
+    mode_t  fc_mode;
+    dev_t   fc_rdev;
+} FsCred;
+
+struct xattr_operations;
+
+typedef struct FsContext
+{
+    char *fs_root;
+    SecModel fs_sm;
+    uid_t uid;
+    struct xattr_operations **xops;
+} FsContext;
+
+void cred_init(FsCred *);
+
+typedef struct FileOperations
+{
+    int (*lstat)(FsContext *, const char *, struct stat *);
+    ssize_t (*readlink)(FsContext *, const char *, char *, size_t);
+    int (*chmod)(FsContext *, const char *, FsCred *);
+    int (*chown)(FsContext *, const char *, FsCred *);
+    int (*mknod)(FsContext *, const char *, FsCred *);
+    int (*utimensat)(FsContext *, const char *, const struct timespec *);
+    int (*remove)(FsContext *, const char *);
+    int (*symlink)(FsContext *, const char *, const char *, FsCred *);
+    int (*link)(FsContext *, const char *, const char *);
+    int (*setuid)(FsContext *, uid_t);
+    int (*close)(FsContext *, int);
+    int (*closedir)(FsContext *, DIR *);
+    DIR *(*opendir)(FsContext *, const char *);
+    int (*open)(FsContext *, const char *, int);
+    int (*open2)(FsContext *, const char *, int, FsCred *);
+    void (*rewinddir)(FsContext *, DIR *);
+    off_t (*telldir)(FsContext *, DIR *);
+    struct dirent *(*readdir)(FsContext *, DIR *);
+    void (*seekdir)(FsContext *, DIR *, off_t);
+    ssize_t (*preadv)(FsContext *, int, const struct iovec *, int, off_t);
+    ssize_t (*pwritev)(FsContext *, int, const struct iovec *, int, off_t);
+    int (*mkdir)(FsContext *, const char *, FsCred *);
+    int (*fstat)(FsContext *, int, struct stat *);
+    int (*rename)(FsContext *, const char *, const char *);
+    int (*truncate)(FsContext *, const char *, off_t);
+    int (*fsync)(FsContext *, int, int);
+    int (*statfs)(FsContext *s, const char *path, struct statfs *stbuf);
+    ssize_t (*lgetxattr)(FsContext *, const char *,
+                         const char *, void *, size_t);
+    ssize_t (*llistxattr)(FsContext *, const char *, void *, size_t);
+    int (*lsetxattr)(FsContext *, const char *,
+                     const char *, void *, size_t, int);
+    int (*lremovexattr)(FsContext *, const char *, const char *);
+    void *opaque;
+} FileOperations;
+
+static inline const char *rpath(FsContext *ctx, const char *path)
+{
+    /* FIXME: so wrong... */
+    static char buffer[4096];
+    snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
+    return buffer;
+}
+#endif
diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h
index a704043..f9f08d3 100644
--- a/fsdev/qemu-fsdev.h
+++ b/fsdev/qemu-fsdev.h
@@ -13,7 +13,7 @@
 #ifndef QEMU_FSDEV_H
 #define QEMU_FSDEV_H
 #include "qemu-option.h"
-#include "hw/file-op-9p.h"
+#include "file-op-9p.h"
 
 
 /*
diff --git a/hw/9pfs/virtio-9p-debug.c b/hw/9pfs/virtio-9p-debug.c
new file mode 100644
index 0000000..6b18842
--- /dev/null
+++ b/hw/9pfs/virtio-9p-debug.c
@@ -0,0 +1,645 @@
+/*
+ * Virtio 9p PDU debug
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori at us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "virtio.h"
+#include "pc.h"
+#include "virtio-9p.h"
+#include "virtio-9p-debug.h"
+
+#define BUG_ON(cond) assert(!(cond))
+
+static FILE *llogfile;
+
+static struct iovec *get_sg(V9fsPDU *pdu, int rx)
+{
+    if (rx) {
+        return pdu->elem.in_sg;
+    }
+    return pdu->elem.out_sg;
+}
+
+static int get_sg_count(V9fsPDU *pdu, int rx)
+{
+    if (rx) {
+        return pdu->elem.in_num;
+    }
+    return pdu->elem.out_num;
+
+}
+
+static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp,
+                        const char *name)
+{
+    size_t copied;
+    int count = get_sg_count(pdu, rx);
+    size_t offset = *offsetp;
+    struct iovec *sg = get_sg(pdu, rx);
+    int8_t value;
+
+    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+    BUG_ON(copied != sizeof(value));
+    offset += sizeof(value);
+    fprintf(llogfile, "%s=0x%x", name, value);
+    *offsetp = offset;
+}
+
+static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp,
+                        const char *name)
+{
+    size_t copied;
+    int count = get_sg_count(pdu, rx);
+    struct iovec *sg = get_sg(pdu, rx);
+    size_t offset = *offsetp;
+    int16_t value;
+
+
+    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+    BUG_ON(copied != sizeof(value));
+    offset += sizeof(value);
+    fprintf(llogfile, "%s=0x%x", name, value);
+    *offsetp = offset;
+}
+
+static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp,
+                        const char *name)
+{
+    size_t copied;
+    int count = get_sg_count(pdu, rx);
+    struct iovec *sg = get_sg(pdu, rx);
+    size_t offset = *offsetp;
+    int32_t value;
+
+
+    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+    BUG_ON(copied != sizeof(value));
+    offset += sizeof(value);
+    fprintf(llogfile, "%s=0x%x", name, value);
+    *offsetp = offset;
+}
+
+static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp,
+                        const char *name)
+{
+    size_t copied;
+    int count = get_sg_count(pdu, rx);
+    struct iovec *sg = get_sg(pdu, rx);
+    size_t offset = *offsetp;
+    int64_t value;
+
+
+    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+    BUG_ON(copied != sizeof(value));
+    offset += sizeof(value);
+    fprintf(llogfile, "%s=0x%" PRIx64, name, value);
+    *offsetp = offset;
+}
+
+static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+    int sg_count = get_sg_count(pdu, rx);
+    struct iovec *sg = get_sg(pdu, rx);
+    size_t offset = *offsetp;
+    uint16_t tmp_size, size;
+    size_t result;
+    size_t copied = 0;
+    int i = 0;
+
+    /* get the size */
+    copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size));
+    BUG_ON(copied != sizeof(tmp_size));
+    size = le16_to_cpupu(&tmp_size);
+    offset += copied;
+
+    fprintf(llogfile, "%s=", name);
+    for (i = 0; size && i < sg_count; i++) {
+        size_t len;
+        if (offset >= sg[i].iov_len) {
+            /* skip this sg */
+            offset -= sg[i].iov_len;
+            continue;
+        } else {
+            len = MIN(sg[i].iov_len - offset, size);
+            result = fwrite(sg[i].iov_base + offset, 1, len, llogfile);
+            BUG_ON(result != len);
+            size -= len;
+            copied += len;
+            if (size) {
+                offset = 0;
+                continue;
+            }
+        }
+    }
+    *offsetp += copied;
+}
+
+static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+    fprintf(llogfile, "%s={", name);
+    pprint_int8(pdu, rx, offsetp, "type");
+    pprint_int32(pdu, rx, offsetp, ", version");
+    pprint_int64(pdu, rx, offsetp, ", path");
+    fprintf(llogfile, "}");
+}
+
+static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+    fprintf(llogfile, "%s={", name);
+    pprint_int16(pdu, rx, offsetp, "size");
+    pprint_int16(pdu, rx, offsetp, ", type");
+    pprint_int32(pdu, rx, offsetp, ", dev");
+    pprint_qid(pdu, rx, offsetp, ", qid");
+    pprint_int32(pdu, rx, offsetp, ", mode");
+    pprint_int32(pdu, rx, offsetp, ", atime");
+    pprint_int32(pdu, rx, offsetp, ", mtime");
+    pprint_int64(pdu, rx, offsetp, ", length");
+    pprint_str(pdu, rx, offsetp, ", name");
+    pprint_str(pdu, rx, offsetp, ", uid");
+    pprint_str(pdu, rx, offsetp, ", gid");
+    pprint_str(pdu, rx, offsetp, ", muid");
+    pprint_str(pdu, rx, offsetp, ", extension");
+    pprint_int32(pdu, rx, offsetp, ", uid");
+    pprint_int32(pdu, rx, offsetp, ", gid");
+    pprint_int32(pdu, rx, offsetp, ", muid");
+    fprintf(llogfile, "}");
+}
+
+static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp,
+                                                  const char *name)
+{
+    fprintf(llogfile, "%s={", name);
+    pprint_qid(pdu, rx, offsetp, "qid");
+    pprint_int32(pdu, rx, offsetp, ", st_mode");
+    pprint_int64(pdu, rx, offsetp, ", st_nlink");
+    pprint_int32(pdu, rx, offsetp, ", st_uid");
+    pprint_int32(pdu, rx, offsetp, ", st_gid");
+    pprint_int64(pdu, rx, offsetp, ", st_rdev");
+    pprint_int64(pdu, rx, offsetp, ", st_size");
+    pprint_int64(pdu, rx, offsetp, ", st_blksize");
+    pprint_int64(pdu, rx, offsetp, ", st_blocks");
+    pprint_int64(pdu, rx, offsetp, ", atime");
+    pprint_int64(pdu, rx, offsetp, ", atime_nsec");
+    pprint_int64(pdu, rx, offsetp, ", mtime");
+    pprint_int64(pdu, rx, offsetp, ", mtime_nsec");
+    pprint_int64(pdu, rx, offsetp, ", ctime");
+    pprint_int64(pdu, rx, offsetp, ", ctime_nsec");
+    fprintf(llogfile, "}");
+}
+
+
+
+static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+    int sg_count = get_sg_count(pdu, rx);
+    struct iovec *sg = get_sg(pdu, rx);
+    size_t offset = *offsetp;
+    uint16_t tmp_count, count, i;
+    size_t copied = 0;
+
+    fprintf(llogfile, "%s={", name);
+
+    /* Get the count */
+    copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
+    BUG_ON(copied != sizeof(tmp_count));
+    count = le16_to_cpupu(&tmp_count);
+    offset += copied;
+
+    for (i = 0; i < count; i++) {
+        char str[512];
+        if (i) {
+            fprintf(llogfile, ", ");
+        }
+        snprintf(str, sizeof(str), "[%d]", i);
+        pprint_str(pdu, rx, &offset, str);
+    }
+
+    fprintf(llogfile, "}");
+
+    *offsetp = offset;
+}
+
+static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+    int sg_count = get_sg_count(pdu, rx);
+    struct iovec *sg = get_sg(pdu, rx);
+    size_t offset = *offsetp;
+    uint16_t tmp_count, count, i;
+    size_t copied = 0;
+
+    fprintf(llogfile, "%s={", name);
+
+    copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
+    BUG_ON(copied != sizeof(tmp_count));
+    count = le16_to_cpupu(&tmp_count);
+    offset += copied;
+
+    for (i = 0; i < count; i++) {
+        char str[512];
+        if (i) {
+            fprintf(llogfile, ", ");
+        }
+        snprintf(str, sizeof(str), "[%d]", i);
+        pprint_qid(pdu, rx, &offset, str);
+    }
+
+    fprintf(llogfile, "}");
+
+    *offsetp = offset;
+}
+
+static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+    struct iovec *sg = get_sg(pdu, rx);
+    unsigned int count;
+    int i;
+
+    if (rx) {
+        count = pdu->elem.in_num;
+    } else {
+        count = pdu->elem.out_num;
+    }
+
+    fprintf(llogfile, "%s={", name);
+    for (i = 0; i < count; i++) {
+        if (i) {
+            fprintf(llogfile, ", ");
+        }
+        fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len);
+    }
+    fprintf(llogfile, "}");
+}
+
+/* FIXME: read from a directory fid returns serialized stat_t's */
+#ifdef DEBUG_DATA
+static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+    struct iovec *sg = get_sg(pdu, rx);
+    size_t offset = *offsetp;
+    unsigned int count;
+    int32_t size;
+    int total, i, j;
+    ssize_t len;
+
+    if (rx) {
+        count = pdu->elem.in_num;
+    } else
+        count = pdu->elem.out_num;
+    }
+
+    BUG_ON((offset + sizeof(size)) > sg[0].iov_len);
+
+    memcpy(&size, sg[0].iov_base + offset, sizeof(size));
+    offset += sizeof(size);
+
+    fprintf(llogfile, "size: %x\n", size);
+
+    sg[0].iov_base += 11; /* skip header */
+    sg[0].iov_len -= 11;
+
+    total = 0;
+    for (i = 0; i < count; i++) {
+        total += sg[i].iov_len;
+        if (total >= size) {
+            /* trim sg list so writev does the right thing */
+            sg[i].iov_len -= (total - size);
+            i++;
+            break;
+        }
+    }
+
+    fprintf(llogfile, "%s={\"", name);
+    fflush(llogfile);
+    for (j = 0; j < i; j++) {
+        if (j) {
+            fprintf(llogfile, "\", \"");
+            fflush(llogfile);
+        }
+
+        do {
+            len = writev(fileno(llogfile), &sg[j], 1);
+        } while (len == -1 && errno == EINTR);
+        fprintf(llogfile, "len == %ld: %m\n", len);
+        BUG_ON(len != sg[j].iov_len);
+    }
+    fprintf(llogfile, "\"}");
+
+    sg[0].iov_base -= 11;
+    sg[0].iov_len += 11;
+
+}
+#endif
+
+void pprint_pdu(V9fsPDU *pdu)
+{
+    size_t offset = 7;
+
+    if (llogfile == NULL) {
+        llogfile = fopen("/tmp/pdu.log", "w");
+    }
+
+    BUG_ON(!llogfile);
+
+    switch (pdu->id) {
+    case P9_TREADDIR:
+        fprintf(llogfile, "TREADDIR: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int64(pdu, 0, &offset, ", initial offset");
+        pprint_int32(pdu, 0, &offset, ", max count");
+        break;
+    case P9_RREADDIR:
+        fprintf(llogfile, "RREADDIR: (");
+        pprint_int32(pdu, 1, &offset, "count");
+#ifdef DEBUG_DATA
+        pprint_data(pdu, 1, &offset, ", data");
+#endif
+        break;
+    case P9_TMKDIR:
+        fprintf(llogfile, "TMKDIR: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_str(pdu, 0, &offset, "name");
+        pprint_int32(pdu, 0, &offset, "mode");
+        pprint_int32(pdu, 0, &offset, "gid");
+        break;
+    case P9_RMKDIR:
+        fprintf(llogfile, "RMKDIR: (");
+        pprint_qid(pdu, 0, &offset, "qid");
+        break;
+    case P9_TVERSION:
+        fprintf(llogfile, "TVERSION: (");
+        pprint_int32(pdu, 0, &offset, "msize");
+        pprint_str(pdu, 0, &offset, ", version");
+        break;
+    case P9_RVERSION:
+        fprintf(llogfile, "RVERSION: (");
+        pprint_int32(pdu, 1, &offset, "msize");
+        pprint_str(pdu, 1, &offset, ", version");
+        break;
+    case P9_TGETATTR:
+        fprintf(llogfile, "TGETATTR: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        break;
+    case P9_RGETATTR:
+        fprintf(llogfile, "RGETATTR: (");
+        pprint_stat_dotl(pdu, 1, &offset, "getattr");
+        break;
+    case P9_TAUTH:
+        fprintf(llogfile, "TAUTH: (");
+        pprint_int32(pdu, 0, &offset, "afid");
+        pprint_str(pdu, 0, &offset, ", uname");
+        pprint_str(pdu, 0, &offset, ", aname");
+        pprint_int32(pdu, 0, &offset, ", n_uname");
+        break;
+    case P9_RAUTH:
+        fprintf(llogfile, "RAUTH: (");
+        pprint_qid(pdu, 1, &offset, "qid");
+        break;
+    case P9_TATTACH:
+        fprintf(llogfile, "TATTACH: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int32(pdu, 0, &offset, ", afid");
+        pprint_str(pdu, 0, &offset, ", uname");
+        pprint_str(pdu, 0, &offset, ", aname");
+        pprint_int32(pdu, 0, &offset, ", n_uname");
+        break;
+    case P9_RATTACH:
+        fprintf(llogfile, "RATTACH: (");
+        pprint_qid(pdu, 1, &offset, "qid");
+        break;
+    case P9_TERROR:
+        fprintf(llogfile, "TERROR: (");
+        break;
+    case P9_RERROR:
+        fprintf(llogfile, "RERROR: (");
+        pprint_str(pdu, 1, &offset, "ename");
+        pprint_int32(pdu, 1, &offset, ", ecode");
+        break;
+    case P9_TFLUSH:
+        fprintf(llogfile, "TFLUSH: (");
+        pprint_int16(pdu, 0, &offset, "oldtag");
+        break;
+    case P9_RFLUSH:
+        fprintf(llogfile, "RFLUSH: (");
+        break;
+    case P9_TWALK:
+        fprintf(llogfile, "TWALK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int32(pdu, 0, &offset, ", newfid");
+        pprint_strs(pdu, 0, &offset, ", wnames");
+        break;
+    case P9_RWALK:
+        fprintf(llogfile, "RWALK: (");
+        pprint_qids(pdu, 1, &offset, "wqids");
+        break;
+    case P9_TOPEN:
+        fprintf(llogfile, "TOPEN: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int8(pdu, 0, &offset, ", mode");
+        break;
+    case P9_ROPEN:
+        fprintf(llogfile, "ROPEN: (");
+        pprint_qid(pdu, 1, &offset, "qid");
+        pprint_int32(pdu, 1, &offset, ", iounit");
+        break;
+    case P9_TCREATE:
+        fprintf(llogfile, "TCREATE: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_str(pdu, 0, &offset, ", name");
+        pprint_int32(pdu, 0, &offset, ", perm");
+        pprint_int8(pdu, 0, &offset, ", mode");
+        pprint_str(pdu, 0, &offset, ", extension");
+        break;
+    case P9_RCREATE:
+        fprintf(llogfile, "RCREATE: (");
+        pprint_qid(pdu, 1, &offset, "qid");
+        pprint_int32(pdu, 1, &offset, ", iounit");
+        break;
+    case P9_TSYMLINK:
+        fprintf(llogfile, "TSYMLINK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_str(pdu, 0, &offset, ", name");
+        pprint_str(pdu, 0, &offset, ", symname");
+        pprint_int32(pdu, 0, &offset, ", gid");
+        break;
+    case P9_RSYMLINK:
+        fprintf(llogfile, "RSYMLINK: (");
+        pprint_qid(pdu, 1, &offset, "qid");
+        break;
+    case P9_TLCREATE:
+        fprintf(llogfile, "TLCREATE: (");
+        pprint_int32(pdu, 0, &offset, "dfid");
+        pprint_str(pdu, 0, &offset, ", name");
+        pprint_int32(pdu, 0, &offset, ", flags");
+        pprint_int32(pdu, 0, &offset, ", mode");
+        pprint_int32(pdu, 0, &offset, ", gid");
+        break;
+    case P9_RLCREATE:
+        fprintf(llogfile, "RLCREATE: (");
+        pprint_qid(pdu, 1, &offset, "qid");
+        pprint_int32(pdu, 1, &offset, ", iounit");
+        break;
+    case P9_TMKNOD:
+	fprintf(llogfile, "TMKNOD: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_str(pdu, 0, &offset, "name");
+        pprint_int32(pdu, 0, &offset, "mode");
+        pprint_int32(pdu, 0, &offset, "major");
+        pprint_int32(pdu, 0, &offset, "minor");
+        pprint_int32(pdu, 0, &offset, "gid");
+        break;
+    case P9_RMKNOD:
+        fprintf(llogfile, "RMKNOD: )");
+        pprint_qid(pdu, 0, &offset, "qid");
+        break;
+    case P9_TREADLINK:
+	fprintf(llogfile, "TREADLINK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        break;
+    case P9_RREADLINK:
+	fprintf(llogfile, "RREADLINK: (");
+        pprint_str(pdu, 0, &offset, "target");
+        break;
+    case P9_TREAD:
+        fprintf(llogfile, "TREAD: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int64(pdu, 0, &offset, ", offset");
+        pprint_int32(pdu, 0, &offset, ", count");
+        pprint_sg(pdu, 0, &offset, ", sg");
+        break;
+    case P9_RREAD:
+        fprintf(llogfile, "RREAD: (");
+        pprint_int32(pdu, 1, &offset, "count");
+        pprint_sg(pdu, 1, &offset, ", sg");
+        offset = 7;
+#ifdef DEBUG_DATA
+        pprint_data(pdu, 1, &offset, ", data");
+#endif
+        break;
+    case P9_TWRITE:
+        fprintf(llogfile, "TWRITE: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int64(pdu, 0, &offset, ", offset");
+        pprint_int32(pdu, 0, &offset, ", count");
+        break;
+    case P9_RWRITE:
+        fprintf(llogfile, "RWRITE: (");
+        pprint_int32(pdu, 1, &offset, "count");
+        break;
+    case P9_TCLUNK:
+        fprintf(llogfile, "TCLUNK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        break;
+    case P9_RCLUNK:
+        fprintf(llogfile, "RCLUNK: (");
+        break;
+    case P9_TFSYNC:
+        fprintf(llogfile, "TFSYNC: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        break;
+    case P9_RFSYNC:
+        fprintf(llogfile, "RFSYNC: (");
+        break;
+    case P9_TLINK:
+        fprintf(llogfile, "TLINK: (");
+        pprint_int32(pdu, 0, &offset, "dfid");
+        pprint_int32(pdu, 0, &offset, ", fid");
+        pprint_str(pdu, 0, &offset, ", newpath");
+        break;
+    case P9_RLINK:
+        fprintf(llogfile, "RLINK: (");
+        break;
+    case P9_TREMOVE:
+        fprintf(llogfile, "TREMOVE: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        break;
+    case P9_RREMOVE:
+        fprintf(llogfile, "RREMOVE: (");
+        break;
+    case P9_TSTAT:
+        fprintf(llogfile, "TSTAT: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        break;
+    case P9_RSTAT:
+        fprintf(llogfile, "RSTAT: (");
+        offset += 2; /* ignored */
+        pprint_stat(pdu, 1, &offset, "stat");
+        break;
+    case P9_TWSTAT:
+        fprintf(llogfile, "TWSTAT: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        offset += 2; /* ignored */
+        pprint_stat(pdu, 0, &offset, ", stat");
+        break;
+    case P9_RWSTAT:
+        fprintf(llogfile, "RWSTAT: (");
+        break;
+    case P9_TXATTRWALK:
+        fprintf(llogfile, "TXATTRWALK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int32(pdu, 0, &offset, ", newfid");
+        pprint_str(pdu, 0, &offset, ", xattr name");
+        break;
+    case P9_RXATTRWALK:
+        fprintf(llogfile, "RXATTRWALK: (");
+        pprint_int64(pdu, 1, &offset, "xattrsize");
+    case P9_TXATTRCREATE:
+        fprintf(llogfile, "TXATTRCREATE: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_str(pdu, 0, &offset, ", name");
+        pprint_int64(pdu, 0, &offset, ", xattrsize");
+        pprint_int32(pdu, 0, &offset, ", flags");
+        break;
+    case P9_RXATTRCREATE:
+        fprintf(llogfile, "RXATTRCREATE: (");
+        break;
+    case P9_TLOCK:
+        fprintf(llogfile, "TLOCK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int8(pdu, 0, &offset, ", type");
+        pprint_int32(pdu, 0, &offset, ", flags");
+        pprint_int64(pdu, 0, &offset, ", start");
+        pprint_int64(pdu, 0, &offset, ", length");
+        pprint_int32(pdu, 0, &offset, ", proc_id");
+        pprint_str(pdu, 0, &offset, ", client_id");
+        break;
+    case P9_RLOCK:
+        fprintf(llogfile, "RLOCK: (");
+        pprint_int8(pdu, 0, &offset, "status");
+        break;
+    case P9_TGETLOCK:
+        fprintf(llogfile, "TGETLOCK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int8(pdu, 0, &offset, ", type");
+        pprint_int64(pdu, 0, &offset, ", start");
+        pprint_int64(pdu, 0, &offset, ", length");
+        pprint_int32(pdu, 0, &offset, ", proc_id");
+        pprint_str(pdu, 0, &offset, ", client_id");
+        break;
+    case P9_RGETLOCK:
+        fprintf(llogfile, "RGETLOCK: (");
+        pprint_int8(pdu, 0, &offset, "type");
+        pprint_int64(pdu, 0, &offset, ", start");
+        pprint_int64(pdu, 0, &offset, ", length");
+        pprint_int32(pdu, 0, &offset, ", proc_id");
+        pprint_str(pdu, 0, &offset, ", client_id");
+        break;
+    default:
+        fprintf(llogfile, "unknown(%d): (", pdu->id);
+        break;
+    }
+
+    fprintf(llogfile, ")\n");
+    /* Flush the log message out */
+    fflush(llogfile);
+}
diff --git a/hw/9pfs/virtio-9p-debug.h b/hw/9pfs/virtio-9p-debug.h
new file mode 100644
index 0000000..d9a2491
--- /dev/null
+++ b/hw/9pfs/virtio-9p-debug.h
@@ -0,0 +1,6 @@
+#ifndef _QEMU_VIRTIO_9P_DEBUG_H
+#define _QEMU_VIRTIO_9P_DEBUG_H
+
+void pprint_pdu(V9fsPDU *pdu);
+
+#endif
diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c
new file mode 100644
index 0000000..a8e7525
--- /dev/null
+++ b/hw/9pfs/virtio-9p-local.c
@@ -0,0 +1,563 @@
+/*
+ * Virtio 9p Posix callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori at us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "virtio-9p-xattr.h"
+#include <arpa/inet.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <attr/xattr.h>
+
+
+static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
+{
+    int err;
+    err =  lstat(rpath(fs_ctx, path), stbuf);
+    if (err) {
+        return err;
+    }
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        /* Actual credentials are part of extended attrs */
+        uid_t tmp_uid;
+        gid_t tmp_gid;
+        mode_t tmp_mode;
+        dev_t tmp_dev;
+        if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid,
+                    sizeof(uid_t)) > 0) {
+            stbuf->st_uid = tmp_uid;
+        }
+        if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid,
+                    sizeof(gid_t)) > 0) {
+            stbuf->st_gid = tmp_gid;
+        }
+        if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode,
+                    sizeof(mode_t)) > 0) {
+            stbuf->st_mode = tmp_mode;
+        }
+        if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev,
+                        sizeof(dev_t)) > 0) {
+                stbuf->st_rdev = tmp_dev;
+        }
+    }
+    return err;
+}
+
+static int local_set_xattr(const char *path, FsCred *credp)
+{
+    int err;
+    if (credp->fc_uid != -1) {
+        err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
+                0);
+        if (err) {
+            return err;
+        }
+    }
+    if (credp->fc_gid != -1) {
+        err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
+                0);
+        if (err) {
+            return err;
+        }
+    }
+    if (credp->fc_mode != -1) {
+        err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
+                sizeof(mode_t), 0);
+        if (err) {
+            return err;
+        }
+    }
+    if (credp->fc_rdev != -1) {
+        err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
+                sizeof(dev_t), 0);
+        if (err) {
+            return err;
+        }
+    }
+    return 0;
+}
+
+static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
+        FsCred *credp)
+{
+    if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
+        return -1;
+    }
+    if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
+        /*
+         * If we fail to change ownership and if we are
+         * using security model none. Ignore the error
+         */
+        if (fs_ctx->fs_sm != SM_NONE) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
+        char *buf, size_t bufsz)
+{
+    ssize_t tsize = -1;
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        int fd;
+        fd = open(rpath(fs_ctx, path), O_RDONLY);
+        if (fd == -1) {
+            return -1;
+        }
+        do {
+            tsize = read(fd, (void *)buf, bufsz);
+        } while (tsize == -1 && errno == EINTR);
+        close(fd);
+        return tsize;
+    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+               (fs_ctx->fs_sm == SM_NONE)) {
+        tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
+    }
+    return tsize;
+}
+
+static int local_close(FsContext *ctx, int fd)
+{
+    return close(fd);
+}
+
+static int local_closedir(FsContext *ctx, DIR *dir)
+{
+    return closedir(dir);
+}
+
+static int local_open(FsContext *ctx, const char *path, int flags)
+{
+    return open(rpath(ctx, path), flags);
+}
+
+static DIR *local_opendir(FsContext *ctx, const char *path)
+{
+    return opendir(rpath(ctx, path));
+}
+
+static void local_rewinddir(FsContext *ctx, DIR *dir)
+{
+    return rewinddir(dir);
+}
+
+static off_t local_telldir(FsContext *ctx, DIR *dir)
+{
+    return telldir(dir);
+}
+
+static struct dirent *local_readdir(FsContext *ctx, DIR *dir)
+{
+    return readdir(dir);
+}
+
+static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
+{
+    return seekdir(dir, off);
+}
+
+static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov,
+                            int iovcnt, off_t offset)
+{
+#ifdef CONFIG_PREADV
+    return preadv(fd, iov, iovcnt, offset);
+#else
+    int err = lseek(fd, offset, SEEK_SET);
+    if (err == -1) {
+        return err;
+    } else {
+        return readv(fd, iov, iovcnt);
+    }
+#endif
+}
+
+static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
+                            int iovcnt, off_t offset)
+{
+#ifdef CONFIG_PREADV
+    return pwritev(fd, iov, iovcnt, offset);
+#else
+    int err = lseek(fd, offset, SEEK_SET);
+    if (err == -1) {
+        return err;
+    } else {
+        return writev(fd, iov, iovcnt);
+    }
+#endif
+}
+
+static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        return local_set_xattr(rpath(fs_ctx, path), credp);
+    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+               (fs_ctx->fs_sm == SM_NONE)) {
+        return chmod(rpath(fs_ctx, path), credp->fc_mode);
+    }
+    return -1;
+}
+
+static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+    int err = -1;
+    int serrno = 0;
+
+    /* Determine the security model */
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        err = mknod(rpath(fs_ctx, path), SM_LOCAL_MODE_BITS|S_IFREG, 0);
+        if (err == -1) {
+            return err;
+        }
+        local_set_xattr(rpath(fs_ctx, path), credp);
+        if (err == -1) {
+            serrno = errno;
+            goto err_end;
+        }
+    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+               (fs_ctx->fs_sm == SM_NONE)) {
+        err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev);
+        if (err == -1) {
+            return err;
+        }
+        err = local_post_create_passthrough(fs_ctx, path, credp);
+        if (err == -1) {
+            serrno = errno;
+            goto err_end;
+        }
+    }
+    return err;
+
+err_end:
+    remove(rpath(fs_ctx, path));
+    errno = serrno;
+    return err;
+}
+
+static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+    int err = -1;
+    int serrno = 0;
+
+    /* Determine the security model */
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS);
+        if (err == -1) {
+            return err;
+        }
+        credp->fc_mode = credp->fc_mode|S_IFDIR;
+        err = local_set_xattr(rpath(fs_ctx, path), credp);
+        if (err == -1) {
+            serrno = errno;
+            goto err_end;
+        }
+    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+               (fs_ctx->fs_sm == SM_NONE)) {
+        err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
+        if (err == -1) {
+            return err;
+        }
+        err = local_post_create_passthrough(fs_ctx, path, credp);
+        if (err == -1) {
+            serrno = errno;
+            goto err_end;
+        }
+    }
+    return err;
+
+err_end:
+    remove(rpath(fs_ctx, path));
+    errno = serrno;
+    return err;
+}
+
+static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
+{
+    int err;
+    err = fstat(fd, stbuf);
+    if (err) {
+        return err;
+    }
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        /* Actual credentials are part of extended attrs */
+        uid_t tmp_uid;
+        gid_t tmp_gid;
+        mode_t tmp_mode;
+        dev_t tmp_dev;
+
+        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
+            stbuf->st_uid = tmp_uid;
+        }
+        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
+            stbuf->st_gid = tmp_gid;
+        }
+        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
+            stbuf->st_mode = tmp_mode;
+        }
+        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
+                stbuf->st_rdev = tmp_dev;
+        }
+    }
+    return err;
+}
+
+static int local_open2(FsContext *fs_ctx, const char *path, int flags,
+        FsCred *credp)
+{
+    int fd = -1;
+    int err = -1;
+    int serrno = 0;
+
+    /* Determine the security model */
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS);
+        if (fd == -1) {
+            return fd;
+        }
+        credp->fc_mode = credp->fc_mode|S_IFREG;
+        /* Set cleint credentials in xattr */
+        err = local_set_xattr(rpath(fs_ctx, path), credp);
+        if (err == -1) {
+            serrno = errno;
+            goto err_end;
+        }
+    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+               (fs_ctx->fs_sm == SM_NONE)) {
+        fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
+        if (fd == -1) {
+            return fd;
+        }
+        err = local_post_create_passthrough(fs_ctx, path, credp);
+        if (err == -1) {
+            serrno = errno;
+            goto err_end;
+        }
+    }
+    return fd;
+
+err_end:
+    close(fd);
+    remove(rpath(fs_ctx, path));
+    errno = serrno;
+    return err;
+}
+
+
+static int local_symlink(FsContext *fs_ctx, const char *oldpath,
+        const char *newpath, FsCred *credp)
+{
+    int err = -1;
+    int serrno = 0;
+
+    /* Determine the security model */
+    if (fs_ctx->fs_sm == SM_MAPPED) {
+        int fd;
+        ssize_t oldpath_size, write_size;
+        fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR,
+                SM_LOCAL_MODE_BITS);
+        if (fd == -1) {
+            return fd;
+        }
+        /* Write the oldpath (target) to the file. */
+        oldpath_size = strlen(oldpath) + 1;
+        do {
+            write_size = write(fd, (void *)oldpath, oldpath_size);
+        } while (write_size == -1 && errno == EINTR);
+
+        if (write_size != oldpath_size) {
+            serrno = errno;
+            close(fd);
+            err = -1;
+            goto err_end;
+        }
+        close(fd);
+        /* Set cleint credentials in symlink's xattr */
+        credp->fc_mode = credp->fc_mode|S_IFLNK;
+        err = local_set_xattr(rpath(fs_ctx, newpath), credp);
+        if (err == -1) {
+            serrno = errno;
+            goto err_end;
+        }
+    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+               (fs_ctx->fs_sm == SM_NONE)) {
+        err = symlink(oldpath, rpath(fs_ctx, newpath));
+        if (err) {
+            return err;
+        }
+        err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
+        if (err == -1) {
+            /*
+             * If we fail to change ownership and if we are
+             * using security model none. Ignore the error
+             */
+            if (fs_ctx->fs_sm != SM_NONE) {
+                serrno = errno;
+                goto err_end;
+            } else
+                err = 0;
+        }
+    }
+    return err;
+
+err_end:
+    remove(rpath(fs_ctx, newpath));
+    errno = serrno;
+    return err;
+}
+
+static int local_link(FsContext *ctx, const char *oldpath, const char *newpath)
+{
+    char *tmp = qemu_strdup(rpath(ctx, oldpath));
+    int err, serrno = 0;
+
+    if (tmp == NULL) {
+        return -ENOMEM;
+    }
+
+    err = link(tmp, rpath(ctx, newpath));
+    if (err == -1) {
+        serrno = errno;
+    }
+
+    qemu_free(tmp);
+
+    if (err == -1) {
+        errno = serrno;
+    }
+
+    return err;
+}
+
+static int local_truncate(FsContext *ctx, const char *path, off_t size)
+{
+    return truncate(rpath(ctx, path), size);
+}
+
+static int local_rename(FsContext *ctx, const char *oldpath,
+                        const char *newpath)
+{
+    char *tmp;
+    int err;
+
+    tmp = qemu_strdup(rpath(ctx, oldpath));
+
+    err = rename(tmp, rpath(ctx, newpath));
+    if (err == -1) {
+        int serrno = errno;
+        qemu_free(tmp);
+        errno = serrno;
+    } else {
+        qemu_free(tmp);
+    }
+
+    return err;
+
+}
+
+static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
+            (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
+        return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
+    } else if (fs_ctx->fs_sm == SM_MAPPED) {
+        return local_set_xattr(rpath(fs_ctx, path), credp);
+    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+               (fs_ctx->fs_sm == SM_NONE)) {
+        return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
+    }
+    return -1;
+}
+
+static int local_utimensat(FsContext *s, const char *path,
+                           const struct timespec *buf)
+{
+    return qemu_utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW);
+}
+
+static int local_remove(FsContext *ctx, const char *path)
+{
+    return remove(rpath(ctx, path));
+}
+
+static int local_fsync(FsContext *ctx, int fd, int datasync)
+{
+    if (datasync) {
+        return qemu_fdatasync(fd);
+    } else {
+        return fsync(fd);
+    }
+}
+
+static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
+{
+   return statfs(rpath(s, path), stbuf);
+}
+
+static ssize_t local_lgetxattr(FsContext *ctx, const char *path,
+                               const char *name, void *value, size_t size)
+{
+    return v9fs_get_xattr(ctx, path, name, value, size);
+}
+
+static ssize_t local_llistxattr(FsContext *ctx, const char *path,
+                                void *value, size_t size)
+{
+    return v9fs_list_xattr(ctx, path, value, size);
+}
+
+static int local_lsetxattr(FsContext *ctx, const char *path, const char *name,
+                           void *value, size_t size, int flags)
+{
+    return v9fs_set_xattr(ctx, path, name, value, size, flags);
+}
+
+static int local_lremovexattr(FsContext *ctx,
+                              const char *path, const char *name)
+{
+    return v9fs_remove_xattr(ctx, path, name);
+}
+
+
+FileOperations local_ops = {
+    .lstat = local_lstat,
+    .readlink = local_readlink,
+    .close = local_close,
+    .closedir = local_closedir,
+    .open = local_open,
+    .opendir = local_opendir,
+    .rewinddir = local_rewinddir,
+    .telldir = local_telldir,
+    .readdir = local_readdir,
+    .seekdir = local_seekdir,
+    .preadv = local_preadv,
+    .pwritev = local_pwritev,
+    .chmod = local_chmod,
+    .mknod = local_mknod,
+    .mkdir = local_mkdir,
+    .fstat = local_fstat,
+    .open2 = local_open2,
+    .symlink = local_symlink,
+    .link = local_link,
+    .truncate = local_truncate,
+    .rename = local_rename,
+    .chown = local_chown,
+    .utimensat = local_utimensat,
+    .remove = local_remove,
+    .fsync = local_fsync,
+    .statfs = local_statfs,
+    .lgetxattr = local_lgetxattr,
+    .llistxattr = local_llistxattr,
+    .lsetxattr = local_lsetxattr,
+    .lremovexattr = local_lremovexattr,
+};
diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c
new file mode 100644
index 0000000..e4e0777
--- /dev/null
+++ b/hw/9pfs/virtio-9p-posix-acl.c
@@ -0,0 +1,140 @@
+/*
+ * Virtio 9p system.posix* xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "fsdev/file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
+#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
+#define ACL_ACCESS "system.posix_acl_access"
+#define ACL_DEFAULT "system.posix_acl_default"
+
+static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path,
+                                const char *name, void *value, size_t size)
+{
+    return lgetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size);
+}
+
+static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
+                                 char *name, void *value, size_t osize)
+{
+    ssize_t len = sizeof(ACL_ACCESS);
+
+    if (!value) {
+        return len;
+    }
+
+    if (osize < len) {
+        errno = ERANGE;
+        return -1;
+    }
+
+    strncpy(value, ACL_ACCESS, len);
+    return 0;
+}
+
+static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name,
+                            void *value, size_t size, int flags)
+{
+    return lsetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size, flags);
+}
+
+static int mp_pacl_removexattr(FsContext *ctx,
+                               const char *path, const char *name)
+{
+    int ret;
+    ret  = lremovexattr(rpath(ctx, path), MAP_ACL_ACCESS);
+    if (ret == -1 && errno == ENODATA) {
+        /*
+         * We don't get ENODATA error when trying to remote a
+         * posix acl that is not present. So don't throw the error
+         * even in case of mapped security model
+         */
+        errno = 0;
+        ret = 0;
+    }
+    return ret;
+}
+
+static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path,
+                                const char *name, void *value, size_t size)
+{
+    return lgetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size);
+}
+
+static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
+                                 char *name, void *value, size_t osize)
+{
+    ssize_t len = sizeof(ACL_DEFAULT);
+
+    if (!value) {
+        return len;
+    }
+
+    if (osize < len) {
+        errno = ERANGE;
+        return -1;
+    }
+
+    strncpy(value, ACL_DEFAULT, len);
+    return 0;
+}
+
+static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
+                            void *value, size_t size, int flags)
+{
+    return lsetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size, flags);
+}
+
+static int mp_dacl_removexattr(FsContext *ctx,
+                               const char *path, const char *name)
+{
+    return lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT);
+}
+
+
+XattrOperations mapped_pacl_xattr = {
+    .name = "system.posix_acl_access",
+    .getxattr = mp_pacl_getxattr,
+    .setxattr = mp_pacl_setxattr,
+    .listxattr = mp_pacl_listxattr,
+    .removexattr = mp_pacl_removexattr,
+};
+
+XattrOperations mapped_dacl_xattr = {
+    .name = "system.posix_acl_default",
+    .getxattr = mp_dacl_getxattr,
+    .setxattr = mp_dacl_setxattr,
+    .listxattr = mp_dacl_listxattr,
+    .removexattr = mp_dacl_removexattr,
+};
+
+XattrOperations passthrough_acl_xattr = {
+    .name = "system.posix_acl_",
+    .getxattr = pt_getxattr,
+    .setxattr = pt_setxattr,
+    .listxattr = pt_listxattr,
+    .removexattr = pt_removexattr,
+};
+
+XattrOperations none_acl_xattr = {
+    .name = "system.posix_acl_",
+    .getxattr = notsup_getxattr,
+    .setxattr = notsup_setxattr,
+    .listxattr = notsup_listxattr,
+    .removexattr = notsup_removexattr,
+};
diff --git a/hw/9pfs/virtio-9p-xattr-user.c b/hw/9pfs/virtio-9p-xattr-user.c
new file mode 100644
index 0000000..bba13ce
--- /dev/null
+++ b/hw/9pfs/virtio-9p-xattr-user.c
@@ -0,0 +1,109 @@
+/*
+ * Virtio 9p user. xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "fsdev/file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+
+static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
+                                const char *name, void *value, size_t size)
+{
+    if (strncmp(name, "user.virtfs.", 12) == 0) {
+        /*
+         * Don't allow fetch of user.virtfs namesapce
+         * in case of mapped security
+         */
+        errno = ENOATTR;
+        return -1;
+    }
+    return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
+                                 char *name, void *value, size_t size)
+{
+    int name_size = strlen(name) + 1;
+    if (strncmp(name, "user.virtfs.", 12) == 0) {
+
+        /*  check if it is a mapped posix acl */
+        if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) {
+            /* adjust the name and size */
+            name += 12;
+            name_size -= 12;
+        } else {
+            /*
+             * Don't allow fetch of user.virtfs namesapce
+             * in case of mapped security
+             */
+            return 0;
+        }
+    }
+    if (!value) {
+        return name_size;
+    }
+
+    if (size < name_size) {
+        errno = ERANGE;
+        return -1;
+    }
+
+    strncpy(value, name, name_size);
+    return name_size;
+}
+
+static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
+                            void *value, size_t size, int flags)
+{
+    if (strncmp(name, "user.virtfs.", 12) == 0) {
+        /*
+         * Don't allow fetch of user.virtfs namesapce
+         * in case of mapped security
+         */
+        errno = EACCES;
+        return -1;
+    }
+    return lsetxattr(rpath(ctx, path), name, value, size, flags);
+}
+
+static int mp_user_removexattr(FsContext *ctx,
+                               const char *path, const char *name)
+{
+    if (strncmp(name, "user.virtfs.", 12) == 0) {
+        /*
+         * Don't allow fetch of user.virtfs namesapce
+         * in case of mapped security
+         */
+        errno = EACCES;
+        return -1;
+    }
+    return lremovexattr(rpath(ctx, path), name);
+}
+
+XattrOperations mapped_user_xattr = {
+    .name = "user.",
+    .getxattr = mp_user_getxattr,
+    .setxattr = mp_user_setxattr,
+    .listxattr = mp_user_listxattr,
+    .removexattr = mp_user_removexattr,
+};
+
+XattrOperations passthrough_user_xattr = {
+    .name = "user.",
+    .getxattr = pt_getxattr,
+    .setxattr = pt_setxattr,
+    .listxattr = pt_listxattr,
+    .removexattr = pt_removexattr,
+};
diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c
new file mode 100644
index 0000000..03c3d3f
--- /dev/null
+++ b/hw/9pfs/virtio-9p-xattr.c
@@ -0,0 +1,159 @@
+/*
+ * Virtio 9p  xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "fsdev/file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+
+static XattrOperations *get_xattr_operations(XattrOperations **h,
+                                             const char *name)
+{
+    XattrOperations *xops;
+    for (xops = *(h)++; xops != NULL; xops = *(h)++) {
+        if (!strncmp(name, xops->name, strlen(xops->name))) {
+            return xops;
+        }
+    }
+    return NULL;
+}
+
+ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
+                       const char *name, void *value, size_t size)
+{
+    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+    if (xops) {
+        return xops->getxattr(ctx, path, name, value, size);
+    }
+    errno = -EOPNOTSUPP;
+    return -1;
+}
+
+ssize_t pt_listxattr(FsContext *ctx, const char *path,
+                     char *name, void *value, size_t size)
+{
+    int name_size = strlen(name) + 1;
+    if (!value) {
+        return name_size;
+    }
+
+    if (size < name_size) {
+        errno = ERANGE;
+        return -1;
+    }
+
+    strncpy(value, name, name_size);
+    return name_size;
+}
+
+
+/*
+ * Get the list and pass to each layer to find out whether
+ * to send the data or not
+ */
+ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
+                        void *value, size_t vsize)
+{
+    ssize_t size = 0;
+    void *ovalue = value;
+    XattrOperations *xops;
+    char *orig_value, *orig_value_start;
+    ssize_t xattr_len, parsed_len = 0, attr_len;
+
+    /* Get the actual len */
+    xattr_len = llistxattr(rpath(ctx, path), value, 0);
+    if (xattr_len <= 0) {
+        return xattr_len;
+    }
+
+    /* Now fetch the xattr and find the actual size */
+    orig_value = qemu_malloc(xattr_len);
+    xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len);
+
+    /* store the orig pointer */
+    orig_value_start = orig_value;
+    while (xattr_len > parsed_len) {
+        xops = get_xattr_operations(ctx->xops, orig_value);
+        if (!xops) {
+            goto next_entry;
+        }
+
+        if (!value) {
+            size += xops->listxattr(ctx, path, orig_value, value, vsize);
+        } else {
+            size = xops->listxattr(ctx, path, orig_value, value, vsize);
+            if (size < 0) {
+                goto err_out;
+            }
+            value += size;
+            vsize -= size;
+        }
+next_entry:
+        /* Got the next entry */
+        attr_len = strlen(orig_value) + 1;
+        parsed_len += attr_len;
+        orig_value += attr_len;
+    }
+    if (value) {
+        size = value - ovalue;
+    }
+
+err_out:
+    qemu_free(orig_value_start);
+    return size;
+}
+
+int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+                   void *value, size_t size, int flags)
+{
+    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+    if (xops) {
+        return xops->setxattr(ctx, path, name, value, size, flags);
+    }
+    errno = -EOPNOTSUPP;
+    return -1;
+
+}
+
+int v9fs_remove_xattr(FsContext *ctx,
+                      const char *path, const char *name)
+{
+    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+    if (xops) {
+        return xops->removexattr(ctx, path, name);
+    }
+    errno = -EOPNOTSUPP;
+    return -1;
+
+}
+
+XattrOperations *mapped_xattr_ops[] = {
+    &mapped_user_xattr,
+    &mapped_pacl_xattr,
+    &mapped_dacl_xattr,
+    NULL,
+};
+
+XattrOperations *passthrough_xattr_ops[] = {
+    &passthrough_user_xattr,
+    &passthrough_acl_xattr,
+    NULL,
+};
+
+/* for .user none model should be same as passthrough */
+XattrOperations *none_xattr_ops[] = {
+    &passthrough_user_xattr,
+    &none_acl_xattr,
+    NULL,
+};
diff --git a/hw/9pfs/virtio-9p-xattr.h b/hw/9pfs/virtio-9p-xattr.h
new file mode 100644
index 0000000..2bbae2d
--- /dev/null
+++ b/hw/9pfs/virtio-9p-xattr.h
@@ -0,0 +1,102 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ *  Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef _QEMU_VIRTIO_9P_XATTR_H
+#define _QEMU_VIRTIO_9P_XATTR_H
+
+#include <attr/xattr.h>
+
+typedef struct xattr_operations
+{
+    const char *name;
+    ssize_t (*getxattr)(FsContext *ctx, const char *path,
+                        const char *name, void *value, size_t size);
+    ssize_t (*listxattr)(FsContext *ctx, const char *path,
+                         char *name, void *value, size_t size);
+    int (*setxattr)(FsContext *ctx, const char *path, const char *name,
+                    void *value, size_t size, int flags);
+    int (*removexattr)(FsContext *ctx,
+                       const char *path, const char *name);
+} XattrOperations;
+
+
+extern XattrOperations mapped_user_xattr;
+extern XattrOperations passthrough_user_xattr;
+
+extern XattrOperations mapped_pacl_xattr;
+extern XattrOperations mapped_dacl_xattr;
+extern XattrOperations passthrough_acl_xattr;
+extern XattrOperations none_acl_xattr;
+
+extern XattrOperations *mapped_xattr_ops[];
+extern XattrOperations *passthrough_xattr_ops[];
+extern XattrOperations *none_xattr_ops[];
+
+ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name,
+                       void *value, size_t size);
+ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value,
+                        size_t vsize);
+int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+                          void *value, size_t size, int flags);
+int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name);
+ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value,
+                     size_t size);
+
+static inline ssize_t pt_getxattr(FsContext *ctx, const char *path,
+                                  const char *name, void *value, size_t size)
+{
+    return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static inline int pt_setxattr(FsContext *ctx, const char *path,
+                              const char *name, void *value,
+                              size_t size, int flags)
+{
+    return lsetxattr(rpath(ctx, path), name, value, size, flags);
+}
+
+static inline int pt_removexattr(FsContext *ctx,
+                                 const char *path, const char *name)
+{
+    return lremovexattr(rpath(ctx, path), name);
+}
+
+static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path,
+                                      const char *name, void *value,
+                                      size_t size)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+static inline int notsup_setxattr(FsContext *ctx, const char *path,
+                                  const char *name, void *value,
+                                  size_t size, int flags)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path,
+                                       char *name, void *value, size_t size)
+{
+    return 0;
+}
+
+static inline int notsup_removexattr(FsContext *ctx,
+                                     const char *path, const char *name)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+#endif
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
new file mode 100644
index 0000000..7e29535
--- /dev/null
+++ b/hw/9pfs/virtio-9p.c
@@ -0,0 +1,3741 @@
+/*
+ * Virtio 9p backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori at us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtio.h"
+#include "pc.h"
+#include "qemu_socket.h"
+#include "virtio-9p.h"
+#include "fsdev/qemu-fsdev.h"
+#include "virtio-9p-debug.h"
+#include "virtio-9p-xattr.h"
+
+int debug_9p_pdu;
+
+enum {
+    Oread   = 0x00,
+    Owrite  = 0x01,
+    Ordwr   = 0x02,
+    Oexec   = 0x03,
+    Oexcl   = 0x04,
+    Otrunc  = 0x10,
+    Orexec  = 0x20,
+    Orclose = 0x40,
+    Oappend = 0x80,
+};
+
+static int omode_to_uflags(int8_t mode)
+{
+    int ret = 0;
+
+    switch (mode & 3) {
+    case Oread:
+        ret = O_RDONLY;
+        break;
+    case Ordwr:
+        ret = O_RDWR;
+        break;
+    case Owrite:
+        ret = O_WRONLY;
+        break;
+    case Oexec:
+        ret = O_RDONLY;
+        break;
+    }
+
+    if (mode & Otrunc) {
+        ret |= O_TRUNC;
+    }
+
+    if (mode & Oappend) {
+        ret |= O_APPEND;
+    }
+
+    if (mode & Oexcl) {
+        ret |= O_EXCL;
+    }
+
+    return ret;
+}
+
+void cred_init(FsCred *credp)
+{
+    credp->fc_uid = -1;
+    credp->fc_gid = -1;
+    credp->fc_mode = -1;
+    credp->fc_rdev = -1;
+}
+
+static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
+{
+    return s->ops->lstat(&s->ctx, path->data, stbuf);
+}
+
+static ssize_t v9fs_do_readlink(V9fsState *s, V9fsString *path, V9fsString *buf)
+{
+    ssize_t len;
+
+    buf->data = qemu_malloc(1024);
+
+    len = s->ops->readlink(&s->ctx, path->data, buf->data, 1024 - 1);
+    if (len > -1) {
+        buf->size = len;
+        buf->data[len] = 0;
+    }
+
+    return len;
+}
+
+static int v9fs_do_close(V9fsState *s, int fd)
+{
+    return s->ops->close(&s->ctx, fd);
+}
+
+static int v9fs_do_closedir(V9fsState *s, DIR *dir)
+{
+    return s->ops->closedir(&s->ctx, dir);
+}
+
+static int v9fs_do_open(V9fsState *s, V9fsString *path, int flags)
+{
+    return s->ops->open(&s->ctx, path->data, flags);
+}
+
+static DIR *v9fs_do_opendir(V9fsState *s, V9fsString *path)
+{
+    return s->ops->opendir(&s->ctx, path->data);
+}
+
+static void v9fs_do_rewinddir(V9fsState *s, DIR *dir)
+{
+    return s->ops->rewinddir(&s->ctx, dir);
+}
+
+static off_t v9fs_do_telldir(V9fsState *s, DIR *dir)
+{
+    return s->ops->telldir(&s->ctx, dir);
+}
+
+static struct dirent *v9fs_do_readdir(V9fsState *s, DIR *dir)
+{
+    return s->ops->readdir(&s->ctx, dir);
+}
+
+static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
+{
+    return s->ops->seekdir(&s->ctx, dir, off);
+}
+
+static int v9fs_do_preadv(V9fsState *s, int fd, const struct iovec *iov,
+                            int iovcnt, int64_t offset)
+{
+    return s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
+}
+
+static int v9fs_do_pwritev(V9fsState *s, int fd, const struct iovec *iov,
+                       int iovcnt, int64_t offset)
+{
+    return s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset);
+}
+
+static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
+{
+    FsCred cred;
+    cred_init(&cred);
+    cred.fc_mode = mode;
+    return s->ops->chmod(&s->ctx, path->data, &cred);
+}
+
+static int v9fs_do_mknod(V9fsState *s, char *name,
+        mode_t mode, dev_t dev, uid_t uid, gid_t gid)
+{
+    FsCred cred;
+    cred_init(&cred);
+    cred.fc_uid = uid;
+    cred.fc_gid = gid;
+    cred.fc_mode = mode;
+    cred.fc_rdev = dev;
+    return s->ops->mknod(&s->ctx, name, &cred);
+}
+
+static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode,
+                uid_t uid, gid_t gid)
+{
+    FsCred cred;
+
+    cred_init(&cred);
+    cred.fc_uid = uid;
+    cred.fc_gid = gid;
+    cred.fc_mode = mode;
+
+    return s->ops->mkdir(&s->ctx, name, &cred);
+}
+
+static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
+{
+    return s->ops->fstat(&s->ctx, fd, stbuf);
+}
+
+static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
+        int flags, int mode)
+{
+    FsCred cred;
+
+    cred_init(&cred);
+    cred.fc_uid = uid;
+    cred.fc_gid = gid;
+    cred.fc_mode = mode & 07777;
+    flags = flags;
+
+    return s->ops->open2(&s->ctx, fullname, flags, &cred);
+}
+
+static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp,
+        const char *oldpath, const char *newpath, gid_t gid)
+{
+    FsCred cred;
+    cred_init(&cred);
+    cred.fc_uid = fidp->uid;
+    cred.fc_gid = gid;
+    cred.fc_mode = 0777;
+
+    return s->ops->symlink(&s->ctx, oldpath, newpath, &cred);
+}
+
+static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath)
+{
+    return s->ops->link(&s->ctx, oldpath->data, newpath->data);
+}
+
+static int v9fs_do_truncate(V9fsState *s, V9fsString *path, off_t size)
+{
+    return s->ops->truncate(&s->ctx, path->data, size);
+}
+
+static int v9fs_do_rename(V9fsState *s, V9fsString *oldpath,
+                            V9fsString *newpath)
+{
+    return s->ops->rename(&s->ctx, oldpath->data, newpath->data);
+}
+
+static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid)
+{
+    FsCred cred;
+    cred_init(&cred);
+    cred.fc_uid = uid;
+    cred.fc_gid = gid;
+
+    return s->ops->chown(&s->ctx, path->data, &cred);
+}
+
+static int v9fs_do_utimensat(V9fsState *s, V9fsString *path,
+                                           const struct timespec times[2])
+{
+    return s->ops->utimensat(&s->ctx, path->data, times);
+}
+
+static int v9fs_do_remove(V9fsState *s, V9fsString *path)
+{
+    return s->ops->remove(&s->ctx, path->data);
+}
+
+static int v9fs_do_fsync(V9fsState *s, int fd, int datasync)
+{
+    return s->ops->fsync(&s->ctx, fd, datasync);
+}
+
+static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
+{
+    return s->ops->statfs(&s->ctx, path->data, stbuf);
+}
+
+static ssize_t v9fs_do_lgetxattr(V9fsState *s, V9fsString *path,
+                             V9fsString *xattr_name,
+                             void *value, size_t size)
+{
+    return s->ops->lgetxattr(&s->ctx, path->data,
+                             xattr_name->data, value, size);
+}
+
+static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path,
+                              void *value, size_t size)
+{
+    return s->ops->llistxattr(&s->ctx, path->data,
+                              value, size);
+}
+
+static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path,
+                             V9fsString *xattr_name,
+                             void *value, size_t size, int flags)
+{
+    return s->ops->lsetxattr(&s->ctx, path->data,
+                             xattr_name->data, value, size, flags);
+}
+
+static int v9fs_do_lremovexattr(V9fsState *s, V9fsString *path,
+                                V9fsString *xattr_name)
+{
+    return s->ops->lremovexattr(&s->ctx, path->data,
+                                xattr_name->data);
+}
+
+
+static void v9fs_string_init(V9fsString *str)
+{
+    str->data = NULL;
+    str->size = 0;
+}
+
+static void v9fs_string_free(V9fsString *str)
+{
+    qemu_free(str->data);
+    str->data = NULL;
+    str->size = 0;
+}
+
+static void v9fs_string_null(V9fsString *str)
+{
+    v9fs_string_free(str);
+}
+
+static int number_to_string(void *arg, char type)
+{
+    unsigned int ret = 0;
+
+    switch (type) {
+    case 'u': {
+        unsigned int num = *(unsigned int *)arg;
+
+        do {
+            ret++;
+            num = num/10;
+        } while (num);
+        break;
+    }
+    case 'U': {
+        unsigned long num = *(unsigned long *)arg;
+        do {
+            ret++;
+            num = num/10;
+        } while (num);
+        break;
+    }
+    default:
+        printf("Number_to_string: Unknown number format\n");
+        return -1;
+    }
+
+    return ret;
+}
+
+static int GCC_FMT_ATTR(2, 0)
+v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
+{
+    va_list ap2;
+    char *iter = (char *)fmt;
+    int len = 0;
+    int nr_args = 0;
+    char *arg_char_ptr;
+    unsigned int arg_uint;
+    unsigned long arg_ulong;
+
+    /* Find the number of %'s that denotes an argument */
+    for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
+        nr_args++;
+        iter++;
+    }
+
+    len = strlen(fmt) - 2*nr_args;
+
+    if (!nr_args) {
+        goto alloc_print;
+    }
+
+    va_copy(ap2, ap);
+
+    iter = (char *)fmt;
+
+    /* Now parse the format string */
+    for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
+        iter++;
+        switch (*iter) {
+        case 'u':
+            arg_uint = va_arg(ap2, unsigned int);
+            len += number_to_string((void *)&arg_uint, 'u');
+            break;
+        case 'l':
+            if (*++iter == 'u') {
+                arg_ulong = va_arg(ap2, unsigned long);
+                len += number_to_string((void *)&arg_ulong, 'U');
+            } else {
+                return -1;
+            }
+            break;
+        case 's':
+            arg_char_ptr = va_arg(ap2, char *);
+            len += strlen(arg_char_ptr);
+            break;
+        case 'c':
+            len += 1;
+            break;
+        default:
+            fprintf(stderr,
+		    "v9fs_string_alloc_printf:Incorrect format %c", *iter);
+            return -1;
+        }
+        iter++;
+    }
+
+alloc_print:
+    *strp = qemu_malloc((len + 1) * sizeof(**strp));
+
+    return vsprintf(*strp, fmt, ap);
+}
+
+static void GCC_FMT_ATTR(2, 3)
+v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
+{
+    va_list ap;
+    int err;
+
+    v9fs_string_free(str);
+
+    va_start(ap, fmt);
+    err = v9fs_string_alloc_printf(&str->data, fmt, ap);
+    BUG_ON(err == -1);
+    va_end(ap);
+
+    str->size = err;
+}
+
+static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
+{
+    v9fs_string_free(lhs);
+    v9fs_string_sprintf(lhs, "%s", rhs->data);
+}
+
+static size_t v9fs_string_size(V9fsString *str)
+{
+    return str->size;
+}
+
+static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
+{
+    V9fsFidState *f;
+
+    for (f = s->fid_list; f; f = f->next) {
+        if (f->fid == fid) {
+            return f;
+        }
+    }
+
+    return NULL;
+}
+
+static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
+{
+    V9fsFidState *f;
+
+    f = lookup_fid(s, fid);
+    if (f) {
+        return NULL;
+    }
+
+    f = qemu_mallocz(sizeof(V9fsFidState));
+
+    f->fid = fid;
+    f->fid_type = P9_FID_NONE;
+
+    f->next = s->fid_list;
+    s->fid_list = f;
+
+    return f;
+}
+
+static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
+{
+    int retval = 0;
+
+    if (fidp->fs.xattr.copied_len == -1) {
+        /* getxattr/listxattr fid */
+        goto free_value;
+    }
+    /*
+     * if this is fid for setxattr. clunk should
+     * result in setxattr localcall
+     */
+    if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
+        /* clunk after partial write */
+        retval = -EINVAL;
+        goto free_out;
+    }
+    if (fidp->fs.xattr.len) {
+        retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name,
+                                   fidp->fs.xattr.value,
+                                   fidp->fs.xattr.len,
+                                   fidp->fs.xattr.flags);
+    } else {
+        retval = v9fs_do_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name);
+    }
+free_out:
+    v9fs_string_free(&fidp->fs.xattr.name);
+free_value:
+    if (fidp->fs.xattr.value) {
+        qemu_free(fidp->fs.xattr.value);
+    }
+    return retval;
+}
+
+static int free_fid(V9fsState *s, int32_t fid)
+{
+    int retval = 0;
+    V9fsFidState **fidpp, *fidp;
+
+    for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
+        if ((*fidpp)->fid == fid) {
+            break;
+        }
+    }
+
+    if (*fidpp == NULL) {
+        return -ENOENT;
+    }
+
+    fidp = *fidpp;
+    *fidpp = fidp->next;
+
+    if (fidp->fid_type == P9_FID_FILE) {
+        v9fs_do_close(s, fidp->fs.fd);
+    } else if (fidp->fid_type == P9_FID_DIR) {
+        v9fs_do_closedir(s, fidp->fs.dir);
+    } else if (fidp->fid_type == P9_FID_XATTR) {
+        retval = v9fs_xattr_fid_clunk(s, fidp);
+    }
+    v9fs_string_free(&fidp->path);
+    qemu_free(fidp);
+
+    return retval;
+}
+
+#define P9_QID_TYPE_DIR         0x80
+#define P9_QID_TYPE_SYMLINK     0x02
+
+#define P9_STAT_MODE_DIR        0x80000000
+#define P9_STAT_MODE_APPEND     0x40000000
+#define P9_STAT_MODE_EXCL       0x20000000
+#define P9_STAT_MODE_MOUNT      0x10000000
+#define P9_STAT_MODE_AUTH       0x08000000
+#define P9_STAT_MODE_TMP        0x04000000
+#define P9_STAT_MODE_SYMLINK    0x02000000
+#define P9_STAT_MODE_LINK       0x01000000
+#define P9_STAT_MODE_DEVICE     0x00800000
+#define P9_STAT_MODE_NAMED_PIPE 0x00200000
+#define P9_STAT_MODE_SOCKET     0x00100000
+#define P9_STAT_MODE_SETUID     0x00080000
+#define P9_STAT_MODE_SETGID     0x00040000
+#define P9_STAT_MODE_SETVTX     0x00010000
+
+#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR |          \
+                                P9_STAT_MODE_SYMLINK |      \
+                                P9_STAT_MODE_LINK |         \
+                                P9_STAT_MODE_DEVICE |       \
+                                P9_STAT_MODE_NAMED_PIPE |   \
+                                P9_STAT_MODE_SOCKET)
+
+/* This is the algorithm from ufs in spfs */
+static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
+{
+    size_t size;
+
+    size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
+    memcpy(&qidp->path, &stbuf->st_ino, size);
+    qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
+    qidp->type = 0;
+    if (S_ISDIR(stbuf->st_mode)) {
+        qidp->type |= P9_QID_TYPE_DIR;
+    }
+    if (S_ISLNK(stbuf->st_mode)) {
+        qidp->type |= P9_QID_TYPE_SYMLINK;
+    }
+}
+
+static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
+{
+    struct stat stbuf;
+    int err;
+
+    err = v9fs_do_lstat(s, &fidp->path, &stbuf);
+    if (err) {
+        return err;
+    }
+
+    stat_to_qid(&stbuf, qidp);
+    return 0;
+}
+
+static V9fsPDU *alloc_pdu(V9fsState *s)
+{
+    V9fsPDU *pdu = NULL;
+
+    if (!QLIST_EMPTY(&s->free_list)) {
+	pdu = QLIST_FIRST(&s->free_list);
+	QLIST_REMOVE(pdu, next);
+    }
+    return pdu;
+}
+
+static void free_pdu(V9fsState *s, V9fsPDU *pdu)
+{
+    if (pdu) {
+	QLIST_INSERT_HEAD(&s->free_list, pdu, next);
+    }
+}
+
+size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
+                        size_t offset, size_t size, int pack)
+{
+    int i = 0;
+    size_t copied = 0;
+
+    for (i = 0; size && i < sg_count; i++) {
+        size_t len;
+        if (offset >= sg[i].iov_len) {
+            /* skip this sg */
+            offset -= sg[i].iov_len;
+            continue;
+        } else {
+            len = MIN(sg[i].iov_len - offset, size);
+            if (pack) {
+                memcpy(sg[i].iov_base + offset, addr, len);
+            } else {
+                memcpy(addr, sg[i].iov_base + offset, len);
+            }
+            size -= len;
+            copied += len;
+            addr += len;
+            if (size) {
+                offset = 0;
+                continue;
+            }
+        }
+    }
+
+    return copied;
+}
+
+static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
+{
+    return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
+                         offset, size, 0);
+}
+
+static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
+                        size_t size)
+{
+    return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
+                             offset, size, 1);
+}
+
+static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
+{
+    size_t pos = 0;
+    int i, j;
+    struct iovec *src_sg;
+    unsigned int num;
+
+    if (rx) {
+        src_sg = pdu->elem.in_sg;
+        num = pdu->elem.in_num;
+    } else {
+        src_sg = pdu->elem.out_sg;
+        num = pdu->elem.out_num;
+    }
+
+    j = 0;
+    for (i = 0; i < num; i++) {
+        if (offset <= pos) {
+            sg[j].iov_base = src_sg[i].iov_base;
+            sg[j].iov_len = src_sg[i].iov_len;
+            j++;
+        } else if (offset < (src_sg[i].iov_len + pos)) {
+            sg[j].iov_base = src_sg[i].iov_base;
+            sg[j].iov_len = src_sg[i].iov_len;
+            sg[j].iov_base += (offset - pos);
+            sg[j].iov_len -= (offset - pos);
+            j++;
+        }
+        pos += src_sg[i].iov_len;
+    }
+
+    return j;
+}
+
+static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
+{
+    size_t old_offset = offset;
+    va_list ap;
+    int i;
+
+    va_start(ap, fmt);
+    for (i = 0; fmt[i]; i++) {
+        switch (fmt[i]) {
+        case 'b': {
+            uint8_t *valp = va_arg(ap, uint8_t *);
+            offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
+            break;
+        }
+        case 'w': {
+            uint16_t val, *valp;
+            valp = va_arg(ap, uint16_t *);
+            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
+            *valp = le16_to_cpu(val);
+            break;
+        }
+        case 'd': {
+            uint32_t val, *valp;
+            valp = va_arg(ap, uint32_t *);
+            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
+            *valp = le32_to_cpu(val);
+            break;
+        }
+        case 'q': {
+            uint64_t val, *valp;
+            valp = va_arg(ap, uint64_t *);
+            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
+            *valp = le64_to_cpu(val);
+            break;
+        }
+        case 'v': {
+            struct iovec *iov = va_arg(ap, struct iovec *);
+            int *iovcnt = va_arg(ap, int *);
+            *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
+            break;
+        }
+        case 's': {
+            V9fsString *str = va_arg(ap, V9fsString *);
+            offset += pdu_unmarshal(pdu, offset, "w", &str->size);
+            /* FIXME: sanity check str->size */
+            str->data = qemu_malloc(str->size + 1);
+            offset += pdu_unpack(str->data, pdu, offset, str->size);
+            str->data[str->size] = 0;
+            break;
+        }
+        case 'Q': {
+            V9fsQID *qidp = va_arg(ap, V9fsQID *);
+            offset += pdu_unmarshal(pdu, offset, "bdq",
+                        &qidp->type, &qidp->version, &qidp->path);
+            break;
+        }
+        case 'S': {
+            V9fsStat *statp = va_arg(ap, V9fsStat *);
+            offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
+                        &statp->size, &statp->type, &statp->dev,
+                        &statp->qid, &statp->mode, &statp->atime,
+                        &statp->mtime, &statp->length,
+                        &statp->name, &statp->uid, &statp->gid,
+                        &statp->muid, &statp->extension,
+                        &statp->n_uid, &statp->n_gid,
+                        &statp->n_muid);
+            break;
+        }
+        case 'I': {
+            V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
+            offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
+                        &iattr->valid, &iattr->mode,
+                        &iattr->uid, &iattr->gid, &iattr->size,
+                        &iattr->atime_sec, &iattr->atime_nsec,
+                        &iattr->mtime_sec, &iattr->mtime_nsec);
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    va_end(ap);
+
+    return offset - old_offset;
+}
+
+static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
+{
+    size_t old_offset = offset;
+    va_list ap;
+    int i;
+
+    va_start(ap, fmt);
+    for (i = 0; fmt[i]; i++) {
+        switch (fmt[i]) {
+        case 'b': {
+            uint8_t val = va_arg(ap, int);
+            offset += pdu_pack(pdu, offset, &val, sizeof(val));
+            break;
+        }
+        case 'w': {
+            uint16_t val;
+            cpu_to_le16w(&val, va_arg(ap, int));
+            offset += pdu_pack(pdu, offset, &val, sizeof(val));
+            break;
+        }
+        case 'd': {
+            uint32_t val;
+            cpu_to_le32w(&val, va_arg(ap, uint32_t));
+            offset += pdu_pack(pdu, offset, &val, sizeof(val));
+            break;
+        }
+        case 'q': {
+            uint64_t val;
+            cpu_to_le64w(&val, va_arg(ap, uint64_t));
+            offset += pdu_pack(pdu, offset, &val, sizeof(val));
+            break;
+        }
+        case 'v': {
+            struct iovec *iov = va_arg(ap, struct iovec *);
+            int *iovcnt = va_arg(ap, int *);
+            *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
+            break;
+        }
+        case 's': {
+            V9fsString *str = va_arg(ap, V9fsString *);
+            offset += pdu_marshal(pdu, offset, "w", str->size);
+            offset += pdu_pack(pdu, offset, str->data, str->size);
+            break;
+        }
+        case 'Q': {
+            V9fsQID *qidp = va_arg(ap, V9fsQID *);
+            offset += pdu_marshal(pdu, offset, "bdq",
+                        qidp->type, qidp->version, qidp->path);
+            break;
+        }
+        case 'S': {
+            V9fsStat *statp = va_arg(ap, V9fsStat *);
+            offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
+                        statp->size, statp->type, statp->dev,
+                        &statp->qid, statp->mode, statp->atime,
+                        statp->mtime, statp->length, &statp->name,
+                        &statp->uid, &statp->gid, &statp->muid,
+                        &statp->extension, statp->n_uid,
+                        statp->n_gid, statp->n_muid);
+            break;
+        }
+        case 'A': {
+            V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
+            offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
+                        statp->st_result_mask,
+                        &statp->qid, statp->st_mode,
+                        statp->st_uid, statp->st_gid,
+                        statp->st_nlink, statp->st_rdev,
+                        statp->st_size, statp->st_blksize, statp->st_blocks,
+                        statp->st_atime_sec, statp->st_atime_nsec,
+                        statp->st_mtime_sec, statp->st_mtime_nsec,
+                        statp->st_ctime_sec, statp->st_ctime_nsec,
+                        statp->st_btime_sec, statp->st_btime_nsec,
+                        statp->st_gen, statp->st_data_version);
+            break;
+        }
+        default:
+            break;
+        }
+    }
+    va_end(ap);
+
+    return offset - old_offset;
+}
+
+static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
+{
+    int8_t id = pdu->id + 1; /* Response */
+
+    if (len < 0) {
+        int err = -len;
+        len = 7;
+
+        if (s->proto_version != V9FS_PROTO_2000L) {
+            V9fsString str;
+
+            str.data = strerror(err);
+            str.size = strlen(str.data);
+
+            len += pdu_marshal(pdu, len, "s", &str);
+            id = P9_RERROR;
+        }
+
+        len += pdu_marshal(pdu, len, "d", err);
+
+        if (s->proto_version == V9FS_PROTO_2000L) {
+            id = P9_RLERROR;
+        }
+    }
+
+    /* fill out the header */
+    pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
+
+    /* keep these in sync */
+    pdu->size = len;
+    pdu->id = id;
+
+    /* push onto queue and notify */
+    virtqueue_push(s->vq, &pdu->elem, len);
+
+    /* FIXME: we should batch these completions */
+    virtio_notify(&s->vdev, s->vq);
+
+    free_pdu(s, pdu);
+}
+
+static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
+{
+    mode_t ret;
+
+    ret = mode & 0777;
+    if (mode & P9_STAT_MODE_DIR) {
+        ret |= S_IFDIR;
+    }
+
+    if (mode & P9_STAT_MODE_SYMLINK) {
+        ret |= S_IFLNK;
+    }
+    if (mode & P9_STAT_MODE_SOCKET) {
+        ret |= S_IFSOCK;
+    }
+    if (mode & P9_STAT_MODE_NAMED_PIPE) {
+        ret |= S_IFIFO;
+    }
+    if (mode & P9_STAT_MODE_DEVICE) {
+        if (extension && extension->data[0] == 'c') {
+            ret |= S_IFCHR;
+        } else {
+            ret |= S_IFBLK;
+        }
+    }
+
+    if (!(ret&~0777)) {
+        ret |= S_IFREG;
+    }
+
+    if (mode & P9_STAT_MODE_SETUID) {
+        ret |= S_ISUID;
+    }
+    if (mode & P9_STAT_MODE_SETGID) {
+        ret |= S_ISGID;
+    }
+    if (mode & P9_STAT_MODE_SETVTX) {
+        ret |= S_ISVTX;
+    }
+
+    return ret;
+}
+
+static int donttouch_stat(V9fsStat *stat)
+{
+    if (stat->type == -1 &&
+        stat->dev == -1 &&
+        stat->qid.type == -1 &&
+        stat->qid.version == -1 &&
+        stat->qid.path == -1 &&
+        stat->mode == -1 &&
+        stat->atime == -1 &&
+        stat->mtime == -1 &&
+        stat->length == -1 &&
+        !stat->name.size &&
+        !stat->uid.size &&
+        !stat->gid.size &&
+        !stat->muid.size &&
+        stat->n_uid == -1 &&
+        stat->n_gid == -1 &&
+        stat->n_muid == -1) {
+        return 1;
+    }
+
+    return 0;
+}
+
+static void v9fs_stat_free(V9fsStat *stat)
+{
+    v9fs_string_free(&stat->name);
+    v9fs_string_free(&stat->uid);
+    v9fs_string_free(&stat->gid);
+    v9fs_string_free(&stat->muid);
+    v9fs_string_free(&stat->extension);
+}
+
+static uint32_t stat_to_v9mode(const struct stat *stbuf)
+{
+    uint32_t mode;
+
+    mode = stbuf->st_mode & 0777;
+    if (S_ISDIR(stbuf->st_mode)) {
+        mode |= P9_STAT_MODE_DIR;
+    }
+
+    if (S_ISLNK(stbuf->st_mode)) {
+        mode |= P9_STAT_MODE_SYMLINK;
+    }
+
+    if (S_ISSOCK(stbuf->st_mode)) {
+        mode |= P9_STAT_MODE_SOCKET;
+    }
+
+    if (S_ISFIFO(stbuf->st_mode)) {
+        mode |= P9_STAT_MODE_NAMED_PIPE;
+    }
+
+    if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
+        mode |= P9_STAT_MODE_DEVICE;
+    }
+
+    if (stbuf->st_mode & S_ISUID) {
+        mode |= P9_STAT_MODE_SETUID;
+    }
+
+    if (stbuf->st_mode & S_ISGID) {
+        mode |= P9_STAT_MODE_SETGID;
+    }
+
+    if (stbuf->st_mode & S_ISVTX) {
+        mode |= P9_STAT_MODE_SETVTX;
+    }
+
+    return mode;
+}
+
+static int stat_to_v9stat(V9fsState *s, V9fsString *name,
+                            const struct stat *stbuf,
+                            V9fsStat *v9stat)
+{
+    int err;
+    const char *str;
+
+    memset(v9stat, 0, sizeof(*v9stat));
+
+    stat_to_qid(stbuf, &v9stat->qid);
+    v9stat->mode = stat_to_v9mode(stbuf);
+    v9stat->atime = stbuf->st_atime;
+    v9stat->mtime = stbuf->st_mtime;
+    v9stat->length = stbuf->st_size;
+
+    v9fs_string_null(&v9stat->uid);
+    v9fs_string_null(&v9stat->gid);
+    v9fs_string_null(&v9stat->muid);
+
+    v9stat->n_uid = stbuf->st_uid;
+    v9stat->n_gid = stbuf->st_gid;
+    v9stat->n_muid = 0;
+
+    v9fs_string_null(&v9stat->extension);
+
+    if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
+        err = v9fs_do_readlink(s, name, &v9stat->extension);
+        if (err == -1) {
+            err = -errno;
+            return err;
+        }
+        v9stat->extension.data[err] = 0;
+        v9stat->extension.size = err;
+    } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
+        v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
+                S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
+                major(stbuf->st_rdev), minor(stbuf->st_rdev));
+    } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
+        v9fs_string_sprintf(&v9stat->extension, "%s %lu",
+                "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
+    }
+
+    str = strrchr(name->data, '/');
+    if (str) {
+        str += 1;
+    } else {
+        str = name->data;
+    }
+
+    v9fs_string_sprintf(&v9stat->name, "%s", str);
+
+    v9stat->size = 61 +
+        v9fs_string_size(&v9stat->name) +
+        v9fs_string_size(&v9stat->uid) +
+        v9fs_string_size(&v9stat->gid) +
+        v9fs_string_size(&v9stat->muid) +
+        v9fs_string_size(&v9stat->extension);
+    return 0;
+}
+
+#define P9_STATS_MODE          0x00000001ULL
+#define P9_STATS_NLINK         0x00000002ULL
+#define P9_STATS_UID           0x00000004ULL
+#define P9_STATS_GID           0x00000008ULL
+#define P9_STATS_RDEV          0x00000010ULL
+#define P9_STATS_ATIME         0x00000020ULL
+#define P9_STATS_MTIME         0x00000040ULL
+#define P9_STATS_CTIME         0x00000080ULL
+#define P9_STATS_INO           0x00000100ULL
+#define P9_STATS_SIZE          0x00000200ULL
+#define P9_STATS_BLOCKS        0x00000400ULL
+
+#define P9_STATS_BTIME         0x00000800ULL
+#define P9_STATS_GEN           0x00001000ULL
+#define P9_STATS_DATA_VERSION  0x00002000ULL
+
+#define P9_STATS_BASIC         0x000007ffULL /* Mask for fields up to BLOCKS */
+#define P9_STATS_ALL           0x00003fffULL /* Mask for All fields above */
+
+
+static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
+                            V9fsStatDotl *v9lstat)
+{
+    memset(v9lstat, 0, sizeof(*v9lstat));
+
+    v9lstat->st_mode = stbuf->st_mode;
+    v9lstat->st_nlink = stbuf->st_nlink;
+    v9lstat->st_uid = stbuf->st_uid;
+    v9lstat->st_gid = stbuf->st_gid;
+    v9lstat->st_rdev = stbuf->st_rdev;
+    v9lstat->st_size = stbuf->st_size;
+    v9lstat->st_blksize = stbuf->st_blksize;
+    v9lstat->st_blocks = stbuf->st_blocks;
+    v9lstat->st_atime_sec = stbuf->st_atime;
+    v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
+    v9lstat->st_mtime_sec = stbuf->st_mtime;
+    v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
+    v9lstat->st_ctime_sec = stbuf->st_ctime;
+    v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
+    /* Currently we only support BASIC fields in stat */
+    v9lstat->st_result_mask = P9_STATS_BASIC;
+
+    stat_to_qid(stbuf, &v9lstat->qid);
+}
+
+static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
+{
+    while (len && *iovcnt) {
+        if (len < sg->iov_len) {
+            sg->iov_len -= len;
+            sg->iov_base += len;
+            len = 0;
+        } else {
+            len -= sg->iov_len;
+            sg++;
+            *iovcnt -= 1;
+        }
+    }
+
+    return sg;
+}
+
+static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt)
+{
+    int i;
+    int total = 0;
+
+    for (i = 0; i < *cnt; i++) {
+        if ((total + sg[i].iov_len) > cap) {
+            sg[i].iov_len -= ((total + sg[i].iov_len) - cap);
+            i++;
+            break;
+        }
+        total += sg[i].iov_len;
+    }
+
+    *cnt = i;
+
+    return sg;
+}
+
+static void print_sg(struct iovec *sg, int cnt)
+{
+    int i;
+
+    printf("sg[%d]: {", cnt);
+    for (i = 0; i < cnt; i++) {
+        if (i) {
+            printf(", ");
+        }
+        printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
+    }
+    printf("}\n");
+}
+
+static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
+{
+    V9fsString str;
+    v9fs_string_init(&str);
+    v9fs_string_copy(&str, dst);
+    v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len);
+    v9fs_string_free(&str);
+}
+
+static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
+{
+    V9fsString version;
+    size_t offset = 7;
+
+    pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
+
+    if (!strcmp(version.data, "9P2000.u")) {
+        s->proto_version = V9FS_PROTO_2000U;
+    } else if (!strcmp(version.data, "9P2000.L")) {
+        s->proto_version = V9FS_PROTO_2000L;
+    } else {
+        v9fs_string_sprintf(&version, "unknown");
+    }
+
+    offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
+    complete_pdu(s, pdu, offset);
+
+    v9fs_string_free(&version);
+}
+
+static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid, afid, n_uname;
+    V9fsString uname, aname;
+    V9fsFidState *fidp;
+    V9fsQID qid;
+    size_t offset = 7;
+    ssize_t err;
+
+    pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
+
+    fidp = alloc_fid(s, fid);
+    if (fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    fidp->uid = n_uname;
+
+    v9fs_string_sprintf(&fidp->path, "%s", "/");
+    err = fid_to_qid(s, fidp, &qid);
+    if (err) {
+        err = -EINVAL;
+        free_fid(s, fid);
+        goto out;
+    }
+
+    offset += pdu_marshal(pdu, offset, "Q", &qid);
+
+    err = offset;
+out:
+    complete_pdu(s, pdu, err);
+    v9fs_string_free(&uname);
+    v9fs_string_free(&aname);
+}
+
+static void v9fs_stat_post_lstat(V9fsState *s, V9fsStatState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    err = stat_to_v9stat(s, &vs->fidp->path, &vs->stbuf, &vs->v9stat);
+    if (err) {
+        goto out;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "wS", 0, &vs->v9stat);
+    err = vs->offset;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_stat_free(&vs->v9stat);
+    qemu_free(vs);
+}
+
+static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsStatState *vs;
+    ssize_t err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    memset(&vs->v9stat, 0, sizeof(vs->v9stat));
+
+    pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+    v9fs_stat_post_lstat(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_stat_free(&vs->v9stat);
+    qemu_free(vs);
+}
+
+static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs,
+                                                                int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl);
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl);
+    err = vs->offset;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_getattr(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsStatStateDotl *vs;
+    ssize_t err = 0;
+    V9fsFidState *fidp;
+    uint64_t request_mask;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl));
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask);
+
+    fidp = lookup_fid(s, fid);
+    if (fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    /* Currently we only support BASIC fields in stat, so there is no
+     * need to look at request_mask.
+     */
+    err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf);
+    v9fs_getattr_post_lstat(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+/* From Linux kernel code */
+#define ATTR_MODE    (1 << 0)
+#define ATTR_UID     (1 << 1)
+#define ATTR_GID     (1 << 2)
+#define ATTR_SIZE    (1 << 3)
+#define ATTR_ATIME   (1 << 4)
+#define ATTR_MTIME   (1 << 5)
+#define ATTR_CTIME   (1 << 6)
+#define ATTR_MASK    127
+#define ATTR_ATIME_SET  (1 << 7)
+#define ATTR_MTIME_SET  (1 << 8)
+
+static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
+                                                                  int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+    err = vs->offset;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    if (vs->v9iattr.valid & (ATTR_SIZE)) {
+        err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
+    }
+    v9fs_setattr_post_truncate(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
+                                                                   int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    /* If the only valid entry in iattr is ctime we can call
+     * chown(-1,-1) to update the ctime of the file
+     */
+    if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
+            ((vs->v9iattr.valid & ATTR_CTIME)
+            && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
+        if (!(vs->v9iattr.valid & ATTR_UID)) {
+            vs->v9iattr.uid = -1;
+        }
+        if (!(vs->v9iattr.valid & ATTR_GID)) {
+            vs->v9iattr.gid = -1;
+        }
+        err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
+                                                vs->v9iattr.gid);
+    }
+    v9fs_setattr_post_chown(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
+        struct timespec times[2];
+        if (vs->v9iattr.valid & ATTR_ATIME) {
+            if (vs->v9iattr.valid & ATTR_ATIME_SET) {
+                times[0].tv_sec = vs->v9iattr.atime_sec;
+                times[0].tv_nsec = vs->v9iattr.atime_nsec;
+            } else {
+                times[0].tv_nsec = UTIME_NOW;
+            }
+        } else {
+            times[0].tv_nsec = UTIME_OMIT;
+        }
+
+        if (vs->v9iattr.valid & ATTR_MTIME) {
+            if (vs->v9iattr.valid & ATTR_MTIME_SET) {
+                times[1].tv_sec = vs->v9iattr.mtime_sec;
+                times[1].tv_nsec = vs->v9iattr.mtime_nsec;
+            } else {
+                times[1].tv_nsec = UTIME_NOW;
+            }
+        } else {
+            times[1].tv_nsec = UTIME_OMIT;
+        }
+        err = v9fs_do_utimensat(s, &vs->fidp->path, times);
+    }
+    v9fs_setattr_post_utimensat(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsSetattrState *vs;
+    int err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    if (vs->v9iattr.valid & ATTR_MODE) {
+        err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
+    }
+
+    v9fs_setattr_post_chmod(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
+{
+    complete_pdu(s, vs->pdu, err);
+
+    if (vs->nwnames) {
+        for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) {
+            v9fs_string_free(&vs->wnames[vs->name_idx]);
+        }
+
+        qemu_free(vs->wnames);
+        qemu_free(vs->qids);
+    }
+}
+
+static void v9fs_walk_marshal(V9fsWalkState *vs)
+{
+    int i;
+    vs->offset = 7;
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "w", vs->nwnames);
+
+    for (i = 0; i < vs->nwnames; i++) {
+        vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qids[i]);
+    }
+}
+
+static void v9fs_walk_post_newfid_lstat(V9fsState *s, V9fsWalkState *vs,
+                                                                int err)
+{
+    if (err == -1) {
+        free_fid(s, vs->newfidp->fid);
+        v9fs_string_free(&vs->path);
+        err = -ENOENT;
+        goto out;
+    }
+
+    stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
+
+    vs->name_idx++;
+    if (vs->name_idx < vs->nwnames) {
+        v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
+                                            vs->wnames[vs->name_idx].data);
+        v9fs_string_copy(&vs->newfidp->path, &vs->path);
+
+        err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
+        v9fs_walk_post_newfid_lstat(s, vs, err);
+        return;
+    }
+
+    v9fs_string_free(&vs->path);
+    v9fs_walk_marshal(vs);
+    err = vs->offset;
+out:
+    v9fs_walk_complete(s, vs, err);
+}
+
+static void v9fs_walk_post_oldfid_lstat(V9fsState *s, V9fsWalkState *vs,
+        int err)
+{
+    if (err == -1) {
+        v9fs_string_free(&vs->path);
+        err = -ENOENT;
+        goto out;
+    }
+
+    stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
+    vs->name_idx++;
+    if (vs->name_idx < vs->nwnames) {
+
+        v9fs_string_sprintf(&vs->path, "%s/%s",
+                vs->fidp->path.data, vs->wnames[vs->name_idx].data);
+        v9fs_string_copy(&vs->fidp->path, &vs->path);
+
+        err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+        v9fs_walk_post_oldfid_lstat(s, vs, err);
+        return;
+    }
+
+    v9fs_string_free(&vs->path);
+    v9fs_walk_marshal(vs);
+    err = vs->offset;
+out:
+    v9fs_walk_complete(s, vs, err);
+}
+
+static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid, newfid;
+    V9fsWalkState *vs;
+    int err = 0;
+    int i;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->wnames = NULL;
+    vs->qids = NULL;
+    vs->offset = 7;
+
+    vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid,
+                                            &newfid, &vs->nwnames);
+
+    if (vs->nwnames) {
+        vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames);
+
+        vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames);
+
+        for (i = 0; i < vs->nwnames; i++) {
+            vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s",
+                                            &vs->wnames[i]);
+        }
+    }
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    /* FIXME: is this really valid? */
+    if (fid == newfid) {
+
+        BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+        v9fs_string_init(&vs->path);
+        vs->name_idx = 0;
+
+        if (vs->name_idx < vs->nwnames) {
+            v9fs_string_sprintf(&vs->path, "%s/%s",
+                vs->fidp->path.data, vs->wnames[vs->name_idx].data);
+            v9fs_string_copy(&vs->fidp->path, &vs->path);
+
+            err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+            v9fs_walk_post_oldfid_lstat(s, vs, err);
+            return;
+        }
+    } else {
+        vs->newfidp = alloc_fid(s, newfid);
+        if (vs->newfidp == NULL) {
+            err = -EINVAL;
+            goto out;
+        }
+
+        vs->newfidp->uid = vs->fidp->uid;
+        v9fs_string_init(&vs->path);
+        vs->name_idx = 0;
+        v9fs_string_copy(&vs->newfidp->path, &vs->fidp->path);
+
+        if (vs->name_idx < vs->nwnames) {
+            v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
+                                vs->wnames[vs->name_idx].data);
+            v9fs_string_copy(&vs->newfidp->path, &vs->path);
+
+            err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
+            v9fs_walk_post_newfid_lstat(s, vs, err);
+            return;
+        }
+    }
+
+    v9fs_walk_marshal(vs);
+    err = vs->offset;
+out:
+    v9fs_walk_complete(s, vs, err);
+}
+
+static int32_t get_iounit(V9fsState *s, V9fsString *name)
+{
+    struct statfs stbuf;
+    int32_t iounit = 0;
+
+    /*
+     * iounit should be multiples of f_bsize (host filesystem block size
+     * and as well as less than (client msize - P9_IOHDRSZ))
+     */
+    if (!v9fs_do_statfs(s, name, &stbuf)) {
+        iounit = stbuf.f_bsize;
+        iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
+    }
+
+    if (!iounit) {
+        iounit = s->msize - P9_IOHDRSZ;
+    }
+    return iounit;
+}
+
+static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
+{
+    if (vs->fidp->fs.dir == NULL) {
+        err = -errno;
+        goto out;
+    }
+    vs->fidp->fid_type = P9_FID_DIR;
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+
+}
+
+static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs)
+{
+    int err;
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+    err = vs->offset;
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
+{
+    if (vs->fidp->fs.fd == -1) {
+        err = -errno;
+        goto out;
+    }
+    vs->fidp->fid_type = P9_FID_FILE;
+    vs->iounit = get_iounit(s, &vs->fidp->path);
+    v9fs_open_post_getiounit(s, vs);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
+{
+    int flags;
+
+    if (err) {
+        err = -errno;
+        goto out;
+    }
+
+    stat_to_qid(&vs->stbuf, &vs->qid);
+
+    if (S_ISDIR(vs->stbuf.st_mode)) {
+        vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path);
+        v9fs_open_post_opendir(s, vs, err);
+    } else {
+        if (s->proto_version == V9FS_PROTO_2000L) {
+            flags = vs->mode;
+            flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
+            /* Ignore direct disk access hint until the server supports it. */
+            flags &= ~O_DIRECT;
+        } else {
+            flags = omode_to_uflags(vs->mode);
+        }
+        vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags);
+        v9fs_open_post_open(s, vs, err);
+    }
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsOpenState *vs;
+    ssize_t err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+    vs->mode = 0;
+
+    if (s->proto_version == V9FS_PROTO_2000L) {
+        pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode);
+    } else {
+        pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
+    }
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+
+    err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+
+    v9fs_open_post_lstat(s, vs, err);
+    return;
+out:
+    complete_pdu(s, pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
+{
+    if (err == 0) {
+        v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+        stat_to_qid(&vs->stbuf, &vs->qid);
+        vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
+                &vs->iounit);
+        err = vs->offset;
+    } else {
+        vs->fidp->fid_type = P9_FID_NONE;
+        err = -errno;
+        if (vs->fidp->fs.fd > 0) {
+            close(vs->fidp->fs.fd);
+        }
+    }
+
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    v9fs_string_free(&vs->fullname);
+    qemu_free(vs);
+}
+
+static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs,
+        int err)
+{
+    if (err) {
+        err = -errno;
+        goto out;
+    }
+    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+
+out:
+    v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs,
+        int err)
+{
+    if (vs->fidp->fs.fd == -1) {
+        err = -errno;
+        goto out;
+    }
+    vs->fidp->fid_type = P9_FID_FILE;
+    vs->iounit =  get_iounit(s, &vs->fullname);
+    v9fs_lcreate_post_get_iounit(s, vs, err);
+    return;
+
+out:
+    v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t dfid, flags, mode;
+    gid_t gid;
+    V9fsLcreateState *vs;
+    ssize_t err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    v9fs_string_init(&vs->fullname);
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags,
+            &mode, &gid);
+
+    vs->fidp = lookup_fid(s, dfid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
+             vs->name.data);
+
+    /* Ignore direct disk access hint until the server supports it. */
+    flags &= ~O_DIRECT;
+
+    vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+            gid, flags, mode);
+    v9fs_lcreate_post_do_open2(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_post_do_fsync(V9fsState *s, V9fsPDU *pdu, int err)
+{
+    if (err == -1) {
+        err = -errno;
+    }
+    complete_pdu(s, pdu, err);
+}
+
+static void v9fs_fsync(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    size_t offset = 7;
+    V9fsFidState *fidp;
+    int datasync;
+    int err;
+
+    pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
+    fidp = lookup_fid(s, fid);
+    if (fidp == NULL) {
+        err = -ENOENT;
+        v9fs_post_do_fsync(s, pdu, err);
+        return;
+    }
+    err = v9fs_do_fsync(s, fidp->fs.fd, datasync);
+    v9fs_post_do_fsync(s, pdu, err);
+}
+
+static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    size_t offset = 7;
+    int err;
+
+    pdu_unmarshal(pdu, offset, "d", &fid);
+
+    err = free_fid(s, fid);
+    if (err < 0) {
+        goto out;
+    }
+
+    offset = 7;
+    err = offset;
+out:
+    complete_pdu(s, pdu, err);
+}
+
+static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t);
+
+static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+    if (err) {
+        goto out;
+    }
+    v9fs_stat_free(&vs->v9stat);
+    v9fs_string_free(&vs->name);
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+    vs->offset += vs->count;
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
+                                    ssize_t err)
+{
+    if (err) {
+        err = -errno;
+        goto out;
+    }
+    err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat);
+    if (err) {
+        goto out;
+    }
+
+    vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S",
+                            &vs->v9stat);
+    if ((vs->len != (vs->v9stat.size + 2)) ||
+            ((vs->count + vs->len) > vs->max_count)) {
+        v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
+        v9fs_read_post_seekdir(s, vs, err);
+        return;
+    }
+    vs->count += vs->len;
+    v9fs_stat_free(&vs->v9stat);
+    v9fs_string_free(&vs->name);
+    vs->dir_pos = vs->dent->d_off;
+    vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+    v9fs_read_post_readdir(s, vs, err);
+    return;
+out:
+    v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
+    v9fs_read_post_seekdir(s, vs, err);
+    return;
+
+}
+
+static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+    if (vs->dent) {
+        memset(&vs->v9stat, 0, sizeof(vs->v9stat));
+        v9fs_string_init(&vs->name);
+        v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data,
+                            vs->dent->d_name);
+        err = v9fs_do_lstat(s, &vs->name, &vs->stbuf);
+        v9fs_read_post_dir_lstat(s, vs, err);
+        return;
+    }
+
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+    vs->offset += vs->count;
+    err = vs->offset;
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+    vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+    v9fs_read_post_readdir(s, vs, err);
+    return;
+}
+
+static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
+                                       ssize_t err)
+{
+    vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
+    v9fs_read_post_telldir(s, vs, err);
+    return;
+}
+
+static void v9fs_read_post_preadv(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+    if (err  < 0) {
+        /* IO error return the error */
+        err = -errno;
+        goto out;
+    }
+    vs->total += vs->len;
+    vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
+    if (vs->total < vs->count && vs->len > 0) {
+        do {
+            if (0) {
+                print_sg(vs->sg, vs->cnt);
+            }
+            vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+                      vs->off);
+            if (vs->len > 0) {
+                vs->off += vs->len;
+            }
+        } while (vs->len == -1 && errno == EINTR);
+        if (vs->len == -1) {
+            err  = -errno;
+        }
+        v9fs_read_post_preadv(s, vs, err);
+        return;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
+    vs->offset += vs->count;
+    err = vs->offset;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
+{
+    ssize_t err = 0;
+    int read_count;
+    int64_t xattr_len;
+
+    xattr_len = vs->fidp->fs.xattr.len;
+    read_count = xattr_len - vs->off;
+    if (read_count > vs->count) {
+        read_count = vs->count;
+    } else if (read_count < 0) {
+        /*
+         * read beyond XATTR value
+         */
+        read_count = 0;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
+    vs->offset += pdu_pack(vs->pdu, vs->offset,
+                           ((char *)vs->fidp->fs.xattr.value) + vs->off,
+                           read_count);
+    err = vs->offset;
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsReadState *vs;
+    ssize_t err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+    vs->total = 0;
+    vs->len = 0;
+    vs->count = 0;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    if (vs->fidp->fid_type == P9_FID_DIR) {
+        vs->max_count = vs->count;
+        vs->count = 0;
+        if (vs->off == 0) {
+            v9fs_do_rewinddir(s, vs->fidp->fs.dir);
+        }
+        v9fs_read_post_rewinddir(s, vs, err);
+        return;
+    } else if (vs->fidp->fid_type == P9_FID_FILE) {
+        vs->sg = vs->iov;
+        pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
+        vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
+        if (vs->total <= vs->count) {
+            vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+                                    vs->off);
+            if (vs->len > 0) {
+                vs->off += vs->len;
+            }
+            err = vs->len;
+            v9fs_read_post_preadv(s, vs, err);
+        }
+        return;
+    } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+        v9fs_xattr_read(s, vs);
+        return;
+    } else {
+        err = -EINVAL;
+    }
+out:
+    complete_pdu(s, pdu, err);
+    qemu_free(vs);
+}
+
+typedef struct V9fsReadDirState {
+    V9fsPDU *pdu;
+    V9fsFidState *fidp;
+    V9fsQID qid;
+    off_t saved_dir_pos;
+    struct dirent *dent;
+    int32_t count;
+    int32_t max_count;
+    size_t offset;
+    int64_t initial_offset;
+    V9fsString name;
+} V9fsReadDirState;
+
+static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs)
+{
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+    vs->offset += vs->count;
+    complete_pdu(s, vs->pdu, vs->offset);
+    qemu_free(vs);
+    return;
+}
+
+/* Size of each dirent on the wire: size of qid (13) + size of offset (8)
+ * size of type (1) + size of name.size (2) + strlen(name.data)
+ */
+#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data))
+
+static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs)
+{
+    int len;
+    size_t size;
+
+    if (vs->dent) {
+        v9fs_string_init(&vs->name);
+        v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name);
+
+        if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) {
+            /* Ran out of buffer. Set dir back to old position and return */
+            v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos);
+            v9fs_readdir_post_seekdir(s, vs);
+            return;
+        }
+
+        /* Fill up just the path field of qid because the client uses
+         * only that. To fill the entire qid structure we will have
+         * to stat each dirent found, which is expensive
+         */
+        size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path));
+        memcpy(&vs->qid.path, &vs->dent->d_ino, size);
+        /* Fill the other fields with dummy values */
+        vs->qid.type = 0;
+        vs->qid.version = 0;
+
+        len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs",
+                              &vs->qid, vs->dent->d_off,
+                              vs->dent->d_type, &vs->name);
+        vs->count += len;
+        v9fs_string_free(&vs->name);
+        vs->saved_dir_pos = vs->dent->d_off;
+        vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+        v9fs_readdir_post_readdir(s, vs);
+        return;
+    }
+
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+    vs->offset += vs->count;
+    complete_pdu(s, vs->pdu, vs->offset);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs)
+{
+    vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+    v9fs_readdir_post_readdir(s, vs);
+    return;
+}
+
+static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs)
+{
+    vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
+    v9fs_readdir_post_telldir(s, vs);
+    return;
+}
+
+static void v9fs_readdir(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsReadDirState *vs;
+    ssize_t err = 0;
+    size_t offset = 7;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+    vs->count = 0;
+
+    pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset,
+                                                        &vs->max_count);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL || !(vs->fidp->fs.dir)) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    if (vs->initial_offset == 0) {
+        v9fs_do_rewinddir(s, vs->fidp->fs.dir);
+    } else {
+        v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset);
+    }
+
+    v9fs_readdir_post_setdir(s, vs);
+    return;
+
+out:
+    complete_pdu(s, pdu, err);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_write_post_pwritev(V9fsState *s, V9fsWriteState *vs,
+                                   ssize_t err)
+{
+    if (err  < 0) {
+        /* IO error return the error */
+        err = -errno;
+        goto out;
+    }
+    vs->total += vs->len;
+    vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
+    if (vs->total < vs->count && vs->len > 0) {
+        do {
+            if (0) {
+                print_sg(vs->sg, vs->cnt);
+            }
+            vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+                      vs->off);
+            if (vs->len > 0) {
+                vs->off += vs->len;
+            }
+        } while (vs->len == -1 && errno == EINTR);
+        if (vs->len == -1) {
+            err  = -errno;
+        }
+        v9fs_write_post_pwritev(s, vs, err);
+        return;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs)
+{
+    int i, to_copy;
+    ssize_t err = 0;
+    int write_count;
+    int64_t xattr_len;
+
+    xattr_len = vs->fidp->fs.xattr.len;
+    write_count = xattr_len - vs->off;
+    if (write_count > vs->count) {
+        write_count = vs->count;
+    } else if (write_count < 0) {
+        /*
+         * write beyond XATTR value len specified in
+         * xattrcreate
+         */
+        err = -ENOSPC;
+        goto out;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count);
+    err = vs->offset;
+    vs->fidp->fs.xattr.copied_len += write_count;
+    /*
+     * Now copy the content from sg list
+     */
+    for (i = 0; i < vs->cnt; i++) {
+        if (write_count > vs->sg[i].iov_len) {
+            to_copy = vs->sg[i].iov_len;
+        } else {
+            to_copy = write_count;
+        }
+        memcpy((char *)vs->fidp->fs.xattr.value + vs->off,
+               vs->sg[i].iov_base, to_copy);
+        /* updating vs->off since we are not using below */
+        vs->off += to_copy;
+        write_count -= to_copy;
+    }
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsWriteState *vs;
+    ssize_t err;
+
+    vs = qemu_malloc(sizeof(*vs));
+
+    vs->pdu = pdu;
+    vs->offset = 7;
+    vs->sg = vs->iov;
+    vs->total = 0;
+    vs->len = 0;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
+                  vs->sg, &vs->cnt);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    if (vs->fidp->fid_type == P9_FID_FILE) {
+        if (vs->fidp->fs.fd == -1) {
+            err = -EINVAL;
+            goto out;
+        }
+    } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+        /*
+         * setxattr operation
+         */
+        v9fs_xattr_write(s, vs);
+        return;
+    } else {
+        err = -EINVAL;
+        goto out;
+    }
+    vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
+    if (vs->total <= vs->count) {
+        vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt, vs->off);
+        if (vs->len > 0) {
+            vs->off += vs->len;
+        }
+        err = vs->len;
+        v9fs_write_post_pwritev(s, vs, err);
+    }
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs)
+{
+    int err;
+    v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+    stat_to_qid(&vs->stbuf, &vs->qid);
+
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+    err = vs->offset;
+
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    v9fs_string_free(&vs->extension);
+    v9fs_string_free(&vs->fullname);
+    qemu_free(vs);
+}
+
+static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
+{
+    if (err == 0) {
+        vs->iounit = get_iounit(s, &vs->fidp->path);
+        v9fs_create_post_getiounit(s, vs);
+        return;
+    }
+
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    v9fs_string_free(&vs->extension);
+    v9fs_string_free(&vs->fullname);
+    qemu_free(vs);
+}
+
+static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err)
+{
+    if (err) {
+        err = -errno;
+    }
+    v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs,
+                                                                    int err)
+{
+    if (!vs->fidp->fs.dir) {
+        err = -errno;
+    }
+    vs->fidp->fid_type = P9_FID_DIR;
+    v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs,
+                                                                    int err)
+{
+    if (err) {
+        err = -errno;
+        goto out;
+    }
+
+    vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname);
+    v9fs_create_post_opendir(s, vs, err);
+    return;
+
+out:
+    v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_mkdir(V9fsState *s, V9fsCreateState *vs, int err)
+{
+    if (err) {
+        err = -errno;
+        goto out;
+    }
+
+    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+    v9fs_create_post_dir_lstat(s, vs, err);
+    return;
+
+out:
+    v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err)
+{
+    if (err) {
+        vs->fidp->fid_type = P9_FID_NONE;
+        close(vs->fidp->fs.fd);
+        err = -errno;
+    }
+    v9fs_post_create(s, vs, err);
+    return;
+}
+
+static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err)
+{
+    if (vs->fidp->fs.fd == -1) {
+        err = -errno;
+        goto out;
+    }
+    vs->fidp->fid_type = P9_FID_FILE;
+    err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+    v9fs_create_post_fstat(s, vs, err);
+
+    return;
+
+out:
+    v9fs_post_create(s, vs, err);
+
+}
+
+static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
+{
+
+    if (err == 0 || errno != ENOENT) {
+        err = -errno;
+        goto out;
+    }
+
+    if (vs->perm & P9_STAT_MODE_DIR) {
+        err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777,
+                vs->fidp->uid, -1);
+        v9fs_create_post_mkdir(s, vs, err);
+    } else if (vs->perm & P9_STAT_MODE_SYMLINK) {
+        err = v9fs_do_symlink(s, vs->fidp, vs->extension.data,
+                vs->fullname.data, -1);
+        v9fs_create_post_perms(s, vs, err);
+    } else if (vs->perm & P9_STAT_MODE_LINK) {
+        int32_t nfid = atoi(vs->extension.data);
+        V9fsFidState *nfidp = lookup_fid(s, nfid);
+        if (nfidp == NULL) {
+            err = -errno;
+            v9fs_post_create(s, vs, err);
+        }
+        err = v9fs_do_link(s, &nfidp->path, &vs->fullname);
+        v9fs_create_post_perms(s, vs, err);
+    } else if (vs->perm & P9_STAT_MODE_DEVICE) {
+        char ctype;
+        uint32_t major, minor;
+        mode_t nmode = 0;
+
+        if (sscanf(vs->extension.data, "%c %u %u", &ctype, &major,
+                                        &minor) != 3) {
+            err = -errno;
+            v9fs_post_create(s, vs, err);
+        }
+
+        switch (ctype) {
+        case 'c':
+            nmode = S_IFCHR;
+            break;
+        case 'b':
+            nmode = S_IFBLK;
+            break;
+        default:
+            err = -EIO;
+            v9fs_post_create(s, vs, err);
+        }
+
+        nmode |= vs->perm & 0777;
+        err = v9fs_do_mknod(s, vs->fullname.data, nmode,
+                makedev(major, minor), vs->fidp->uid, -1);
+        v9fs_create_post_perms(s, vs, err);
+    } else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) {
+        err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777),
+                0, vs->fidp->uid, -1);
+        v9fs_post_create(s, vs, err);
+    } else if (vs->perm & P9_STAT_MODE_SOCKET) {
+        err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777),
+                0, vs->fidp->uid, -1);
+        v9fs_post_create(s, vs, err);
+    } else {
+        vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+                -1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm);
+
+        v9fs_create_post_open2(s, vs, err);
+    }
+
+    return;
+
+out:
+    v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsCreateState *vs;
+    int err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    v9fs_string_init(&vs->fullname);
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dsdbs", &fid, &vs->name,
+                                &vs->perm, &vs->mode, &vs->extension);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
+                                                        vs->name.data);
+
+    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+    v9fs_create_post_lstat(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    v9fs_string_free(&vs->extension);
+    qemu_free(vs);
+}
+
+static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err)
+{
+    if (err == 0) {
+        stat_to_qid(&vs->stbuf, &vs->qid);
+        vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+        err = vs->offset;
+    } else {
+        err = -errno;
+    }
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    v9fs_string_free(&vs->symname);
+    v9fs_string_free(&vs->fullname);
+    qemu_free(vs);
+}
+
+static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs,
+        int err)
+{
+    if (err) {
+        goto out;
+    }
+    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+out:
+    v9fs_post_symlink(s, vs, err);
+}
+
+static void v9fs_symlink(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t dfid;
+    V9fsSymlinkState *vs;
+    int err = 0;
+    gid_t gid;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    v9fs_string_init(&vs->fullname);
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name,
+            &vs->symname, &gid);
+
+    vs->dfidp = lookup_fid(s, dfid);
+    if (vs->dfidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data,
+            vs->name.data);
+    err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data,
+            vs->fullname.data, gid);
+    v9fs_symlink_post_do_symlink(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    v9fs_string_free(&vs->symname);
+    qemu_free(vs);
+}
+
+static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
+{
+    /* A nop call with no return */
+    complete_pdu(s, pdu, 7);
+}
+
+static void v9fs_link(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t dfid, oldfid;
+    V9fsFidState *dfidp, *oldfidp;
+    V9fsString name, fullname;
+    size_t offset = 7;
+    int err = 0;
+
+    v9fs_string_init(&fullname);
+
+    pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
+
+    dfidp = lookup_fid(s, dfid);
+    if (dfidp == NULL) {
+        err = -errno;
+        goto out;
+    }
+
+    oldfidp = lookup_fid(s, oldfid);
+    if (oldfidp == NULL) {
+        err = -errno;
+        goto out;
+    }
+
+    v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
+    err = offset;
+    err = v9fs_do_link(s, &oldfidp->path, &fullname);
+    if (err) {
+        err = -errno;
+    }
+    v9fs_string_free(&fullname);
+
+out:
+    v9fs_string_free(&name);
+    complete_pdu(s, pdu, err);
+}
+
+static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs,
+                                                                int err)
+{
+    if (err < 0) {
+        err = -errno;
+    } else {
+        err = vs->offset;
+    }
+
+    /* For TREMOVE we need to clunk the fid even on failed remove */
+    free_fid(s, vs->fidp->fid);
+
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsRemoveState *vs;
+    int err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    err = v9fs_do_remove(s, &vs->fidp->path);
+    v9fs_remove_post_remove(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat_post_truncate(V9fsState *s, V9fsWstatState *vs, int err)
+{
+    if (err < 0) {
+        goto out;
+    }
+
+    err = vs->offset;
+
+out:
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err)
+{
+    if (err < 0) {
+        goto out;
+    }
+    if (vs->v9stat.length != -1) {
+        if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) {
+            err = -errno;
+        }
+    }
+    v9fs_wstat_post_truncate(s, vs, err);
+    return;
+
+out:
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs)
+{
+    int err = 0;
+    char *old_name, *new_name;
+    char *end;
+
+    if (vs->newdirfid != -1) {
+        V9fsFidState *dirfidp;
+        dirfidp = lookup_fid(s, vs->newdirfid);
+
+        if (dirfidp == NULL) {
+            err = -ENOENT;
+            goto out;
+        }
+
+        BUG_ON(dirfidp->fid_type != P9_FID_NONE);
+
+        new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2);
+
+        strcpy(new_name, dirfidp->path.data);
+        strcat(new_name, "/");
+        strcat(new_name + dirfidp->path.size, vs->name.data);
+    } else {
+        old_name = vs->fidp->path.data;
+        end = strrchr(old_name, '/');
+        if (end) {
+            end++;
+        } else {
+            end = old_name;
+        }
+        new_name = qemu_mallocz(end - old_name + vs->name.size + 1);
+
+        strncat(new_name, old_name, end - old_name);
+        strncat(new_name + (end - old_name), vs->name.data, vs->name.size);
+    }
+
+    v9fs_string_free(&vs->name);
+    vs->name.data = qemu_strdup(new_name);
+    vs->name.size = strlen(new_name);
+
+    if (strcmp(new_name, vs->fidp->path.data) != 0) {
+        if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) {
+            err = -errno;
+        } else {
+            V9fsFidState *fidp;
+            /*
+            * Fixup fid's pointing to the old name to
+            * start pointing to the new name
+            */
+            for (fidp = s->fid_list; fidp; fidp = fidp->next) {
+                if (vs->fidp == fidp) {
+                    /*
+                    * we replace name of this fid towards the end
+                    * so that our below strcmp will work
+                    */
+                    continue;
+                }
+                if (!strncmp(vs->fidp->path.data, fidp->path.data,
+                    strlen(vs->fidp->path.data))) {
+                    /* replace the name */
+                    v9fs_fix_path(&fidp->path, &vs->name,
+                                  strlen(vs->fidp->path.data));
+                }
+            }
+            v9fs_string_copy(&vs->fidp->path, &vs->name);
+        }
+    }
+out:
+    v9fs_string_free(&vs->name);
+    return err;
+}
+
+static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err)
+{
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
+{
+    if (err < 0) {
+        goto out;
+    }
+
+    if (vs->v9stat.name.size != 0) {
+        V9fsRenameState *vr;
+
+        vr = qemu_mallocz(sizeof(V9fsRenameState));
+        vr->newdirfid = -1;
+        vr->pdu = vs->pdu;
+        vr->fidp = vs->fidp;
+        vr->offset = vs->offset;
+        vr->name.size = vs->v9stat.name.size;
+        vr->name.data = qemu_strdup(vs->v9stat.name.data);
+
+        err = v9fs_complete_rename(s, vr);
+        qemu_free(vr);
+    }
+    v9fs_wstat_post_rename(s, vs, err);
+    return;
+
+out:
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_rename(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsRenameState *vs;
+    ssize_t err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+
+    err = v9fs_complete_rename(s, vs);
+    v9fs_rename_post_rename(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err)
+{
+    if (err < 0) {
+        goto out;
+    }
+
+    if (vs->v9stat.n_gid != -1 || vs->v9stat.n_uid != -1) {
+        if (v9fs_do_chown(s, &vs->fidp->path, vs->v9stat.n_uid,
+                    vs->v9stat.n_gid)) {
+            err = -errno;
+        }
+    }
+    v9fs_wstat_post_chown(s, vs, err);
+    return;
+
+out:
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err)
+{
+    if (err < 0) {
+        goto out;
+    }
+
+    if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) {
+        struct timespec times[2];
+        if (vs->v9stat.atime != -1) {
+            times[0].tv_sec = vs->v9stat.atime;
+            times[0].tv_nsec = 0;
+        } else {
+            times[0].tv_nsec = UTIME_OMIT;
+        }
+        if (vs->v9stat.mtime != -1) {
+            times[1].tv_sec = vs->v9stat.mtime;
+            times[1].tv_nsec = 0;
+        } else {
+            times[1].tv_nsec = UTIME_OMIT;
+        }
+
+        if (v9fs_do_utimensat(s, &vs->fidp->path, times)) {
+            err = -errno;
+        }
+    }
+
+    v9fs_wstat_post_utime(s, vs, err);
+    return;
+
+out:
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat_post_fsync(V9fsState *s, V9fsWstatState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+    }
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat_post_lstat(V9fsState *s, V9fsWstatState *vs, int err)
+{
+    uint32_t v9_mode;
+
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    v9_mode = stat_to_v9mode(&vs->stbuf);
+
+    if ((vs->v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
+        (v9_mode & P9_STAT_MODE_TYPE_BITS)) {
+            /* Attempting to change the type */
+            err = -EIO;
+            goto out;
+    }
+
+    if (v9fs_do_chmod(s, &vs->fidp->path, v9mode_to_mode(vs->v9stat.mode,
+                    &vs->v9stat.extension))) {
+            err = -errno;
+     }
+    v9fs_wstat_post_chmod(s, vs, err);
+    return;
+
+out:
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsWstatState *vs;
+    int err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(pdu, vs->offset, "dwS", &fid, &vs->unused, &vs->v9stat);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    /* do we need to sync the file? */
+    if (donttouch_stat(&vs->v9stat)) {
+        err = v9fs_do_fsync(s, vs->fidp->fs.fd, 0);
+        v9fs_wstat_post_fsync(s, vs, err);
+        return;
+    }
+
+    if (vs->v9stat.mode != -1) {
+        err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+        v9fs_wstat_post_lstat(s, vs, err);
+        return;
+    }
+
+    v9fs_wstat_post_chmod(s, vs, err);
+    return;
+
+out:
+    v9fs_stat_free(&vs->v9stat);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err)
+{
+    int32_t bsize_factor;
+
+    if (err) {
+        err = -errno;
+        goto out;
+    }
+
+    /*
+     * compute bsize factor based on host file system block size
+     * and client msize
+     */
+    bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize;
+    if (!bsize_factor) {
+        bsize_factor = 1;
+    }
+    vs->v9statfs.f_type = vs->stbuf.f_type;
+    vs->v9statfs.f_bsize = vs->stbuf.f_bsize;
+    vs->v9statfs.f_bsize *= bsize_factor;
+    /*
+     * f_bsize is adjusted(multiplied) by bsize factor, so we need to
+     * adjust(divide) the number of blocks, free blocks and available
+     * blocks by bsize factor
+     */
+    vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor;
+    vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor;
+    vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor;
+    vs->v9statfs.f_files = vs->stbuf.f_files;
+    vs->v9statfs.f_ffree = vs->stbuf.f_ffree;
+    vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] |
+			(unsigned long long)vs->stbuf.f_fsid.__val[1] << 32;
+    vs->v9statfs.f_namelen = vs->stbuf.f_namelen;
+
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd",
+         vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks,
+         vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files,
+         vs->v9statfs.f_ffree, vs->v9statfs.fsid_val,
+         vs->v9statfs.f_namelen);
+
+out:
+    complete_pdu(s, vs->pdu, vs->offset);
+    qemu_free(vs);
+}
+
+static void v9fs_statfs(V9fsState *s, V9fsPDU *pdu)
+{
+    V9fsStatfsState *vs;
+    ssize_t err = 0;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    memset(&vs->v9statfs, 0, sizeof(vs->v9statfs));
+
+    pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid);
+
+    vs->fidp = lookup_fid(s, vs->fid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf);
+    v9fs_statfs_post_statfs(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    stat_to_qid(&vs->stbuf, &vs->qid);
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->fullname);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+    v9fs_mknod_post_lstat(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->fullname);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_mknod(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsMkState *vs;
+    int err = 0;
+    V9fsFidState *fidp;
+    gid_t gid;
+    int mode;
+    int major, minor;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    v9fs_string_init(&vs->fullname);
+    pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode,
+        &major, &minor, &gid);
+
+    fidp = lookup_fid(s, fid);
+    if (fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+    err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor),
+        fidp->uid, gid);
+    v9fs_mknod_post_mknod(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->fullname);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+/*
+ * Implement posix byte range locking code
+ * Server side handling of locking code is very simple, because 9p server in
+ * QEMU can handle only one client. And most of the lock handling
+ * (like conflict, merging) etc is done by the VFS layer itself, so no need to
+ * do any thing in * qemu 9p server side lock code path.
+ * So when a TLOCK request comes, always return success
+ */
+
+static void v9fs_lock(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid, err = 0;
+    V9fsLockState *vs;
+
+    vs = qemu_mallocz(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    vs->flock = qemu_malloc(sizeof(*vs->flock));
+    pdu_unmarshal(vs->pdu, vs->offset, "dbdqqds", &fid, &vs->flock->type,
+                &vs->flock->flags, &vs->flock->start, &vs->flock->length,
+                            &vs->flock->proc_id, &vs->flock->client_id);
+
+    vs->status = P9_LOCK_ERROR;
+
+    /* We support only block flag now (that too ignored currently) */
+    if (vs->flock->flags & ~P9_LOCK_FLAGS_BLOCK) {
+        err = -EINVAL;
+        goto out;
+    }
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+    if (err < 0) {
+        err = -errno;
+        goto out;
+    }
+    vs->status = P9_LOCK_SUCCESS;
+out:
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "b", vs->status);
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs->flock);
+    qemu_free(vs);
+}
+
+/*
+ * When a TGETLOCK request comes, always return success because all lock
+ * handling is done by client's VFS layer.
+ */
+
+static void v9fs_getlock(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid, err = 0;
+    V9fsGetlockState *vs;
+
+    vs = qemu_mallocz(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    vs->glock = qemu_malloc(sizeof(*vs->glock));
+    pdu_unmarshal(vs->pdu, vs->offset, "dbqqds", &fid, &vs->glock->type,
+                &vs->glock->start, &vs->glock->length, &vs->glock->proc_id,
+		&vs->glock->client_id);
+
+    vs->fidp = lookup_fid(s, fid);
+    if (vs->fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+    if (err < 0) {
+        err = -errno;
+        goto out;
+    }
+    vs->glock->type = F_UNLCK;
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "bqqds", vs->glock->type,
+                vs->glock->start, vs->glock->length, vs->glock->proc_id,
+		&vs->glock->client_id);
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs->glock);
+    qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    stat_to_qid(&vs->stbuf, &vs->qid);
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->fullname);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err)
+{
+    if (err == -1) {
+        err = -errno;
+        goto out;
+    }
+
+    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+    v9fs_mkdir_post_lstat(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->fullname);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_mkdir(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsMkState *vs;
+    int err = 0;
+    V9fsFidState *fidp;
+    gid_t gid;
+    int mode;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    v9fs_string_init(&vs->fullname);
+    pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode,
+        &gid);
+
+    fidp = lookup_fid(s, fid);
+    if (fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+    err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid);
+    v9fs_mkdir_post_mkdir(s, vs, err);
+    return;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->fullname);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
+{
+
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, ssize_t err)
+{
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    /*
+     * Read the xattr value
+     */
+    vs->xattr_fidp->fs.xattr.len = vs->size;
+    vs->xattr_fidp->fid_type = P9_FID_XATTR;
+    vs->xattr_fidp->fs.xattr.copied_len = -1;
+    if (vs->size) {
+        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+        err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+                                &vs->name, vs->xattr_fidp->fs.xattr.value,
+                                vs->xattr_fidp->fs.xattr.len);
+    }
+    v9fs_post_xattr_getvalue(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_post_lxattr_getvalue(V9fsState *s,
+                                      V9fsXattrState *vs, int err)
+{
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_post_lxattr_check(V9fsState *s,
+                                   V9fsXattrState *vs, ssize_t err)
+{
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    /*
+     * Read the xattr value
+     */
+    vs->xattr_fidp->fs.xattr.len = vs->size;
+    vs->xattr_fidp->fid_type = P9_FID_XATTR;
+    vs->xattr_fidp->fs.xattr.copied_len = -1;
+    if (vs->size) {
+        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+        err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+                                 vs->xattr_fidp->fs.xattr.value,
+                                 vs->xattr_fidp->fs.xattr.len);
+    }
+    v9fs_post_lxattr_getvalue(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_xattrwalk(V9fsState *s, V9fsPDU *pdu)
+{
+    ssize_t err = 0;
+    V9fsXattrState *vs;
+    int32_t fid, newfid;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name);
+    vs->file_fidp = lookup_fid(s, fid);
+    if (vs->file_fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    vs->xattr_fidp = alloc_fid(s, newfid);
+    if (vs->xattr_fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path);
+    if (vs->name.data[0] == 0) {
+        /*
+         * listxattr request. Get the size first
+         */
+        vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+                                      NULL, 0);
+        if (vs->size < 0) {
+            err = vs->size;
+        }
+        v9fs_post_lxattr_check(s, vs, err);
+        return;
+    } else {
+        /*
+         * specific xattr fid. We check for xattr
+         * presence also collect the xattr size
+         */
+        vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+                                     &vs->name, NULL, 0);
+        if (vs->size < 0) {
+            err = vs->size;
+        }
+        v9fs_post_xattr_check(s, vs, err);
+        return;
+    }
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_xattrcreate(V9fsState *s, V9fsPDU *pdu)
+{
+    int flags;
+    int32_t fid;
+    ssize_t err = 0;
+    V9fsXattrState *vs;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dsqd",
+                  &fid, &vs->name, &vs->size, &flags);
+
+    vs->file_fidp = lookup_fid(s, fid);
+    if (vs->file_fidp == NULL) {
+        err = -EINVAL;
+        goto out;
+    }
+
+    /* Make the file fid point to xattr */
+    vs->xattr_fidp = vs->file_fidp;
+    vs->xattr_fidp->fid_type = P9_FID_XATTR;
+    vs->xattr_fidp->fs.xattr.copied_len = 0;
+    vs->xattr_fidp->fs.xattr.len = vs->size;
+    vs->xattr_fidp->fs.xattr.flags = flags;
+    v9fs_string_init(&vs->xattr_fidp->fs.xattr.name);
+    v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name);
+    if (vs->size)
+        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+    else
+        vs->xattr_fidp->fs.xattr.value = NULL;
+
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_readlink_post_readlink(V9fsState *s, V9fsReadLinkState *vs,
+                                                    int err)
+{
+    if (err < 0) {
+        err = -errno;
+        goto out;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "s", &vs->target);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->target);
+    qemu_free(vs);
+}
+
+static void v9fs_readlink(V9fsState *s, V9fsPDU *pdu)
+{
+    int32_t fid;
+    V9fsReadLinkState *vs;
+    int err = 0;
+    V9fsFidState *fidp;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
+
+    fidp = lookup_fid(s, fid);
+    if (fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    v9fs_string_init(&vs->target);
+    err = v9fs_do_readlink(s, &fidp->path, &vs->target);
+    v9fs_readlink_post_readlink(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
+typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
+
+static pdu_handler_t *pdu_handlers[] = {
+    [P9_TREADDIR] = v9fs_readdir,
+    [P9_TSTATFS] = v9fs_statfs,
+    [P9_TGETATTR] = v9fs_getattr,
+    [P9_TSETATTR] = v9fs_setattr,
+    [P9_TXATTRWALK] = v9fs_xattrwalk,
+    [P9_TXATTRCREATE] = v9fs_xattrcreate,
+    [P9_TMKNOD] = v9fs_mknod,
+    [P9_TRENAME] = v9fs_rename,
+    [P9_TLOCK] = v9fs_lock,
+    [P9_TGETLOCK] = v9fs_getlock,
+    [P9_TREADLINK] = v9fs_readlink,
+    [P9_TMKDIR] = v9fs_mkdir,
+    [P9_TVERSION] = v9fs_version,
+    [P9_TLOPEN] = v9fs_open,
+    [P9_TATTACH] = v9fs_attach,
+    [P9_TSTAT] = v9fs_stat,
+    [P9_TWALK] = v9fs_walk,
+    [P9_TCLUNK] = v9fs_clunk,
+    [P9_TFSYNC] = v9fs_fsync,
+    [P9_TOPEN] = v9fs_open,
+    [P9_TREAD] = v9fs_read,
+#if 0
+    [P9_TAUTH] = v9fs_auth,
+#endif
+    [P9_TFLUSH] = v9fs_flush,
+    [P9_TLINK] = v9fs_link,
+    [P9_TSYMLINK] = v9fs_symlink,
+    [P9_TCREATE] = v9fs_create,
+    [P9_TLCREATE] = v9fs_lcreate,
+    [P9_TWRITE] = v9fs_write,
+    [P9_TWSTAT] = v9fs_wstat,
+    [P9_TREMOVE] = v9fs_remove,
+};
+
+static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
+{
+    pdu_handler_t *handler;
+
+    if (debug_9p_pdu) {
+        pprint_pdu(pdu);
+    }
+
+    BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
+
+    handler = pdu_handlers[pdu->id];
+    BUG_ON(handler == NULL);
+
+    handler(s, pdu);
+}
+
+static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+    V9fsState *s = (V9fsState *)vdev;
+    V9fsPDU *pdu;
+    ssize_t len;
+
+    while ((pdu = alloc_pdu(s)) &&
+            (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
+        uint8_t *ptr;
+
+        BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
+        BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
+
+        ptr = pdu->elem.out_sg[0].iov_base;
+
+        memcpy(&pdu->size, ptr, 4);
+        pdu->id = ptr[4];
+        memcpy(&pdu->tag, ptr + 5, 2);
+
+        submit_pdu(s, pdu);
+    }
+
+    free_pdu(s, pdu);
+}
+
+static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
+{
+    features |= 1 << VIRTIO_9P_MOUNT_TAG;
+    return features;
+}
+
+static V9fsState *to_virtio_9p(VirtIODevice *vdev)
+{
+    return (V9fsState *)vdev;
+}
+
+static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+    struct virtio_9p_config *cfg;
+    V9fsState *s = to_virtio_9p(vdev);
+
+    cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
+                        s->tag_len);
+    stw_raw(&cfg->tag_len, s->tag_len);
+    memcpy(cfg->tag, s->tag, s->tag_len);
+    memcpy(config, cfg, s->config_size);
+    qemu_free(cfg);
+}
+
+VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
+ {
+    V9fsState *s;
+    int i, len;
+    struct stat stat;
+    FsTypeEntry *fse;
+
+
+    s = (V9fsState *)virtio_common_init("virtio-9p",
+                                    VIRTIO_ID_9P,
+                                    sizeof(struct virtio_9p_config)+
+                                    MAX_TAG_LEN,
+                                    sizeof(V9fsState));
+
+    /* initialize pdu allocator */
+    QLIST_INIT(&s->free_list);
+    for (i = 0; i < (MAX_REQ - 1); i++) {
+	QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
+    }
+
+    s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
+
+    fse = get_fsdev_fsentry(conf->fsdev_id);
+
+    if (!fse) {
+        /* We don't have a fsdev identified by fsdev_id */
+        fprintf(stderr, "Virtio-9p device couldn't find fsdev with the "
+                "id = %s\n", conf->fsdev_id ? conf->fsdev_id : "NULL");
+        exit(1);
+    }
+
+    if (!fse->path || !conf->tag) {
+        /* we haven't specified a mount_tag or the path */
+        fprintf(stderr, "fsdev with id %s needs path "
+                "and Virtio-9p device needs mount_tag arguments\n",
+                conf->fsdev_id);
+        exit(1);
+    }
+
+    if (!strcmp(fse->security_model, "passthrough")) {
+        /* Files on the Fileserver set to client user credentials */
+        s->ctx.fs_sm = SM_PASSTHROUGH;
+        s->ctx.xops = passthrough_xattr_ops;
+    } else if (!strcmp(fse->security_model, "mapped")) {
+        /* Files on the fileserver are set to QEMU credentials.
+         * Client user credentials are saved in extended attributes.
+         */
+        s->ctx.fs_sm = SM_MAPPED;
+        s->ctx.xops = mapped_xattr_ops;
+    } else if (!strcmp(fse->security_model, "none")) {
+        /*
+         * Files on the fileserver are set to QEMU credentials.
+         */
+        s->ctx.fs_sm = SM_NONE;
+        s->ctx.xops = none_xattr_ops;
+    } else {
+        fprintf(stderr, "Default to security_model=none. You may want"
+                " enable advanced security model using "
+                "security option:\n\t security_model=passthrough \n\t "
+                "security_model=mapped\n");
+        s->ctx.fs_sm = SM_NONE;
+        s->ctx.xops = none_xattr_ops;
+    }
+
+    if (lstat(fse->path, &stat)) {
+        fprintf(stderr, "share path %s does not exist\n", fse->path);
+        exit(1);
+    } else if (!S_ISDIR(stat.st_mode)) {
+        fprintf(stderr, "share path %s is not a directory \n", fse->path);
+        exit(1);
+    }
+
+    s->ctx.fs_root = qemu_strdup(fse->path);
+    len = strlen(conf->tag);
+    if (len > MAX_TAG_LEN) {
+        len = MAX_TAG_LEN;
+    }
+    /* s->tag is non-NULL terminated string */
+    s->tag = qemu_malloc(len);
+    memcpy(s->tag, conf->tag, len);
+    s->tag_len = len;
+    s->ctx.uid = -1;
+
+    s->ops = fse->ops;
+    s->vdev.get_features = virtio_9p_get_features;
+    s->config_size = sizeof(struct virtio_9p_config) +
+                        s->tag_len;
+    s->vdev.get_config = virtio_9p_get_config;
+
+    return &s->vdev;
+}
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
new file mode 100644
index 0000000..95e4977
--- /dev/null
+++ b/hw/9pfs/virtio-9p.h
@@ -0,0 +1,507 @@
+#ifndef _QEMU_VIRTIO_9P_H
+#define _QEMU_VIRTIO_9P_H
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <utime.h>
+
+#include "fsdev/file-op-9p.h"
+
+/* The feature bitmap for virtio 9P */
+/* The mount point is specified in a config variable */
+#define VIRTIO_9P_MOUNT_TAG 0
+
+enum {
+    P9_TLERROR = 6,
+    P9_RLERROR,
+    P9_TSTATFS = 8,
+    P9_RSTATFS,
+    P9_TLOPEN = 12,
+    P9_RLOPEN,
+    P9_TLCREATE = 14,
+    P9_RLCREATE,
+    P9_TSYMLINK = 16,
+    P9_RSYMLINK,
+    P9_TMKNOD = 18,
+    P9_RMKNOD,
+    P9_TRENAME = 20,
+    P9_RRENAME,
+    P9_TREADLINK = 22,
+    P9_RREADLINK,
+    P9_TGETATTR = 24,
+    P9_RGETATTR,
+    P9_TSETATTR = 26,
+    P9_RSETATTR,
+    P9_TXATTRWALK = 30,
+    P9_RXATTRWALK,
+    P9_TXATTRCREATE = 32,
+    P9_RXATTRCREATE,
+    P9_TREADDIR = 40,
+    P9_RREADDIR,
+    P9_TFSYNC = 50,
+    P9_RFSYNC,
+    P9_TLOCK = 52,
+    P9_RLOCK,
+    P9_TGETLOCK = 54,
+    P9_RGETLOCK,
+    P9_TLINK = 70,
+    P9_RLINK,
+    P9_TMKDIR = 72,
+    P9_RMKDIR,
+    P9_TVERSION = 100,
+    P9_RVERSION,
+    P9_TAUTH = 102,
+    P9_RAUTH,
+    P9_TATTACH = 104,
+    P9_RATTACH,
+    P9_TERROR = 106,
+    P9_RERROR,
+    P9_TFLUSH = 108,
+    P9_RFLUSH,
+    P9_TWALK = 110,
+    P9_RWALK,
+    P9_TOPEN = 112,
+    P9_ROPEN,
+    P9_TCREATE = 114,
+    P9_RCREATE,
+    P9_TREAD = 116,
+    P9_RREAD,
+    P9_TWRITE = 118,
+    P9_RWRITE,
+    P9_TCLUNK = 120,
+    P9_RCLUNK,
+    P9_TREMOVE = 122,
+    P9_RREMOVE,
+    P9_TSTAT = 124,
+    P9_RSTAT,
+    P9_TWSTAT = 126,
+    P9_RWSTAT,
+};
+
+
+/* qid.types */
+enum {
+    P9_QTDIR = 0x80,
+    P9_QTAPPEND = 0x40,
+    P9_QTEXCL = 0x20,
+    P9_QTMOUNT = 0x10,
+    P9_QTAUTH = 0x08,
+    P9_QTTMP = 0x04,
+    P9_QTSYMLINK = 0x02,
+    P9_QTLINK = 0x01,
+    P9_QTFILE = 0x00,
+};
+
+enum p9_proto_version {
+    V9FS_PROTO_2000U = 0x01,
+    V9FS_PROTO_2000L = 0x02,
+};
+
+#define P9_NOTAG    (u16)(~0)
+#define P9_NOFID    (u32)(~0)
+#define P9_MAXWELEM 16
+
+/*
+ * ample room for Twrite/Rread header
+ * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
+ */
+#define P9_IOHDRSZ 24
+
+typedef struct V9fsPDU V9fsPDU;
+
+struct V9fsPDU
+{
+    uint32_t size;
+    uint16_t tag;
+    uint8_t id;
+    VirtQueueElement elem;
+    QLIST_ENTRY(V9fsPDU) next;
+};
+
+
+/* FIXME
+ * 1) change user needs to set groups and stuff
+ */
+
+/* from Linux's linux/virtio_9p.h */
+
+/* The ID for virtio console */
+#define VIRTIO_ID_9P    9
+#define MAX_REQ         128
+#define MAX_TAG_LEN     32
+
+#define BUG_ON(cond) assert(!(cond))
+
+typedef struct V9fsFidState V9fsFidState;
+
+typedef struct V9fsString
+{
+    int16_t size;
+    char *data;
+} V9fsString;
+
+typedef struct V9fsQID
+{
+    int8_t type;
+    int32_t version;
+    int64_t path;
+} V9fsQID;
+
+typedef struct V9fsStat
+{
+    int16_t size;
+    int16_t type;
+    int32_t dev;
+    V9fsQID qid;
+    int32_t mode;
+    int32_t atime;
+    int32_t mtime;
+    int64_t length;
+    V9fsString name;
+    V9fsString uid;
+    V9fsString gid;
+    V9fsString muid;
+    /* 9p2000.u */
+    V9fsString extension;
+   int32_t n_uid;
+    int32_t n_gid;
+    int32_t n_muid;
+} V9fsStat;
+
+enum {
+    P9_FID_NONE = 0,
+    P9_FID_FILE,
+    P9_FID_DIR,
+    P9_FID_XATTR,
+};
+
+typedef struct V9fsXattr
+{
+    int64_t copied_len;
+    int64_t len;
+    void *value;
+    V9fsString name;
+    int flags;
+} V9fsXattr;
+
+struct V9fsFidState
+{
+    int fid_type;
+    int32_t fid;
+    V9fsString path;
+    union {
+	int fd;
+	DIR *dir;
+	V9fsXattr xattr;
+    } fs;
+    uid_t uid;
+    V9fsFidState *next;
+};
+
+typedef struct V9fsState
+{
+    VirtIODevice vdev;
+    VirtQueue *vq;
+    V9fsPDU pdus[MAX_REQ];
+    QLIST_HEAD(, V9fsPDU) free_list;
+    V9fsFidState *fid_list;
+    FileOperations *ops;
+    FsContext ctx;
+    uint16_t tag_len;
+    uint8_t *tag;
+    size_t config_size;
+    enum p9_proto_version proto_version;
+    int32_t msize;
+} V9fsState;
+
+typedef struct V9fsCreateState {
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsFidState *fidp;
+    V9fsQID qid;
+    int32_t perm;
+    int8_t mode;
+    struct stat stbuf;
+    V9fsString name;
+    V9fsString extension;
+    V9fsString fullname;
+    int iounit;
+} V9fsCreateState;
+
+typedef struct V9fsLcreateState {
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsFidState *fidp;
+    V9fsQID qid;
+    int32_t iounit;
+    struct stat stbuf;
+    V9fsString name;
+    V9fsString fullname;
+} V9fsLcreateState;
+
+typedef struct V9fsStatState {
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsStat v9stat;
+    V9fsFidState *fidp;
+    struct stat stbuf;
+} V9fsStatState;
+
+typedef struct V9fsStatDotl {
+    uint64_t st_result_mask;
+    V9fsQID qid;
+    uint32_t st_mode;
+    uint32_t st_uid;
+    uint32_t st_gid;
+    uint64_t st_nlink;
+    uint64_t st_rdev;
+    uint64_t st_size;
+    uint64_t st_blksize;
+    uint64_t st_blocks;
+    uint64_t st_atime_sec;
+    uint64_t st_atime_nsec;
+    uint64_t st_mtime_sec;
+    uint64_t st_mtime_nsec;
+    uint64_t st_ctime_sec;
+    uint64_t st_ctime_nsec;
+    uint64_t st_btime_sec;
+    uint64_t st_btime_nsec;
+    uint64_t st_gen;
+    uint64_t st_data_version;
+} V9fsStatDotl;
+
+typedef struct V9fsStatStateDotl {
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsStatDotl v9stat_dotl;
+    struct stat stbuf;
+} V9fsStatStateDotl;
+
+
+typedef struct V9fsWalkState {
+    V9fsPDU *pdu;
+    size_t offset;
+    int16_t nwnames;
+    int name_idx;
+    V9fsQID *qids;
+    V9fsFidState *fidp;
+    V9fsFidState *newfidp;
+    V9fsString path;
+    V9fsString *wnames;
+    struct stat stbuf;
+} V9fsWalkState;
+
+typedef struct V9fsOpenState {
+    V9fsPDU *pdu;
+    size_t offset;
+    int32_t mode;
+    V9fsFidState *fidp;
+    V9fsQID qid;
+    struct stat stbuf;
+    int iounit;
+} V9fsOpenState;
+
+typedef struct V9fsReadState {
+    V9fsPDU *pdu;
+    size_t offset;
+    int32_t count;
+    int32_t total;
+    int64_t off;
+    V9fsFidState *fidp;
+    struct iovec iov[128]; /* FIXME: bad, bad, bad */
+    struct iovec *sg;
+    off_t dir_pos;
+    struct dirent *dent;
+    struct stat stbuf;
+    V9fsString name;
+    V9fsStat v9stat;
+    int32_t len;
+    int32_t cnt;
+    int32_t max_count;
+} V9fsReadState;
+
+typedef struct V9fsWriteState {
+    V9fsPDU *pdu;
+    size_t offset;
+    int32_t len;
+    int32_t count;
+    int32_t total;
+    int64_t off;
+    V9fsFidState *fidp;
+    struct iovec iov[128]; /* FIXME: bad, bad, bad */
+    struct iovec *sg;
+    int cnt;
+} V9fsWriteState;
+
+typedef struct V9fsRemoveState {
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsFidState *fidp;
+} V9fsRemoveState;
+
+typedef struct V9fsWstatState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    int16_t unused;
+    V9fsStat v9stat;
+    V9fsFidState *fidp;
+    struct stat stbuf;
+} V9fsWstatState;
+
+typedef struct V9fsSymlinkState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsString name;
+    V9fsString symname;
+    V9fsString fullname;
+    V9fsFidState *dfidp;
+    V9fsQID qid;
+    struct stat stbuf;
+} V9fsSymlinkState;
+
+typedef struct V9fsIattr
+{
+    int32_t valid;
+    int32_t mode;
+    int32_t uid;
+    int32_t gid;
+    int64_t size;
+    int64_t atime_sec;
+    int64_t atime_nsec;
+    int64_t mtime_sec;
+    int64_t mtime_nsec;
+} V9fsIattr;
+
+typedef struct V9fsSetattrState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsIattr v9iattr;
+    V9fsFidState *fidp;
+} V9fsSetattrState;
+
+struct virtio_9p_config
+{
+    /* number of characters in tag */
+    uint16_t tag_len;
+    /* Variable size tag name */
+    uint8_t tag[0];
+} __attribute__((packed));
+
+typedef struct V9fsStatfs
+{
+    uint32_t f_type;
+    uint32_t f_bsize;
+    uint64_t f_blocks;
+    uint64_t f_bfree;
+    uint64_t f_bavail;
+    uint64_t f_files;
+    uint64_t f_ffree;
+    uint64_t fsid_val;
+    uint32_t f_namelen;
+} V9fsStatfs;
+
+typedef struct V9fsStatfsState {
+    V9fsPDU *pdu;
+    size_t offset;
+    int32_t fid;
+    V9fsStatfs v9statfs;
+    V9fsFidState *fidp;
+    struct statfs stbuf;
+} V9fsStatfsState;
+
+typedef struct V9fsMkState {
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsQID qid;
+    struct stat stbuf;
+    V9fsString name;
+    V9fsString fullname;
+} V9fsMkState;
+
+typedef struct V9fsRenameState {
+    V9fsPDU *pdu;
+    V9fsFidState *fidp;
+    size_t offset;
+    int32_t newdirfid;
+    V9fsString name;
+} V9fsRenameState;
+
+typedef struct V9fsXattrState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsFidState *file_fidp;
+    V9fsFidState *xattr_fidp;
+    V9fsString name;
+    int64_t size;
+    int flags;
+    void *value;
+} V9fsXattrState;
+
+#define P9_LOCK_SUCCESS 0
+#define P9_LOCK_BLOCKED 1
+#define P9_LOCK_ERROR 2
+#define P9_LOCK_GRACE 3
+
+#define P9_LOCK_FLAGS_BLOCK 1
+#define P9_LOCK_FLAGS_RECLAIM 2
+
+typedef struct V9fsFlock
+{
+    uint8_t type;
+    uint32_t flags;
+    uint64_t start; /* absolute offset */
+    uint64_t length;
+    uint32_t proc_id;
+    V9fsString client_id;
+} V9fsFlock;
+
+typedef struct V9fsLockState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    int8_t status;
+    struct stat stbuf;
+    V9fsFidState *fidp;
+    V9fsFlock *flock;
+} V9fsLockState;
+
+typedef struct V9fsGetlock
+{
+    uint8_t type;
+    uint64_t start; /* absolute offset */
+    uint64_t length;
+    uint32_t proc_id;
+    V9fsString client_id;
+} V9fsGetlock;
+
+typedef struct V9fsGetlockState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    struct stat stbuf;
+    V9fsFidState *fidp;
+    V9fsGetlock *glock;
+} V9fsGetlockState;
+
+typedef struct V9fsReadLinkState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsString target;
+} V9fsReadLinkState;
+
+size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
+                      size_t offset, size_t size, int pack);
+
+static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count,
+                        size_t offset, size_t size)
+{
+    return pdu_packunpack(dst, sg, sg_count, offset, size, 0);
+}
+
+#endif
diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h
deleted file mode 100644
index 126e60e..0000000
--- a/hw/file-op-9p.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Virtio 9p
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- *  Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-#ifndef _FILEOP_H
-#define _FILEOP_H
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <utime.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <sys/vfs.h>
-#define SM_LOCAL_MODE_BITS    0600
-#define SM_LOCAL_DIR_MODE_BITS    0700
-
-typedef enum
-{
-    /*
-     * Server will try to set uid/gid.
-     * On failure ignore the error.
-     */
-    SM_NONE = 0,
-    /*
-     * uid/gid set on fileserver files
-     */
-    SM_PASSTHROUGH = 1,
-    /*
-     * uid/gid part of xattr
-     */
-    SM_MAPPED,
-} SecModel;
-
-typedef struct FsCred
-{
-    uid_t   fc_uid;
-    gid_t   fc_gid;
-    mode_t  fc_mode;
-    dev_t   fc_rdev;
-} FsCred;
-
-struct xattr_operations;
-
-typedef struct FsContext
-{
-    char *fs_root;
-    SecModel fs_sm;
-    uid_t uid;
-    struct xattr_operations **xops;
-} FsContext;
-
-void cred_init(FsCred *);
-
-typedef struct FileOperations
-{
-    int (*lstat)(FsContext *, const char *, struct stat *);
-    ssize_t (*readlink)(FsContext *, const char *, char *, size_t);
-    int (*chmod)(FsContext *, const char *, FsCred *);
-    int (*chown)(FsContext *, const char *, FsCred *);
-    int (*mknod)(FsContext *, const char *, FsCred *);
-    int (*utimensat)(FsContext *, const char *, const struct timespec *);
-    int (*remove)(FsContext *, const char *);
-    int (*symlink)(FsContext *, const char *, const char *, FsCred *);
-    int (*link)(FsContext *, const char *, const char *);
-    int (*setuid)(FsContext *, uid_t);
-    int (*close)(FsContext *, int);
-    int (*closedir)(FsContext *, DIR *);
-    DIR *(*opendir)(FsContext *, const char *);
-    int (*open)(FsContext *, const char *, int);
-    int (*open2)(FsContext *, const char *, int, FsCred *);
-    void (*rewinddir)(FsContext *, DIR *);
-    off_t (*telldir)(FsContext *, DIR *);
-    struct dirent *(*readdir)(FsContext *, DIR *);
-    void (*seekdir)(FsContext *, DIR *, off_t);
-    ssize_t (*preadv)(FsContext *, int, const struct iovec *, int, off_t);
-    ssize_t (*pwritev)(FsContext *, int, const struct iovec *, int, off_t);
-    int (*mkdir)(FsContext *, const char *, FsCred *);
-    int (*fstat)(FsContext *, int, struct stat *);
-    int (*rename)(FsContext *, const char *, const char *);
-    int (*truncate)(FsContext *, const char *, off_t);
-    int (*fsync)(FsContext *, int, int);
-    int (*statfs)(FsContext *s, const char *path, struct statfs *stbuf);
-    ssize_t (*lgetxattr)(FsContext *, const char *,
-                         const char *, void *, size_t);
-    ssize_t (*llistxattr)(FsContext *, const char *, void *, size_t);
-    int (*lsetxattr)(FsContext *, const char *,
-                     const char *, void *, size_t, int);
-    int (*lremovexattr)(FsContext *, const char *, const char *);
-    void *opaque;
-} FileOperations;
-
-static inline const char *rpath(FsContext *ctx, const char *path)
-{
-    /* FIXME: so wrong... */
-    static char buffer[4096];
-    snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
-    return buffer;
-}
-#endif
diff --git a/hw/virtio-9p-debug.c b/hw/virtio-9p-debug.c
deleted file mode 100644
index 6b18842..0000000
--- a/hw/virtio-9p-debug.c
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Virtio 9p PDU debug
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- *  Anthony Liguori   <aliguori at us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-#include "virtio.h"
-#include "pc.h"
-#include "virtio-9p.h"
-#include "virtio-9p-debug.h"
-
-#define BUG_ON(cond) assert(!(cond))
-
-static FILE *llogfile;
-
-static struct iovec *get_sg(V9fsPDU *pdu, int rx)
-{
-    if (rx) {
-        return pdu->elem.in_sg;
-    }
-    return pdu->elem.out_sg;
-}
-
-static int get_sg_count(V9fsPDU *pdu, int rx)
-{
-    if (rx) {
-        return pdu->elem.in_num;
-    }
-    return pdu->elem.out_num;
-
-}
-
-static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    size_t offset = *offsetp;
-    struct iovec *sg = get_sg(pdu, rx);
-    int8_t value;
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%x", name, value);
-    *offsetp = offset;
-}
-
-static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    int16_t value;
-
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%x", name, value);
-    *offsetp = offset;
-}
-
-static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    int32_t value;
-
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%x", name, value);
-    *offsetp = offset;
-}
-
-static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    int64_t value;
-
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%" PRIx64, name, value);
-    *offsetp = offset;
-}
-
-static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    int sg_count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    uint16_t tmp_size, size;
-    size_t result;
-    size_t copied = 0;
-    int i = 0;
-
-    /* get the size */
-    copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size));
-    BUG_ON(copied != sizeof(tmp_size));
-    size = le16_to_cpupu(&tmp_size);
-    offset += copied;
-
-    fprintf(llogfile, "%s=", name);
-    for (i = 0; size && i < sg_count; i++) {
-        size_t len;
-        if (offset >= sg[i].iov_len) {
-            /* skip this sg */
-            offset -= sg[i].iov_len;
-            continue;
-        } else {
-            len = MIN(sg[i].iov_len - offset, size);
-            result = fwrite(sg[i].iov_base + offset, 1, len, llogfile);
-            BUG_ON(result != len);
-            size -= len;
-            copied += len;
-            if (size) {
-                offset = 0;
-                continue;
-            }
-        }
-    }
-    *offsetp += copied;
-}
-
-static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    fprintf(llogfile, "%s={", name);
-    pprint_int8(pdu, rx, offsetp, "type");
-    pprint_int32(pdu, rx, offsetp, ", version");
-    pprint_int64(pdu, rx, offsetp, ", path");
-    fprintf(llogfile, "}");
-}
-
-static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    fprintf(llogfile, "%s={", name);
-    pprint_int16(pdu, rx, offsetp, "size");
-    pprint_int16(pdu, rx, offsetp, ", type");
-    pprint_int32(pdu, rx, offsetp, ", dev");
-    pprint_qid(pdu, rx, offsetp, ", qid");
-    pprint_int32(pdu, rx, offsetp, ", mode");
-    pprint_int32(pdu, rx, offsetp, ", atime");
-    pprint_int32(pdu, rx, offsetp, ", mtime");
-    pprint_int64(pdu, rx, offsetp, ", length");
-    pprint_str(pdu, rx, offsetp, ", name");
-    pprint_str(pdu, rx, offsetp, ", uid");
-    pprint_str(pdu, rx, offsetp, ", gid");
-    pprint_str(pdu, rx, offsetp, ", muid");
-    pprint_str(pdu, rx, offsetp, ", extension");
-    pprint_int32(pdu, rx, offsetp, ", uid");
-    pprint_int32(pdu, rx, offsetp, ", gid");
-    pprint_int32(pdu, rx, offsetp, ", muid");
-    fprintf(llogfile, "}");
-}
-
-static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp,
-                                                  const char *name)
-{
-    fprintf(llogfile, "%s={", name);
-    pprint_qid(pdu, rx, offsetp, "qid");
-    pprint_int32(pdu, rx, offsetp, ", st_mode");
-    pprint_int64(pdu, rx, offsetp, ", st_nlink");
-    pprint_int32(pdu, rx, offsetp, ", st_uid");
-    pprint_int32(pdu, rx, offsetp, ", st_gid");
-    pprint_int64(pdu, rx, offsetp, ", st_rdev");
-    pprint_int64(pdu, rx, offsetp, ", st_size");
-    pprint_int64(pdu, rx, offsetp, ", st_blksize");
-    pprint_int64(pdu, rx, offsetp, ", st_blocks");
-    pprint_int64(pdu, rx, offsetp, ", atime");
-    pprint_int64(pdu, rx, offsetp, ", atime_nsec");
-    pprint_int64(pdu, rx, offsetp, ", mtime");
-    pprint_int64(pdu, rx, offsetp, ", mtime_nsec");
-    pprint_int64(pdu, rx, offsetp, ", ctime");
-    pprint_int64(pdu, rx, offsetp, ", ctime_nsec");
-    fprintf(llogfile, "}");
-}
-
-
-
-static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    int sg_count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    uint16_t tmp_count, count, i;
-    size_t copied = 0;
-
-    fprintf(llogfile, "%s={", name);
-
-    /* Get the count */
-    copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
-    BUG_ON(copied != sizeof(tmp_count));
-    count = le16_to_cpupu(&tmp_count);
-    offset += copied;
-
-    for (i = 0; i < count; i++) {
-        char str[512];
-        if (i) {
-            fprintf(llogfile, ", ");
-        }
-        snprintf(str, sizeof(str), "[%d]", i);
-        pprint_str(pdu, rx, &offset, str);
-    }
-
-    fprintf(llogfile, "}");
-
-    *offsetp = offset;
-}
-
-static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    int sg_count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    uint16_t tmp_count, count, i;
-    size_t copied = 0;
-
-    fprintf(llogfile, "%s={", name);
-
-    copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
-    BUG_ON(copied != sizeof(tmp_count));
-    count = le16_to_cpupu(&tmp_count);
-    offset += copied;
-
-    for (i = 0; i < count; i++) {
-        char str[512];
-        if (i) {
-            fprintf(llogfile, ", ");
-        }
-        snprintf(str, sizeof(str), "[%d]", i);
-        pprint_qid(pdu, rx, &offset, str);
-    }
-
-    fprintf(llogfile, "}");
-
-    *offsetp = offset;
-}
-
-static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    struct iovec *sg = get_sg(pdu, rx);
-    unsigned int count;
-    int i;
-
-    if (rx) {
-        count = pdu->elem.in_num;
-    } else {
-        count = pdu->elem.out_num;
-    }
-
-    fprintf(llogfile, "%s={", name);
-    for (i = 0; i < count; i++) {
-        if (i) {
-            fprintf(llogfile, ", ");
-        }
-        fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len);
-    }
-    fprintf(llogfile, "}");
-}
-
-/* FIXME: read from a directory fid returns serialized stat_t's */
-#ifdef DEBUG_DATA
-static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    unsigned int count;
-    int32_t size;
-    int total, i, j;
-    ssize_t len;
-
-    if (rx) {
-        count = pdu->elem.in_num;
-    } else
-        count = pdu->elem.out_num;
-    }
-
-    BUG_ON((offset + sizeof(size)) > sg[0].iov_len);
-
-    memcpy(&size, sg[0].iov_base + offset, sizeof(size));
-    offset += sizeof(size);
-
-    fprintf(llogfile, "size: %x\n", size);
-
-    sg[0].iov_base += 11; /* skip header */
-    sg[0].iov_len -= 11;
-
-    total = 0;
-    for (i = 0; i < count; i++) {
-        total += sg[i].iov_len;
-        if (total >= size) {
-            /* trim sg list so writev does the right thing */
-            sg[i].iov_len -= (total - size);
-            i++;
-            break;
-        }
-    }
-
-    fprintf(llogfile, "%s={\"", name);
-    fflush(llogfile);
-    for (j = 0; j < i; j++) {
-        if (j) {
-            fprintf(llogfile, "\", \"");
-            fflush(llogfile);
-        }
-
-        do {
-            len = writev(fileno(llogfile), &sg[j], 1);
-        } while (len == -1 && errno == EINTR);
-        fprintf(llogfile, "len == %ld: %m\n", len);
-        BUG_ON(len != sg[j].iov_len);
-    }
-    fprintf(llogfile, "\"}");
-
-    sg[0].iov_base -= 11;
-    sg[0].iov_len += 11;
-
-}
-#endif
-
-void pprint_pdu(V9fsPDU *pdu)
-{
-    size_t offset = 7;
-
-    if (llogfile == NULL) {
-        llogfile = fopen("/tmp/pdu.log", "w");
-    }
-
-    BUG_ON(!llogfile);
-
-    switch (pdu->id) {
-    case P9_TREADDIR:
-        fprintf(llogfile, "TREADDIR: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int64(pdu, 0, &offset, ", initial offset");
-        pprint_int32(pdu, 0, &offset, ", max count");
-        break;
-    case P9_RREADDIR:
-        fprintf(llogfile, "RREADDIR: (");
-        pprint_int32(pdu, 1, &offset, "count");
-#ifdef DEBUG_DATA
-        pprint_data(pdu, 1, &offset, ", data");
-#endif
-        break;
-    case P9_TMKDIR:
-        fprintf(llogfile, "TMKDIR: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, "name");
-        pprint_int32(pdu, 0, &offset, "mode");
-        pprint_int32(pdu, 0, &offset, "gid");
-        break;
-    case P9_RMKDIR:
-        fprintf(llogfile, "RMKDIR: (");
-        pprint_qid(pdu, 0, &offset, "qid");
-        break;
-    case P9_TVERSION:
-        fprintf(llogfile, "TVERSION: (");
-        pprint_int32(pdu, 0, &offset, "msize");
-        pprint_str(pdu, 0, &offset, ", version");
-        break;
-    case P9_RVERSION:
-        fprintf(llogfile, "RVERSION: (");
-        pprint_int32(pdu, 1, &offset, "msize");
-        pprint_str(pdu, 1, &offset, ", version");
-        break;
-    case P9_TGETATTR:
-        fprintf(llogfile, "TGETATTR: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RGETATTR:
-        fprintf(llogfile, "RGETATTR: (");
-        pprint_stat_dotl(pdu, 1, &offset, "getattr");
-        break;
-    case P9_TAUTH:
-        fprintf(llogfile, "TAUTH: (");
-        pprint_int32(pdu, 0, &offset, "afid");
-        pprint_str(pdu, 0, &offset, ", uname");
-        pprint_str(pdu, 0, &offset, ", aname");
-        pprint_int32(pdu, 0, &offset, ", n_uname");
-        break;
-    case P9_RAUTH:
-        fprintf(llogfile, "RAUTH: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        break;
-    case P9_TATTACH:
-        fprintf(llogfile, "TATTACH: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int32(pdu, 0, &offset, ", afid");
-        pprint_str(pdu, 0, &offset, ", uname");
-        pprint_str(pdu, 0, &offset, ", aname");
-        pprint_int32(pdu, 0, &offset, ", n_uname");
-        break;
-    case P9_RATTACH:
-        fprintf(llogfile, "RATTACH: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        break;
-    case P9_TERROR:
-        fprintf(llogfile, "TERROR: (");
-        break;
-    case P9_RERROR:
-        fprintf(llogfile, "RERROR: (");
-        pprint_str(pdu, 1, &offset, "ename");
-        pprint_int32(pdu, 1, &offset, ", ecode");
-        break;
-    case P9_TFLUSH:
-        fprintf(llogfile, "TFLUSH: (");
-        pprint_int16(pdu, 0, &offset, "oldtag");
-        break;
-    case P9_RFLUSH:
-        fprintf(llogfile, "RFLUSH: (");
-        break;
-    case P9_TWALK:
-        fprintf(llogfile, "TWALK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int32(pdu, 0, &offset, ", newfid");
-        pprint_strs(pdu, 0, &offset, ", wnames");
-        break;
-    case P9_RWALK:
-        fprintf(llogfile, "RWALK: (");
-        pprint_qids(pdu, 1, &offset, "wqids");
-        break;
-    case P9_TOPEN:
-        fprintf(llogfile, "TOPEN: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int8(pdu, 0, &offset, ", mode");
-        break;
-    case P9_ROPEN:
-        fprintf(llogfile, "ROPEN: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        pprint_int32(pdu, 1, &offset, ", iounit");
-        break;
-    case P9_TCREATE:
-        fprintf(llogfile, "TCREATE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_int32(pdu, 0, &offset, ", perm");
-        pprint_int8(pdu, 0, &offset, ", mode");
-        pprint_str(pdu, 0, &offset, ", extension");
-        break;
-    case P9_RCREATE:
-        fprintf(llogfile, "RCREATE: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        pprint_int32(pdu, 1, &offset, ", iounit");
-        break;
-    case P9_TSYMLINK:
-        fprintf(llogfile, "TSYMLINK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_str(pdu, 0, &offset, ", symname");
-        pprint_int32(pdu, 0, &offset, ", gid");
-        break;
-    case P9_RSYMLINK:
-        fprintf(llogfile, "RSYMLINK: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        break;
-    case P9_TLCREATE:
-        fprintf(llogfile, "TLCREATE: (");
-        pprint_int32(pdu, 0, &offset, "dfid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_int32(pdu, 0, &offset, ", flags");
-        pprint_int32(pdu, 0, &offset, ", mode");
-        pprint_int32(pdu, 0, &offset, ", gid");
-        break;
-    case P9_RLCREATE:
-        fprintf(llogfile, "RLCREATE: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        pprint_int32(pdu, 1, &offset, ", iounit");
-        break;
-    case P9_TMKNOD:
-	fprintf(llogfile, "TMKNOD: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, "name");
-        pprint_int32(pdu, 0, &offset, "mode");
-        pprint_int32(pdu, 0, &offset, "major");
-        pprint_int32(pdu, 0, &offset, "minor");
-        pprint_int32(pdu, 0, &offset, "gid");
-        break;
-    case P9_RMKNOD:
-        fprintf(llogfile, "RMKNOD: )");
-        pprint_qid(pdu, 0, &offset, "qid");
-        break;
-    case P9_TREADLINK:
-	fprintf(llogfile, "TREADLINK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RREADLINK:
-	fprintf(llogfile, "RREADLINK: (");
-        pprint_str(pdu, 0, &offset, "target");
-        break;
-    case P9_TREAD:
-        fprintf(llogfile, "TREAD: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int64(pdu, 0, &offset, ", offset");
-        pprint_int32(pdu, 0, &offset, ", count");
-        pprint_sg(pdu, 0, &offset, ", sg");
-        break;
-    case P9_RREAD:
-        fprintf(llogfile, "RREAD: (");
-        pprint_int32(pdu, 1, &offset, "count");
-        pprint_sg(pdu, 1, &offset, ", sg");
-        offset = 7;
-#ifdef DEBUG_DATA
-        pprint_data(pdu, 1, &offset, ", data");
-#endif
-        break;
-    case P9_TWRITE:
-        fprintf(llogfile, "TWRITE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int64(pdu, 0, &offset, ", offset");
-        pprint_int32(pdu, 0, &offset, ", count");
-        break;
-    case P9_RWRITE:
-        fprintf(llogfile, "RWRITE: (");
-        pprint_int32(pdu, 1, &offset, "count");
-        break;
-    case P9_TCLUNK:
-        fprintf(llogfile, "TCLUNK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RCLUNK:
-        fprintf(llogfile, "RCLUNK: (");
-        break;
-    case P9_TFSYNC:
-        fprintf(llogfile, "TFSYNC: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RFSYNC:
-        fprintf(llogfile, "RFSYNC: (");
-        break;
-    case P9_TLINK:
-        fprintf(llogfile, "TLINK: (");
-        pprint_int32(pdu, 0, &offset, "dfid");
-        pprint_int32(pdu, 0, &offset, ", fid");
-        pprint_str(pdu, 0, &offset, ", newpath");
-        break;
-    case P9_RLINK:
-        fprintf(llogfile, "RLINK: (");
-        break;
-    case P9_TREMOVE:
-        fprintf(llogfile, "TREMOVE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RREMOVE:
-        fprintf(llogfile, "RREMOVE: (");
-        break;
-    case P9_TSTAT:
-        fprintf(llogfile, "TSTAT: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RSTAT:
-        fprintf(llogfile, "RSTAT: (");
-        offset += 2; /* ignored */
-        pprint_stat(pdu, 1, &offset, "stat");
-        break;
-    case P9_TWSTAT:
-        fprintf(llogfile, "TWSTAT: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        offset += 2; /* ignored */
-        pprint_stat(pdu, 0, &offset, ", stat");
-        break;
-    case P9_RWSTAT:
-        fprintf(llogfile, "RWSTAT: (");
-        break;
-    case P9_TXATTRWALK:
-        fprintf(llogfile, "TXATTRWALK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int32(pdu, 0, &offset, ", newfid");
-        pprint_str(pdu, 0, &offset, ", xattr name");
-        break;
-    case P9_RXATTRWALK:
-        fprintf(llogfile, "RXATTRWALK: (");
-        pprint_int64(pdu, 1, &offset, "xattrsize");
-    case P9_TXATTRCREATE:
-        fprintf(llogfile, "TXATTRCREATE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_int64(pdu, 0, &offset, ", xattrsize");
-        pprint_int32(pdu, 0, &offset, ", flags");
-        break;
-    case P9_RXATTRCREATE:
-        fprintf(llogfile, "RXATTRCREATE: (");
-        break;
-    case P9_TLOCK:
-        fprintf(llogfile, "TLOCK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int8(pdu, 0, &offset, ", type");
-        pprint_int32(pdu, 0, &offset, ", flags");
-        pprint_int64(pdu, 0, &offset, ", start");
-        pprint_int64(pdu, 0, &offset, ", length");
-        pprint_int32(pdu, 0, &offset, ", proc_id");
-        pprint_str(pdu, 0, &offset, ", client_id");
-        break;
-    case P9_RLOCK:
-        fprintf(llogfile, "RLOCK: (");
-        pprint_int8(pdu, 0, &offset, "status");
-        break;
-    case P9_TGETLOCK:
-        fprintf(llogfile, "TGETLOCK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int8(pdu, 0, &offset, ", type");
-        pprint_int64(pdu, 0, &offset, ", start");
-        pprint_int64(pdu, 0, &offset, ", length");
-        pprint_int32(pdu, 0, &offset, ", proc_id");
-        pprint_str(pdu, 0, &offset, ", client_id");
-        break;
-    case P9_RGETLOCK:
-        fprintf(llogfile, "RGETLOCK: (");
-        pprint_int8(pdu, 0, &offset, "type");
-        pprint_int64(pdu, 0, &offset, ", start");
-        pprint_int64(pdu, 0, &offset, ", length");
-        pprint_int32(pdu, 0, &offset, ", proc_id");
-        pprint_str(pdu, 0, &offset, ", client_id");
-        break;
-    default:
-        fprintf(llogfile, "unknown(%d): (", pdu->id);
-        break;
-    }
-
-    fprintf(llogfile, ")\n");
-    /* Flush the log message out */
-    fflush(llogfile);
-}
diff --git a/hw/virtio-9p-debug.h b/hw/virtio-9p-debug.h
deleted file mode 100644
index d9a2491..0000000
--- a/hw/virtio-9p-debug.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _QEMU_VIRTIO_9P_DEBUG_H
-#define _QEMU_VIRTIO_9P_DEBUG_H
-
-void pprint_pdu(V9fsPDU *pdu);
-
-#endif
diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
deleted file mode 100644
index a8e7525..0000000
--- a/hw/virtio-9p-local.c
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * Virtio 9p Posix callback
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- *  Anthony Liguori   <aliguori at us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-#include "virtio.h"
-#include "virtio-9p.h"
-#include "virtio-9p-xattr.h"
-#include <arpa/inet.h>
-#include <pwd.h>
-#include <grp.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <attr/xattr.h>
-
-
-static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
-{
-    int err;
-    err =  lstat(rpath(fs_ctx, path), stbuf);
-    if (err) {
-        return err;
-    }
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        /* Actual credentials are part of extended attrs */
-        uid_t tmp_uid;
-        gid_t tmp_gid;
-        mode_t tmp_mode;
-        dev_t tmp_dev;
-        if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid,
-                    sizeof(uid_t)) > 0) {
-            stbuf->st_uid = tmp_uid;
-        }
-        if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid,
-                    sizeof(gid_t)) > 0) {
-            stbuf->st_gid = tmp_gid;
-        }
-        if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode,
-                    sizeof(mode_t)) > 0) {
-            stbuf->st_mode = tmp_mode;
-        }
-        if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev,
-                        sizeof(dev_t)) > 0) {
-                stbuf->st_rdev = tmp_dev;
-        }
-    }
-    return err;
-}
-
-static int local_set_xattr(const char *path, FsCred *credp)
-{
-    int err;
-    if (credp->fc_uid != -1) {
-        err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
-                0);
-        if (err) {
-            return err;
-        }
-    }
-    if (credp->fc_gid != -1) {
-        err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
-                0);
-        if (err) {
-            return err;
-        }
-    }
-    if (credp->fc_mode != -1) {
-        err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
-                sizeof(mode_t), 0);
-        if (err) {
-            return err;
-        }
-    }
-    if (credp->fc_rdev != -1) {
-        err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
-                sizeof(dev_t), 0);
-        if (err) {
-            return err;
-        }
-    }
-    return 0;
-}
-
-static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
-        FsCred *credp)
-{
-    if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
-        return -1;
-    }
-    if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
-        /*
-         * If we fail to change ownership and if we are
-         * using security model none. Ignore the error
-         */
-        if (fs_ctx->fs_sm != SM_NONE) {
-            return -1;
-        }
-    }
-    return 0;
-}
-
-static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
-        char *buf, size_t bufsz)
-{
-    ssize_t tsize = -1;
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        int fd;
-        fd = open(rpath(fs_ctx, path), O_RDONLY);
-        if (fd == -1) {
-            return -1;
-        }
-        do {
-            tsize = read(fd, (void *)buf, bufsz);
-        } while (tsize == -1 && errno == EINTR);
-        close(fd);
-        return tsize;
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
-    }
-    return tsize;
-}
-
-static int local_close(FsContext *ctx, int fd)
-{
-    return close(fd);
-}
-
-static int local_closedir(FsContext *ctx, DIR *dir)
-{
-    return closedir(dir);
-}
-
-static int local_open(FsContext *ctx, const char *path, int flags)
-{
-    return open(rpath(ctx, path), flags);
-}
-
-static DIR *local_opendir(FsContext *ctx, const char *path)
-{
-    return opendir(rpath(ctx, path));
-}
-
-static void local_rewinddir(FsContext *ctx, DIR *dir)
-{
-    return rewinddir(dir);
-}
-
-static off_t local_telldir(FsContext *ctx, DIR *dir)
-{
-    return telldir(dir);
-}
-
-static struct dirent *local_readdir(FsContext *ctx, DIR *dir)
-{
-    return readdir(dir);
-}
-
-static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
-{
-    return seekdir(dir, off);
-}
-
-static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov,
-                            int iovcnt, off_t offset)
-{
-#ifdef CONFIG_PREADV
-    return preadv(fd, iov, iovcnt, offset);
-#else
-    int err = lseek(fd, offset, SEEK_SET);
-    if (err == -1) {
-        return err;
-    } else {
-        return readv(fd, iov, iovcnt);
-    }
-#endif
-}
-
-static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
-                            int iovcnt, off_t offset)
-{
-#ifdef CONFIG_PREADV
-    return pwritev(fd, iov, iovcnt, offset);
-#else
-    int err = lseek(fd, offset, SEEK_SET);
-    if (err == -1) {
-        return err;
-    } else {
-        return writev(fd, iov, iovcnt);
-    }
-#endif
-}
-
-static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
-{
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        return local_set_xattr(rpath(fs_ctx, path), credp);
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        return chmod(rpath(fs_ctx, path), credp->fc_mode);
-    }
-    return -1;
-}
-
-static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp)
-{
-    int err = -1;
-    int serrno = 0;
-
-    /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        err = mknod(rpath(fs_ctx, path), SM_LOCAL_MODE_BITS|S_IFREG, 0);
-        if (err == -1) {
-            return err;
-        }
-        local_set_xattr(rpath(fs_ctx, path), credp);
-        if (err == -1) {
-            serrno = errno;
-            goto err_end;
-        }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev);
-        if (err == -1) {
-            return err;
-        }
-        err = local_post_create_passthrough(fs_ctx, path, credp);
-        if (err == -1) {
-            serrno = errno;
-            goto err_end;
-        }
-    }
-    return err;
-
-err_end:
-    remove(rpath(fs_ctx, path));
-    errno = serrno;
-    return err;
-}
-
-static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
-{
-    int err = -1;
-    int serrno = 0;
-
-    /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS);
-        if (err == -1) {
-            return err;
-        }
-        credp->fc_mode = credp->fc_mode|S_IFDIR;
-        err = local_set_xattr(rpath(fs_ctx, path), credp);
-        if (err == -1) {
-            serrno = errno;
-            goto err_end;
-        }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
-        if (err == -1) {
-            return err;
-        }
-        err = local_post_create_passthrough(fs_ctx, path, credp);
-        if (err == -1) {
-            serrno = errno;
-            goto err_end;
-        }
-    }
-    return err;
-
-err_end:
-    remove(rpath(fs_ctx, path));
-    errno = serrno;
-    return err;
-}
-
-static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
-{
-    int err;
-    err = fstat(fd, stbuf);
-    if (err) {
-        return err;
-    }
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        /* Actual credentials are part of extended attrs */
-        uid_t tmp_uid;
-        gid_t tmp_gid;
-        mode_t tmp_mode;
-        dev_t tmp_dev;
-
-        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
-            stbuf->st_uid = tmp_uid;
-        }
-        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
-            stbuf->st_gid = tmp_gid;
-        }
-        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
-            stbuf->st_mode = tmp_mode;
-        }
-        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
-                stbuf->st_rdev = tmp_dev;
-        }
-    }
-    return err;
-}
-
-static int local_open2(FsContext *fs_ctx, const char *path, int flags,
-        FsCred *credp)
-{
-    int fd = -1;
-    int err = -1;
-    int serrno = 0;
-
-    /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS);
-        if (fd == -1) {
-            return fd;
-        }
-        credp->fc_mode = credp->fc_mode|S_IFREG;
-        /* Set cleint credentials in xattr */
-        err = local_set_xattr(rpath(fs_ctx, path), credp);
-        if (err == -1) {
-            serrno = errno;
-            goto err_end;
-        }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
-        if (fd == -1) {
-            return fd;
-        }
-        err = local_post_create_passthrough(fs_ctx, path, credp);
-        if (err == -1) {
-            serrno = errno;
-            goto err_end;
-        }
-    }
-    return fd;
-
-err_end:
-    close(fd);
-    remove(rpath(fs_ctx, path));
-    errno = serrno;
-    return err;
-}
-
-
-static int local_symlink(FsContext *fs_ctx, const char *oldpath,
-        const char *newpath, FsCred *credp)
-{
-    int err = -1;
-    int serrno = 0;
-
-    /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
-        int fd;
-        ssize_t oldpath_size, write_size;
-        fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR,
-                SM_LOCAL_MODE_BITS);
-        if (fd == -1) {
-            return fd;
-        }
-        /* Write the oldpath (target) to the file. */
-        oldpath_size = strlen(oldpath) + 1;
-        do {
-            write_size = write(fd, (void *)oldpath, oldpath_size);
-        } while (write_size == -1 && errno == EINTR);
-
-        if (write_size != oldpath_size) {
-            serrno = errno;
-            close(fd);
-            err = -1;
-            goto err_end;
-        }
-        close(fd);
-        /* Set cleint credentials in symlink's xattr */
-        credp->fc_mode = credp->fc_mode|S_IFLNK;
-        err = local_set_xattr(rpath(fs_ctx, newpath), credp);
-        if (err == -1) {
-            serrno = errno;
-            goto err_end;
-        }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        err = symlink(oldpath, rpath(fs_ctx, newpath));
-        if (err) {
-            return err;
-        }
-        err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
-        if (err == -1) {
-            /*
-             * If we fail to change ownership and if we are
-             * using security model none. Ignore the error
-             */
-            if (fs_ctx->fs_sm != SM_NONE) {
-                serrno = errno;
-                goto err_end;
-            } else
-                err = 0;
-        }
-    }
-    return err;
-
-err_end:
-    remove(rpath(fs_ctx, newpath));
-    errno = serrno;
-    return err;
-}
-
-static int local_link(FsContext *ctx, const char *oldpath, const char *newpath)
-{
-    char *tmp = qemu_strdup(rpath(ctx, oldpath));
-    int err, serrno = 0;
-
-    if (tmp == NULL) {
-        return -ENOMEM;
-    }
-
-    err = link(tmp, rpath(ctx, newpath));
-    if (err == -1) {
-        serrno = errno;
-    }
-
-    qemu_free(tmp);
-
-    if (err == -1) {
-        errno = serrno;
-    }
-
-    return err;
-}
-
-static int local_truncate(FsContext *ctx, const char *path, off_t size)
-{
-    return truncate(rpath(ctx, path), size);
-}
-
-static int local_rename(FsContext *ctx, const char *oldpath,
-                        const char *newpath)
-{
-    char *tmp;
-    int err;
-
-    tmp = qemu_strdup(rpath(ctx, oldpath));
-
-    err = rename(tmp, rpath(ctx, newpath));
-    if (err == -1) {
-        int serrno = errno;
-        qemu_free(tmp);
-        errno = serrno;
-    } else {
-        qemu_free(tmp);
-    }
-
-    return err;
-
-}
-
-static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
-{
-    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
-            (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
-        return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
-    } else if (fs_ctx->fs_sm == SM_MAPPED) {
-        return local_set_xattr(rpath(fs_ctx, path), credp);
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
-    }
-    return -1;
-}
-
-static int local_utimensat(FsContext *s, const char *path,
-                           const struct timespec *buf)
-{
-    return qemu_utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW);
-}
-
-static int local_remove(FsContext *ctx, const char *path)
-{
-    return remove(rpath(ctx, path));
-}
-
-static int local_fsync(FsContext *ctx, int fd, int datasync)
-{
-    if (datasync) {
-        return qemu_fdatasync(fd);
-    } else {
-        return fsync(fd);
-    }
-}
-
-static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
-{
-   return statfs(rpath(s, path), stbuf);
-}
-
-static ssize_t local_lgetxattr(FsContext *ctx, const char *path,
-                               const char *name, void *value, size_t size)
-{
-    return v9fs_get_xattr(ctx, path, name, value, size);
-}
-
-static ssize_t local_llistxattr(FsContext *ctx, const char *path,
-                                void *value, size_t size)
-{
-    return v9fs_list_xattr(ctx, path, value, size);
-}
-
-static int local_lsetxattr(FsContext *ctx, const char *path, const char *name,
-                           void *value, size_t size, int flags)
-{
-    return v9fs_set_xattr(ctx, path, name, value, size, flags);
-}
-
-static int local_lremovexattr(FsContext *ctx,
-                              const char *path, const char *name)
-{
-    return v9fs_remove_xattr(ctx, path, name);
-}
-
-
-FileOperations local_ops = {
-    .lstat = local_lstat,
-    .readlink = local_readlink,
-    .close = local_close,
-    .closedir = local_closedir,
-    .open = local_open,
-    .opendir = local_opendir,
-    .rewinddir = local_rewinddir,
-    .telldir = local_telldir,
-    .readdir = local_readdir,
-    .seekdir = local_seekdir,
-    .preadv = local_preadv,
-    .pwritev = local_pwritev,
-    .chmod = local_chmod,
-    .mknod = local_mknod,
-    .mkdir = local_mkdir,
-    .fstat = local_fstat,
-    .open2 = local_open2,
-    .symlink = local_symlink,
-    .link = local_link,
-    .truncate = local_truncate,
-    .rename = local_rename,
-    .chown = local_chown,
-    .utimensat = local_utimensat,
-    .remove = local_remove,
-    .fsync = local_fsync,
-    .statfs = local_statfs,
-    .lgetxattr = local_lgetxattr,
-    .llistxattr = local_llistxattr,
-    .lsetxattr = local_lsetxattr,
-    .lremovexattr = local_lremovexattr,
-};
diff --git a/hw/virtio-9p-posix-acl.c b/hw/virtio-9p-posix-acl.c
deleted file mode 100644
index 3978d0c..0000000
--- a/hw/virtio-9p-posix-acl.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Virtio 9p system.posix* xattr callback
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- * Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include <sys/types.h>
-#include <attr/xattr.h>
-#include "virtio.h"
-#include "virtio-9p.h"
-#include "file-op-9p.h"
-#include "virtio-9p-xattr.h"
-
-#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
-#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
-#define ACL_ACCESS "system.posix_acl_access"
-#define ACL_DEFAULT "system.posix_acl_default"
-
-static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path,
-                                const char *name, void *value, size_t size)
-{
-    return lgetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size);
-}
-
-static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
-                                 char *name, void *value, size_t osize)
-{
-    ssize_t len = sizeof(ACL_ACCESS);
-
-    if (!value) {
-        return len;
-    }
-
-    if (osize < len) {
-        errno = ERANGE;
-        return -1;
-    }
-
-    strncpy(value, ACL_ACCESS, len);
-    return 0;
-}
-
-static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name,
-                            void *value, size_t size, int flags)
-{
-    return lsetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size, flags);
-}
-
-static int mp_pacl_removexattr(FsContext *ctx,
-                               const char *path, const char *name)
-{
-    int ret;
-    ret  = lremovexattr(rpath(ctx, path), MAP_ACL_ACCESS);
-    if (ret == -1 && errno == ENODATA) {
-        /*
-         * We don't get ENODATA error when trying to remote a
-         * posix acl that is not present. So don't throw the error
-         * even in case of mapped security model
-         */
-        errno = 0;
-        ret = 0;
-    }
-    return ret;
-}
-
-static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path,
-                                const char *name, void *value, size_t size)
-{
-    return lgetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size);
-}
-
-static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
-                                 char *name, void *value, size_t osize)
-{
-    ssize_t len = sizeof(ACL_DEFAULT);
-
-    if (!value) {
-        return len;
-    }
-
-    if (osize < len) {
-        errno = ERANGE;
-        return -1;
-    }
-
-    strncpy(value, ACL_DEFAULT, len);
-    return 0;
-}
-
-static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
-                            void *value, size_t size, int flags)
-{
-    return lsetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size, flags);
-}
-
-static int mp_dacl_removexattr(FsContext *ctx,
-                               const char *path, const char *name)
-{
-    return lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT);
-}
-
-
-XattrOperations mapped_pacl_xattr = {
-    .name = "system.posix_acl_access",
-    .getxattr = mp_pacl_getxattr,
-    .setxattr = mp_pacl_setxattr,
-    .listxattr = mp_pacl_listxattr,
-    .removexattr = mp_pacl_removexattr,
-};
-
-XattrOperations mapped_dacl_xattr = {
-    .name = "system.posix_acl_default",
-    .getxattr = mp_dacl_getxattr,
-    .setxattr = mp_dacl_setxattr,
-    .listxattr = mp_dacl_listxattr,
-    .removexattr = mp_dacl_removexattr,
-};
-
-XattrOperations passthrough_acl_xattr = {
-    .name = "system.posix_acl_",
-    .getxattr = pt_getxattr,
-    .setxattr = pt_setxattr,
-    .listxattr = pt_listxattr,
-    .removexattr = pt_removexattr,
-};
-
-XattrOperations none_acl_xattr = {
-    .name = "system.posix_acl_",
-    .getxattr = notsup_getxattr,
-    .setxattr = notsup_setxattr,
-    .listxattr = notsup_listxattr,
-    .removexattr = notsup_removexattr,
-};
diff --git a/hw/virtio-9p-xattr-user.c b/hw/virtio-9p-xattr-user.c
deleted file mode 100644
index faa02a1..0000000
--- a/hw/virtio-9p-xattr-user.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Virtio 9p user. xattr callback
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- * Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include <sys/types.h>
-#include "virtio.h"
-#include "virtio-9p.h"
-#include "file-op-9p.h"
-#include "virtio-9p-xattr.h"
-
-
-static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
-                                const char *name, void *value, size_t size)
-{
-    if (strncmp(name, "user.virtfs.", 12) == 0) {
-        /*
-         * Don't allow fetch of user.virtfs namesapce
-         * in case of mapped security
-         */
-        errno = ENOATTR;
-        return -1;
-    }
-    return lgetxattr(rpath(ctx, path), name, value, size);
-}
-
-static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
-                                 char *name, void *value, size_t size)
-{
-    int name_size = strlen(name) + 1;
-    if (strncmp(name, "user.virtfs.", 12) == 0) {
-
-        /*  check if it is a mapped posix acl */
-        if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) {
-            /* adjust the name and size */
-            name += 12;
-            name_size -= 12;
-        } else {
-            /*
-             * Don't allow fetch of user.virtfs namesapce
-             * in case of mapped security
-             */
-            return 0;
-        }
-    }
-    if (!value) {
-        return name_size;
-    }
-
-    if (size < name_size) {
-        errno = ERANGE;
-        return -1;
-    }
-
-    strncpy(value, name, name_size);
-    return name_size;
-}
-
-static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
-                            void *value, size_t size, int flags)
-{
-    if (strncmp(name, "user.virtfs.", 12) == 0) {
-        /*
-         * Don't allow fetch of user.virtfs namesapce
-         * in case of mapped security
-         */
-        errno = EACCES;
-        return -1;
-    }
-    return lsetxattr(rpath(ctx, path), name, value, size, flags);
-}
-
-static int mp_user_removexattr(FsContext *ctx,
-                               const char *path, const char *name)
-{
-    if (strncmp(name, "user.virtfs.", 12) == 0) {
-        /*
-         * Don't allow fetch of user.virtfs namesapce
-         * in case of mapped security
-         */
-        errno = EACCES;
-        return -1;
-    }
-    return lremovexattr(rpath(ctx, path), name);
-}
-
-XattrOperations mapped_user_xattr = {
-    .name = "user.",
-    .getxattr = mp_user_getxattr,
-    .setxattr = mp_user_setxattr,
-    .listxattr = mp_user_listxattr,
-    .removexattr = mp_user_removexattr,
-};
-
-XattrOperations passthrough_user_xattr = {
-    .name = "user.",
-    .getxattr = pt_getxattr,
-    .setxattr = pt_setxattr,
-    .listxattr = pt_listxattr,
-    .removexattr = pt_removexattr,
-};
diff --git a/hw/virtio-9p-xattr.c b/hw/virtio-9p-xattr.c
deleted file mode 100644
index 1aab081..0000000
--- a/hw/virtio-9p-xattr.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Virtio 9p  xattr callback
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- * Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include "virtio.h"
-#include "virtio-9p.h"
-#include "file-op-9p.h"
-#include "virtio-9p-xattr.h"
-
-
-static XattrOperations *get_xattr_operations(XattrOperations **h,
-                                             const char *name)
-{
-    XattrOperations *xops;
-    for (xops = *(h)++; xops != NULL; xops = *(h)++) {
-        if (!strncmp(name, xops->name, strlen(xops->name))) {
-            return xops;
-        }
-    }
-    return NULL;
-}
-
-ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
-                       const char *name, void *value, size_t size)
-{
-    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
-    if (xops) {
-        return xops->getxattr(ctx, path, name, value, size);
-    }
-    errno = -EOPNOTSUPP;
-    return -1;
-}
-
-ssize_t pt_listxattr(FsContext *ctx, const char *path,
-                     char *name, void *value, size_t size)
-{
-    int name_size = strlen(name) + 1;
-    if (!value) {
-        return name_size;
-    }
-
-    if (size < name_size) {
-        errno = ERANGE;
-        return -1;
-    }
-
-    strncpy(value, name, name_size);
-    return name_size;
-}
-
-
-/*
- * Get the list and pass to each layer to find out whether
- * to send the data or not
- */
-ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
-                        void *value, size_t vsize)
-{
-    ssize_t size = 0;
-    void *ovalue = value;
-    XattrOperations *xops;
-    char *orig_value, *orig_value_start;
-    ssize_t xattr_len, parsed_len = 0, attr_len;
-
-    /* Get the actual len */
-    xattr_len = llistxattr(rpath(ctx, path), value, 0);
-    if (xattr_len <= 0) {
-        return xattr_len;
-    }
-
-    /* Now fetch the xattr and find the actual size */
-    orig_value = qemu_malloc(xattr_len);
-    xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len);
-
-    /* store the orig pointer */
-    orig_value_start = orig_value;
-    while (xattr_len > parsed_len) {
-        xops = get_xattr_operations(ctx->xops, orig_value);
-        if (!xops) {
-            goto next_entry;
-        }
-
-        if (!value) {
-            size += xops->listxattr(ctx, path, orig_value, value, vsize);
-        } else {
-            size = xops->listxattr(ctx, path, orig_value, value, vsize);
-            if (size < 0) {
-                goto err_out;
-            }
-            value += size;
-            vsize -= size;
-        }
-next_entry:
-        /* Got the next entry */
-        attr_len = strlen(orig_value) + 1;
-        parsed_len += attr_len;
-        orig_value += attr_len;
-    }
-    if (value) {
-        size = value - ovalue;
-    }
-
-err_out:
-    qemu_free(orig_value_start);
-    return size;
-}
-
-int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
-                   void *value, size_t size, int flags)
-{
-    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
-    if (xops) {
-        return xops->setxattr(ctx, path, name, value, size, flags);
-    }
-    errno = -EOPNOTSUPP;
-    return -1;
-
-}
-
-int v9fs_remove_xattr(FsContext *ctx,
-                      const char *path, const char *name)
-{
-    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
-    if (xops) {
-        return xops->removexattr(ctx, path, name);
-    }
-    errno = -EOPNOTSUPP;
-    return -1;
-
-}
-
-XattrOperations *mapped_xattr_ops[] = {
-    &mapped_user_xattr,
-    &mapped_pacl_xattr,
-    &mapped_dacl_xattr,
-    NULL,
-};
-
-XattrOperations *passthrough_xattr_ops[] = {
-    &passthrough_user_xattr,
-    &passthrough_acl_xattr,
-    NULL,
-};
-
-/* for .user none model should be same as passthrough */
-XattrOperations *none_xattr_ops[] = {
-    &passthrough_user_xattr,
-    &none_acl_xattr,
-    NULL,
-};
diff --git a/hw/virtio-9p-xattr.h b/hw/virtio-9p-xattr.h
deleted file mode 100644
index 2bbae2d..0000000
--- a/hw/virtio-9p-xattr.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Virtio 9p
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- *  Aneesh Kumar K.V <aneesh.kumar at linux.vnet.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-#ifndef _QEMU_VIRTIO_9P_XATTR_H
-#define _QEMU_VIRTIO_9P_XATTR_H
-
-#include <attr/xattr.h>
-
-typedef struct xattr_operations
-{
-    const char *name;
-    ssize_t (*getxattr)(FsContext *ctx, const char *path,
-                        const char *name, void *value, size_t size);
-    ssize_t (*listxattr)(FsContext *ctx, const char *path,
-                         char *name, void *value, size_t size);
-    int (*setxattr)(FsContext *ctx, const char *path, const char *name,
-                    void *value, size_t size, int flags);
-    int (*removexattr)(FsContext *ctx,
-                       const char *path, const char *name);
-} XattrOperations;
-
-
-extern XattrOperations mapped_user_xattr;
-extern XattrOperations passthrough_user_xattr;
-
-extern XattrOperations mapped_pacl_xattr;
-extern XattrOperations mapped_dacl_xattr;
-extern XattrOperations passthrough_acl_xattr;
-extern XattrOperations none_acl_xattr;
-
-extern XattrOperations *mapped_xattr_ops[];
-extern XattrOperations *passthrough_xattr_ops[];
-extern XattrOperations *none_xattr_ops[];
-
-ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name,
-                       void *value, size_t size);
-ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value,
-                        size_t vsize);
-int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
-                          void *value, size_t size, int flags);
-int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name);
-ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value,
-                     size_t size);
-
-static inline ssize_t pt_getxattr(FsContext *ctx, const char *path,
-                                  const char *name, void *value, size_t size)
-{
-    return lgetxattr(rpath(ctx, path), name, value, size);
-}
-
-static inline int pt_setxattr(FsContext *ctx, const char *path,
-                              const char *name, void *value,
-                              size_t size, int flags)
-{
-    return lsetxattr(rpath(ctx, path), name, value, size, flags);
-}
-
-static inline int pt_removexattr(FsContext *ctx,
-                                 const char *path, const char *name)
-{
-    return lremovexattr(rpath(ctx, path), name);
-}
-
-static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path,
-                                      const char *name, void *value,
-                                      size_t size)
-{
-    errno = ENOTSUP;
-    return -1;
-}
-
-static inline int notsup_setxattr(FsContext *ctx, const char *path,
-                                  const char *name, void *value,
-                                  size_t size, int flags)
-{
-    errno = ENOTSUP;
-    return -1;
-}
-
-static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path,
-                                       char *name, void *value, size_t size)
-{
-    return 0;
-}
-
-static inline int notsup_removexattr(FsContext *ctx,
-                                     const char *path, const char *name)
-{
-    errno = ENOTSUP;
-    return -1;
-}
-
-#endif
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
deleted file mode 100644
index 7e29535..0000000
--- a/hw/virtio-9p.c
+++ /dev/null
@@ -1,3741 +0,0 @@
-/*
- * Virtio 9p backend
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- *  Anthony Liguori   <aliguori at us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include "virtio.h"
-#include "pc.h"
-#include "qemu_socket.h"
-#include "virtio-9p.h"
-#include "fsdev/qemu-fsdev.h"
-#include "virtio-9p-debug.h"
-#include "virtio-9p-xattr.h"
-
-int debug_9p_pdu;
-
-enum {
-    Oread   = 0x00,
-    Owrite  = 0x01,
-    Ordwr   = 0x02,
-    Oexec   = 0x03,
-    Oexcl   = 0x04,
-    Otrunc  = 0x10,
-    Orexec  = 0x20,
-    Orclose = 0x40,
-    Oappend = 0x80,
-};
-
-static int omode_to_uflags(int8_t mode)
-{
-    int ret = 0;
-
-    switch (mode & 3) {
-    case Oread:
-        ret = O_RDONLY;
-        break;
-    case Ordwr:
-        ret = O_RDWR;
-        break;
-    case Owrite:
-        ret = O_WRONLY;
-        break;
-    case Oexec:
-        ret = O_RDONLY;
-        break;
-    }
-
-    if (mode & Otrunc) {
-        ret |= O_TRUNC;
-    }
-
-    if (mode & Oappend) {
-        ret |= O_APPEND;
-    }
-
-    if (mode & Oexcl) {
-        ret |= O_EXCL;
-    }
-
-    return ret;
-}
-
-void cred_init(FsCred *credp)
-{
-    credp->fc_uid = -1;
-    credp->fc_gid = -1;
-    credp->fc_mode = -1;
-    credp->fc_rdev = -1;
-}
-
-static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
-{
-    return s->ops->lstat(&s->ctx, path->data, stbuf);
-}
-
-static ssize_t v9fs_do_readlink(V9fsState *s, V9fsString *path, V9fsString *buf)
-{
-    ssize_t len;
-
-    buf->data = qemu_malloc(1024);
-
-    len = s->ops->readlink(&s->ctx, path->data, buf->data, 1024 - 1);
-    if (len > -1) {
-        buf->size = len;
-        buf->data[len] = 0;
-    }
-
-    return len;
-}
-
-static int v9fs_do_close(V9fsState *s, int fd)
-{
-    return s->ops->close(&s->ctx, fd);
-}
-
-static int v9fs_do_closedir(V9fsState *s, DIR *dir)
-{
-    return s->ops->closedir(&s->ctx, dir);
-}
-
-static int v9fs_do_open(V9fsState *s, V9fsString *path, int flags)
-{
-    return s->ops->open(&s->ctx, path->data, flags);
-}
-
-static DIR *v9fs_do_opendir(V9fsState *s, V9fsString *path)
-{
-    return s->ops->opendir(&s->ctx, path->data);
-}
-
-static void v9fs_do_rewinddir(V9fsState *s, DIR *dir)
-{
-    return s->ops->rewinddir(&s->ctx, dir);
-}
-
-static off_t v9fs_do_telldir(V9fsState *s, DIR *dir)
-{
-    return s->ops->telldir(&s->ctx, dir);
-}
-
-static struct dirent *v9fs_do_readdir(V9fsState *s, DIR *dir)
-{
-    return s->ops->readdir(&s->ctx, dir);
-}
-
-static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
-{
-    return s->ops->seekdir(&s->ctx, dir, off);
-}
-
-static int v9fs_do_preadv(V9fsState *s, int fd, const struct iovec *iov,
-                            int iovcnt, int64_t offset)
-{
-    return s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
-}
-
-static int v9fs_do_pwritev(V9fsState *s, int fd, const struct iovec *iov,
-                       int iovcnt, int64_t offset)
-{
-    return s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset);
-}
-
-static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
-{
-    FsCred cred;
-    cred_init(&cred);
-    cred.fc_mode = mode;
-    return s->ops->chmod(&s->ctx, path->data, &cred);
-}
-
-static int v9fs_do_mknod(V9fsState *s, char *name,
-        mode_t mode, dev_t dev, uid_t uid, gid_t gid)
-{
-    FsCred cred;
-    cred_init(&cred);
-    cred.fc_uid = uid;
-    cred.fc_gid = gid;
-    cred.fc_mode = mode;
-    cred.fc_rdev = dev;
-    return s->ops->mknod(&s->ctx, name, &cred);
-}
-
-static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode,
-                uid_t uid, gid_t gid)
-{
-    FsCred cred;
-
-    cred_init(&cred);
-    cred.fc_uid = uid;
-    cred.fc_gid = gid;
-    cred.fc_mode = mode;
-
-    return s->ops->mkdir(&s->ctx, name, &cred);
-}
-
-static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
-{
-    return s->ops->fstat(&s->ctx, fd, stbuf);
-}
-
-static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
-        int flags, int mode)
-{
-    FsCred cred;
-
-    cred_init(&cred);
-    cred.fc_uid = uid;
-    cred.fc_gid = gid;
-    cred.fc_mode = mode & 07777;
-    flags = flags;
-
-    return s->ops->open2(&s->ctx, fullname, flags, &cred);
-}
-
-static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp,
-        const char *oldpath, const char *newpath, gid_t gid)
-{
-    FsCred cred;
-    cred_init(&cred);
-    cred.fc_uid = fidp->uid;
-    cred.fc_gid = gid;
-    cred.fc_mode = 0777;
-
-    return s->ops->symlink(&s->ctx, oldpath, newpath, &cred);
-}
-
-static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath)
-{
-    return s->ops->link(&s->ctx, oldpath->data, newpath->data);
-}
-
-static int v9fs_do_truncate(V9fsState *s, V9fsString *path, off_t size)
-{
-    return s->ops->truncate(&s->ctx, path->data, size);
-}
-
-static int v9fs_do_rename(V9fsState *s, V9fsString *oldpath,
-                            V9fsString *newpath)
-{
-    return s->ops->rename(&s->ctx, oldpath->data, newpath->data);
-}
-
-static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid)
-{
-    FsCred cred;
-    cred_init(&cred);
-    cred.fc_uid = uid;
-    cred.fc_gid = gid;
-
-    return s->ops->chown(&s->ctx, path->data, &cred);
-}
-
-static int v9fs_do_utimensat(V9fsState *s, V9fsString *path,
-                                           const struct timespec times[2])
-{
-    return s->ops->utimensat(&s->ctx, path->data, times);
-}
-
-static int v9fs_do_remove(V9fsState *s, V9fsString *path)
-{
-    return s->ops->remove(&s->ctx, path->data);
-}
-
-static int v9fs_do_fsync(V9fsState *s, int fd, int datasync)
-{
-    return s->ops->fsync(&s->ctx, fd, datasync);
-}
-
-static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
-{
-    return s->ops->statfs(&s->ctx, path->data, stbuf);
-}
-
-static ssize_t v9fs_do_lgetxattr(V9fsState *s, V9fsString *path,
-                             V9fsString *xattr_name,
-                             void *value, size_t size)
-{
-    return s->ops->lgetxattr(&s->ctx, path->data,
-                             xattr_name->data, value, size);
-}
-
-static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path,
-                              void *value, size_t size)
-{
-    return s->ops->llistxattr(&s->ctx, path->data,
-                              value, size);
-}
-
-static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path,
-                             V9fsString *xattr_name,
-                             void *value, size_t size, int flags)
-{
-    return s->ops->lsetxattr(&s->ctx, path->data,
-                             xattr_name->data, value, size, flags);
-}
-
-static int v9fs_do_lremovexattr(V9fsState *s, V9fsString *path,
-                                V9fsString *xattr_name)
-{
-    return s->ops->lremovexattr(&s->ctx, path->data,
-                                xattr_name->data);
-}
-
-
-static void v9fs_string_init(V9fsString *str)
-{
-    str->data = NULL;
-    str->size = 0;
-}
-
-static void v9fs_string_free(V9fsString *str)
-{
-    qemu_free(str->data);
-    str->data = NULL;
-    str->size = 0;
-}
-
-static void v9fs_string_null(V9fsString *str)
-{
-    v9fs_string_free(str);
-}
-
-static int number_to_string(void *arg, char type)
-{
-    unsigned int ret = 0;
-
-    switch (type) {
-    case 'u': {
-        unsigned int num = *(unsigned int *)arg;
-
-        do {
-            ret++;
-            num = num/10;
-        } while (num);
-        break;
-    }
-    case 'U': {
-        unsigned long num = *(unsigned long *)arg;
-        do {
-            ret++;
-            num = num/10;
-        } while (num);
-        break;
-    }
-    default:
-        printf("Number_to_string: Unknown number format\n");
-        return -1;
-    }
-
-    return ret;
-}
-
-static int GCC_FMT_ATTR(2, 0)
-v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
-{
-    va_list ap2;
-    char *iter = (char *)fmt;
-    int len = 0;
-    int nr_args = 0;
-    char *arg_char_ptr;
-    unsigned int arg_uint;
-    unsigned long arg_ulong;
-
-    /* Find the number of %'s that denotes an argument */
-    for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
-        nr_args++;
-        iter++;
-    }
-
-    len = strlen(fmt) - 2*nr_args;
-
-    if (!nr_args) {
-        goto alloc_print;
-    }
-
-    va_copy(ap2, ap);
-
-    iter = (char *)fmt;
-
-    /* Now parse the format string */
-    for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
-        iter++;
-        switch (*iter) {
-        case 'u':
-            arg_uint = va_arg(ap2, unsigned int);
-            len += number_to_string((void *)&arg_uint, 'u');
-            break;
-        case 'l':
-            if (*++iter == 'u') {
-                arg_ulong = va_arg(ap2, unsigned long);
-                len += number_to_string((void *)&arg_ulong, 'U');
-            } else {
-                return -1;
-            }
-            break;
-        case 's':
-            arg_char_ptr = va_arg(ap2, char *);
-            len += strlen(arg_char_ptr);
-            break;
-        case 'c':
-            len += 1;
-            break;
-        default:
-            fprintf(stderr,
-		    "v9fs_string_alloc_printf:Incorrect format %c", *iter);
-            return -1;
-        }
-        iter++;
-    }
-
-alloc_print:
-    *strp = qemu_malloc((len + 1) * sizeof(**strp));
-
-    return vsprintf(*strp, fmt, ap);
-}
-
-static void GCC_FMT_ATTR(2, 3)
-v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
-{
-    va_list ap;
-    int err;
-
-    v9fs_string_free(str);
-
-    va_start(ap, fmt);
-    err = v9fs_string_alloc_printf(&str->data, fmt, ap);
-    BUG_ON(err == -1);
-    va_end(ap);
-
-    str->size = err;
-}
-
-static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
-{
-    v9fs_string_free(lhs);
-    v9fs_string_sprintf(lhs, "%s", rhs->data);
-}
-
-static size_t v9fs_string_size(V9fsString *str)
-{
-    return str->size;
-}
-
-static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
-{
-    V9fsFidState *f;
-
-    for (f = s->fid_list; f; f = f->next) {
-        if (f->fid == fid) {
-            return f;
-        }
-    }
-
-    return NULL;
-}
-
-static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
-{
-    V9fsFidState *f;
-
-    f = lookup_fid(s, fid);
-    if (f) {
-        return NULL;
-    }
-
-    f = qemu_mallocz(sizeof(V9fsFidState));
-
-    f->fid = fid;
-    f->fid_type = P9_FID_NONE;
-
-    f->next = s->fid_list;
-    s->fid_list = f;
-
-    return f;
-}
-
-static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
-{
-    int retval = 0;
-
-    if (fidp->fs.xattr.copied_len == -1) {
-        /* getxattr/listxattr fid */
-        goto free_value;
-    }
-    /*
-     * if this is fid for setxattr. clunk should
-     * result in setxattr localcall
-     */
-    if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
-        /* clunk after partial write */
-        retval = -EINVAL;
-        goto free_out;
-    }
-    if (fidp->fs.xattr.len) {
-        retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name,
-                                   fidp->fs.xattr.value,
-                                   fidp->fs.xattr.len,
-                                   fidp->fs.xattr.flags);
-    } else {
-        retval = v9fs_do_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name);
-    }
-free_out:
-    v9fs_string_free(&fidp->fs.xattr.name);
-free_value:
-    if (fidp->fs.xattr.value) {
-        qemu_free(fidp->fs.xattr.value);
-    }
-    return retval;
-}
-
-static int free_fid(V9fsState *s, int32_t fid)
-{
-    int retval = 0;
-    V9fsFidState **fidpp, *fidp;
-
-    for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
-        if ((*fidpp)->fid == fid) {
-            break;
-        }
-    }
-
-    if (*fidpp == NULL) {
-        return -ENOENT;
-    }
-
-    fidp = *fidpp;
-    *fidpp = fidp->next;
-
-    if (fidp->fid_type == P9_FID_FILE) {
-        v9fs_do_close(s, fidp->fs.fd);
-    } else if (fidp->fid_type == P9_FID_DIR) {
-        v9fs_do_closedir(s, fidp->fs.dir);
-    } else if (fidp->fid_type == P9_FID_XATTR) {
-        retval = v9fs_xattr_fid_clunk(s, fidp);
-    }
-    v9fs_string_free(&fidp->path);
-    qemu_free(fidp);
-
-    return retval;
-}
-
-#define P9_QID_TYPE_DIR         0x80
-#define P9_QID_TYPE_SYMLINK     0x02
-
-#define P9_STAT_MODE_DIR        0x80000000
-#define P9_STAT_MODE_APPEND     0x40000000
-#define P9_STAT_MODE_EXCL       0x20000000
-#define P9_STAT_MODE_MOUNT      0x10000000
-#define P9_STAT_MODE_AUTH       0x08000000
-#define P9_STAT_MODE_TMP        0x04000000
-#define P9_STAT_MODE_SYMLINK    0x02000000
-#define P9_STAT_MODE_LINK       0x01000000
-#define P9_STAT_MODE_DEVICE     0x00800000
-#define P9_STAT_MODE_NAMED_PIPE 0x00200000
-#define P9_STAT_MODE_SOCKET     0x00100000
-#define P9_STAT_MODE_SETUID     0x00080000
-#define P9_STAT_MODE_SETGID     0x00040000
-#define P9_STAT_MODE_SETVTX     0x00010000
-
-#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR |          \
-                                P9_STAT_MODE_SYMLINK |      \
-                                P9_STAT_MODE_LINK |         \
-                                P9_STAT_MODE_DEVICE |       \
-                                P9_STAT_MODE_NAMED_PIPE |   \
-                                P9_STAT_MODE_SOCKET)
-
-/* This is the algorithm from ufs in spfs */
-static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
-{
-    size_t size;
-
-    size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
-    memcpy(&qidp->path, &stbuf->st_ino, size);
-    qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
-    qidp->type = 0;
-    if (S_ISDIR(stbuf->st_mode)) {
-        qidp->type |= P9_QID_TYPE_DIR;
-    }
-    if (S_ISLNK(stbuf->st_mode)) {
-        qidp->type |= P9_QID_TYPE_SYMLINK;
-    }
-}
-
-static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
-{
-    struct stat stbuf;
-    int err;
-
-    err = v9fs_do_lstat(s, &fidp->path, &stbuf);
-    if (err) {
-        return err;
-    }
-
-    stat_to_qid(&stbuf, qidp);
-    return 0;
-}
-
-static V9fsPDU *alloc_pdu(V9fsState *s)
-{
-    V9fsPDU *pdu = NULL;
-
-    if (!QLIST_EMPTY(&s->free_list)) {
-	pdu = QLIST_FIRST(&s->free_list);
-	QLIST_REMOVE(pdu, next);
-    }
-    return pdu;
-}
-
-static void free_pdu(V9fsState *s, V9fsPDU *pdu)
-{
-    if (pdu) {
-	QLIST_INSERT_HEAD(&s->free_list, pdu, next);
-    }
-}
-
-size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
-                        size_t offset, size_t size, int pack)
-{
-    int i = 0;
-    size_t copied = 0;
-
-    for (i = 0; size && i < sg_count; i++) {
-        size_t len;
-        if (offset >= sg[i].iov_len) {
-            /* skip this sg */
-            offset -= sg[i].iov_len;
-            continue;
-        } else {
-            len = MIN(sg[i].iov_len - offset, size);
-            if (pack) {
-                memcpy(sg[i].iov_base + offset, addr, len);
-            } else {
-                memcpy(addr, sg[i].iov_base + offset, len);
-            }
-            size -= len;
-            copied += len;
-            addr += len;
-            if (size) {
-                offset = 0;
-                continue;
-            }
-        }
-    }
-
-    return copied;
-}
-
-static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
-{
-    return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
-                         offset, size, 0);
-}
-
-static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
-                        size_t size)
-{
-    return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
-                             offset, size, 1);
-}
-
-static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
-{
-    size_t pos = 0;
-    int i, j;
-    struct iovec *src_sg;
-    unsigned int num;
-
-    if (rx) {
-        src_sg = pdu->elem.in_sg;
-        num = pdu->elem.in_num;
-    } else {
-        src_sg = pdu->elem.out_sg;
-        num = pdu->elem.out_num;
-    }
-
-    j = 0;
-    for (i = 0; i < num; i++) {
-        if (offset <= pos) {
-            sg[j].iov_base = src_sg[i].iov_base;
-            sg[j].iov_len = src_sg[i].iov_len;
-            j++;
-        } else if (offset < (src_sg[i].iov_len + pos)) {
-            sg[j].iov_base = src_sg[i].iov_base;
-            sg[j].iov_len = src_sg[i].iov_len;
-            sg[j].iov_base += (offset - pos);
-            sg[j].iov_len -= (offset - pos);
-            j++;
-        }
-        pos += src_sg[i].iov_len;
-    }
-
-    return j;
-}
-
-static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
-{
-    size_t old_offset = offset;
-    va_list ap;
-    int i;
-
-    va_start(ap, fmt);
-    for (i = 0; fmt[i]; i++) {
-        switch (fmt[i]) {
-        case 'b': {
-            uint8_t *valp = va_arg(ap, uint8_t *);
-            offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
-            break;
-        }
-        case 'w': {
-            uint16_t val, *valp;
-            valp = va_arg(ap, uint16_t *);
-            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
-            *valp = le16_to_cpu(val);
-            break;
-        }
-        case 'd': {
-            uint32_t val, *valp;
-            valp = va_arg(ap, uint32_t *);
-            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
-            *valp = le32_to_cpu(val);
-            break;
-        }
-        case 'q': {
-            uint64_t val, *valp;
-            valp = va_arg(ap, uint64_t *);
-            offset += pdu_unpack(&val, pdu, offset, sizeof(val));
-            *valp = le64_to_cpu(val);
-            break;
-        }
-        case 'v': {
-            struct iovec *iov = va_arg(ap, struct iovec *);
-            int *iovcnt = va_arg(ap, int *);
-            *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
-            break;
-        }
-        case 's': {
-            V9fsString *str = va_arg(ap, V9fsString *);
-            offset += pdu_unmarshal(pdu, offset, "w", &str->size);
-            /* FIXME: sanity check str->size */
-            str->data = qemu_malloc(str->size + 1);
-            offset += pdu_unpack(str->data, pdu, offset, str->size);
-            str->data[str->size] = 0;
-            break;
-        }
-        case 'Q': {
-            V9fsQID *qidp = va_arg(ap, V9fsQID *);
-            offset += pdu_unmarshal(pdu, offset, "bdq",
-                        &qidp->type, &qidp->version, &qidp->path);
-            break;
-        }
-        case 'S': {
-            V9fsStat *statp = va_arg(ap, V9fsStat *);
-            offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
-                        &statp->size, &statp->type, &statp->dev,
-                        &statp->qid, &statp->mode, &statp->atime,
-                        &statp->mtime, &statp->length,
-                        &statp->name, &statp->uid, &statp->gid,
-                        &statp->muid, &statp->extension,
-                        &statp->n_uid, &statp->n_gid,
-                        &statp->n_muid);
-            break;
-        }
-        case 'I': {
-            V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
-            offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
-                        &iattr->valid, &iattr->mode,
-                        &iattr->uid, &iattr->gid, &iattr->size,
-                        &iattr->atime_sec, &iattr->atime_nsec,
-                        &iattr->mtime_sec, &iattr->mtime_nsec);
-            break;
-        }
-        default:
-            break;
-        }
-    }
-
-    va_end(ap);
-
-    return offset - old_offset;
-}
-
-static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
-{
-    size_t old_offset = offset;
-    va_list ap;
-    int i;
-
-    va_start(ap, fmt);
-    for (i = 0; fmt[i]; i++) {
-        switch (fmt[i]) {
-        case 'b': {
-            uint8_t val = va_arg(ap, int);
-            offset += pdu_pack(pdu, offset, &val, sizeof(val));
-            break;
-        }
-        case 'w': {
-            uint16_t val;
-            cpu_to_le16w(&val, va_arg(ap, int));
-            offset += pdu_pack(pdu, offset, &val, sizeof(val));
-            break;
-        }
-        case 'd': {
-            uint32_t val;
-            cpu_to_le32w(&val, va_arg(ap, uint32_t));
-            offset += pdu_pack(pdu, offset, &val, sizeof(val));
-            break;
-        }
-        case 'q': {
-            uint64_t val;
-            cpu_to_le64w(&val, va_arg(ap, uint64_t));
-            offset += pdu_pack(pdu, offset, &val, sizeof(val));
-            break;
-        }
-        case 'v': {
-            struct iovec *iov = va_arg(ap, struct iovec *);
-            int *iovcnt = va_arg(ap, int *);
-            *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
-            break;
-        }
-        case 's': {
-            V9fsString *str = va_arg(ap, V9fsString *);
-            offset += pdu_marshal(pdu, offset, "w", str->size);
-            offset += pdu_pack(pdu, offset, str->data, str->size);
-            break;
-        }
-        case 'Q': {
-            V9fsQID *qidp = va_arg(ap, V9fsQID *);
-            offset += pdu_marshal(pdu, offset, "bdq",
-                        qidp->type, qidp->version, qidp->path);
-            break;
-        }
-        case 'S': {
-            V9fsStat *statp = va_arg(ap, V9fsStat *);
-            offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
-                        statp->size, statp->type, statp->dev,
-                        &statp->qid, statp->mode, statp->atime,
-                        statp->mtime, statp->length, &statp->name,
-                        &statp->uid, &statp->gid, &statp->muid,
-                        &statp->extension, statp->n_uid,
-                        statp->n_gid, statp->n_muid);
-            break;
-        }
-        case 'A': {
-            V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
-            offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
-                        statp->st_result_mask,
-                        &statp->qid, statp->st_mode,
-                        statp->st_uid, statp->st_gid,
-                        statp->st_nlink, statp->st_rdev,
-                        statp->st_size, statp->st_blksize, statp->st_blocks,
-                        statp->st_atime_sec, statp->st_atime_nsec,
-                        statp->st_mtime_sec, statp->st_mtime_nsec,
-                        statp->st_ctime_sec, statp->st_ctime_nsec,
-                        statp->st_btime_sec, statp->st_btime_nsec,
-                        statp->st_gen, statp->st_data_version);
-            break;
-        }
-        default:
-            break;
-        }
-    }
-    va_end(ap);
-
-    return offset - old_offset;
-}
-
-static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
-{
-    int8_t id = pdu->id + 1; /* Response */
-
-    if (len < 0) {
-        int err = -len;
-        len = 7;
-
-        if (s->proto_version != V9FS_PROTO_2000L) {
-            V9fsString str;
-
-            str.data = strerror(err);
-            str.size = strlen(str.data);
-
-            len += pdu_marshal(pdu, len, "s", &str);
-            id = P9_RERROR;
-        }
-
-        len += pdu_marshal(pdu, len, "d", err);
-
-        if (s->proto_version == V9FS_PROTO_2000L) {
-            id = P9_RLERROR;
-        }
-    }
-
-    /* fill out the header */
-    pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
-
-    /* keep these in sync */
-    pdu->size = len;
-    pdu->id = id;
-
-    /* push onto queue and notify */
-    virtqueue_push(s->vq, &pdu->elem, len);
-
-    /* FIXME: we should batch these completions */
-    virtio_notify(&s->vdev, s->vq);
-
-    free_pdu(s, pdu);
-}
-
-static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
-{
-    mode_t ret;
-
-    ret = mode & 0777;
-    if (mode & P9_STAT_MODE_DIR) {
-        ret |= S_IFDIR;
-    }
-
-    if (mode & P9_STAT_MODE_SYMLINK) {
-        ret |= S_IFLNK;
-    }
-    if (mode & P9_STAT_MODE_SOCKET) {
-        ret |= S_IFSOCK;
-    }
-    if (mode & P9_STAT_MODE_NAMED_PIPE) {
-        ret |= S_IFIFO;
-    }
-    if (mode & P9_STAT_MODE_DEVICE) {
-        if (extension && extension->data[0] == 'c') {
-            ret |= S_IFCHR;
-        } else {
-            ret |= S_IFBLK;
-        }
-    }
-
-    if (!(ret&~0777)) {
-        ret |= S_IFREG;
-    }
-
-    if (mode & P9_STAT_MODE_SETUID) {
-        ret |= S_ISUID;
-    }
-    if (mode & P9_STAT_MODE_SETGID) {
-        ret |= S_ISGID;
-    }
-    if (mode & P9_STAT_MODE_SETVTX) {
-        ret |= S_ISVTX;
-    }
-
-    return ret;
-}
-
-static int donttouch_stat(V9fsStat *stat)
-{
-    if (stat->type == -1 &&
-        stat->dev == -1 &&
-        stat->qid.type == -1 &&
-        stat->qid.version == -1 &&
-        stat->qid.path == -1 &&
-        stat->mode == -1 &&
-        stat->atime == -1 &&
-        stat->mtime == -1 &&
-        stat->length == -1 &&
-        !stat->name.size &&
-        !stat->uid.size &&
-        !stat->gid.size &&
-        !stat->muid.size &&
-        stat->n_uid == -1 &&
-        stat->n_gid == -1 &&
-        stat->n_muid == -1) {
-        return 1;
-    }
-
-    return 0;
-}
-
-static void v9fs_stat_free(V9fsStat *stat)
-{
-    v9fs_string_free(&stat->name);
-    v9fs_string_free(&stat->uid);
-    v9fs_string_free(&stat->gid);
-    v9fs_string_free(&stat->muid);
-    v9fs_string_free(&stat->extension);
-}
-
-static uint32_t stat_to_v9mode(const struct stat *stbuf)
-{
-    uint32_t mode;
-
-    mode = stbuf->st_mode & 0777;
-    if (S_ISDIR(stbuf->st_mode)) {
-        mode |= P9_STAT_MODE_DIR;
-    }
-
-    if (S_ISLNK(stbuf->st_mode)) {
-        mode |= P9_STAT_MODE_SYMLINK;
-    }
-
-    if (S_ISSOCK(stbuf->st_mode)) {
-        mode |= P9_STAT_MODE_SOCKET;
-    }
-
-    if (S_ISFIFO(stbuf->st_mode)) {
-        mode |= P9_STAT_MODE_NAMED_PIPE;
-    }
-
-    if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
-        mode |= P9_STAT_MODE_DEVICE;
-    }
-
-    if (stbuf->st_mode & S_ISUID) {
-        mode |= P9_STAT_MODE_SETUID;
-    }
-
-    if (stbuf->st_mode & S_ISGID) {
-        mode |= P9_STAT_MODE_SETGID;
-    }
-
-    if (stbuf->st_mode & S_ISVTX) {
-        mode |= P9_STAT_MODE_SETVTX;
-    }
-
-    return mode;
-}
-
-static int stat_to_v9stat(V9fsState *s, V9fsString *name,
-                            const struct stat *stbuf,
-                            V9fsStat *v9stat)
-{
-    int err;
-    const char *str;
-
-    memset(v9stat, 0, sizeof(*v9stat));
-
-    stat_to_qid(stbuf, &v9stat->qid);
-    v9stat->mode = stat_to_v9mode(stbuf);
-    v9stat->atime = stbuf->st_atime;
-    v9stat->mtime = stbuf->st_mtime;
-    v9stat->length = stbuf->st_size;
-
-    v9fs_string_null(&v9stat->uid);
-    v9fs_string_null(&v9stat->gid);
-    v9fs_string_null(&v9stat->muid);
-
-    v9stat->n_uid = stbuf->st_uid;
-    v9stat->n_gid = stbuf->st_gid;
-    v9stat->n_muid = 0;
-
-    v9fs_string_null(&v9stat->extension);
-
-    if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
-        err = v9fs_do_readlink(s, name, &v9stat->extension);
-        if (err == -1) {
-            err = -errno;
-            return err;
-        }
-        v9stat->extension.data[err] = 0;
-        v9stat->extension.size = err;
-    } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
-        v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
-                S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
-                major(stbuf->st_rdev), minor(stbuf->st_rdev));
-    } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
-        v9fs_string_sprintf(&v9stat->extension, "%s %lu",
-                "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
-    }
-
-    str = strrchr(name->data, '/');
-    if (str) {
-        str += 1;
-    } else {
-        str = name->data;
-    }
-
-    v9fs_string_sprintf(&v9stat->name, "%s", str);
-
-    v9stat->size = 61 +
-        v9fs_string_size(&v9stat->name) +
-        v9fs_string_size(&v9stat->uid) +
-        v9fs_string_size(&v9stat->gid) +
-        v9fs_string_size(&v9stat->muid) +
-        v9fs_string_size(&v9stat->extension);
-    return 0;
-}
-
-#define P9_STATS_MODE          0x00000001ULL
-#define P9_STATS_NLINK         0x00000002ULL
-#define P9_STATS_UID           0x00000004ULL
-#define P9_STATS_GID           0x00000008ULL
-#define P9_STATS_RDEV          0x00000010ULL
-#define P9_STATS_ATIME         0x00000020ULL
-#define P9_STATS_MTIME         0x00000040ULL
-#define P9_STATS_CTIME         0x00000080ULL
-#define P9_STATS_INO           0x00000100ULL
-#define P9_STATS_SIZE          0x00000200ULL
-#define P9_STATS_BLOCKS        0x00000400ULL
-
-#define P9_STATS_BTIME         0x00000800ULL
-#define P9_STATS_GEN           0x00001000ULL
-#define P9_STATS_DATA_VERSION  0x00002000ULL
-
-#define P9_STATS_BASIC         0x000007ffULL /* Mask for fields up to BLOCKS */
-#define P9_STATS_ALL           0x00003fffULL /* Mask for All fields above */
-
-
-static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
-                            V9fsStatDotl *v9lstat)
-{
-    memset(v9lstat, 0, sizeof(*v9lstat));
-
-    v9lstat->st_mode = stbuf->st_mode;
-    v9lstat->st_nlink = stbuf->st_nlink;
-    v9lstat->st_uid = stbuf->st_uid;
-    v9lstat->st_gid = stbuf->st_gid;
-    v9lstat->st_rdev = stbuf->st_rdev;
-    v9lstat->st_size = stbuf->st_size;
-    v9lstat->st_blksize = stbuf->st_blksize;
-    v9lstat->st_blocks = stbuf->st_blocks;
-    v9lstat->st_atime_sec = stbuf->st_atime;
-    v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
-    v9lstat->st_mtime_sec = stbuf->st_mtime;
-    v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
-    v9lstat->st_ctime_sec = stbuf->st_ctime;
-    v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
-    /* Currently we only support BASIC fields in stat */
-    v9lstat->st_result_mask = P9_STATS_BASIC;
-
-    stat_to_qid(stbuf, &v9lstat->qid);
-}
-
-static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
-{
-    while (len && *iovcnt) {
-        if (len < sg->iov_len) {
-            sg->iov_len -= len;
-            sg->iov_base += len;
-            len = 0;
-        } else {
-            len -= sg->iov_len;
-            sg++;
-            *iovcnt -= 1;
-        }
-    }
-
-    return sg;
-}
-
-static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt)
-{
-    int i;
-    int total = 0;
-
-    for (i = 0; i < *cnt; i++) {
-        if ((total + sg[i].iov_len) > cap) {
-            sg[i].iov_len -= ((total + sg[i].iov_len) - cap);
-            i++;
-            break;
-        }
-        total += sg[i].iov_len;
-    }
-
-    *cnt = i;
-
-    return sg;
-}
-
-static void print_sg(struct iovec *sg, int cnt)
-{
-    int i;
-
-    printf("sg[%d]: {", cnt);
-    for (i = 0; i < cnt; i++) {
-        if (i) {
-            printf(", ");
-        }
-        printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
-    }
-    printf("}\n");
-}
-
-static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
-{
-    V9fsString str;
-    v9fs_string_init(&str);
-    v9fs_string_copy(&str, dst);
-    v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len);
-    v9fs_string_free(&str);
-}
-
-static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
-{
-    V9fsString version;
-    size_t offset = 7;
-
-    pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
-
-    if (!strcmp(version.data, "9P2000.u")) {
-        s->proto_version = V9FS_PROTO_2000U;
-    } else if (!strcmp(version.data, "9P2000.L")) {
-        s->proto_version = V9FS_PROTO_2000L;
-    } else {
-        v9fs_string_sprintf(&version, "unknown");
-    }
-
-    offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
-    complete_pdu(s, pdu, offset);
-
-    v9fs_string_free(&version);
-}
-
-static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid, afid, n_uname;
-    V9fsString uname, aname;
-    V9fsFidState *fidp;
-    V9fsQID qid;
-    size_t offset = 7;
-    ssize_t err;
-
-    pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
-
-    fidp = alloc_fid(s, fid);
-    if (fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    fidp->uid = n_uname;
-
-    v9fs_string_sprintf(&fidp->path, "%s", "/");
-    err = fid_to_qid(s, fidp, &qid);
-    if (err) {
-        err = -EINVAL;
-        free_fid(s, fid);
-        goto out;
-    }
-
-    offset += pdu_marshal(pdu, offset, "Q", &qid);
-
-    err = offset;
-out:
-    complete_pdu(s, pdu, err);
-    v9fs_string_free(&uname);
-    v9fs_string_free(&aname);
-}
-
-static void v9fs_stat_post_lstat(V9fsState *s, V9fsStatState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    err = stat_to_v9stat(s, &vs->fidp->path, &vs->stbuf, &vs->v9stat);
-    if (err) {
-        goto out;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "wS", 0, &vs->v9stat);
-    err = vs->offset;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_stat_free(&vs->v9stat);
-    qemu_free(vs);
-}
-
-static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsStatState *vs;
-    ssize_t err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    memset(&vs->v9stat, 0, sizeof(vs->v9stat));
-
-    pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
-    v9fs_stat_post_lstat(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_stat_free(&vs->v9stat);
-    qemu_free(vs);
-}
-
-static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs,
-                                                                int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl);
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl);
-    err = vs->offset;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_getattr(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsStatStateDotl *vs;
-    ssize_t err = 0;
-    V9fsFidState *fidp;
-    uint64_t request_mask;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl));
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask);
-
-    fidp = lookup_fid(s, fid);
-    if (fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    /* Currently we only support BASIC fields in stat, so there is no
-     * need to look at request_mask.
-     */
-    err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf);
-    v9fs_getattr_post_lstat(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-/* From Linux kernel code */
-#define ATTR_MODE    (1 << 0)
-#define ATTR_UID     (1 << 1)
-#define ATTR_GID     (1 << 2)
-#define ATTR_SIZE    (1 << 3)
-#define ATTR_ATIME   (1 << 4)
-#define ATTR_MTIME   (1 << 5)
-#define ATTR_CTIME   (1 << 6)
-#define ATTR_MASK    127
-#define ATTR_ATIME_SET  (1 << 7)
-#define ATTR_MTIME_SET  (1 << 8)
-
-static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
-                                                                  int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-    err = vs->offset;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    if (vs->v9iattr.valid & (ATTR_SIZE)) {
-        err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
-    }
-    v9fs_setattr_post_truncate(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
-                                                                   int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    /* If the only valid entry in iattr is ctime we can call
-     * chown(-1,-1) to update the ctime of the file
-     */
-    if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
-            ((vs->v9iattr.valid & ATTR_CTIME)
-            && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
-        if (!(vs->v9iattr.valid & ATTR_UID)) {
-            vs->v9iattr.uid = -1;
-        }
-        if (!(vs->v9iattr.valid & ATTR_GID)) {
-            vs->v9iattr.gid = -1;
-        }
-        err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
-                                                vs->v9iattr.gid);
-    }
-    v9fs_setattr_post_chown(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
-        struct timespec times[2];
-        if (vs->v9iattr.valid & ATTR_ATIME) {
-            if (vs->v9iattr.valid & ATTR_ATIME_SET) {
-                times[0].tv_sec = vs->v9iattr.atime_sec;
-                times[0].tv_nsec = vs->v9iattr.atime_nsec;
-            } else {
-                times[0].tv_nsec = UTIME_NOW;
-            }
-        } else {
-            times[0].tv_nsec = UTIME_OMIT;
-        }
-
-        if (vs->v9iattr.valid & ATTR_MTIME) {
-            if (vs->v9iattr.valid & ATTR_MTIME_SET) {
-                times[1].tv_sec = vs->v9iattr.mtime_sec;
-                times[1].tv_nsec = vs->v9iattr.mtime_nsec;
-            } else {
-                times[1].tv_nsec = UTIME_NOW;
-            }
-        } else {
-            times[1].tv_nsec = UTIME_OMIT;
-        }
-        err = v9fs_do_utimensat(s, &vs->fidp->path, times);
-    }
-    v9fs_setattr_post_utimensat(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsSetattrState *vs;
-    int err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    if (vs->v9iattr.valid & ATTR_MODE) {
-        err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
-    }
-
-    v9fs_setattr_post_chmod(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
-{
-    complete_pdu(s, vs->pdu, err);
-
-    if (vs->nwnames) {
-        for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) {
-            v9fs_string_free(&vs->wnames[vs->name_idx]);
-        }
-
-        qemu_free(vs->wnames);
-        qemu_free(vs->qids);
-    }
-}
-
-static void v9fs_walk_marshal(V9fsWalkState *vs)
-{
-    int i;
-    vs->offset = 7;
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "w", vs->nwnames);
-
-    for (i = 0; i < vs->nwnames; i++) {
-        vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qids[i]);
-    }
-}
-
-static void v9fs_walk_post_newfid_lstat(V9fsState *s, V9fsWalkState *vs,
-                                                                int err)
-{
-    if (err == -1) {
-        free_fid(s, vs->newfidp->fid);
-        v9fs_string_free(&vs->path);
-        err = -ENOENT;
-        goto out;
-    }
-
-    stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
-
-    vs->name_idx++;
-    if (vs->name_idx < vs->nwnames) {
-        v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
-                                            vs->wnames[vs->name_idx].data);
-        v9fs_string_copy(&vs->newfidp->path, &vs->path);
-
-        err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
-        v9fs_walk_post_newfid_lstat(s, vs, err);
-        return;
-    }
-
-    v9fs_string_free(&vs->path);
-    v9fs_walk_marshal(vs);
-    err = vs->offset;
-out:
-    v9fs_walk_complete(s, vs, err);
-}
-
-static void v9fs_walk_post_oldfid_lstat(V9fsState *s, V9fsWalkState *vs,
-        int err)
-{
-    if (err == -1) {
-        v9fs_string_free(&vs->path);
-        err = -ENOENT;
-        goto out;
-    }
-
-    stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
-    vs->name_idx++;
-    if (vs->name_idx < vs->nwnames) {
-
-        v9fs_string_sprintf(&vs->path, "%s/%s",
-                vs->fidp->path.data, vs->wnames[vs->name_idx].data);
-        v9fs_string_copy(&vs->fidp->path, &vs->path);
-
-        err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
-        v9fs_walk_post_oldfid_lstat(s, vs, err);
-        return;
-    }
-
-    v9fs_string_free(&vs->path);
-    v9fs_walk_marshal(vs);
-    err = vs->offset;
-out:
-    v9fs_walk_complete(s, vs, err);
-}
-
-static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid, newfid;
-    V9fsWalkState *vs;
-    int err = 0;
-    int i;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->wnames = NULL;
-    vs->qids = NULL;
-    vs->offset = 7;
-
-    vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid,
-                                            &newfid, &vs->nwnames);
-
-    if (vs->nwnames) {
-        vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames);
-
-        vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames);
-
-        for (i = 0; i < vs->nwnames; i++) {
-            vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s",
-                                            &vs->wnames[i]);
-        }
-    }
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    /* FIXME: is this really valid? */
-    if (fid == newfid) {
-
-        BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
-        v9fs_string_init(&vs->path);
-        vs->name_idx = 0;
-
-        if (vs->name_idx < vs->nwnames) {
-            v9fs_string_sprintf(&vs->path, "%s/%s",
-                vs->fidp->path.data, vs->wnames[vs->name_idx].data);
-            v9fs_string_copy(&vs->fidp->path, &vs->path);
-
-            err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
-            v9fs_walk_post_oldfid_lstat(s, vs, err);
-            return;
-        }
-    } else {
-        vs->newfidp = alloc_fid(s, newfid);
-        if (vs->newfidp == NULL) {
-            err = -EINVAL;
-            goto out;
-        }
-
-        vs->newfidp->uid = vs->fidp->uid;
-        v9fs_string_init(&vs->path);
-        vs->name_idx = 0;
-        v9fs_string_copy(&vs->newfidp->path, &vs->fidp->path);
-
-        if (vs->name_idx < vs->nwnames) {
-            v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
-                                vs->wnames[vs->name_idx].data);
-            v9fs_string_copy(&vs->newfidp->path, &vs->path);
-
-            err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
-            v9fs_walk_post_newfid_lstat(s, vs, err);
-            return;
-        }
-    }
-
-    v9fs_walk_marshal(vs);
-    err = vs->offset;
-out:
-    v9fs_walk_complete(s, vs, err);
-}
-
-static int32_t get_iounit(V9fsState *s, V9fsString *name)
-{
-    struct statfs stbuf;
-    int32_t iounit = 0;
-
-    /*
-     * iounit should be multiples of f_bsize (host filesystem block size
-     * and as well as less than (client msize - P9_IOHDRSZ))
-     */
-    if (!v9fs_do_statfs(s, name, &stbuf)) {
-        iounit = stbuf.f_bsize;
-        iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
-    }
-
-    if (!iounit) {
-        iounit = s->msize - P9_IOHDRSZ;
-    }
-    return iounit;
-}
-
-static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
-{
-    if (vs->fidp->fs.dir == NULL) {
-        err = -errno;
-        goto out;
-    }
-    vs->fidp->fid_type = P9_FID_DIR;
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-
-}
-
-static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs)
-{
-    int err;
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
-    err = vs->offset;
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
-{
-    if (vs->fidp->fs.fd == -1) {
-        err = -errno;
-        goto out;
-    }
-    vs->fidp->fid_type = P9_FID_FILE;
-    vs->iounit = get_iounit(s, &vs->fidp->path);
-    v9fs_open_post_getiounit(s, vs);
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
-{
-    int flags;
-
-    if (err) {
-        err = -errno;
-        goto out;
-    }
-
-    stat_to_qid(&vs->stbuf, &vs->qid);
-
-    if (S_ISDIR(vs->stbuf.st_mode)) {
-        vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path);
-        v9fs_open_post_opendir(s, vs, err);
-    } else {
-        if (s->proto_version == V9FS_PROTO_2000L) {
-            flags = vs->mode;
-            flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
-            /* Ignore direct disk access hint until the server supports it. */
-            flags &= ~O_DIRECT;
-        } else {
-            flags = omode_to_uflags(vs->mode);
-        }
-        vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags);
-        v9fs_open_post_open(s, vs, err);
-    }
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsOpenState *vs;
-    ssize_t err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-    vs->mode = 0;
-
-    if (s->proto_version == V9FS_PROTO_2000L) {
-        pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode);
-    } else {
-        pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
-    }
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
-
-    err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
-
-    v9fs_open_post_lstat(s, vs, err);
-    return;
-out:
-    complete_pdu(s, pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
-{
-    if (err == 0) {
-        v9fs_string_copy(&vs->fidp->path, &vs->fullname);
-        stat_to_qid(&vs->stbuf, &vs->qid);
-        vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
-                &vs->iounit);
-        err = vs->offset;
-    } else {
-        vs->fidp->fid_type = P9_FID_NONE;
-        err = -errno;
-        if (vs->fidp->fs.fd > 0) {
-            close(vs->fidp->fs.fd);
-        }
-    }
-
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    v9fs_string_free(&vs->fullname);
-    qemu_free(vs);
-}
-
-static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs,
-        int err)
-{
-    if (err) {
-        err = -errno;
-        goto out;
-    }
-    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
-
-out:
-    v9fs_post_lcreate(s, vs, err);
-}
-
-static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs,
-        int err)
-{
-    if (vs->fidp->fs.fd == -1) {
-        err = -errno;
-        goto out;
-    }
-    vs->fidp->fid_type = P9_FID_FILE;
-    vs->iounit =  get_iounit(s, &vs->fullname);
-    v9fs_lcreate_post_get_iounit(s, vs, err);
-    return;
-
-out:
-    v9fs_post_lcreate(s, vs, err);
-}
-
-static void v9fs_lcreate(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t dfid, flags, mode;
-    gid_t gid;
-    V9fsLcreateState *vs;
-    ssize_t err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    v9fs_string_init(&vs->fullname);
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags,
-            &mode, &gid);
-
-    vs->fidp = lookup_fid(s, dfid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
-             vs->name.data);
-
-    /* Ignore direct disk access hint until the server supports it. */
-    flags &= ~O_DIRECT;
-
-    vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
-            gid, flags, mode);
-    v9fs_lcreate_post_do_open2(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_post_do_fsync(V9fsState *s, V9fsPDU *pdu, int err)
-{
-    if (err == -1) {
-        err = -errno;
-    }
-    complete_pdu(s, pdu, err);
-}
-
-static void v9fs_fsync(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    size_t offset = 7;
-    V9fsFidState *fidp;
-    int datasync;
-    int err;
-
-    pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
-    fidp = lookup_fid(s, fid);
-    if (fidp == NULL) {
-        err = -ENOENT;
-        v9fs_post_do_fsync(s, pdu, err);
-        return;
-    }
-    err = v9fs_do_fsync(s, fidp->fs.fd, datasync);
-    v9fs_post_do_fsync(s, pdu, err);
-}
-
-static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    size_t offset = 7;
-    int err;
-
-    pdu_unmarshal(pdu, offset, "d", &fid);
-
-    err = free_fid(s, fid);
-    if (err < 0) {
-        goto out;
-    }
-
-    offset = 7;
-    err = offset;
-out:
-    complete_pdu(s, pdu, err);
-}
-
-static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t);
-
-static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
-{
-    if (err) {
-        goto out;
-    }
-    v9fs_stat_free(&vs->v9stat);
-    v9fs_string_free(&vs->name);
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
-    vs->offset += vs->count;
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-    return;
-}
-
-static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
-                                    ssize_t err)
-{
-    if (err) {
-        err = -errno;
-        goto out;
-    }
-    err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat);
-    if (err) {
-        goto out;
-    }
-
-    vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S",
-                            &vs->v9stat);
-    if ((vs->len != (vs->v9stat.size + 2)) ||
-            ((vs->count + vs->len) > vs->max_count)) {
-        v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
-        v9fs_read_post_seekdir(s, vs, err);
-        return;
-    }
-    vs->count += vs->len;
-    v9fs_stat_free(&vs->v9stat);
-    v9fs_string_free(&vs->name);
-    vs->dir_pos = vs->dent->d_off;
-    vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
-    v9fs_read_post_readdir(s, vs, err);
-    return;
-out:
-    v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
-    v9fs_read_post_seekdir(s, vs, err);
-    return;
-
-}
-
-static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
-{
-    if (vs->dent) {
-        memset(&vs->v9stat, 0, sizeof(vs->v9stat));
-        v9fs_string_init(&vs->name);
-        v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data,
-                            vs->dent->d_name);
-        err = v9fs_do_lstat(s, &vs->name, &vs->stbuf);
-        v9fs_read_post_dir_lstat(s, vs, err);
-        return;
-    }
-
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
-    vs->offset += vs->count;
-    err = vs->offset;
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-    return;
-}
-
-static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
-{
-    vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
-    v9fs_read_post_readdir(s, vs, err);
-    return;
-}
-
-static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
-                                       ssize_t err)
-{
-    vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
-    v9fs_read_post_telldir(s, vs, err);
-    return;
-}
-
-static void v9fs_read_post_preadv(V9fsState *s, V9fsReadState *vs, ssize_t err)
-{
-    if (err  < 0) {
-        /* IO error return the error */
-        err = -errno;
-        goto out;
-    }
-    vs->total += vs->len;
-    vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
-    if (vs->total < vs->count && vs->len > 0) {
-        do {
-            if (0) {
-                print_sg(vs->sg, vs->cnt);
-            }
-            vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
-                      vs->off);
-            if (vs->len > 0) {
-                vs->off += vs->len;
-            }
-        } while (vs->len == -1 && errno == EINTR);
-        if (vs->len == -1) {
-            err  = -errno;
-        }
-        v9fs_read_post_preadv(s, vs, err);
-        return;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
-    vs->offset += vs->count;
-    err = vs->offset;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
-{
-    ssize_t err = 0;
-    int read_count;
-    int64_t xattr_len;
-
-    xattr_len = vs->fidp->fs.xattr.len;
-    read_count = xattr_len - vs->off;
-    if (read_count > vs->count) {
-        read_count = vs->count;
-    } else if (read_count < 0) {
-        /*
-         * read beyond XATTR value
-         */
-        read_count = 0;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
-    vs->offset += pdu_pack(vs->pdu, vs->offset,
-                           ((char *)vs->fidp->fs.xattr.value) + vs->off,
-                           read_count);
-    err = vs->offset;
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsReadState *vs;
-    ssize_t err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-    vs->total = 0;
-    vs->len = 0;
-    vs->count = 0;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    if (vs->fidp->fid_type == P9_FID_DIR) {
-        vs->max_count = vs->count;
-        vs->count = 0;
-        if (vs->off == 0) {
-            v9fs_do_rewinddir(s, vs->fidp->fs.dir);
-        }
-        v9fs_read_post_rewinddir(s, vs, err);
-        return;
-    } else if (vs->fidp->fid_type == P9_FID_FILE) {
-        vs->sg = vs->iov;
-        pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
-        vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
-        if (vs->total <= vs->count) {
-            vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
-                                    vs->off);
-            if (vs->len > 0) {
-                vs->off += vs->len;
-            }
-            err = vs->len;
-            v9fs_read_post_preadv(s, vs, err);
-        }
-        return;
-    } else if (vs->fidp->fid_type == P9_FID_XATTR) {
-        v9fs_xattr_read(s, vs);
-        return;
-    } else {
-        err = -EINVAL;
-    }
-out:
-    complete_pdu(s, pdu, err);
-    qemu_free(vs);
-}
-
-typedef struct V9fsReadDirState {
-    V9fsPDU *pdu;
-    V9fsFidState *fidp;
-    V9fsQID qid;
-    off_t saved_dir_pos;
-    struct dirent *dent;
-    int32_t count;
-    int32_t max_count;
-    size_t offset;
-    int64_t initial_offset;
-    V9fsString name;
-} V9fsReadDirState;
-
-static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs)
-{
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
-    vs->offset += vs->count;
-    complete_pdu(s, vs->pdu, vs->offset);
-    qemu_free(vs);
-    return;
-}
-
-/* Size of each dirent on the wire: size of qid (13) + size of offset (8)
- * size of type (1) + size of name.size (2) + strlen(name.data)
- */
-#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data))
-
-static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs)
-{
-    int len;
-    size_t size;
-
-    if (vs->dent) {
-        v9fs_string_init(&vs->name);
-        v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name);
-
-        if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) {
-            /* Ran out of buffer. Set dir back to old position and return */
-            v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos);
-            v9fs_readdir_post_seekdir(s, vs);
-            return;
-        }
-
-        /* Fill up just the path field of qid because the client uses
-         * only that. To fill the entire qid structure we will have
-         * to stat each dirent found, which is expensive
-         */
-        size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path));
-        memcpy(&vs->qid.path, &vs->dent->d_ino, size);
-        /* Fill the other fields with dummy values */
-        vs->qid.type = 0;
-        vs->qid.version = 0;
-
-        len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs",
-                              &vs->qid, vs->dent->d_off,
-                              vs->dent->d_type, &vs->name);
-        vs->count += len;
-        v9fs_string_free(&vs->name);
-        vs->saved_dir_pos = vs->dent->d_off;
-        vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
-        v9fs_readdir_post_readdir(s, vs);
-        return;
-    }
-
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
-    vs->offset += vs->count;
-    complete_pdu(s, vs->pdu, vs->offset);
-    qemu_free(vs);
-    return;
-}
-
-static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs)
-{
-    vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
-    v9fs_readdir_post_readdir(s, vs);
-    return;
-}
-
-static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs)
-{
-    vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
-    v9fs_readdir_post_telldir(s, vs);
-    return;
-}
-
-static void v9fs_readdir(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsReadDirState *vs;
-    ssize_t err = 0;
-    size_t offset = 7;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-    vs->count = 0;
-
-    pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset,
-                                                        &vs->max_count);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL || !(vs->fidp->fs.dir)) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    if (vs->initial_offset == 0) {
-        v9fs_do_rewinddir(s, vs->fidp->fs.dir);
-    } else {
-        v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset);
-    }
-
-    v9fs_readdir_post_setdir(s, vs);
-    return;
-
-out:
-    complete_pdu(s, pdu, err);
-    qemu_free(vs);
-    return;
-}
-
-static void v9fs_write_post_pwritev(V9fsState *s, V9fsWriteState *vs,
-                                   ssize_t err)
-{
-    if (err  < 0) {
-        /* IO error return the error */
-        err = -errno;
-        goto out;
-    }
-    vs->total += vs->len;
-    vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
-    if (vs->total < vs->count && vs->len > 0) {
-        do {
-            if (0) {
-                print_sg(vs->sg, vs->cnt);
-            }
-            vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
-                      vs->off);
-            if (vs->len > 0) {
-                vs->off += vs->len;
-            }
-        } while (vs->len == -1 && errno == EINTR);
-        if (vs->len == -1) {
-            err  = -errno;
-        }
-        v9fs_write_post_pwritev(s, vs, err);
-        return;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs)
-{
-    int i, to_copy;
-    ssize_t err = 0;
-    int write_count;
-    int64_t xattr_len;
-
-    xattr_len = vs->fidp->fs.xattr.len;
-    write_count = xattr_len - vs->off;
-    if (write_count > vs->count) {
-        write_count = vs->count;
-    } else if (write_count < 0) {
-        /*
-         * write beyond XATTR value len specified in
-         * xattrcreate
-         */
-        err = -ENOSPC;
-        goto out;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count);
-    err = vs->offset;
-    vs->fidp->fs.xattr.copied_len += write_count;
-    /*
-     * Now copy the content from sg list
-     */
-    for (i = 0; i < vs->cnt; i++) {
-        if (write_count > vs->sg[i].iov_len) {
-            to_copy = vs->sg[i].iov_len;
-        } else {
-            to_copy = write_count;
-        }
-        memcpy((char *)vs->fidp->fs.xattr.value + vs->off,
-               vs->sg[i].iov_base, to_copy);
-        /* updating vs->off since we are not using below */
-        vs->off += to_copy;
-        write_count -= to_copy;
-    }
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsWriteState *vs;
-    ssize_t err;
-
-    vs = qemu_malloc(sizeof(*vs));
-
-    vs->pdu = pdu;
-    vs->offset = 7;
-    vs->sg = vs->iov;
-    vs->total = 0;
-    vs->len = 0;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
-                  vs->sg, &vs->cnt);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    if (vs->fidp->fid_type == P9_FID_FILE) {
-        if (vs->fidp->fs.fd == -1) {
-            err = -EINVAL;
-            goto out;
-        }
-    } else if (vs->fidp->fid_type == P9_FID_XATTR) {
-        /*
-         * setxattr operation
-         */
-        v9fs_xattr_write(s, vs);
-        return;
-    } else {
-        err = -EINVAL;
-        goto out;
-    }
-    vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
-    if (vs->total <= vs->count) {
-        vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt, vs->off);
-        if (vs->len > 0) {
-            vs->off += vs->len;
-        }
-        err = vs->len;
-        v9fs_write_post_pwritev(s, vs, err);
-    }
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs)
-{
-    int err;
-    v9fs_string_copy(&vs->fidp->path, &vs->fullname);
-    stat_to_qid(&vs->stbuf, &vs->qid);
-
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
-    err = vs->offset;
-
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    v9fs_string_free(&vs->extension);
-    v9fs_string_free(&vs->fullname);
-    qemu_free(vs);
-}
-
-static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
-{
-    if (err == 0) {
-        vs->iounit = get_iounit(s, &vs->fidp->path);
-        v9fs_create_post_getiounit(s, vs);
-        return;
-    }
-
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    v9fs_string_free(&vs->extension);
-    v9fs_string_free(&vs->fullname);
-    qemu_free(vs);
-}
-
-static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err)
-{
-    if (err) {
-        err = -errno;
-    }
-    v9fs_post_create(s, vs, err);
-}
-
-static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs,
-                                                                    int err)
-{
-    if (!vs->fidp->fs.dir) {
-        err = -errno;
-    }
-    vs->fidp->fid_type = P9_FID_DIR;
-    v9fs_post_create(s, vs, err);
-}
-
-static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs,
-                                                                    int err)
-{
-    if (err) {
-        err = -errno;
-        goto out;
-    }
-
-    vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname);
-    v9fs_create_post_opendir(s, vs, err);
-    return;
-
-out:
-    v9fs_post_create(s, vs, err);
-}
-
-static void v9fs_create_post_mkdir(V9fsState *s, V9fsCreateState *vs, int err)
-{
-    if (err) {
-        err = -errno;
-        goto out;
-    }
-
-    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
-    v9fs_create_post_dir_lstat(s, vs, err);
-    return;
-
-out:
-    v9fs_post_create(s, vs, err);
-}
-
-static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err)
-{
-    if (err) {
-        vs->fidp->fid_type = P9_FID_NONE;
-        close(vs->fidp->fs.fd);
-        err = -errno;
-    }
-    v9fs_post_create(s, vs, err);
-    return;
-}
-
-static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err)
-{
-    if (vs->fidp->fs.fd == -1) {
-        err = -errno;
-        goto out;
-    }
-    vs->fidp->fid_type = P9_FID_FILE;
-    err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
-    v9fs_create_post_fstat(s, vs, err);
-
-    return;
-
-out:
-    v9fs_post_create(s, vs, err);
-
-}
-
-static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
-{
-
-    if (err == 0 || errno != ENOENT) {
-        err = -errno;
-        goto out;
-    }
-
-    if (vs->perm & P9_STAT_MODE_DIR) {
-        err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777,
-                vs->fidp->uid, -1);
-        v9fs_create_post_mkdir(s, vs, err);
-    } else if (vs->perm & P9_STAT_MODE_SYMLINK) {
-        err = v9fs_do_symlink(s, vs->fidp, vs->extension.data,
-                vs->fullname.data, -1);
-        v9fs_create_post_perms(s, vs, err);
-    } else if (vs->perm & P9_STAT_MODE_LINK) {
-        int32_t nfid = atoi(vs->extension.data);
-        V9fsFidState *nfidp = lookup_fid(s, nfid);
-        if (nfidp == NULL) {
-            err = -errno;
-            v9fs_post_create(s, vs, err);
-        }
-        err = v9fs_do_link(s, &nfidp->path, &vs->fullname);
-        v9fs_create_post_perms(s, vs, err);
-    } else if (vs->perm & P9_STAT_MODE_DEVICE) {
-        char ctype;
-        uint32_t major, minor;
-        mode_t nmode = 0;
-
-        if (sscanf(vs->extension.data, "%c %u %u", &ctype, &major,
-                                        &minor) != 3) {
-            err = -errno;
-            v9fs_post_create(s, vs, err);
-        }
-
-        switch (ctype) {
-        case 'c':
-            nmode = S_IFCHR;
-            break;
-        case 'b':
-            nmode = S_IFBLK;
-            break;
-        default:
-            err = -EIO;
-            v9fs_post_create(s, vs, err);
-        }
-
-        nmode |= vs->perm & 0777;
-        err = v9fs_do_mknod(s, vs->fullname.data, nmode,
-                makedev(major, minor), vs->fidp->uid, -1);
-        v9fs_create_post_perms(s, vs, err);
-    } else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) {
-        err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777),
-                0, vs->fidp->uid, -1);
-        v9fs_post_create(s, vs, err);
-    } else if (vs->perm & P9_STAT_MODE_SOCKET) {
-        err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777),
-                0, vs->fidp->uid, -1);
-        v9fs_post_create(s, vs, err);
-    } else {
-        vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
-                -1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm);
-
-        v9fs_create_post_open2(s, vs, err);
-    }
-
-    return;
-
-out:
-    v9fs_post_create(s, vs, err);
-}
-
-static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsCreateState *vs;
-    int err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    v9fs_string_init(&vs->fullname);
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dsdbs", &fid, &vs->name,
-                                &vs->perm, &vs->mode, &vs->extension);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
-                                                        vs->name.data);
-
-    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
-    v9fs_create_post_lstat(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    v9fs_string_free(&vs->extension);
-    qemu_free(vs);
-}
-
-static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err)
-{
-    if (err == 0) {
-        stat_to_qid(&vs->stbuf, &vs->qid);
-        vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
-        err = vs->offset;
-    } else {
-        err = -errno;
-    }
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    v9fs_string_free(&vs->symname);
-    v9fs_string_free(&vs->fullname);
-    qemu_free(vs);
-}
-
-static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs,
-        int err)
-{
-    if (err) {
-        goto out;
-    }
-    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
-out:
-    v9fs_post_symlink(s, vs, err);
-}
-
-static void v9fs_symlink(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t dfid;
-    V9fsSymlinkState *vs;
-    int err = 0;
-    gid_t gid;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    v9fs_string_init(&vs->fullname);
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name,
-            &vs->symname, &gid);
-
-    vs->dfidp = lookup_fid(s, dfid);
-    if (vs->dfidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data,
-            vs->name.data);
-    err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data,
-            vs->fullname.data, gid);
-    v9fs_symlink_post_do_symlink(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    v9fs_string_free(&vs->symname);
-    qemu_free(vs);
-}
-
-static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
-{
-    /* A nop call with no return */
-    complete_pdu(s, pdu, 7);
-}
-
-static void v9fs_link(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t dfid, oldfid;
-    V9fsFidState *dfidp, *oldfidp;
-    V9fsString name, fullname;
-    size_t offset = 7;
-    int err = 0;
-
-    v9fs_string_init(&fullname);
-
-    pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
-
-    dfidp = lookup_fid(s, dfid);
-    if (dfidp == NULL) {
-        err = -errno;
-        goto out;
-    }
-
-    oldfidp = lookup_fid(s, oldfid);
-    if (oldfidp == NULL) {
-        err = -errno;
-        goto out;
-    }
-
-    v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
-    err = offset;
-    err = v9fs_do_link(s, &oldfidp->path, &fullname);
-    if (err) {
-        err = -errno;
-    }
-    v9fs_string_free(&fullname);
-
-out:
-    v9fs_string_free(&name);
-    complete_pdu(s, pdu, err);
-}
-
-static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs,
-                                                                int err)
-{
-    if (err < 0) {
-        err = -errno;
-    } else {
-        err = vs->offset;
-    }
-
-    /* For TREMOVE we need to clunk the fid even on failed remove */
-    free_fid(s, vs->fidp->fid);
-
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsRemoveState *vs;
-    int err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    err = v9fs_do_remove(s, &vs->fidp->path);
-    v9fs_remove_post_remove(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat_post_truncate(V9fsState *s, V9fsWstatState *vs, int err)
-{
-    if (err < 0) {
-        goto out;
-    }
-
-    err = vs->offset;
-
-out:
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err)
-{
-    if (err < 0) {
-        goto out;
-    }
-    if (vs->v9stat.length != -1) {
-        if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) {
-            err = -errno;
-        }
-    }
-    v9fs_wstat_post_truncate(s, vs, err);
-    return;
-
-out:
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs)
-{
-    int err = 0;
-    char *old_name, *new_name;
-    char *end;
-
-    if (vs->newdirfid != -1) {
-        V9fsFidState *dirfidp;
-        dirfidp = lookup_fid(s, vs->newdirfid);
-
-        if (dirfidp == NULL) {
-            err = -ENOENT;
-            goto out;
-        }
-
-        BUG_ON(dirfidp->fid_type != P9_FID_NONE);
-
-        new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2);
-
-        strcpy(new_name, dirfidp->path.data);
-        strcat(new_name, "/");
-        strcat(new_name + dirfidp->path.size, vs->name.data);
-    } else {
-        old_name = vs->fidp->path.data;
-        end = strrchr(old_name, '/');
-        if (end) {
-            end++;
-        } else {
-            end = old_name;
-        }
-        new_name = qemu_mallocz(end - old_name + vs->name.size + 1);
-
-        strncat(new_name, old_name, end - old_name);
-        strncat(new_name + (end - old_name), vs->name.data, vs->name.size);
-    }
-
-    v9fs_string_free(&vs->name);
-    vs->name.data = qemu_strdup(new_name);
-    vs->name.size = strlen(new_name);
-
-    if (strcmp(new_name, vs->fidp->path.data) != 0) {
-        if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) {
-            err = -errno;
-        } else {
-            V9fsFidState *fidp;
-            /*
-            * Fixup fid's pointing to the old name to
-            * start pointing to the new name
-            */
-            for (fidp = s->fid_list; fidp; fidp = fidp->next) {
-                if (vs->fidp == fidp) {
-                    /*
-                    * we replace name of this fid towards the end
-                    * so that our below strcmp will work
-                    */
-                    continue;
-                }
-                if (!strncmp(vs->fidp->path.data, fidp->path.data,
-                    strlen(vs->fidp->path.data))) {
-                    /* replace the name */
-                    v9fs_fix_path(&fidp->path, &vs->name,
-                                  strlen(vs->fidp->path.data));
-                }
-            }
-            v9fs_string_copy(&vs->fidp->path, &vs->name);
-        }
-    }
-out:
-    v9fs_string_free(&vs->name);
-    return err;
-}
-
-static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err)
-{
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
-{
-    if (err < 0) {
-        goto out;
-    }
-
-    if (vs->v9stat.name.size != 0) {
-        V9fsRenameState *vr;
-
-        vr = qemu_mallocz(sizeof(V9fsRenameState));
-        vr->newdirfid = -1;
-        vr->pdu = vs->pdu;
-        vr->fidp = vs->fidp;
-        vr->offset = vs->offset;
-        vr->name.size = vs->v9stat.name.size;
-        vr->name.data = qemu_strdup(vs->v9stat.name.data);
-
-        err = v9fs_complete_rename(s, vr);
-        qemu_free(vr);
-    }
-    v9fs_wstat_post_rename(s, vs, err);
-    return;
-
-out:
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_rename(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsRenameState *vs;
-    ssize_t err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
-
-    err = v9fs_complete_rename(s, vs);
-    v9fs_rename_post_rename(s, vs, err);
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err)
-{
-    if (err < 0) {
-        goto out;
-    }
-
-    if (vs->v9stat.n_gid != -1 || vs->v9stat.n_uid != -1) {
-        if (v9fs_do_chown(s, &vs->fidp->path, vs->v9stat.n_uid,
-                    vs->v9stat.n_gid)) {
-            err = -errno;
-        }
-    }
-    v9fs_wstat_post_chown(s, vs, err);
-    return;
-
-out:
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err)
-{
-    if (err < 0) {
-        goto out;
-    }
-
-    if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) {
-        struct timespec times[2];
-        if (vs->v9stat.atime != -1) {
-            times[0].tv_sec = vs->v9stat.atime;
-            times[0].tv_nsec = 0;
-        } else {
-            times[0].tv_nsec = UTIME_OMIT;
-        }
-        if (vs->v9stat.mtime != -1) {
-            times[1].tv_sec = vs->v9stat.mtime;
-            times[1].tv_nsec = 0;
-        } else {
-            times[1].tv_nsec = UTIME_OMIT;
-        }
-
-        if (v9fs_do_utimensat(s, &vs->fidp->path, times)) {
-            err = -errno;
-        }
-    }
-
-    v9fs_wstat_post_utime(s, vs, err);
-    return;
-
-out:
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat_post_fsync(V9fsState *s, V9fsWstatState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-    }
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat_post_lstat(V9fsState *s, V9fsWstatState *vs, int err)
-{
-    uint32_t v9_mode;
-
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    v9_mode = stat_to_v9mode(&vs->stbuf);
-
-    if ((vs->v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
-        (v9_mode & P9_STAT_MODE_TYPE_BITS)) {
-            /* Attempting to change the type */
-            err = -EIO;
-            goto out;
-    }
-
-    if (v9fs_do_chmod(s, &vs->fidp->path, v9mode_to_mode(vs->v9stat.mode,
-                    &vs->v9stat.extension))) {
-            err = -errno;
-     }
-    v9fs_wstat_post_chmod(s, vs, err);
-    return;
-
-out:
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsWstatState *vs;
-    int err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    pdu_unmarshal(pdu, vs->offset, "dwS", &fid, &vs->unused, &vs->v9stat);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    /* do we need to sync the file? */
-    if (donttouch_stat(&vs->v9stat)) {
-        err = v9fs_do_fsync(s, vs->fidp->fs.fd, 0);
-        v9fs_wstat_post_fsync(s, vs, err);
-        return;
-    }
-
-    if (vs->v9stat.mode != -1) {
-        err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
-        v9fs_wstat_post_lstat(s, vs, err);
-        return;
-    }
-
-    v9fs_wstat_post_chmod(s, vs, err);
-    return;
-
-out:
-    v9fs_stat_free(&vs->v9stat);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err)
-{
-    int32_t bsize_factor;
-
-    if (err) {
-        err = -errno;
-        goto out;
-    }
-
-    /*
-     * compute bsize factor based on host file system block size
-     * and client msize
-     */
-    bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize;
-    if (!bsize_factor) {
-        bsize_factor = 1;
-    }
-    vs->v9statfs.f_type = vs->stbuf.f_type;
-    vs->v9statfs.f_bsize = vs->stbuf.f_bsize;
-    vs->v9statfs.f_bsize *= bsize_factor;
-    /*
-     * f_bsize is adjusted(multiplied) by bsize factor, so we need to
-     * adjust(divide) the number of blocks, free blocks and available
-     * blocks by bsize factor
-     */
-    vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor;
-    vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor;
-    vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor;
-    vs->v9statfs.f_files = vs->stbuf.f_files;
-    vs->v9statfs.f_ffree = vs->stbuf.f_ffree;
-    vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] |
-			(unsigned long long)vs->stbuf.f_fsid.__val[1] << 32;
-    vs->v9statfs.f_namelen = vs->stbuf.f_namelen;
-
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd",
-         vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks,
-         vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files,
-         vs->v9statfs.f_ffree, vs->v9statfs.fsid_val,
-         vs->v9statfs.f_namelen);
-
-out:
-    complete_pdu(s, vs->pdu, vs->offset);
-    qemu_free(vs);
-}
-
-static void v9fs_statfs(V9fsState *s, V9fsPDU *pdu)
-{
-    V9fsStatfsState *vs;
-    ssize_t err = 0;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    memset(&vs->v9statfs, 0, sizeof(vs->v9statfs));
-
-    pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid);
-
-    vs->fidp = lookup_fid(s, vs->fid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf);
-    v9fs_statfs_post_statfs(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    stat_to_qid(&vs->stbuf, &vs->qid);
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->fullname);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
-    v9fs_mknod_post_lstat(s, vs, err);
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->fullname);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_mknod(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsMkState *vs;
-    int err = 0;
-    V9fsFidState *fidp;
-    gid_t gid;
-    int mode;
-    int major, minor;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    v9fs_string_init(&vs->fullname);
-    pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode,
-        &major, &minor, &gid);
-
-    fidp = lookup_fid(s, fid);
-    if (fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
-    err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor),
-        fidp->uid, gid);
-    v9fs_mknod_post_mknod(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->fullname);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-/*
- * Implement posix byte range locking code
- * Server side handling of locking code is very simple, because 9p server in
- * QEMU can handle only one client. And most of the lock handling
- * (like conflict, merging) etc is done by the VFS layer itself, so no need to
- * do any thing in * qemu 9p server side lock code path.
- * So when a TLOCK request comes, always return success
- */
-
-static void v9fs_lock(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid, err = 0;
-    V9fsLockState *vs;
-
-    vs = qemu_mallocz(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    vs->flock = qemu_malloc(sizeof(*vs->flock));
-    pdu_unmarshal(vs->pdu, vs->offset, "dbdqqds", &fid, &vs->flock->type,
-                &vs->flock->flags, &vs->flock->start, &vs->flock->length,
-                            &vs->flock->proc_id, &vs->flock->client_id);
-
-    vs->status = P9_LOCK_ERROR;
-
-    /* We support only block flag now (that too ignored currently) */
-    if (vs->flock->flags & ~P9_LOCK_FLAGS_BLOCK) {
-        err = -EINVAL;
-        goto out;
-    }
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
-    if (err < 0) {
-        err = -errno;
-        goto out;
-    }
-    vs->status = P9_LOCK_SUCCESS;
-out:
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "b", vs->status);
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs->flock);
-    qemu_free(vs);
-}
-
-/*
- * When a TGETLOCK request comes, always return success because all lock
- * handling is done by client's VFS layer.
- */
-
-static void v9fs_getlock(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid, err = 0;
-    V9fsGetlockState *vs;
-
-    vs = qemu_mallocz(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    vs->glock = qemu_malloc(sizeof(*vs->glock));
-    pdu_unmarshal(vs->pdu, vs->offset, "dbqqds", &fid, &vs->glock->type,
-                &vs->glock->start, &vs->glock->length, &vs->glock->proc_id,
-		&vs->glock->client_id);
-
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
-    if (err < 0) {
-        err = -errno;
-        goto out;
-    }
-    vs->glock->type = F_UNLCK;
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "bqqds", vs->glock->type,
-                vs->glock->start, vs->glock->length, vs->glock->proc_id,
-		&vs->glock->client_id);
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs->glock);
-    qemu_free(vs);
-}
-
-static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    stat_to_qid(&vs->stbuf, &vs->qid);
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->fullname);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err)
-{
-    if (err == -1) {
-        err = -errno;
-        goto out;
-    }
-
-    err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
-    v9fs_mkdir_post_lstat(s, vs, err);
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->fullname);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_mkdir(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsMkState *vs;
-    int err = 0;
-    V9fsFidState *fidp;
-    gid_t gid;
-    int mode;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    v9fs_string_init(&vs->fullname);
-    pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode,
-        &gid);
-
-    fidp = lookup_fid(s, fid);
-    if (fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
-    err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid);
-    v9fs_mkdir_post_mkdir(s, vs, err);
-    return;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->fullname);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
-{
-
-    if (err < 0) {
-        err = -errno;
-        free_fid(s, vs->xattr_fidp->fid);
-        goto out;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-    return;
-}
-
-static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, ssize_t err)
-{
-    if (err < 0) {
-        err = -errno;
-        free_fid(s, vs->xattr_fidp->fid);
-        goto out;
-    }
-    /*
-     * Read the xattr value
-     */
-    vs->xattr_fidp->fs.xattr.len = vs->size;
-    vs->xattr_fidp->fid_type = P9_FID_XATTR;
-    vs->xattr_fidp->fs.xattr.copied_len = -1;
-    if (vs->size) {
-        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
-        err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
-                                &vs->name, vs->xattr_fidp->fs.xattr.value,
-                                vs->xattr_fidp->fs.xattr.len);
-    }
-    v9fs_post_xattr_getvalue(s, vs, err);
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_post_lxattr_getvalue(V9fsState *s,
-                                      V9fsXattrState *vs, int err)
-{
-    if (err < 0) {
-        err = -errno;
-        free_fid(s, vs->xattr_fidp->fid);
-        goto out;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-    return;
-}
-
-static void v9fs_post_lxattr_check(V9fsState *s,
-                                   V9fsXattrState *vs, ssize_t err)
-{
-    if (err < 0) {
-        err = -errno;
-        free_fid(s, vs->xattr_fidp->fid);
-        goto out;
-    }
-    /*
-     * Read the xattr value
-     */
-    vs->xattr_fidp->fs.xattr.len = vs->size;
-    vs->xattr_fidp->fid_type = P9_FID_XATTR;
-    vs->xattr_fidp->fs.xattr.copied_len = -1;
-    if (vs->size) {
-        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
-        err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
-                                 vs->xattr_fidp->fs.xattr.value,
-                                 vs->xattr_fidp->fs.xattr.len);
-    }
-    v9fs_post_lxattr_getvalue(s, vs, err);
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_xattrwalk(V9fsState *s, V9fsPDU *pdu)
-{
-    ssize_t err = 0;
-    V9fsXattrState *vs;
-    int32_t fid, newfid;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name);
-    vs->file_fidp = lookup_fid(s, fid);
-    if (vs->file_fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    vs->xattr_fidp = alloc_fid(s, newfid);
-    if (vs->xattr_fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path);
-    if (vs->name.data[0] == 0) {
-        /*
-         * listxattr request. Get the size first
-         */
-        vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
-                                      NULL, 0);
-        if (vs->size < 0) {
-            err = vs->size;
-        }
-        v9fs_post_lxattr_check(s, vs, err);
-        return;
-    } else {
-        /*
-         * specific xattr fid. We check for xattr
-         * presence also collect the xattr size
-         */
-        vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
-                                     &vs->name, NULL, 0);
-        if (vs->size < 0) {
-            err = vs->size;
-        }
-        v9fs_post_xattr_check(s, vs, err);
-        return;
-    }
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_xattrcreate(V9fsState *s, V9fsPDU *pdu)
-{
-    int flags;
-    int32_t fid;
-    ssize_t err = 0;
-    V9fsXattrState *vs;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dsqd",
-                  &fid, &vs->name, &vs->size, &flags);
-
-    vs->file_fidp = lookup_fid(s, fid);
-    if (vs->file_fidp == NULL) {
-        err = -EINVAL;
-        goto out;
-    }
-
-    /* Make the file fid point to xattr */
-    vs->xattr_fidp = vs->file_fidp;
-    vs->xattr_fidp->fid_type = P9_FID_XATTR;
-    vs->xattr_fidp->fs.xattr.copied_len = 0;
-    vs->xattr_fidp->fs.xattr.len = vs->size;
-    vs->xattr_fidp->fs.xattr.flags = flags;
-    v9fs_string_init(&vs->xattr_fidp->fs.xattr.name);
-    v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name);
-    if (vs->size)
-        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
-    else
-        vs->xattr_fidp->fs.xattr.value = NULL;
-
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->name);
-    qemu_free(vs);
-}
-
-static void v9fs_readlink_post_readlink(V9fsState *s, V9fsReadLinkState *vs,
-                                                    int err)
-{
-    if (err < 0) {
-        err = -errno;
-        goto out;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "s", &vs->target);
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_string_free(&vs->target);
-    qemu_free(vs);
-}
-
-static void v9fs_readlink(V9fsState *s, V9fsPDU *pdu)
-{
-    int32_t fid;
-    V9fsReadLinkState *vs;
-    int err = 0;
-    V9fsFidState *fidp;
-
-    vs = qemu_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
-
-    fidp = lookup_fid(s, fid);
-    if (fidp == NULL) {
-        err = -ENOENT;
-        goto out;
-    }
-
-    v9fs_string_init(&vs->target);
-    err = v9fs_do_readlink(s, &fidp->path, &vs->target);
-    v9fs_readlink_post_readlink(s, vs, err);
-    return;
-out:
-    complete_pdu(s, vs->pdu, err);
-    qemu_free(vs);
-}
-
-typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
-
-static pdu_handler_t *pdu_handlers[] = {
-    [P9_TREADDIR] = v9fs_readdir,
-    [P9_TSTATFS] = v9fs_statfs,
-    [P9_TGETATTR] = v9fs_getattr,
-    [P9_TSETATTR] = v9fs_setattr,
-    [P9_TXATTRWALK] = v9fs_xattrwalk,
-    [P9_TXATTRCREATE] = v9fs_xattrcreate,
-    [P9_TMKNOD] = v9fs_mknod,
-    [P9_TRENAME] = v9fs_rename,
-    [P9_TLOCK] = v9fs_lock,
-    [P9_TGETLOCK] = v9fs_getlock,
-    [P9_TREADLINK] = v9fs_readlink,
-    [P9_TMKDIR] = v9fs_mkdir,
-    [P9_TVERSION] = v9fs_version,
-    [P9_TLOPEN] = v9fs_open,
-    [P9_TATTACH] = v9fs_attach,
-    [P9_TSTAT] = v9fs_stat,
-    [P9_TWALK] = v9fs_walk,
-    [P9_TCLUNK] = v9fs_clunk,
-    [P9_TFSYNC] = v9fs_fsync,
-    [P9_TOPEN] = v9fs_open,
-    [P9_TREAD] = v9fs_read,
-#if 0
-    [P9_TAUTH] = v9fs_auth,
-#endif
-    [P9_TFLUSH] = v9fs_flush,
-    [P9_TLINK] = v9fs_link,
-    [P9_TSYMLINK] = v9fs_symlink,
-    [P9_TCREATE] = v9fs_create,
-    [P9_TLCREATE] = v9fs_lcreate,
-    [P9_TWRITE] = v9fs_write,
-    [P9_TWSTAT] = v9fs_wstat,
-    [P9_TREMOVE] = v9fs_remove,
-};
-
-static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
-{
-    pdu_handler_t *handler;
-
-    if (debug_9p_pdu) {
-        pprint_pdu(pdu);
-    }
-
-    BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
-
-    handler = pdu_handlers[pdu->id];
-    BUG_ON(handler == NULL);
-
-    handler(s, pdu);
-}
-
-static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
-{
-    V9fsState *s = (V9fsState *)vdev;
-    V9fsPDU *pdu;
-    ssize_t len;
-
-    while ((pdu = alloc_pdu(s)) &&
-            (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
-        uint8_t *ptr;
-
-        BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
-        BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
-
-        ptr = pdu->elem.out_sg[0].iov_base;
-
-        memcpy(&pdu->size, ptr, 4);
-        pdu->id = ptr[4];
-        memcpy(&pdu->tag, ptr + 5, 2);
-
-        submit_pdu(s, pdu);
-    }
-
-    free_pdu(s, pdu);
-}
-
-static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
-{
-    features |= 1 << VIRTIO_9P_MOUNT_TAG;
-    return features;
-}
-
-static V9fsState *to_virtio_9p(VirtIODevice *vdev)
-{
-    return (V9fsState *)vdev;
-}
-
-static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
-{
-    struct virtio_9p_config *cfg;
-    V9fsState *s = to_virtio_9p(vdev);
-
-    cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
-                        s->tag_len);
-    stw_raw(&cfg->tag_len, s->tag_len);
-    memcpy(cfg->tag, s->tag, s->tag_len);
-    memcpy(config, cfg, s->config_size);
-    qemu_free(cfg);
-}
-
-VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
- {
-    V9fsState *s;
-    int i, len;
-    struct stat stat;
-    FsTypeEntry *fse;
-
-
-    s = (V9fsState *)virtio_common_init("virtio-9p",
-                                    VIRTIO_ID_9P,
-                                    sizeof(struct virtio_9p_config)+
-                                    MAX_TAG_LEN,
-                                    sizeof(V9fsState));
-
-    /* initialize pdu allocator */
-    QLIST_INIT(&s->free_list);
-    for (i = 0; i < (MAX_REQ - 1); i++) {
-	QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
-    }
-
-    s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
-
-    fse = get_fsdev_fsentry(conf->fsdev_id);
-
-    if (!fse) {
-        /* We don't have a fsdev identified by fsdev_id */
-        fprintf(stderr, "Virtio-9p device couldn't find fsdev with the "
-                "id = %s\n", conf->fsdev_id ? conf->fsdev_id : "NULL");
-        exit(1);
-    }
-
-    if (!fse->path || !conf->tag) {
-        /* we haven't specified a mount_tag or the path */
-        fprintf(stderr, "fsdev with id %s needs path "
-                "and Virtio-9p device needs mount_tag arguments\n",
-                conf->fsdev_id);
-        exit(1);
-    }
-
-    if (!strcmp(fse->security_model, "passthrough")) {
-        /* Files on the Fileserver set to client user credentials */
-        s->ctx.fs_sm = SM_PASSTHROUGH;
-        s->ctx.xops = passthrough_xattr_ops;
-    } else if (!strcmp(fse->security_model, "mapped")) {
-        /* Files on the fileserver are set to QEMU credentials.
-         * Client user credentials are saved in extended attributes.
-         */
-        s->ctx.fs_sm = SM_MAPPED;
-        s->ctx.xops = mapped_xattr_ops;
-    } else if (!strcmp(fse->security_model, "none")) {
-        /*
-         * Files on the fileserver are set to QEMU credentials.
-         */
-        s->ctx.fs_sm = SM_NONE;
-        s->ctx.xops = none_xattr_ops;
-    } else {
-        fprintf(stderr, "Default to security_model=none. You may want"
-                " enable advanced security model using "
-                "security option:\n\t security_model=passthrough \n\t "
-                "security_model=mapped\n");
-        s->ctx.fs_sm = SM_NONE;
-        s->ctx.xops = none_xattr_ops;
-    }
-
-    if (lstat(fse->path, &stat)) {
-        fprintf(stderr, "share path %s does not exist\n", fse->path);
-        exit(1);
-    } else if (!S_ISDIR(stat.st_mode)) {
-        fprintf(stderr, "share path %s is not a directory \n", fse->path);
-        exit(1);
-    }
-
-    s->ctx.fs_root = qemu_strdup(fse->path);
-    len = strlen(conf->tag);
-    if (len > MAX_TAG_LEN) {
-        len = MAX_TAG_LEN;
-    }
-    /* s->tag is non-NULL terminated string */
-    s->tag = qemu_malloc(len);
-    memcpy(s->tag, conf->tag, len);
-    s->tag_len = len;
-    s->ctx.uid = -1;
-
-    s->ops = fse->ops;
-    s->vdev.get_features = virtio_9p_get_features;
-    s->config_size = sizeof(struct virtio_9p_config) +
-                        s->tag_len;
-    s->vdev.get_config = virtio_9p_get_config;
-
-    return &s->vdev;
-}
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
deleted file mode 100644
index 2ae4ce7..0000000
--- a/hw/virtio-9p.h
+++ /dev/null
@@ -1,507 +0,0 @@
-#ifndef _QEMU_VIRTIO_9P_H
-#define _QEMU_VIRTIO_9P_H
-
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <utime.h>
-
-#include "file-op-9p.h"
-
-/* The feature bitmap for virtio 9P */
-/* The mount point is specified in a config variable */
-#define VIRTIO_9P_MOUNT_TAG 0
-
-enum {
-    P9_TLERROR = 6,
-    P9_RLERROR,
-    P9_TSTATFS = 8,
-    P9_RSTATFS,
-    P9_TLOPEN = 12,
-    P9_RLOPEN,
-    P9_TLCREATE = 14,
-    P9_RLCREATE,
-    P9_TSYMLINK = 16,
-    P9_RSYMLINK,
-    P9_TMKNOD = 18,
-    P9_RMKNOD,
-    P9_TRENAME = 20,
-    P9_RRENAME,
-    P9_TREADLINK = 22,
-    P9_RREADLINK,
-    P9_TGETATTR = 24,
-    P9_RGETATTR,
-    P9_TSETATTR = 26,
-    P9_RSETATTR,
-    P9_TXATTRWALK = 30,
-    P9_RXATTRWALK,
-    P9_TXATTRCREATE = 32,
-    P9_RXATTRCREATE,
-    P9_TREADDIR = 40,
-    P9_RREADDIR,
-    P9_TFSYNC = 50,
-    P9_RFSYNC,
-    P9_TLOCK = 52,
-    P9_RLOCK,
-    P9_TGETLOCK = 54,
-    P9_RGETLOCK,
-    P9_TLINK = 70,
-    P9_RLINK,
-    P9_TMKDIR = 72,
-    P9_RMKDIR,
-    P9_TVERSION = 100,
-    P9_RVERSION,
-    P9_TAUTH = 102,
-    P9_RAUTH,
-    P9_TATTACH = 104,
-    P9_RATTACH,
-    P9_TERROR = 106,
-    P9_RERROR,
-    P9_TFLUSH = 108,
-    P9_RFLUSH,
-    P9_TWALK = 110,
-    P9_RWALK,
-    P9_TOPEN = 112,
-    P9_ROPEN,
-    P9_TCREATE = 114,
-    P9_RCREATE,
-    P9_TREAD = 116,
-    P9_RREAD,
-    P9_TWRITE = 118,
-    P9_RWRITE,
-    P9_TCLUNK = 120,
-    P9_RCLUNK,
-    P9_TREMOVE = 122,
-    P9_RREMOVE,
-    P9_TSTAT = 124,
-    P9_RSTAT,
-    P9_TWSTAT = 126,
-    P9_RWSTAT,
-};
-
-
-/* qid.types */
-enum {
-    P9_QTDIR = 0x80,
-    P9_QTAPPEND = 0x40,
-    P9_QTEXCL = 0x20,
-    P9_QTMOUNT = 0x10,
-    P9_QTAUTH = 0x08,
-    P9_QTTMP = 0x04,
-    P9_QTSYMLINK = 0x02,
-    P9_QTLINK = 0x01,
-    P9_QTFILE = 0x00,
-};
-
-enum p9_proto_version {
-    V9FS_PROTO_2000U = 0x01,
-    V9FS_PROTO_2000L = 0x02,
-};
-
-#define P9_NOTAG    (u16)(~0)
-#define P9_NOFID    (u32)(~0)
-#define P9_MAXWELEM 16
-
-/*
- * ample room for Twrite/Rread header
- * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
- */
-#define P9_IOHDRSZ 24
-
-typedef struct V9fsPDU V9fsPDU;
-
-struct V9fsPDU
-{
-    uint32_t size;
-    uint16_t tag;
-    uint8_t id;
-    VirtQueueElement elem;
-    QLIST_ENTRY(V9fsPDU) next;
-};
-
-
-/* FIXME
- * 1) change user needs to set groups and stuff
- */
-
-/* from Linux's linux/virtio_9p.h */
-
-/* The ID for virtio console */
-#define VIRTIO_ID_9P    9
-#define MAX_REQ         128
-#define MAX_TAG_LEN     32
-
-#define BUG_ON(cond) assert(!(cond))
-
-typedef struct V9fsFidState V9fsFidState;
-
-typedef struct V9fsString
-{
-    int16_t size;
-    char *data;
-} V9fsString;
-
-typedef struct V9fsQID
-{
-    int8_t type;
-    int32_t version;
-    int64_t path;
-} V9fsQID;
-
-typedef struct V9fsStat
-{
-    int16_t size;
-    int16_t type;
-    int32_t dev;
-    V9fsQID qid;
-    int32_t mode;
-    int32_t atime;
-    int32_t mtime;
-    int64_t length;
-    V9fsString name;
-    V9fsString uid;
-    V9fsString gid;
-    V9fsString muid;
-    /* 9p2000.u */
-    V9fsString extension;
-   int32_t n_uid;
-    int32_t n_gid;
-    int32_t n_muid;
-} V9fsStat;
-
-enum {
-    P9_FID_NONE = 0,
-    P9_FID_FILE,
-    P9_FID_DIR,
-    P9_FID_XATTR,
-};
-
-typedef struct V9fsXattr
-{
-    int64_t copied_len;
-    int64_t len;
-    void *value;
-    V9fsString name;
-    int flags;
-} V9fsXattr;
-
-struct V9fsFidState
-{
-    int fid_type;
-    int32_t fid;
-    V9fsString path;
-    union {
-	int fd;
-	DIR *dir;
-	V9fsXattr xattr;
-    } fs;
-    uid_t uid;
-    V9fsFidState *next;
-};
-
-typedef struct V9fsState
-{
-    VirtIODevice vdev;
-    VirtQueue *vq;
-    V9fsPDU pdus[MAX_REQ];
-    QLIST_HEAD(, V9fsPDU) free_list;
-    V9fsFidState *fid_list;
-    FileOperations *ops;
-    FsContext ctx;
-    uint16_t tag_len;
-    uint8_t *tag;
-    size_t config_size;
-    enum p9_proto_version proto_version;
-    int32_t msize;
-} V9fsState;
-
-typedef struct V9fsCreateState {
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsFidState *fidp;
-    V9fsQID qid;
-    int32_t perm;
-    int8_t mode;
-    struct stat stbuf;
-    V9fsString name;
-    V9fsString extension;
-    V9fsString fullname;
-    int iounit;
-} V9fsCreateState;
-
-typedef struct V9fsLcreateState {
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsFidState *fidp;
-    V9fsQID qid;
-    int32_t iounit;
-    struct stat stbuf;
-    V9fsString name;
-    V9fsString fullname;
-} V9fsLcreateState;
-
-typedef struct V9fsStatState {
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsStat v9stat;
-    V9fsFidState *fidp;
-    struct stat stbuf;
-} V9fsStatState;
-
-typedef struct V9fsStatDotl {
-    uint64_t st_result_mask;
-    V9fsQID qid;
-    uint32_t st_mode;
-    uint32_t st_uid;
-    uint32_t st_gid;
-    uint64_t st_nlink;
-    uint64_t st_rdev;
-    uint64_t st_size;
-    uint64_t st_blksize;
-    uint64_t st_blocks;
-    uint64_t st_atime_sec;
-    uint64_t st_atime_nsec;
-    uint64_t st_mtime_sec;
-    uint64_t st_mtime_nsec;
-    uint64_t st_ctime_sec;
-    uint64_t st_ctime_nsec;
-    uint64_t st_btime_sec;
-    uint64_t st_btime_nsec;
-    uint64_t st_gen;
-    uint64_t st_data_version;
-} V9fsStatDotl;
-
-typedef struct V9fsStatStateDotl {
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsStatDotl v9stat_dotl;
-    struct stat stbuf;
-} V9fsStatStateDotl;
-
-
-typedef struct V9fsWalkState {
-    V9fsPDU *pdu;
-    size_t offset;
-    int16_t nwnames;
-    int name_idx;
-    V9fsQID *qids;
-    V9fsFidState *fidp;
-    V9fsFidState *newfidp;
-    V9fsString path;
-    V9fsString *wnames;
-    struct stat stbuf;
-} V9fsWalkState;
-
-typedef struct V9fsOpenState {
-    V9fsPDU *pdu;
-    size_t offset;
-    int32_t mode;
-    V9fsFidState *fidp;
-    V9fsQID qid;
-    struct stat stbuf;
-    int iounit;
-} V9fsOpenState;
-
-typedef struct V9fsReadState {
-    V9fsPDU *pdu;
-    size_t offset;
-    int32_t count;
-    int32_t total;
-    int64_t off;
-    V9fsFidState *fidp;
-    struct iovec iov[128]; /* FIXME: bad, bad, bad */
-    struct iovec *sg;
-    off_t dir_pos;
-    struct dirent *dent;
-    struct stat stbuf;
-    V9fsString name;
-    V9fsStat v9stat;
-    int32_t len;
-    int32_t cnt;
-    int32_t max_count;
-} V9fsReadState;
-
-typedef struct V9fsWriteState {
-    V9fsPDU *pdu;
-    size_t offset;
-    int32_t len;
-    int32_t count;
-    int32_t total;
-    int64_t off;
-    V9fsFidState *fidp;
-    struct iovec iov[128]; /* FIXME: bad, bad, bad */
-    struct iovec *sg;
-    int cnt;
-} V9fsWriteState;
-
-typedef struct V9fsRemoveState {
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsFidState *fidp;
-} V9fsRemoveState;
-
-typedef struct V9fsWstatState
-{
-    V9fsPDU *pdu;
-    size_t offset;
-    int16_t unused;
-    V9fsStat v9stat;
-    V9fsFidState *fidp;
-    struct stat stbuf;
-} V9fsWstatState;
-
-typedef struct V9fsSymlinkState
-{
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsString name;
-    V9fsString symname;
-    V9fsString fullname;
-    V9fsFidState *dfidp;
-    V9fsQID qid;
-    struct stat stbuf;
-} V9fsSymlinkState;
-
-typedef struct V9fsIattr
-{
-    int32_t valid;
-    int32_t mode;
-    int32_t uid;
-    int32_t gid;
-    int64_t size;
-    int64_t atime_sec;
-    int64_t atime_nsec;
-    int64_t mtime_sec;
-    int64_t mtime_nsec;
-} V9fsIattr;
-
-typedef struct V9fsSetattrState
-{
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsIattr v9iattr;
-    V9fsFidState *fidp;
-} V9fsSetattrState;
-
-struct virtio_9p_config
-{
-    /* number of characters in tag */
-    uint16_t tag_len;
-    /* Variable size tag name */
-    uint8_t tag[0];
-} __attribute__((packed));
-
-typedef struct V9fsStatfs
-{
-    uint32_t f_type;
-    uint32_t f_bsize;
-    uint64_t f_blocks;
-    uint64_t f_bfree;
-    uint64_t f_bavail;
-    uint64_t f_files;
-    uint64_t f_ffree;
-    uint64_t fsid_val;
-    uint32_t f_namelen;
-} V9fsStatfs;
-
-typedef struct V9fsStatfsState {
-    V9fsPDU *pdu;
-    size_t offset;
-    int32_t fid;
-    V9fsStatfs v9statfs;
-    V9fsFidState *fidp;
-    struct statfs stbuf;
-} V9fsStatfsState;
-
-typedef struct V9fsMkState {
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsQID qid;
-    struct stat stbuf;
-    V9fsString name;
-    V9fsString fullname;
-} V9fsMkState;
-
-typedef struct V9fsRenameState {
-    V9fsPDU *pdu;
-    V9fsFidState *fidp;
-    size_t offset;
-    int32_t newdirfid;
-    V9fsString name;
-} V9fsRenameState;
-
-typedef struct V9fsXattrState
-{
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsFidState *file_fidp;
-    V9fsFidState *xattr_fidp;
-    V9fsString name;
-    int64_t size;
-    int flags;
-    void *value;
-} V9fsXattrState;
-
-#define P9_LOCK_SUCCESS 0
-#define P9_LOCK_BLOCKED 1
-#define P9_LOCK_ERROR 2
-#define P9_LOCK_GRACE 3
-
-#define P9_LOCK_FLAGS_BLOCK 1
-#define P9_LOCK_FLAGS_RECLAIM 2
-
-typedef struct V9fsFlock
-{
-    uint8_t type;
-    uint32_t flags;
-    uint64_t start; /* absolute offset */
-    uint64_t length;
-    uint32_t proc_id;
-    V9fsString client_id;
-} V9fsFlock;
-
-typedef struct V9fsLockState
-{
-    V9fsPDU *pdu;
-    size_t offset;
-    int8_t status;
-    struct stat stbuf;
-    V9fsFidState *fidp;
-    V9fsFlock *flock;
-} V9fsLockState;
-
-typedef struct V9fsGetlock
-{
-    uint8_t type;
-    uint64_t start; /* absolute offset */
-    uint64_t length;
-    uint32_t proc_id;
-    V9fsString client_id;
-} V9fsGetlock;
-
-typedef struct V9fsGetlockState
-{
-    V9fsPDU *pdu;
-    size_t offset;
-    struct stat stbuf;
-    V9fsFidState *fidp;
-    V9fsGetlock *glock;
-} V9fsGetlockState;
-
-typedef struct V9fsReadLinkState
-{
-    V9fsPDU *pdu;
-    size_t offset;
-    V9fsString target;
-} V9fsReadLinkState;
-
-size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
-                      size_t offset, size_t size, int pack);
-
-static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count,
-                        size_t offset, size_t size)
-{
-    return pdu_packunpack(dst, sg, sg_count, offset, size, 0);
-}
-
-#endif
commit 661bfc80e876d32da8befe53ba0234d87fc0bcc2
Author: Jan Kiszka <jan.kiszka at web.de>
Date:   Sun Apr 10 12:53:39 2011 +0200

    pflash: Restore & fix lazy ROMD switching
    
    Commit 5145b3d1cc revealed a bug in the lazy ROMD switch-back logic, but
    resolved it by breaking that feature. This approach addresses the issue
    by switching back to ROMD after a certain amount of read accesses
    without further unlock sequences.
    
    Signed-off-by: Jan Kiszka <jan.kiszka at web.de>
    Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>

diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c
index 370c5ee..14bbc34 100644
--- a/hw/pflash_cfi02.c
+++ b/hw/pflash_cfi02.c
@@ -50,6 +50,8 @@ do {                                               \
 #define DPRINTF(fmt, ...) do { } while (0)
 #endif
 
+#define PFLASH_LAZY_ROMD_THRESHOLD 42
+
 struct pflash_t {
     BlockDriverState *bs;
     target_phys_addr_t base;
@@ -70,6 +72,7 @@ struct pflash_t {
     ram_addr_t off;
     int fl_mem;
     int rom_mode;
+    int read_counter; /* used for lazy switch-back to rom mode */
     void *storage;
 };
 
@@ -112,10 +115,10 @@ static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset,
 
     DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset);
     ret = -1;
-    if (!pfl->rom_mode) {
-        /* Lazy reset of to ROMD mode */
-        if (pfl->wcycle == 0)
-            pflash_register_memory(pfl, 1);
+    /* Lazy reset to ROMD mode after a certain amount of read accesses */
+    if (!pfl->rom_mode && pfl->wcycle == 0 &&
+        ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
+        pflash_register_memory(pfl, 1);
     }
     offset &= pfl->chip_len - 1;
     boff = offset & 0xFF;
@@ -254,6 +257,7 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset,
         /* Set the device in I/O access mode if required */
         if (pfl->rom_mode)
             pflash_register_memory(pfl, 0);
+        pfl->read_counter = 0;
         /* We're in read mode */
     check_unlock0:
         if (boff == 0x55 && cmd == 0x98) {
commit 70afb8ff90e9d922ed729e6dbabaff6d67c461aa
Author: Stefan Weil <weil at mail.berlios.de>
Date:   Sun Apr 3 18:22:45 2011 +0200

    darwin-user: Remove unneeded null pointer check
    
    cppcheck reports this error:
    
    commpage.c:223: error: Possible null pointer dereference:
    value - otherwise it is redundant to check if value is null at line 214
    
    The null pointer check in line 214 is indeed not needed.
    If value were null, the code would crash in line 223.
    See do_compare_and_swap64 were for a reference.
    
    Signed-off-by: Stefan Weil <weil at mail.berlios.de>
    Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>

diff --git a/darwin-user/commpage.c b/darwin-user/commpage.c
index f6aa71e..cc29bdd 100644
--- a/darwin-user/commpage.c
+++ b/darwin-user/commpage.c
@@ -211,7 +211,7 @@ void do_compare_and_swap32(void *cpu_env, int num)
     uint32_t *value = (uint32_t*)((CPUX86State*)cpu_env)->regs[R_ECX];
     DPRINTF("commpage: compare_and_swap32(%x,new,%p)\n", old, value);
 
-    if(value && old == tswap32(*value))
+    if(old == tswap32(*value))
     {
         uint32_t new = ((CPUX86State*)cpu_env)->regs[R_EDX];
         *value = tswap32(new);
commit aa348082d8e89d2ab021caadbcc97c93038fffb2
Merge: c6a0487... df6e008...
Author: Aurelien Jarno <aurelien at aurel32.net>
Date:   Wed Apr 27 16:26:18 2011 +0200

    Merge branch 'for-anthony' of git://repo.or.cz/qemu/kevin
    
    * 'for-anthony' of git://repo.or.cz/qemu/kevin:
      Remove obsolete 'enabled' variable from progress state
      Add dd-style SIGUSR1 progress reporting
      qed: Fix consistency check on 32-bit hosts
      ide/atapi: Introduce CHECK_READY flag for commands
      ide/atapi: Replace bdrv_get_geometry calls by s->nb_sectors
      ide/atapi: Use table instead of switch for commands
      ide/atapi: Factor commands out
      ide: Split atapi.c out
      Improve accuracy of block migration bandwidth calculation
      atapi: Add 'medium ready' to 'medium not ready' transition on cd change
      qemu-img: allow rebase to a NULL backing file when unsafe

commit c6a0487b1fc29bc6047da0e484f79d4d627f9018
Author: Stefan Weil <weil at mail.berlios.de>
Date:   Tue Apr 26 10:17:48 2011 +0200

    rtl8139: Fix compilation for w32/w64
    
    Compilation for Windows needs a different declaration for the
    printf format attribute, so use the macro which was defined for
    this purpose.
    
    Cc: Benjamin Poirier <benjamin.poirier at gmail.com>
    Signed-off-by: Stefan Weil <weil at mail.berlios.de>
    Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>

diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index cbf667a..515652f 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -88,8 +88,7 @@
 #  define DPRINTF(fmt, ...) \
     do { fprintf(stderr, "RTL8139: " fmt, ## __VA_ARGS__); } while (0)
 #else
-static inline __attribute__ ((format (printf, 1, 2)))
-    int DPRINTF(const char *fmt, ...)
+static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...)
 {
     return 0;
 }
commit df6e008a8814af9db872f1319b58784d87987c93
Author: Jes Sorensen <Jes.Sorensen at redhat.com>
Date:   Wed Apr 27 14:31:51 2011 +0200

    Remove obsolete 'enabled' variable from progress state
    
    Signed-off-by: Jes Sorensen <Jes.Sorensen at redhat.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/qemu-progress.c b/qemu-progress.c
index b4b751c..e1feb89 100644
--- a/qemu-progress.c
+++ b/qemu-progress.c
@@ -29,7 +29,6 @@
 #include <signal.h>
 
 struct progress_state {
-    int enabled;
     float current;
     float last_print;
     float min_skip;
@@ -46,10 +45,8 @@ static struct progress_state state;
  */
 static void progress_simple_print(void)
 {
-    if (state.enabled) {
-        printf("    (%3.2f/100%%)\r", state.current);
-        fflush(stdout);
-    }
+    printf("    (%3.2f/100%%)\r", state.current);
+    fflush(stdout);
 }
 
 static void progress_simple_end(void)
@@ -96,7 +93,6 @@ static void progress_dummy_init(void)
 
 void qemu_progress_init(int enabled, float min_skip)
 {
-    state.enabled = enabled;
     state.min_skip = min_skip;
     if (enabled) {
         progress_simple_init();
commit a55c73ba3fb3f5700788933c519c193c5e85c878
Author: Jes Sorensen <Jes.Sorensen at redhat.com>
Date:   Wed Apr 27 14:31:50 2011 +0200

    Add dd-style SIGUSR1 progress reporting
    
    This introduces support for dd-style progress reporting on POSIX
    systems, if the user hasn't specified -p to report progress. If sent a
    SIGUSR1, qemu-img will report current progress for commands that
    support progress reporting.
    
    Signed-off-by: Jes Sorensen <Jes.Sorensen at redhat.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/qemu-progress.c b/qemu-progress.c
index 656e065..b4b751c 100644
--- a/qemu-progress.c
+++ b/qemu-progress.c
@@ -26,12 +26,15 @@
 #include "osdep.h"
 #include "sysemu.h"
 #include <stdio.h>
+#include <signal.h>
 
 struct progress_state {
     int enabled;
     float current;
     float last_print;
     float min_skip;
+    void (*print)(void);
+    void (*end)(void);
 };
 
 static struct progress_state state;
@@ -51,20 +54,60 @@ static void progress_simple_print(void)
 
 static void progress_simple_end(void)
 {
-    if (state.enabled) {
-        printf("\n");
-    }
+    printf("\n");
+}
+
+static void progress_simple_init(void)
+{
+    state.print = progress_simple_print;
+    state.end = progress_simple_end;
+}
+
+#ifdef CONFIG_POSIX
+static void sigusr_print(int signal)
+{
+    printf("    (%3.2f/100%%)\n", state.current);
+}
+#endif
+
+static void progress_dummy_print(void)
+{
+}
+
+static void progress_dummy_end(void)
+{
+}
+
+static void progress_dummy_init(void)
+{
+#ifdef CONFIG_POSIX
+    struct sigaction action;
+
+    memset(&action, 0, sizeof(action));
+    sigfillset(&action.sa_mask);
+    action.sa_handler = sigusr_print;
+    action.sa_flags = 0;
+    sigaction(SIGUSR1, &action, NULL);
+#endif
+
+    state.print = progress_dummy_print;
+    state.end = progress_dummy_end;
 }
 
 void qemu_progress_init(int enabled, float min_skip)
 {
     state.enabled = enabled;
     state.min_skip = min_skip;
+    if (enabled) {
+        progress_simple_init();
+    } else {
+        progress_dummy_init();
+    }
 }
 
 void qemu_progress_end(void)
 {
-    progress_simple_end();
+    state.end();
 }
 
 void qemu_progress_print(float percent, int max)
@@ -84,6 +127,6 @@ void qemu_progress_print(float percent, int max)
     if (current > (state.last_print + state.min_skip) ||
         (current == 100) || (current == 0)) {
         state.last_print = state.current;
-        progress_simple_print();
+        state.print();
     }
 }
commit 19dfc44a94f759848a0f7de7378b2f8b9af6b5d0
Author: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Date:   Sun Apr 24 18:38:58 2011 +0100

    qed: Fix consistency check on 32-bit hosts
    
    The qed_bytes_to_clusters() function is normally used with size_t
    lengths.  Consistency check used it with file size length and therefore
    failed on 32-bit hosts when the image file is 4 GB or more.
    
    Make qed_bytes_to_clusters() explicitly 64-bit and update consistency
    check to keep 64-bit cluster counts.
    
    Reported-by: Michael Tokarev <mjt at tls.msk.ru>
    Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/block/qed-check.c b/block/qed-check.c
index ea4ebc8..22cd07f 100644
--- a/block/qed-check.c
+++ b/block/qed-check.c
@@ -18,7 +18,7 @@ typedef struct {
     BdrvCheckResult *result;
     bool fix;                           /* whether to fix invalid offsets */
 
-    size_t nclusters;
+    uint64_t nclusters;
     uint32_t *used_clusters;            /* referenced cluster bitmap */
 
     QEDRequest request;
@@ -177,7 +177,7 @@ static int qed_check_l1_table(QEDCheck *check, QEDTable *table)
 static void qed_check_for_leaks(QEDCheck *check)
 {
     BDRVQEDState *s = check->s;
-    size_t i;
+    uint64_t i;
 
     for (i = s->header.header_size; i < check->nclusters; i++) {
         if (!qed_test_bit(check->used_clusters, i)) {
diff --git a/block/qed.h b/block/qed.h
index 3e1ab84..1d1421f 100644
--- a/block/qed.h
+++ b/block/qed.h
@@ -252,7 +252,7 @@ static inline uint64_t qed_offset_into_cluster(BDRVQEDState *s, uint64_t offset)
     return offset & (s->header.cluster_size - 1);
 }
 
-static inline unsigned int qed_bytes_to_clusters(BDRVQEDState *s, size_t bytes)
+static inline uint64_t qed_bytes_to_clusters(BDRVQEDState *s, uint64_t bytes)
 {
     return qed_start_of_cluster(s, bytes + (s->header.cluster_size - 1)) /
            (s->header.cluster_size - 1);
commit 7a2c4b82340d621bff462672b29c88d2020d68c1
Author: Kevin Wolf <kwolf at redhat.com>
Date:   Tue Apr 19 13:15:52 2011 +0200

    ide/atapi: Introduce CHECK_READY flag for commands
    
    Some commands are supposed to report a Not Ready Condition (i.e. they require
    a medium to be present in order to execute successfully). Instead of
    duplicating the check in each command implementation, let's add a flag and
    check it before calling the command.
    
    This patch only converts existing checks, it does not introduce new checks for
    any of the other commands that can/should report a Not Ready Condition.
    
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 0452337..690a0ab 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -813,11 +813,9 @@ error_cmd:
 
 static void cmd_test_unit_ready(IDEState *s, uint8_t *buf)
 {
-    if (bdrv_is_inserted(s->bs)) {
-        ide_atapi_cmd_ok(s);
-    } else {
-        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-    }
+    /* Not Ready Conditions are already handled in ide_atapi_cmd(), so if we
+     * come here, we know that it's ready. */
+    ide_atapi_cmd_ok(s);
 }
 
 static void cmd_prevent_allow_medium_removal(IDEState *s, uint8_t* buf)
@@ -883,11 +881,6 @@ static void cmd_seek(IDEState *s, uint8_t* buf)
     unsigned int lba;
     uint64_t total_sectors = s->nb_sectors >> 2;
 
-    if (total_sectors == 0) {
-        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-        return;
-    }
-
     lba = ube32_to_cpu(buf + 2);
     if (lba >= total_sectors) {
         ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
@@ -941,13 +934,8 @@ static void cmd_mechanism_status(IDEState *s, uint8_t* buf)
 static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf)
 {
     int format, msf, start_track, len;
-    uint64_t total_sectors = s->nb_sectors >> 2;
     int max_len;
-
-    if (total_sectors == 0) {
-        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-        return;
-    }
+    uint64_t total_sectors = s->nb_sectors >> 2;
 
     max_len = ube16_to_cpu(buf + 7);
     format = buf[9] >> 6;
@@ -986,11 +974,6 @@ static void cmd_read_cdvd_capacity(IDEState *s, uint8_t* buf)
 {
     uint64_t total_sectors = s->nb_sectors >> 2;
 
-    if (total_sectors == 0) {
-        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-        return;
-    }
-
     /* NOTE: it is really the number of sectors minus 1 */
     cpu_to_ube32(buf, total_sectors - 1);
     cpu_to_ube32(buf + 4, 2048);
@@ -1062,22 +1045,29 @@ enum {
      * unit attention condition. (See MMC-5, section 4.1.6.1)
      */
     ALLOW_UA = 0x01,
+
+    /*
+     * Commands flagged with CHECK_READY can only execute if a medium is present.
+     * Otherwise they report the Not Ready Condition. (See MMC-5, section
+     * 4.1.8)
+     */
+    CHECK_READY = 0x02,
 };
 
 static const struct {
     void (*handler)(IDEState *s, uint8_t *buf);
     int flags;
 } atapi_cmd_table[0x100] = {
-    [ 0x00 ] = { cmd_test_unit_ready,               0 },
+    [ 0x00 ] = { cmd_test_unit_ready,               CHECK_READY },
     [ 0x03 ] = { cmd_request_sense,                 ALLOW_UA },
     [ 0x12 ] = { cmd_inquiry,                       ALLOW_UA },
     [ 0x1a ] = { cmd_mode_sense, /* (6) */          0 },
     [ 0x1b ] = { cmd_start_stop_unit,               0 },
     [ 0x1e ] = { cmd_prevent_allow_medium_removal,  0 },
-    [ 0x25 ] = { cmd_read_cdvd_capacity,            0 },
+    [ 0x25 ] = { cmd_read_cdvd_capacity,            CHECK_READY },
     [ 0x28 ] = { cmd_read, /* (10) */               0 },
-    [ 0x2b ] = { cmd_seek,                          0 },
-    [ 0x43 ] = { cmd_read_toc_pma_atip,             0 },
+    [ 0x2b ] = { cmd_seek,                          CHECK_READY },
+    [ 0x43 ] = { cmd_read_toc_pma_atip,             CHECK_READY },
     [ 0x46 ] = { cmd_get_configuration,             ALLOW_UA },
     [ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA },
     [ 0x5a ] = { cmd_mode_sense, /* (10) */         0 },
@@ -1126,6 +1116,14 @@ void ide_atapi_cmd(IDEState *s)
         return;
     }
 
+    /* Report a Not Ready condition if appropriate for the command */
+    if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
+        (!media_present(s) || !bdrv_is_inserted(s->bs)))
+    {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+        return;
+    }
+
     /* Execute the command */
     if (atapi_cmd_table[s->io_buffer[0]].handler) {
         atapi_cmd_table[s->io_buffer[0]].handler(s, buf);
commit e119bcaceb8dc17fe4874d6b0d2b62752639e488
Author: Kevin Wolf <kwolf at redhat.com>
Date:   Tue Apr 19 13:13:44 2011 +0200

    ide/atapi: Replace bdrv_get_geometry calls by s->nb_sectors
    
    The disk size can only change when the medium is changed, and the change
    callback takes care of updating s->nb_sectors in this case.
    
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 277404b..0452337 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -416,10 +416,10 @@ static int ide_dvd_read_structure(IDEState *s, int format,
                 if (layer != 0)
                     return -ASC_INV_FIELD_IN_CMD_PACKET;
 
-                bdrv_get_geometry(s->bs, &total_sectors);
-                total_sectors >>= 2;
-                if (total_sectors == 0)
+                total_sectors = s->nb_sectors >> 2;
+                if (total_sectors == 0) {
                     return -ASC_MEDIUM_NOT_PRESENT;
+                }
 
                 buf[4] = 1;   /* DVD-ROM, part version 1 */
                 buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
@@ -881,11 +881,8 @@ static void cmd_read_cd(IDEState *s, uint8_t* buf)
 static void cmd_seek(IDEState *s, uint8_t* buf)
 {
     unsigned int lba;
-    uint64_t total_sectors;
-
-    bdrv_get_geometry(s->bs, &total_sectors);
+    uint64_t total_sectors = s->nb_sectors >> 2;
 
-    total_sectors >>= 2;
     if (total_sectors == 0) {
         ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
         return;
@@ -944,12 +941,9 @@ static void cmd_mechanism_status(IDEState *s, uint8_t* buf)
 static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf)
 {
     int format, msf, start_track, len;
-    uint64_t total_sectors;
+    uint64_t total_sectors = s->nb_sectors >> 2;
     int max_len;
 
-    bdrv_get_geometry(s->bs, &total_sectors);
-
-    total_sectors >>= 2;
     if (total_sectors == 0) {
         ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
         return;
@@ -990,11 +984,8 @@ static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf)
 
 static void cmd_read_cdvd_capacity(IDEState *s, uint8_t* buf)
 {
-    uint64_t total_sectors;
-
-    bdrv_get_geometry(s->bs, &total_sectors);
+    uint64_t total_sectors = s->nb_sectors >> 2;
 
-    total_sectors >>= 2;
     if (total_sectors == 0) {
         ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
         return;
commit e1a064f982802ebb4a865482b7c0fe5e68d047f9
Author: Kevin Wolf <kwolf at redhat.com>
Date:   Mon Apr 18 17:55:08 2011 +0200

    ide/atapi: Use table instead of switch for commands
    
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 25b796e..277404b 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -533,10 +533,11 @@ static unsigned int event_status_media(IDEState *s,
     return 8; /* We wrote to 4 extra bytes from the header */
 }
 
-static void handle_get_event_status_notification(IDEState *s,
-                                                 uint8_t *buf,
-                                                 const uint8_t *packet)
+static void cmd_get_event_status_notification(IDEState *s,
+                                              uint8_t *buf)
 {
+    const uint8_t *packet = buf;
+
     struct {
         uint8_t opcode;
         uint8_t polled;        /* lsb bit is polled; others are reserved */
@@ -1064,6 +1065,38 @@ static void cmd_set_speed(IDEState *s, uint8_t* buf)
     ide_atapi_cmd_ok(s);
 }
 
+enum {
+    /*
+     * Only commands flagged as ALLOW_UA are allowed to run under a
+     * unit attention condition. (See MMC-5, section 4.1.6.1)
+     */
+    ALLOW_UA = 0x01,
+};
+
+static const struct {
+    void (*handler)(IDEState *s, uint8_t *buf);
+    int flags;
+} atapi_cmd_table[0x100] = {
+    [ 0x00 ] = { cmd_test_unit_ready,               0 },
+    [ 0x03 ] = { cmd_request_sense,                 ALLOW_UA },
+    [ 0x12 ] = { cmd_inquiry,                       ALLOW_UA },
+    [ 0x1a ] = { cmd_mode_sense, /* (6) */          0 },
+    [ 0x1b ] = { cmd_start_stop_unit,               0 },
+    [ 0x1e ] = { cmd_prevent_allow_medium_removal,  0 },
+    [ 0x25 ] = { cmd_read_cdvd_capacity,            0 },
+    [ 0x28 ] = { cmd_read, /* (10) */               0 },
+    [ 0x2b ] = { cmd_seek,                          0 },
+    [ 0x43 ] = { cmd_read_toc_pma_atip,             0 },
+    [ 0x46 ] = { cmd_get_configuration,             ALLOW_UA },
+    [ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA },
+    [ 0x5a ] = { cmd_mode_sense, /* (10) */         0 },
+    [ 0xa8 ] = { cmd_read, /* (12) */               0 },
+    [ 0xad ] = { cmd_read_dvd_structure,            0 },
+    [ 0xbb ] = { cmd_set_speed,                     0 },
+    [ 0xbd ] = { cmd_mechanism_status,              0 },
+    [ 0xbe ] = { cmd_read_cd,                       0 },
+};
+
 void ide_atapi_cmd(IDEState *s)
 {
     const uint8_t *packet;
@@ -1082,21 +1115,17 @@ void ide_atapi_cmd(IDEState *s)
     }
 #endif
     /*
-     * If there's a UNIT_ATTENTION condition pending, only
-     * REQUEST_SENSE, INQUIRY, GET_CONFIGURATION and
-     * GET_EVENT_STATUS_NOTIFICATION commands are allowed to complete.
-     * MMC-5, section 4.1.6.1 lists only these commands being allowed
-     * to complete, with other commands getting a CHECK condition
-     * response unless a higher priority status, defined by the drive
+     * If there's a UNIT_ATTENTION condition pending, only command flagged with
+     * ALLOW_UA are allowed to complete. with other commands getting a CHECK
+     * condition response unless a higher priority status, defined by the drive
      * here, is pending.
      */
     if (s->sense_key == SENSE_UNIT_ATTENTION &&
-        s->io_buffer[0] != GPCMD_REQUEST_SENSE &&
-        s->io_buffer[0] != GPCMD_INQUIRY &&
-        s->io_buffer[0] != GPCMD_GET_EVENT_STATUS_NOTIFICATION) {
+        !(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) {
         ide_atapi_cmd_check_status(s);
         return;
     }
+
     if (bdrv_is_inserted(s->bs) && s->cdrom_changed) {
         ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
 
@@ -1105,60 +1134,12 @@ void ide_atapi_cmd(IDEState *s)
         s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
         return;
     }
-    switch(s->io_buffer[0]) {
-    case GPCMD_TEST_UNIT_READY:
-        cmd_test_unit_ready(s, buf);
-        break;
-    case GPCMD_MODE_SENSE_6:
-    case GPCMD_MODE_SENSE_10:
-        cmd_mode_sense(s, buf);
-        break;
-    case GPCMD_REQUEST_SENSE:
-        cmd_request_sense(s, buf);
-        break;
-    case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
-        cmd_prevent_allow_medium_removal(s, buf);
-        break;
-    case GPCMD_READ_10:
-    case GPCMD_READ_12:
-        cmd_read(s, buf);
-        break;
-    case GPCMD_READ_CD:
-        cmd_read_cd(s, buf);
-        break;
-    case GPCMD_SEEK:
-        cmd_seek(s, buf);
-        break;
-    case GPCMD_START_STOP_UNIT:
-        cmd_start_stop_unit(s, buf);
-        break;
-    case GPCMD_MECHANISM_STATUS:
-        cmd_mechanism_status(s, buf);
-        break;
-    case GPCMD_READ_TOC_PMA_ATIP:
-        cmd_read_toc_pma_atip(s, buf);
-        break;
-    case GPCMD_READ_CDVD_CAPACITY:
-        cmd_read_cdvd_capacity(s, buf);
-        break;
-    case GPCMD_READ_DVD_STRUCTURE:
-        cmd_read_dvd_structure(s, buf);
-        break;
-    case GPCMD_SET_SPEED:
-        cmd_set_speed(s, buf);
-        break;
-    case GPCMD_INQUIRY:
-        cmd_inquiry(s, buf);
-        break;
-    case GPCMD_GET_CONFIGURATION:
-        cmd_get_configuration(s, buf);
-        break;
-    case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
-        handle_get_event_status_notification(s, buf, packet);
-        break;
-    default:
-        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                            ASC_ILLEGAL_OPCODE);
-        break;
+
+    /* Execute the command */
+    if (atapi_cmd_table[s->io_buffer[0]].handler) {
+        atapi_cmd_table[s->io_buffer[0]].handler(s, buf);
+        return;
     }
+
+    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
 }
commit a60cf7e7eba06bed118ae1b161f7e481ab72693c
Author: Kevin Wolf <kwolf at redhat.com>
Date:   Mon Apr 18 17:55:08 2011 +0200

    ide/atapi: Factor commands out
    
    In preparation for a table of function pointers, factor each command out from
    ide_atapi_cmd() into its own function.
    
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 0edfd58..25b796e 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -621,11 +621,453 @@ static void handle_get_event_status_notification(IDEState *s,
     ide_atapi_cmd_reply(s, used_len, max_len);
 }
 
+static void cmd_request_sense(IDEState *s, uint8_t *buf)
+{
+    int max_len = buf[4];
+
+    memset(buf, 0, 18);
+    buf[0] = 0x70 | (1 << 7);
+    buf[2] = s->sense_key;
+    buf[7] = 10;
+    buf[12] = s->asc;
+
+    if (s->sense_key == SENSE_UNIT_ATTENTION) {
+        s->sense_key = SENSE_NONE;
+    }
+
+    ide_atapi_cmd_reply(s, 18, max_len);
+}
+
+static void cmd_inquiry(IDEState *s, uint8_t *buf)
+{
+    int max_len = buf[4];
+
+    buf[0] = 0x05; /* CD-ROM */
+    buf[1] = 0x80; /* removable */
+    buf[2] = 0x00; /* ISO */
+    buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
+    buf[4] = 31; /* additional length */
+    buf[5] = 0; /* reserved */
+    buf[6] = 0; /* reserved */
+    buf[7] = 0; /* reserved */
+    padstr8(buf + 8, 8, "QEMU");
+    padstr8(buf + 16, 16, "QEMU DVD-ROM");
+    padstr8(buf + 32, 4, s->version);
+    ide_atapi_cmd_reply(s, 36, max_len);
+}
+
+static void cmd_get_configuration(IDEState *s, uint8_t *buf)
+{
+    uint32_t len;
+    uint8_t index = 0;
+    int max_len;
+
+    /* only feature 0 is supported */
+    if (buf[2] != 0 || buf[3] != 0) {
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                            ASC_INV_FIELD_IN_CMD_PACKET);
+        return;
+    }
+
+    /* XXX: could result in alignment problems in some architectures */
+    max_len = ube16_to_cpu(buf + 7);
+
+    /*
+     * XXX: avoid overflow for io_buffer if max_len is bigger than
+     *      the size of that buffer (dimensioned to max number of
+     *      sectors to transfer at once)
+     *
+     *      Only a problem if the feature/profiles grow.
+     */
+    if (max_len > 512) {
+        /* XXX: assume 1 sector */
+        max_len = 512;
+    }
+
+    memset(buf, 0, max_len);
+    /*
+     * the number of sectors from the media tells us which profile
+     * to use as current.  0 means there is no media
+     */
+    if (media_is_dvd(s)) {
+        cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM);
+    } else if (media_is_cd(s)) {
+        cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM);
+    }
+
+    buf[10] = 0x02 | 0x01; /* persistent and current */
+    len = 12; /* headers: 8 + 4 */
+    len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
+    len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
+    cpu_to_ube32(buf, len - 4); /* data length */
+
+    ide_atapi_cmd_reply(s, len, max_len);
+}
+
+static void cmd_mode_sense(IDEState *s, uint8_t *buf)
+{
+    int action, code;
+    int max_len;
+
+    if (buf[0] == GPCMD_MODE_SENSE_10) {
+        max_len = ube16_to_cpu(buf + 7);
+    } else {
+        max_len = buf[4];
+    }
+
+    action = buf[2] >> 6;
+    code = buf[2] & 0x3f;
+
+    switch(action) {
+    case 0: /* current values */
+        switch(code) {
+        case GPMODE_R_W_ERROR_PAGE: /* error recovery */
+            cpu_to_ube16(&buf[0], 16 + 6);
+            buf[2] = 0x70;
+            buf[3] = 0;
+            buf[4] = 0;
+            buf[5] = 0;
+            buf[6] = 0;
+            buf[7] = 0;
+
+            buf[8] = 0x01;
+            buf[9] = 0x06;
+            buf[10] = 0x00;
+            buf[11] = 0x05;
+            buf[12] = 0x00;
+            buf[13] = 0x00;
+            buf[14] = 0x00;
+            buf[15] = 0x00;
+            ide_atapi_cmd_reply(s, 16, max_len);
+            break;
+        case GPMODE_AUDIO_CTL_PAGE:
+            cpu_to_ube16(&buf[0], 24 + 6);
+            buf[2] = 0x70;
+            buf[3] = 0;
+            buf[4] = 0;
+            buf[5] = 0;
+            buf[6] = 0;
+            buf[7] = 0;
+
+            /* Fill with CDROM audio volume */
+            buf[17] = 0;
+            buf[19] = 0;
+            buf[21] = 0;
+            buf[23] = 0;
+
+            ide_atapi_cmd_reply(s, 24, max_len);
+            break;
+        case GPMODE_CAPABILITIES_PAGE:
+            cpu_to_ube16(&buf[0], 28 + 6);
+            buf[2] = 0x70;
+            buf[3] = 0;
+            buf[4] = 0;
+            buf[5] = 0;
+            buf[6] = 0;
+            buf[7] = 0;
+
+            buf[8] = 0x2a;
+            buf[9] = 0x12;
+            buf[10] = 0x00;
+            buf[11] = 0x00;
+
+            /* Claim PLAY_AUDIO capability (0x01) since some Linux
+               code checks for this to automount media. */
+            buf[12] = 0x71;
+            buf[13] = 3 << 5;
+            buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
+            if (bdrv_is_locked(s->bs))
+                buf[6] |= 1 << 1;
+            buf[15] = 0x00;
+            cpu_to_ube16(&buf[16], 706);
+            buf[18] = 0;
+            buf[19] = 2;
+            cpu_to_ube16(&buf[20], 512);
+            cpu_to_ube16(&buf[22], 706);
+            buf[24] = 0;
+            buf[25] = 0;
+            buf[26] = 0;
+            buf[27] = 0;
+            ide_atapi_cmd_reply(s, 28, max_len);
+            break;
+        default:
+            goto error_cmd;
+        }
+        break;
+    case 1: /* changeable values */
+        goto error_cmd;
+    case 2: /* default values */
+        goto error_cmd;
+    default:
+    case 3: /* saved values */
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                            ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+        break;
+    }
+    return;
+
+error_cmd:
+    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+}
+
+static void cmd_test_unit_ready(IDEState *s, uint8_t *buf)
+{
+    if (bdrv_is_inserted(s->bs)) {
+        ide_atapi_cmd_ok(s);
+    } else {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+    }
+}
+
+static void cmd_prevent_allow_medium_removal(IDEState *s, uint8_t* buf)
+{
+    bdrv_set_locked(s->bs, buf[4] & 1);
+    ide_atapi_cmd_ok(s);
+}
+
+static void cmd_read(IDEState *s, uint8_t* buf)
+{
+    int nb_sectors, lba;
+
+    if (buf[0] == GPCMD_READ_10) {
+        nb_sectors = ube16_to_cpu(buf + 7);
+    } else {
+        nb_sectors = ube32_to_cpu(buf + 6);
+    }
+
+    lba = ube32_to_cpu(buf + 2);
+    if (nb_sectors == 0) {
+        ide_atapi_cmd_ok(s);
+        return;
+    }
+
+    ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+}
+
+static void cmd_read_cd(IDEState *s, uint8_t* buf)
+{
+    int nb_sectors, lba, transfer_request;
+
+    nb_sectors = (buf[6] << 16) | (buf[7] << 8) | buf[8];
+    lba = ube32_to_cpu(buf + 2);
+
+    if (nb_sectors == 0) {
+        ide_atapi_cmd_ok(s);
+        return;
+    }
+
+    transfer_request = buf[9];
+    switch(transfer_request & 0xf8) {
+    case 0x00:
+        /* nothing */
+        ide_atapi_cmd_ok(s);
+        break;
+    case 0x10:
+        /* normal read */
+        ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+        break;
+    case 0xf8:
+        /* read all data */
+        ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
+        break;
+    default:
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                            ASC_INV_FIELD_IN_CMD_PACKET);
+        break;
+    }
+}
+
+static void cmd_seek(IDEState *s, uint8_t* buf)
+{
+    unsigned int lba;
+    uint64_t total_sectors;
+
+    bdrv_get_geometry(s->bs, &total_sectors);
+
+    total_sectors >>= 2;
+    if (total_sectors == 0) {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+        return;
+    }
+
+    lba = ube32_to_cpu(buf + 2);
+    if (lba >= total_sectors) {
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
+        return;
+    }
+
+    ide_atapi_cmd_ok(s);
+}
+
+static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
+{
+    int start, eject, sense, err = 0;
+    start = buf[4] & 1;
+    eject = (buf[4] >> 1) & 1;
+
+    if (eject) {
+        err = bdrv_eject(s->bs, !start);
+    }
+
+    switch (err) {
+    case 0:
+        ide_atapi_cmd_ok(s);
+        break;
+    case -EBUSY:
+        sense = SENSE_NOT_READY;
+        if (bdrv_is_inserted(s->bs)) {
+            sense = SENSE_ILLEGAL_REQUEST;
+        }
+        ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED);
+        break;
+    default:
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+        break;
+    }
+}
+
+static void cmd_mechanism_status(IDEState *s, uint8_t* buf)
+{
+    int max_len = ube16_to_cpu(buf + 8);
+
+    cpu_to_ube16(buf, 0);
+    /* no current LBA */
+    buf[2] = 0;
+    buf[3] = 0;
+    buf[4] = 0;
+    buf[5] = 1;
+    cpu_to_ube16(buf + 6, 0);
+    ide_atapi_cmd_reply(s, 8, max_len);
+}
+
+static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf)
+{
+    int format, msf, start_track, len;
+    uint64_t total_sectors;
+    int max_len;
+
+    bdrv_get_geometry(s->bs, &total_sectors);
+
+    total_sectors >>= 2;
+    if (total_sectors == 0) {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+        return;
+    }
+
+    max_len = ube16_to_cpu(buf + 7);
+    format = buf[9] >> 6;
+    msf = (buf[1] >> 1) & 1;
+    start_track = buf[6];
+
+    switch(format) {
+    case 0:
+        len = cdrom_read_toc(total_sectors, buf, msf, start_track);
+        if (len < 0)
+            goto error_cmd;
+        ide_atapi_cmd_reply(s, len, max_len);
+        break;
+    case 1:
+        /* multi session : only a single session defined */
+        memset(buf, 0, 12);
+        buf[1] = 0x0a;
+        buf[2] = 0x01;
+        buf[3] = 0x01;
+        ide_atapi_cmd_reply(s, 12, max_len);
+        break;
+    case 2:
+        len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track);
+        if (len < 0)
+            goto error_cmd;
+        ide_atapi_cmd_reply(s, len, max_len);
+        break;
+    default:
+    error_cmd:
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                            ASC_INV_FIELD_IN_CMD_PACKET);
+    }
+}
+
+static void cmd_read_cdvd_capacity(IDEState *s, uint8_t* buf)
+{
+    uint64_t total_sectors;
+
+    bdrv_get_geometry(s->bs, &total_sectors);
+
+    total_sectors >>= 2;
+    if (total_sectors == 0) {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+        return;
+    }
+
+    /* NOTE: it is really the number of sectors minus 1 */
+    cpu_to_ube32(buf, total_sectors - 1);
+    cpu_to_ube32(buf + 4, 2048);
+    ide_atapi_cmd_reply(s, 8, 8);
+}
+
+static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
+{
+    int max_len;
+    int media = buf[1];
+    int format = buf[7];
+    int ret;
+
+    max_len = ube16_to_cpu(buf + 8);
+
+    if (format < 0xff) {
+        if (media_is_cd(s)) {
+            ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                ASC_INCOMPATIBLE_FORMAT);
+            return;
+        } else if (!media_present(s)) {
+            ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                ASC_INV_FIELD_IN_CMD_PACKET);
+            return;
+        }
+    }
+
+    memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ?
+           IDE_DMA_BUF_SECTORS * 512 + 4 : max_len);
+
+    switch (format) {
+        case 0x00 ... 0x7f:
+        case 0xff:
+            if (media == 0) {
+                ret = ide_dvd_read_structure(s, format, buf, buf);
+
+                if (ret < 0) {
+                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
+                } else {
+                    ide_atapi_cmd_reply(s, ret, max_len);
+                }
+
+                break;
+            }
+            /* TODO: BD support, fall through for now */
+
+        /* Generic disk structures */
+        case 0x80: /* TODO: AACS volume identifier */
+        case 0x81: /* TODO: AACS media serial number */
+        case 0x82: /* TODO: AACS media identifier */
+        case 0x83: /* TODO: AACS media key block */
+        case 0x90: /* TODO: List of recognized format layers */
+        case 0xc0: /* TODO: Write protection status */
+        default:
+            ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                ASC_INV_FIELD_IN_CMD_PACKET);
+            break;
+    }
+}
+
+static void cmd_set_speed(IDEState *s, uint8_t* buf)
+{
+    ide_atapi_cmd_ok(s);
+}
+
 void ide_atapi_cmd(IDEState *s)
 {
     const uint8_t *packet;
     uint8_t *buf;
-    int max_len;
 
     packet = s->io_buffer;
     buf = s->io_buffer;
@@ -665,413 +1107,52 @@ void ide_atapi_cmd(IDEState *s)
     }
     switch(s->io_buffer[0]) {
     case GPCMD_TEST_UNIT_READY:
-        if (bdrv_is_inserted(s->bs)) {
-            ide_atapi_cmd_ok(s);
-        } else {
-            ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                ASC_MEDIUM_NOT_PRESENT);
-        }
+        cmd_test_unit_ready(s, buf);
         break;
     case GPCMD_MODE_SENSE_6:
     case GPCMD_MODE_SENSE_10:
-        {
-            int action, code;
-            if (packet[0] == GPCMD_MODE_SENSE_10)
-                max_len = ube16_to_cpu(packet + 7);
-            else
-                max_len = packet[4];
-            action = packet[2] >> 6;
-            code = packet[2] & 0x3f;
-            switch(action) {
-            case 0: /* current values */
-                switch(code) {
-                case GPMODE_R_W_ERROR_PAGE: /* error recovery */
-                    cpu_to_ube16(&buf[0], 16 + 6);
-                    buf[2] = 0x70;
-                    buf[3] = 0;
-                    buf[4] = 0;
-                    buf[5] = 0;
-                    buf[6] = 0;
-                    buf[7] = 0;
-
-                    buf[8] = 0x01;
-                    buf[9] = 0x06;
-                    buf[10] = 0x00;
-                    buf[11] = 0x05;
-                    buf[12] = 0x00;
-                    buf[13] = 0x00;
-                    buf[14] = 0x00;
-                    buf[15] = 0x00;
-                    ide_atapi_cmd_reply(s, 16, max_len);
-                    break;
-                case GPMODE_AUDIO_CTL_PAGE:
-                    cpu_to_ube16(&buf[0], 24 + 6);
-                    buf[2] = 0x70;
-                    buf[3] = 0;
-                    buf[4] = 0;
-                    buf[5] = 0;
-                    buf[6] = 0;
-                    buf[7] = 0;
-
-                    /* Fill with CDROM audio volume */
-                    buf[17] = 0;
-                    buf[19] = 0;
-                    buf[21] = 0;
-                    buf[23] = 0;
-
-                    ide_atapi_cmd_reply(s, 24, max_len);
-                    break;
-                case GPMODE_CAPABILITIES_PAGE:
-                    cpu_to_ube16(&buf[0], 28 + 6);
-                    buf[2] = 0x70;
-                    buf[3] = 0;
-                    buf[4] = 0;
-                    buf[5] = 0;
-                    buf[6] = 0;
-                    buf[7] = 0;
-
-                    buf[8] = 0x2a;
-                    buf[9] = 0x12;
-                    buf[10] = 0x00;
-                    buf[11] = 0x00;
-
-                    /* Claim PLAY_AUDIO capability (0x01) since some Linux
-                       code checks for this to automount media. */
-                    buf[12] = 0x71;
-                    buf[13] = 3 << 5;
-                    buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
-                    if (bdrv_is_locked(s->bs))
-                        buf[6] |= 1 << 1;
-                    buf[15] = 0x00;
-                    cpu_to_ube16(&buf[16], 706);
-                    buf[18] = 0;
-                    buf[19] = 2;
-                    cpu_to_ube16(&buf[20], 512);
-                    cpu_to_ube16(&buf[22], 706);
-                    buf[24] = 0;
-                    buf[25] = 0;
-                    buf[26] = 0;
-                    buf[27] = 0;
-                    ide_atapi_cmd_reply(s, 28, max_len);
-                    break;
-                default:
-                    goto error_cmd;
-                }
-                break;
-            case 1: /* changeable values */
-                goto error_cmd;
-            case 2: /* default values */
-                goto error_cmd;
-            default:
-            case 3: /* saved values */
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
-                break;
-            }
-        }
+        cmd_mode_sense(s, buf);
         break;
     case GPCMD_REQUEST_SENSE:
-        max_len = packet[4];
-        memset(buf, 0, 18);
-        buf[0] = 0x70 | (1 << 7);
-        buf[2] = s->sense_key;
-        buf[7] = 10;
-        buf[12] = s->asc;
-        if (s->sense_key == SENSE_UNIT_ATTENTION)
-            s->sense_key = SENSE_NONE;
-        ide_atapi_cmd_reply(s, 18, max_len);
+        cmd_request_sense(s, buf);
         break;
     case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
-        bdrv_set_locked(s->bs, packet[4] & 1);
-        ide_atapi_cmd_ok(s);
+        cmd_prevent_allow_medium_removal(s, buf);
         break;
     case GPCMD_READ_10:
     case GPCMD_READ_12:
-        {
-            int nb_sectors, lba;
-
-            if (packet[0] == GPCMD_READ_10)
-                nb_sectors = ube16_to_cpu(packet + 7);
-            else
-                nb_sectors = ube32_to_cpu(packet + 6);
-            lba = ube32_to_cpu(packet + 2);
-            if (nb_sectors == 0) {
-                ide_atapi_cmd_ok(s);
-                break;
-            }
-            ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
-        }
+        cmd_read(s, buf);
         break;
     case GPCMD_READ_CD:
-        {
-            int nb_sectors, lba, transfer_request;
-
-            nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8];
-            lba = ube32_to_cpu(packet + 2);
-            if (nb_sectors == 0) {
-                ide_atapi_cmd_ok(s);
-                break;
-            }
-            transfer_request = packet[9];
-            switch(transfer_request & 0xf8) {
-            case 0x00:
-                /* nothing */
-                ide_atapi_cmd_ok(s);
-                break;
-            case 0x10:
-                /* normal read */
-                ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
-                break;
-            case 0xf8:
-                /* read all data */
-                ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
-                break;
-            default:
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_INV_FIELD_IN_CMD_PACKET);
-                break;
-            }
-        }
+        cmd_read_cd(s, buf);
         break;
     case GPCMD_SEEK:
-        {
-            unsigned int lba;
-            uint64_t total_sectors;
-
-            bdrv_get_geometry(s->bs, &total_sectors);
-            total_sectors >>= 2;
-            if (total_sectors == 0) {
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-            lba = ube32_to_cpu(packet + 2);
-            if (lba >= total_sectors) {
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_LOGICAL_BLOCK_OOR);
-                break;
-            }
-            ide_atapi_cmd_ok(s);
-        }
+        cmd_seek(s, buf);
         break;
     case GPCMD_START_STOP_UNIT:
-        {
-            int start, eject, sense, err = 0;
-            start = packet[4] & 1;
-            eject = (packet[4] >> 1) & 1;
-
-            if (eject) {
-                err = bdrv_eject(s->bs, !start);
-            }
-
-            switch (err) {
-            case 0:
-                ide_atapi_cmd_ok(s);
-                break;
-            case -EBUSY:
-                sense = SENSE_NOT_READY;
-                if (bdrv_is_inserted(s->bs)) {
-                    sense = SENSE_ILLEGAL_REQUEST;
-                }
-                ide_atapi_cmd_error(s, sense,
-                                    ASC_MEDIA_REMOVAL_PREVENTED);
-                break;
-            default:
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-        }
+        cmd_start_stop_unit(s, buf);
         break;
     case GPCMD_MECHANISM_STATUS:
-        {
-            max_len = ube16_to_cpu(packet + 8);
-            cpu_to_ube16(buf, 0);
-            /* no current LBA */
-            buf[2] = 0;
-            buf[3] = 0;
-            buf[4] = 0;
-            buf[5] = 1;
-            cpu_to_ube16(buf + 6, 0);
-            ide_atapi_cmd_reply(s, 8, max_len);
-        }
+        cmd_mechanism_status(s, buf);
         break;
     case GPCMD_READ_TOC_PMA_ATIP:
-        {
-            int format, msf, start_track, len;
-            uint64_t total_sectors;
-
-            bdrv_get_geometry(s->bs, &total_sectors);
-            total_sectors >>= 2;
-            if (total_sectors == 0) {
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-            max_len = ube16_to_cpu(packet + 7);
-            format = packet[9] >> 6;
-            msf = (packet[1] >> 1) & 1;
-            start_track = packet[6];
-            switch(format) {
-            case 0:
-                len = cdrom_read_toc(total_sectors, buf, msf, start_track);
-                if (len < 0)
-                    goto error_cmd;
-                ide_atapi_cmd_reply(s, len, max_len);
-                break;
-            case 1:
-                /* multi session : only a single session defined */
-                memset(buf, 0, 12);
-                buf[1] = 0x0a;
-                buf[2] = 0x01;
-                buf[3] = 0x01;
-                ide_atapi_cmd_reply(s, 12, max_len);
-                break;
-            case 2:
-                len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track);
-                if (len < 0)
-                    goto error_cmd;
-                ide_atapi_cmd_reply(s, len, max_len);
-                break;
-            default:
-            error_cmd:
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_INV_FIELD_IN_CMD_PACKET);
-                break;
-            }
-        }
+        cmd_read_toc_pma_atip(s, buf);
         break;
     case GPCMD_READ_CDVD_CAPACITY:
-        {
-            uint64_t total_sectors;
-
-            bdrv_get_geometry(s->bs, &total_sectors);
-            total_sectors >>= 2;
-            if (total_sectors == 0) {
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-            /* NOTE: it is really the number of sectors minus 1 */
-            cpu_to_ube32(buf, total_sectors - 1);
-            cpu_to_ube32(buf + 4, 2048);
-            ide_atapi_cmd_reply(s, 8, 8);
-        }
+        cmd_read_cdvd_capacity(s, buf);
         break;
     case GPCMD_READ_DVD_STRUCTURE:
-        {
-            int media = packet[1];
-            int format = packet[7];
-            int ret;
-
-            max_len = ube16_to_cpu(packet + 8);
-
-            if (format < 0xff) {
-                if (media_is_cd(s)) {
-                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                        ASC_INCOMPATIBLE_FORMAT);
-                    break;
-                } else if (!media_present(s)) {
-                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                        ASC_INV_FIELD_IN_CMD_PACKET);
-                    break;
-                }
-            }
-
-            memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ?
-                   IDE_DMA_BUF_SECTORS * 512 + 4 : max_len);
-
-            switch (format) {
-                case 0x00 ... 0x7f:
-                case 0xff:
-                    if (media == 0) {
-                        ret = ide_dvd_read_structure(s, format, packet, buf);
-
-                        if (ret < 0)
-                            ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
-                        else
-                            ide_atapi_cmd_reply(s, ret, max_len);
-
-                        break;
-                    }
-                    /* TODO: BD support, fall through for now */
-
-                /* Generic disk structures */
-                case 0x80: /* TODO: AACS volume identifier */
-                case 0x81: /* TODO: AACS media serial number */
-                case 0x82: /* TODO: AACS media identifier */
-                case 0x83: /* TODO: AACS media key block */
-                case 0x90: /* TODO: List of recognized format layers */
-                case 0xc0: /* TODO: Write protection status */
-                default:
-                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                        ASC_INV_FIELD_IN_CMD_PACKET);
-                    break;
-            }
-        }
+        cmd_read_dvd_structure(s, buf);
         break;
     case GPCMD_SET_SPEED:
-        ide_atapi_cmd_ok(s);
+        cmd_set_speed(s, buf);
         break;
     case GPCMD_INQUIRY:
-        max_len = packet[4];
-        buf[0] = 0x05; /* CD-ROM */
-        buf[1] = 0x80; /* removable */
-        buf[2] = 0x00; /* ISO */
-        buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
-        buf[4] = 31; /* additional length */
-        buf[5] = 0; /* reserved */
-        buf[6] = 0; /* reserved */
-        buf[7] = 0; /* reserved */
-        padstr8(buf + 8, 8, "QEMU");
-        padstr8(buf + 16, 16, "QEMU DVD-ROM");
-        padstr8(buf + 32, 4, s->version);
-        ide_atapi_cmd_reply(s, 36, max_len);
+        cmd_inquiry(s, buf);
         break;
     case GPCMD_GET_CONFIGURATION:
-        {
-            uint32_t len;
-            uint8_t index = 0;
-
-            /* only feature 0 is supported */
-            if (packet[2] != 0 || packet[3] != 0) {
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_INV_FIELD_IN_CMD_PACKET);
-                break;
-            }
-
-            /* XXX: could result in alignment problems in some architectures */
-            max_len = ube16_to_cpu(packet + 7);
-
-            /*
-             * XXX: avoid overflow for io_buffer if max_len is bigger than
-             *      the size of that buffer (dimensioned to max number of
-             *      sectors to transfer at once)
-             *
-             *      Only a problem if the feature/profiles grow.
-             */
-            if (max_len > 512) /* XXX: assume 1 sector */
-                max_len = 512;
-
-            memset(buf, 0, max_len);
-            /*
-             * the number of sectors from the media tells us which profile
-             * to use as current.  0 means there is no media
-             */
-            if (media_is_dvd(s))
-                cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM);
-            else if (media_is_cd(s))
-                cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM);
-
-            buf[10] = 0x02 | 0x01; /* persistent and current */
-            len = 12; /* headers: 8 + 4 */
-            len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
-            len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
-            cpu_to_ube32(buf, len - 4); /* data length */
-
-            ide_atapi_cmd_reply(s, len, max_len);
-            break;
-        }
+        cmd_get_configuration(s, buf);
+        break;
     case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
         handle_get_event_status_notification(s, buf, packet);
         break;
commit 33231e0e221543759b11a4a79b54b9a88d4b455e
Author: Kevin Wolf <kwolf at redhat.com>
Date:   Mon Apr 18 16:45:49 2011 +0200

    ide: Split atapi.c out
    
    Besides moving code, this patch only fixes some whitespace issues in the moved
    code and makes all functions in atapi.c static which can be static.
    
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/Makefile.objs b/Makefile.objs
index 44ce368..1b44695 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -242,7 +242,7 @@ hw-obj-$(CONFIG_LAN9118) += lan9118.o
 hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
 
 # IDE
-hw-obj-$(CONFIG_IDE_CORE) += ide/core.o
+hw-obj-$(CONFIG_IDE_CORE) += ide/core.o ide/atapi.o
 hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o
 hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o
 hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
new file mode 100644
index 0000000..0edfd58
--- /dev/null
+++ b/hw/ide/atapi.c
@@ -0,0 +1,1083 @@
+/*
+ * QEMU ATAPI Emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/ide/internal.h"
+#include "hw/scsi.h"
+
+static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret);
+
+static void padstr8(uint8_t *buf, int buf_size, const char *src)
+{
+    int i;
+    for(i = 0; i < buf_size; i++) {
+        if (*src)
+            buf[i] = *src++;
+        else
+            buf[i] = ' ';
+    }
+}
+
+static inline void cpu_to_ube16(uint8_t *buf, int val)
+{
+    buf[0] = val >> 8;
+    buf[1] = val & 0xff;
+}
+
+static inline void cpu_to_ube32(uint8_t *buf, unsigned int val)
+{
+    buf[0] = val >> 24;
+    buf[1] = val >> 16;
+    buf[2] = val >> 8;
+    buf[3] = val & 0xff;
+}
+
+static inline int ube16_to_cpu(const uint8_t *buf)
+{
+    return (buf[0] << 8) | buf[1];
+}
+
+static inline int ube32_to_cpu(const uint8_t *buf)
+{
+    return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+    lba += 150;
+    buf[0] = (lba / 75) / 60;
+    buf[1] = (lba / 75) % 60;
+    buf[2] = lba % 75;
+}
+
+/* XXX: DVDs that could fit on a CD will be reported as a CD */
+static inline int media_present(IDEState *s)
+{
+    return (s->nb_sectors > 0);
+}
+
+static inline int media_is_dvd(IDEState *s)
+{
+    return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS);
+}
+
+static inline int media_is_cd(IDEState *s)
+{
+    return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS);
+}
+
+static void cd_data_to_raw(uint8_t *buf, int lba)
+{
+    /* sync bytes */
+    buf[0] = 0x00;
+    memset(buf + 1, 0xff, 10);
+    buf[11] = 0x00;
+    buf += 12;
+    /* MSF */
+    lba_to_msf(buf, lba);
+    buf[3] = 0x01; /* mode 1 data */
+    buf += 4;
+    /* data */
+    buf += 2048;
+    /* XXX: ECC not computed */
+    memset(buf, 0, 288);
+}
+
+static int cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf,
+                           int sector_size)
+{
+    int ret;
+
+    switch(sector_size) {
+    case 2048:
+        ret = bdrv_read(bs, (int64_t)lba << 2, buf, 4);
+        break;
+    case 2352:
+        ret = bdrv_read(bs, (int64_t)lba << 2, buf + 16, 4);
+        if (ret < 0)
+            return ret;
+        cd_data_to_raw(buf, lba);
+        break;
+    default:
+        ret = -EIO;
+        break;
+    }
+    return ret;
+}
+
+void ide_atapi_cmd_ok(IDEState *s)
+{
+    s->error = 0;
+    s->status = READY_STAT | SEEK_STAT;
+    s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+    ide_set_irq(s->bus);
+}
+
+void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc)
+{
+#ifdef DEBUG_IDE_ATAPI
+    printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc);
+#endif
+    s->error = sense_key << 4;
+    s->status = READY_STAT | ERR_STAT;
+    s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+    s->sense_key = sense_key;
+    s->asc = asc;
+    ide_set_irq(s->bus);
+}
+
+void ide_atapi_io_error(IDEState *s, int ret)
+{
+    /* XXX: handle more errors */
+    if (ret == -ENOMEDIUM) {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY,
+                            ASC_MEDIUM_NOT_PRESENT);
+    } else {
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                            ASC_LOGICAL_BLOCK_OOR);
+    }
+}
+
+/* The whole ATAPI transfer logic is handled in this function */
+void ide_atapi_cmd_reply_end(IDEState *s)
+{
+    int byte_count_limit, size, ret;
+#ifdef DEBUG_IDE_ATAPI
+    printf("reply: tx_size=%d elem_tx_size=%d index=%d\n",
+           s->packet_transfer_size,
+           s->elementary_transfer_size,
+           s->io_buffer_index);
+#endif
+    if (s->packet_transfer_size <= 0) {
+        /* end of transfer */
+        ide_transfer_stop(s);
+        s->status = READY_STAT | SEEK_STAT;
+        s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+        ide_set_irq(s->bus);
+#ifdef DEBUG_IDE_ATAPI
+        printf("status=0x%x\n", s->status);
+#endif
+    } else {
+        /* see if a new sector must be read */
+        if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
+            ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
+            if (ret < 0) {
+                ide_transfer_stop(s);
+                ide_atapi_io_error(s, ret);
+                return;
+            }
+            s->lba++;
+            s->io_buffer_index = 0;
+        }
+        if (s->elementary_transfer_size > 0) {
+            /* there are some data left to transmit in this elementary
+               transfer */
+            size = s->cd_sector_size - s->io_buffer_index;
+            if (size > s->elementary_transfer_size)
+                size = s->elementary_transfer_size;
+            s->packet_transfer_size -= size;
+            s->elementary_transfer_size -= size;
+            s->io_buffer_index += size;
+            ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
+                               size, ide_atapi_cmd_reply_end);
+        } else {
+            /* a new transfer is needed */
+            s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+            byte_count_limit = s->lcyl | (s->hcyl << 8);
+#ifdef DEBUG_IDE_ATAPI
+            printf("byte_count_limit=%d\n", byte_count_limit);
+#endif
+            if (byte_count_limit == 0xffff)
+                byte_count_limit--;
+            size = s->packet_transfer_size;
+            if (size > byte_count_limit) {
+                /* byte count limit must be even if this case */
+                if (byte_count_limit & 1)
+                    byte_count_limit--;
+                size = byte_count_limit;
+            }
+            s->lcyl = size;
+            s->hcyl = size >> 8;
+            s->elementary_transfer_size = size;
+            /* we cannot transmit more than one sector at a time */
+            if (s->lba != -1) {
+                if (size > (s->cd_sector_size - s->io_buffer_index))
+                    size = (s->cd_sector_size - s->io_buffer_index);
+            }
+            s->packet_transfer_size -= size;
+            s->elementary_transfer_size -= size;
+            s->io_buffer_index += size;
+            ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
+                               size, ide_atapi_cmd_reply_end);
+            ide_set_irq(s->bus);
+#ifdef DEBUG_IDE_ATAPI
+            printf("status=0x%x\n", s->status);
+#endif
+        }
+    }
+}
+
+/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */
+static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
+{
+    if (size > max_size)
+        size = max_size;
+    s->lba = -1; /* no sector read */
+    s->packet_transfer_size = size;
+    s->io_buffer_size = size;    /* dma: send the reply data as one chunk */
+    s->elementary_transfer_size = 0;
+    s->io_buffer_index = 0;
+
+    if (s->atapi_dma) {
+        s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+        s->bus->dma->ops->start_dma(s->bus->dma, s,
+                                   ide_atapi_cmd_read_dma_cb);
+    } else {
+        s->status = READY_STAT | SEEK_STAT;
+        ide_atapi_cmd_reply_end(s);
+    }
+}
+
+/* start a CD-CDROM read command */
+static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
+                                   int sector_size)
+{
+    s->lba = lba;
+    s->packet_transfer_size = nb_sectors * sector_size;
+    s->elementary_transfer_size = 0;
+    s->io_buffer_index = sector_size;
+    s->cd_sector_size = sector_size;
+
+    s->status = READY_STAT | SEEK_STAT;
+    ide_atapi_cmd_reply_end(s);
+}
+
+static void ide_atapi_cmd_check_status(IDEState *s)
+{
+#ifdef DEBUG_IDE_ATAPI
+    printf("atapi_cmd_check_status\n");
+#endif
+    s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4);
+    s->status = ERR_STAT;
+    s->nsector = 0;
+    ide_set_irq(s->bus);
+}
+/* ATAPI DMA support */
+
+/* XXX: handle read errors */
+static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
+{
+    IDEState *s = opaque;
+    int data_offset, n;
+
+    if (ret < 0) {
+        ide_atapi_io_error(s, ret);
+        goto eot;
+    }
+
+    if (s->io_buffer_size > 0) {
+        /*
+         * For a cdrom read sector command (s->lba != -1),
+         * adjust the lba for the next s->io_buffer_size chunk
+         * and dma the current chunk.
+         * For a command != read (s->lba == -1), just transfer
+         * the reply data.
+         */
+        if (s->lba != -1) {
+            if (s->cd_sector_size == 2352) {
+                n = 1;
+                cd_data_to_raw(s->io_buffer, s->lba);
+            } else {
+                n = s->io_buffer_size >> 11;
+            }
+            s->lba += n;
+        }
+        s->packet_transfer_size -= s->io_buffer_size;
+        if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0)
+            goto eot;
+    }
+
+    if (s->packet_transfer_size <= 0) {
+        s->status = READY_STAT | SEEK_STAT;
+        s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+        ide_set_irq(s->bus);
+    eot:
+        s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
+        ide_set_inactive(s);
+        return;
+    }
+
+    s->io_buffer_index = 0;
+    if (s->cd_sector_size == 2352) {
+        n = 1;
+        s->io_buffer_size = s->cd_sector_size;
+        data_offset = 16;
+    } else {
+        n = s->packet_transfer_size >> 11;
+        if (n > (IDE_DMA_BUF_SECTORS / 4))
+            n = (IDE_DMA_BUF_SECTORS / 4);
+        s->io_buffer_size = n * 2048;
+        data_offset = 0;
+    }
+#ifdef DEBUG_AIO
+    printf("aio_read_cd: lba=%u n=%d\n", s->lba, n);
+#endif
+    s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset);
+    s->bus->dma->iov.iov_len = n * 4 * 512;
+    qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
+    s->bus->dma->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2,
+                                       &s->bus->dma->qiov, n * 4,
+                                       ide_atapi_cmd_read_dma_cb, s);
+    if (!s->bus->dma->aiocb) {
+        /* Note: media not present is the most likely case */
+        ide_atapi_cmd_error(s, SENSE_NOT_READY,
+                            ASC_MEDIUM_NOT_PRESENT);
+        goto eot;
+    }
+}
+
+/* start a CD-CDROM read command with DMA */
+/* XXX: test if DMA is available */
+static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors,
+                                   int sector_size)
+{
+    s->lba = lba;
+    s->packet_transfer_size = nb_sectors * sector_size;
+    s->io_buffer_index = 0;
+    s->io_buffer_size = 0;
+    s->cd_sector_size = sector_size;
+
+    /* XXX: check if BUSY_STAT should be set */
+    s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
+    s->bus->dma->ops->start_dma(s->bus->dma, s,
+                               ide_atapi_cmd_read_dma_cb);
+}
+
+static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
+                               int sector_size)
+{
+#ifdef DEBUG_IDE_ATAPI
+    printf("read %s: LBA=%d nb_sectors=%d\n", s->atapi_dma ? "dma" : "pio",
+        lba, nb_sectors);
+#endif
+    if (s->atapi_dma) {
+        ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size);
+    } else {
+        ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size);
+    }
+}
+
+static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index,
+                                            uint16_t profile)
+{
+    uint8_t *buf_profile = buf + 12; /* start of profiles */
+
+    buf_profile += ((*index) * 4); /* start of indexed profile */
+    cpu_to_ube16 (buf_profile, profile);
+    buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7]));
+
+    /* each profile adds 4 bytes to the response */
+    (*index)++;
+    buf[11] += 4; /* Additional Length */
+
+    return 4;
+}
+
+static int ide_dvd_read_structure(IDEState *s, int format,
+                                  const uint8_t *packet, uint8_t *buf)
+{
+    switch (format) {
+        case 0x0: /* Physical format information */
+            {
+                int layer = packet[6];
+                uint64_t total_sectors;
+
+                if (layer != 0)
+                    return -ASC_INV_FIELD_IN_CMD_PACKET;
+
+                bdrv_get_geometry(s->bs, &total_sectors);
+                total_sectors >>= 2;
+                if (total_sectors == 0)
+                    return -ASC_MEDIUM_NOT_PRESENT;
+
+                buf[4] = 1;   /* DVD-ROM, part version 1 */
+                buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
+                buf[6] = 1;   /* one layer, read-only (per MMC-2 spec) */
+                buf[7] = 0;   /* default densities */
+
+                /* FIXME: 0x30000 per spec? */
+                cpu_to_ube32(buf + 8, 0); /* start sector */
+                cpu_to_ube32(buf + 12, total_sectors - 1); /* end sector */
+                cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */
+
+                /* Size of buffer, not including 2 byte size field */
+                cpu_to_be16wu((uint16_t *)buf, 2048 + 2);
+
+                /* 2k data + 4 byte header */
+                return (2048 + 4);
+            }
+
+        case 0x01: /* DVD copyright information */
+            buf[4] = 0; /* no copyright data */
+            buf[5] = 0; /* no region restrictions */
+
+            /* Size of buffer, not including 2 byte size field */
+            cpu_to_be16wu((uint16_t *)buf, 4 + 2);
+
+            /* 4 byte header + 4 byte data */
+            return (4 + 4);
+
+        case 0x03: /* BCA information - invalid field for no BCA info */
+            return -ASC_INV_FIELD_IN_CMD_PACKET;
+
+        case 0x04: /* DVD disc manufacturing information */
+            /* Size of buffer, not including 2 byte size field */
+            cpu_to_be16wu((uint16_t *)buf, 2048 + 2);
+
+            /* 2k data + 4 byte header */
+            return (2048 + 4);
+
+        case 0xff:
+            /*
+             * This lists all the command capabilities above.  Add new ones
+             * in order and update the length and buffer return values.
+             */
+
+            buf[4] = 0x00; /* Physical format */
+            buf[5] = 0x40; /* Not writable, is readable */
+            cpu_to_be16wu((uint16_t *)(buf + 6), 2048 + 4);
+
+            buf[8] = 0x01; /* Copyright info */
+            buf[9] = 0x40; /* Not writable, is readable */
+            cpu_to_be16wu((uint16_t *)(buf + 10), 4 + 4);
+
+            buf[12] = 0x03; /* BCA info */
+            buf[13] = 0x40; /* Not writable, is readable */
+            cpu_to_be16wu((uint16_t *)(buf + 14), 188 + 4);
+
+            buf[16] = 0x04; /* Manufacturing info */
+            buf[17] = 0x40; /* Not writable, is readable */
+            cpu_to_be16wu((uint16_t *)(buf + 18), 2048 + 4);
+
+            /* Size of buffer, not including 2 byte size field */
+            cpu_to_be16wu((uint16_t *)buf, 16 + 2);
+
+            /* data written + 4 byte header */
+            return (16 + 4);
+
+        default: /* TODO: formats beyond DVD-ROM requires */
+            return -ASC_INV_FIELD_IN_CMD_PACKET;
+    }
+}
+
+static unsigned int event_status_media(IDEState *s,
+                                       uint8_t *buf)
+{
+    enum media_event_code {
+        MEC_NO_CHANGE = 0,       /* Status unchanged */
+        MEC_EJECT_REQUESTED,     /* received a request from user to eject */
+        MEC_NEW_MEDIA,           /* new media inserted and ready for access */
+        MEC_MEDIA_REMOVAL,       /* only for media changers */
+        MEC_MEDIA_CHANGED,       /* only for media changers */
+        MEC_BG_FORMAT_COMPLETED, /* MRW or DVD+RW b/g format completed */
+        MEC_BG_FORMAT_RESTARTED, /* MRW or DVD+RW b/g format restarted */
+    };
+    enum media_status {
+        MS_TRAY_OPEN = 1,
+        MS_MEDIA_PRESENT = 2,
+    };
+    uint8_t event_code, media_status;
+
+    media_status = 0;
+    if (s->bs->tray_open) {
+        media_status = MS_TRAY_OPEN;
+    } else if (bdrv_is_inserted(s->bs)) {
+        media_status = MS_MEDIA_PRESENT;
+    }
+
+    /* Event notification descriptor */
+    event_code = MEC_NO_CHANGE;
+    if (media_status != MS_TRAY_OPEN && s->events.new_media) {
+        event_code = MEC_NEW_MEDIA;
+        s->events.new_media = false;
+    }
+
+    buf[4] = event_code;
+    buf[5] = media_status;
+
+    /* These fields are reserved, just clear them. */
+    buf[6] = 0;
+    buf[7] = 0;
+
+    return 8; /* We wrote to 4 extra bytes from the header */
+}
+
+static void handle_get_event_status_notification(IDEState *s,
+                                                 uint8_t *buf,
+                                                 const uint8_t *packet)
+{
+    struct {
+        uint8_t opcode;
+        uint8_t polled;        /* lsb bit is polled; others are reserved */
+        uint8_t reserved2[2];
+        uint8_t class;
+        uint8_t reserved3[2];
+        uint16_t len;
+        uint8_t control;
+    } __attribute__((packed)) *gesn_cdb;
+
+    struct {
+        uint16_t len;
+        uint8_t notification_class;
+        uint8_t supported_events;
+    } __attribute((packed)) *gesn_event_header;
+
+    enum notification_class_request_type {
+        NCR_RESERVED1 = 1 << 0,
+        NCR_OPERATIONAL_CHANGE = 1 << 1,
+        NCR_POWER_MANAGEMENT = 1 << 2,
+        NCR_EXTERNAL_REQUEST = 1 << 3,
+        NCR_MEDIA = 1 << 4,
+        NCR_MULTI_HOST = 1 << 5,
+        NCR_DEVICE_BUSY = 1 << 6,
+        NCR_RESERVED2 = 1 << 7,
+    };
+    enum event_notification_class_field {
+        ENC_NO_EVENTS = 0,
+        ENC_OPERATIONAL_CHANGE,
+        ENC_POWER_MANAGEMENT,
+        ENC_EXTERNAL_REQUEST,
+        ENC_MEDIA,
+        ENC_MULTIPLE_HOSTS,
+        ENC_DEVICE_BUSY,
+        ENC_RESERVED,
+    };
+    unsigned int max_len, used_len;
+
+    gesn_cdb = (void *)packet;
+    gesn_event_header = (void *)buf;
+
+    max_len = be16_to_cpu(gesn_cdb->len);
+
+    /* It is fine by the MMC spec to not support async mode operations */
+    if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */
+        /* Only polling is supported, asynchronous mode is not. */
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                            ASC_INV_FIELD_IN_CMD_PACKET);
+        return;
+    }
+
+    /* polling mode operation */
+
+    /*
+     * These are the supported events.
+     *
+     * We currently only support requests of the 'media' type.
+     */
+    gesn_event_header->supported_events = NCR_MEDIA;
+
+    /*
+     * We use |= below to set the class field; other bits in this byte
+     * are reserved now but this is useful to do if we have to use the
+     * reserved fields later.
+     */
+    gesn_event_header->notification_class = 0;
+
+    /*
+     * Responses to requests are to be based on request priority.  The
+     * notification_class_request_type enum above specifies the
+     * priority: upper elements are higher prio than lower ones.
+     */
+    if (gesn_cdb->class & NCR_MEDIA) {
+        gesn_event_header->notification_class |= ENC_MEDIA;
+        used_len = event_status_media(s, buf);
+    } else {
+        gesn_event_header->notification_class = 0x80; /* No event available */
+        used_len = sizeof(*gesn_event_header);
+    }
+    gesn_event_header->len = cpu_to_be16(used_len
+                                         - sizeof(*gesn_event_header));
+    ide_atapi_cmd_reply(s, used_len, max_len);
+}
+
+void ide_atapi_cmd(IDEState *s)
+{
+    const uint8_t *packet;
+    uint8_t *buf;
+    int max_len;
+
+    packet = s->io_buffer;
+    buf = s->io_buffer;
+#ifdef DEBUG_IDE_ATAPI
+    {
+        int i;
+        printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8));
+        for(i = 0; i < ATAPI_PACKET_SIZE; i++) {
+            printf(" %02x", packet[i]);
+        }
+        printf("\n");
+    }
+#endif
+    /*
+     * If there's a UNIT_ATTENTION condition pending, only
+     * REQUEST_SENSE, INQUIRY, GET_CONFIGURATION and
+     * GET_EVENT_STATUS_NOTIFICATION commands are allowed to complete.
+     * MMC-5, section 4.1.6.1 lists only these commands being allowed
+     * to complete, with other commands getting a CHECK condition
+     * response unless a higher priority status, defined by the drive
+     * here, is pending.
+     */
+    if (s->sense_key == SENSE_UNIT_ATTENTION &&
+        s->io_buffer[0] != GPCMD_REQUEST_SENSE &&
+        s->io_buffer[0] != GPCMD_INQUIRY &&
+        s->io_buffer[0] != GPCMD_GET_EVENT_STATUS_NOTIFICATION) {
+        ide_atapi_cmd_check_status(s);
+        return;
+    }
+    if (bdrv_is_inserted(s->bs) && s->cdrom_changed) {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+
+        s->cdrom_changed = 0;
+        s->sense_key = SENSE_UNIT_ATTENTION;
+        s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
+        return;
+    }
+    switch(s->io_buffer[0]) {
+    case GPCMD_TEST_UNIT_READY:
+        if (bdrv_is_inserted(s->bs)) {
+            ide_atapi_cmd_ok(s);
+        } else {
+            ide_atapi_cmd_error(s, SENSE_NOT_READY,
+                                ASC_MEDIUM_NOT_PRESENT);
+        }
+        break;
+    case GPCMD_MODE_SENSE_6:
+    case GPCMD_MODE_SENSE_10:
+        {
+            int action, code;
+            if (packet[0] == GPCMD_MODE_SENSE_10)
+                max_len = ube16_to_cpu(packet + 7);
+            else
+                max_len = packet[4];
+            action = packet[2] >> 6;
+            code = packet[2] & 0x3f;
+            switch(action) {
+            case 0: /* current values */
+                switch(code) {
+                case GPMODE_R_W_ERROR_PAGE: /* error recovery */
+                    cpu_to_ube16(&buf[0], 16 + 6);
+                    buf[2] = 0x70;
+                    buf[3] = 0;
+                    buf[4] = 0;
+                    buf[5] = 0;
+                    buf[6] = 0;
+                    buf[7] = 0;
+
+                    buf[8] = 0x01;
+                    buf[9] = 0x06;
+                    buf[10] = 0x00;
+                    buf[11] = 0x05;
+                    buf[12] = 0x00;
+                    buf[13] = 0x00;
+                    buf[14] = 0x00;
+                    buf[15] = 0x00;
+                    ide_atapi_cmd_reply(s, 16, max_len);
+                    break;
+                case GPMODE_AUDIO_CTL_PAGE:
+                    cpu_to_ube16(&buf[0], 24 + 6);
+                    buf[2] = 0x70;
+                    buf[3] = 0;
+                    buf[4] = 0;
+                    buf[5] = 0;
+                    buf[6] = 0;
+                    buf[7] = 0;
+
+                    /* Fill with CDROM audio volume */
+                    buf[17] = 0;
+                    buf[19] = 0;
+                    buf[21] = 0;
+                    buf[23] = 0;
+
+                    ide_atapi_cmd_reply(s, 24, max_len);
+                    break;
+                case GPMODE_CAPABILITIES_PAGE:
+                    cpu_to_ube16(&buf[0], 28 + 6);
+                    buf[2] = 0x70;
+                    buf[3] = 0;
+                    buf[4] = 0;
+                    buf[5] = 0;
+                    buf[6] = 0;
+                    buf[7] = 0;
+
+                    buf[8] = 0x2a;
+                    buf[9] = 0x12;
+                    buf[10] = 0x00;
+                    buf[11] = 0x00;
+
+                    /* Claim PLAY_AUDIO capability (0x01) since some Linux
+                       code checks for this to automount media. */
+                    buf[12] = 0x71;
+                    buf[13] = 3 << 5;
+                    buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
+                    if (bdrv_is_locked(s->bs))
+                        buf[6] |= 1 << 1;
+                    buf[15] = 0x00;
+                    cpu_to_ube16(&buf[16], 706);
+                    buf[18] = 0;
+                    buf[19] = 2;
+                    cpu_to_ube16(&buf[20], 512);
+                    cpu_to_ube16(&buf[22], 706);
+                    buf[24] = 0;
+                    buf[25] = 0;
+                    buf[26] = 0;
+                    buf[27] = 0;
+                    ide_atapi_cmd_reply(s, 28, max_len);
+                    break;
+                default:
+                    goto error_cmd;
+                }
+                break;
+            case 1: /* changeable values */
+                goto error_cmd;
+            case 2: /* default values */
+                goto error_cmd;
+            default:
+            case 3: /* saved values */
+                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                    ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+                break;
+            }
+        }
+        break;
+    case GPCMD_REQUEST_SENSE:
+        max_len = packet[4];
+        memset(buf, 0, 18);
+        buf[0] = 0x70 | (1 << 7);
+        buf[2] = s->sense_key;
+        buf[7] = 10;
+        buf[12] = s->asc;
+        if (s->sense_key == SENSE_UNIT_ATTENTION)
+            s->sense_key = SENSE_NONE;
+        ide_atapi_cmd_reply(s, 18, max_len);
+        break;
+    case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+        bdrv_set_locked(s->bs, packet[4] & 1);
+        ide_atapi_cmd_ok(s);
+        break;
+    case GPCMD_READ_10:
+    case GPCMD_READ_12:
+        {
+            int nb_sectors, lba;
+
+            if (packet[0] == GPCMD_READ_10)
+                nb_sectors = ube16_to_cpu(packet + 7);
+            else
+                nb_sectors = ube32_to_cpu(packet + 6);
+            lba = ube32_to_cpu(packet + 2);
+            if (nb_sectors == 0) {
+                ide_atapi_cmd_ok(s);
+                break;
+            }
+            ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+        }
+        break;
+    case GPCMD_READ_CD:
+        {
+            int nb_sectors, lba, transfer_request;
+
+            nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8];
+            lba = ube32_to_cpu(packet + 2);
+            if (nb_sectors == 0) {
+                ide_atapi_cmd_ok(s);
+                break;
+            }
+            transfer_request = packet[9];
+            switch(transfer_request & 0xf8) {
+            case 0x00:
+                /* nothing */
+                ide_atapi_cmd_ok(s);
+                break;
+            case 0x10:
+                /* normal read */
+                ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+                break;
+            case 0xf8:
+                /* read all data */
+                ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
+                break;
+            default:
+                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                    ASC_INV_FIELD_IN_CMD_PACKET);
+                break;
+            }
+        }
+        break;
+    case GPCMD_SEEK:
+        {
+            unsigned int lba;
+            uint64_t total_sectors;
+
+            bdrv_get_geometry(s->bs, &total_sectors);
+            total_sectors >>= 2;
+            if (total_sectors == 0) {
+                ide_atapi_cmd_error(s, SENSE_NOT_READY,
+                                    ASC_MEDIUM_NOT_PRESENT);
+                break;
+            }
+            lba = ube32_to_cpu(packet + 2);
+            if (lba >= total_sectors) {
+                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                    ASC_LOGICAL_BLOCK_OOR);
+                break;
+            }
+            ide_atapi_cmd_ok(s);
+        }
+        break;
+    case GPCMD_START_STOP_UNIT:
+        {
+            int start, eject, sense, err = 0;
+            start = packet[4] & 1;
+            eject = (packet[4] >> 1) & 1;
+
+            if (eject) {
+                err = bdrv_eject(s->bs, !start);
+            }
+
+            switch (err) {
+            case 0:
+                ide_atapi_cmd_ok(s);
+                break;
+            case -EBUSY:
+                sense = SENSE_NOT_READY;
+                if (bdrv_is_inserted(s->bs)) {
+                    sense = SENSE_ILLEGAL_REQUEST;
+                }
+                ide_atapi_cmd_error(s, sense,
+                                    ASC_MEDIA_REMOVAL_PREVENTED);
+                break;
+            default:
+                ide_atapi_cmd_error(s, SENSE_NOT_READY,
+                                    ASC_MEDIUM_NOT_PRESENT);
+                break;
+            }
+        }
+        break;
+    case GPCMD_MECHANISM_STATUS:
+        {
+            max_len = ube16_to_cpu(packet + 8);
+            cpu_to_ube16(buf, 0);
+            /* no current LBA */
+            buf[2] = 0;
+            buf[3] = 0;
+            buf[4] = 0;
+            buf[5] = 1;
+            cpu_to_ube16(buf + 6, 0);
+            ide_atapi_cmd_reply(s, 8, max_len);
+        }
+        break;
+    case GPCMD_READ_TOC_PMA_ATIP:
+        {
+            int format, msf, start_track, len;
+            uint64_t total_sectors;
+
+            bdrv_get_geometry(s->bs, &total_sectors);
+            total_sectors >>= 2;
+            if (total_sectors == 0) {
+                ide_atapi_cmd_error(s, SENSE_NOT_READY,
+                                    ASC_MEDIUM_NOT_PRESENT);
+                break;
+            }
+            max_len = ube16_to_cpu(packet + 7);
+            format = packet[9] >> 6;
+            msf = (packet[1] >> 1) & 1;
+            start_track = packet[6];
+            switch(format) {
+            case 0:
+                len = cdrom_read_toc(total_sectors, buf, msf, start_track);
+                if (len < 0)
+                    goto error_cmd;
+                ide_atapi_cmd_reply(s, len, max_len);
+                break;
+            case 1:
+                /* multi session : only a single session defined */
+                memset(buf, 0, 12);
+                buf[1] = 0x0a;
+                buf[2] = 0x01;
+                buf[3] = 0x01;
+                ide_atapi_cmd_reply(s, 12, max_len);
+                break;
+            case 2:
+                len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track);
+                if (len < 0)
+                    goto error_cmd;
+                ide_atapi_cmd_reply(s, len, max_len);
+                break;
+            default:
+            error_cmd:
+                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                    ASC_INV_FIELD_IN_CMD_PACKET);
+                break;
+            }
+        }
+        break;
+    case GPCMD_READ_CDVD_CAPACITY:
+        {
+            uint64_t total_sectors;
+
+            bdrv_get_geometry(s->bs, &total_sectors);
+            total_sectors >>= 2;
+            if (total_sectors == 0) {
+                ide_atapi_cmd_error(s, SENSE_NOT_READY,
+                                    ASC_MEDIUM_NOT_PRESENT);
+                break;
+            }
+            /* NOTE: it is really the number of sectors minus 1 */
+            cpu_to_ube32(buf, total_sectors - 1);
+            cpu_to_ube32(buf + 4, 2048);
+            ide_atapi_cmd_reply(s, 8, 8);
+        }
+        break;
+    case GPCMD_READ_DVD_STRUCTURE:
+        {
+            int media = packet[1];
+            int format = packet[7];
+            int ret;
+
+            max_len = ube16_to_cpu(packet + 8);
+
+            if (format < 0xff) {
+                if (media_is_cd(s)) {
+                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                        ASC_INCOMPATIBLE_FORMAT);
+                    break;
+                } else if (!media_present(s)) {
+                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                        ASC_INV_FIELD_IN_CMD_PACKET);
+                    break;
+                }
+            }
+
+            memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ?
+                   IDE_DMA_BUF_SECTORS * 512 + 4 : max_len);
+
+            switch (format) {
+                case 0x00 ... 0x7f:
+                case 0xff:
+                    if (media == 0) {
+                        ret = ide_dvd_read_structure(s, format, packet, buf);
+
+                        if (ret < 0)
+                            ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
+                        else
+                            ide_atapi_cmd_reply(s, ret, max_len);
+
+                        break;
+                    }
+                    /* TODO: BD support, fall through for now */
+
+                /* Generic disk structures */
+                case 0x80: /* TODO: AACS volume identifier */
+                case 0x81: /* TODO: AACS media serial number */
+                case 0x82: /* TODO: AACS media identifier */
+                case 0x83: /* TODO: AACS media key block */
+                case 0x90: /* TODO: List of recognized format layers */
+                case 0xc0: /* TODO: Write protection status */
+                default:
+                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                        ASC_INV_FIELD_IN_CMD_PACKET);
+                    break;
+            }
+        }
+        break;
+    case GPCMD_SET_SPEED:
+        ide_atapi_cmd_ok(s);
+        break;
+    case GPCMD_INQUIRY:
+        max_len = packet[4];
+        buf[0] = 0x05; /* CD-ROM */
+        buf[1] = 0x80; /* removable */
+        buf[2] = 0x00; /* ISO */
+        buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
+        buf[4] = 31; /* additional length */
+        buf[5] = 0; /* reserved */
+        buf[6] = 0; /* reserved */
+        buf[7] = 0; /* reserved */
+        padstr8(buf + 8, 8, "QEMU");
+        padstr8(buf + 16, 16, "QEMU DVD-ROM");
+        padstr8(buf + 32, 4, s->version);
+        ide_atapi_cmd_reply(s, 36, max_len);
+        break;
+    case GPCMD_GET_CONFIGURATION:
+        {
+            uint32_t len;
+            uint8_t index = 0;
+
+            /* only feature 0 is supported */
+            if (packet[2] != 0 || packet[3] != 0) {
+                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                                    ASC_INV_FIELD_IN_CMD_PACKET);
+                break;
+            }
+
+            /* XXX: could result in alignment problems in some architectures */
+            max_len = ube16_to_cpu(packet + 7);
+
+            /*
+             * XXX: avoid overflow for io_buffer if max_len is bigger than
+             *      the size of that buffer (dimensioned to max number of
+             *      sectors to transfer at once)
+             *
+             *      Only a problem if the feature/profiles grow.
+             */
+            if (max_len > 512) /* XXX: assume 1 sector */
+                max_len = 512;
+
+            memset(buf, 0, max_len);
+            /*
+             * the number of sectors from the media tells us which profile
+             * to use as current.  0 means there is no media
+             */
+            if (media_is_dvd(s))
+                cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM);
+            else if (media_is_cd(s))
+                cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM);
+
+            buf[10] = 0x02 | 0x01; /* persistent and current */
+            len = 12; /* headers: 8 + 4 */
+            len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
+            len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
+            cpu_to_ube32(buf, len - 4); /* data length */
+
+            ide_atapi_cmd_reply(s, len, max_len);
+            break;
+        }
+    case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
+        handle_get_event_status_notification(s, buf, packet);
+        break;
+    default:
+        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+                            ASC_ILLEGAL_OPCODE);
+        break;
+    }
+}
diff --git a/hw/ide/core.c b/hw/ide/core.c
index d8c613a..90f553b 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -25,7 +25,6 @@
 #include <hw/hw.h>
 #include <hw/pc.h>
 #include <hw/pci.h>
-#include <hw/scsi.h>
 #include "qemu-error.h"
 #include "qemu-timer.h"
 #include "sysemu.h"
@@ -56,23 +55,6 @@ static const int smart_attributes[][12] = {
     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
 };
 
-/* XXX: DVDs that could fit on a CD will be reported as a CD */
-static inline int media_present(IDEState *s)
-{
-    return (s->nb_sectors > 0);
-}
-
-static inline int media_is_dvd(IDEState *s)
-{
-    return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS);
-}
-
-static inline int media_is_cd(IDEState *s)
-{
-    return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS);
-}
-
-static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret);
 static int ide_handle_rw_error(IDEState *s, int error, int op);
 
 static void padstr(char *str, const char *src, int len)
@@ -87,17 +69,6 @@ static void padstr(char *str, const char *src, int len)
     }
 }
 
-static void padstr8(uint8_t *buf, int buf_size, const char *src)
-{
-    int i;
-    for(i = 0; i < buf_size; i++) {
-        if (*src)
-            buf[i] = *src++;
-        else
-            buf[i] = ' ';
-    }
-}
-
 static void put_le16(uint16_t *p, unsigned int v)
 {
     *p = cpu_to_le16(v);
@@ -335,8 +306,8 @@ static inline void ide_abort_command(IDEState *s)
 }
 
 /* prepare data transfer and tell what to do after */
-static void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
-                               EndTransferFunc *end_transfer_func)
+void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
+                        EndTransferFunc *end_transfer_func)
 {
     s->end_transfer_func = end_transfer_func;
     s->data_ptr = buf;
@@ -347,7 +318,7 @@ static void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
     s->bus->dma->ops->start_transfer(s->bus->dma);
 }
 
-static void ide_transfer_stop(IDEState *s)
+void ide_transfer_stop(IDEState *s)
 {
     s->end_transfer_func = ide_transfer_stop;
     s->data_ptr = s->io_buffer;
@@ -447,7 +418,7 @@ static void dma_buf_commit(IDEState *s, int is_write)
     qemu_sglist_destroy(&s->sg);
 }
 
-static void ide_set_inactive(IDEState *s)
+void ide_set_inactive(IDEState *s)
 {
     s->bus->dma->aiocb = NULL;
     s->bus->dma->ops->set_inactive(s->bus->dma);
@@ -617,38 +588,6 @@ void ide_sector_write(IDEState *s)
     }
 }
 
-void ide_atapi_cmd_ok(IDEState *s)
-{
-    s->error = 0;
-    s->status = READY_STAT | SEEK_STAT;
-    s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
-    ide_set_irq(s->bus);
-}
-
-void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc)
-{
-#ifdef DEBUG_IDE_ATAPI
-    printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc);
-#endif
-    s->error = sense_key << 4;
-    s->status = READY_STAT | ERR_STAT;
-    s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
-    s->sense_key = sense_key;
-    s->asc = asc;
-    ide_set_irq(s->bus);
-}
-
-static void ide_atapi_cmd_check_status(IDEState *s)
-{
-#ifdef DEBUG_IDE_ATAPI
-    printf("atapi_cmd_check_status\n");
-#endif
-    s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4);
-    s->status = ERR_STAT;
-    s->nsector = 0;
-    ide_set_irq(s->bus);
-}
-
 static void ide_flush_cb(void *opaque, int ret)
 {
     IDEState *s = opaque;
@@ -679,1002 +618,6 @@ void ide_flush_cache(IDEState *s)
     }
 }
 
-static inline void cpu_to_ube16(uint8_t *buf, int val)
-{
-    buf[0] = val >> 8;
-    buf[1] = val & 0xff;
-}
-
-static inline void cpu_to_ube32(uint8_t *buf, unsigned int val)
-{
-    buf[0] = val >> 24;
-    buf[1] = val >> 16;
-    buf[2] = val >> 8;
-    buf[3] = val & 0xff;
-}
-
-static inline int ube16_to_cpu(const uint8_t *buf)
-{
-    return (buf[0] << 8) | buf[1];
-}
-
-static inline int ube32_to_cpu(const uint8_t *buf)
-{
-    return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
-}
-
-static void lba_to_msf(uint8_t *buf, int lba)
-{
-    lba += 150;
-    buf[0] = (lba / 75) / 60;
-    buf[1] = (lba / 75) % 60;
-    buf[2] = lba % 75;
-}
-
-static void cd_data_to_raw(uint8_t *buf, int lba)
-{
-    /* sync bytes */
-    buf[0] = 0x00;
-    memset(buf + 1, 0xff, 10);
-    buf[11] = 0x00;
-    buf += 12;
-    /* MSF */
-    lba_to_msf(buf, lba);
-    buf[3] = 0x01; /* mode 1 data */
-    buf += 4;
-    /* data */
-    buf += 2048;
-    /* XXX: ECC not computed */
-    memset(buf, 0, 288);
-}
-
-static int cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf,
-                           int sector_size)
-{
-    int ret;
-
-    switch(sector_size) {
-    case 2048:
-        ret = bdrv_read(bs, (int64_t)lba << 2, buf, 4);
-        break;
-    case 2352:
-        ret = bdrv_read(bs, (int64_t)lba << 2, buf + 16, 4);
-        if (ret < 0)
-            return ret;
-        cd_data_to_raw(buf, lba);
-        break;
-    default:
-        ret = -EIO;
-        break;
-    }
-    return ret;
-}
-
-void ide_atapi_io_error(IDEState *s, int ret)
-{
-    /* XXX: handle more errors */
-    if (ret == -ENOMEDIUM) {
-        ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                            ASC_MEDIUM_NOT_PRESENT);
-    } else {
-        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                            ASC_LOGICAL_BLOCK_OOR);
-    }
-}
-
-/* The whole ATAPI transfer logic is handled in this function */
-static void ide_atapi_cmd_reply_end(IDEState *s)
-{
-    int byte_count_limit, size, ret;
-#ifdef DEBUG_IDE_ATAPI
-    printf("reply: tx_size=%d elem_tx_size=%d index=%d\n",
-           s->packet_transfer_size,
-           s->elementary_transfer_size,
-           s->io_buffer_index);
-#endif
-    if (s->packet_transfer_size <= 0) {
-        /* end of transfer */
-        ide_transfer_stop(s);
-        s->status = READY_STAT | SEEK_STAT;
-        s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
-        ide_set_irq(s->bus);
-#ifdef DEBUG_IDE_ATAPI
-        printf("status=0x%x\n", s->status);
-#endif
-    } else {
-        /* see if a new sector must be read */
-        if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
-            ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
-            if (ret < 0) {
-                ide_transfer_stop(s);
-                ide_atapi_io_error(s, ret);
-                return;
-            }
-            s->lba++;
-            s->io_buffer_index = 0;
-        }
-        if (s->elementary_transfer_size > 0) {
-            /* there are some data left to transmit in this elementary
-               transfer */
-            size = s->cd_sector_size - s->io_buffer_index;
-            if (size > s->elementary_transfer_size)
-                size = s->elementary_transfer_size;
-            s->packet_transfer_size -= size;
-            s->elementary_transfer_size -= size;
-            s->io_buffer_index += size;
-            ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
-                               size, ide_atapi_cmd_reply_end);
-        } else {
-            /* a new transfer is needed */
-            s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
-            byte_count_limit = s->lcyl | (s->hcyl << 8);
-#ifdef DEBUG_IDE_ATAPI
-            printf("byte_count_limit=%d\n", byte_count_limit);
-#endif
-            if (byte_count_limit == 0xffff)
-                byte_count_limit--;
-            size = s->packet_transfer_size;
-            if (size > byte_count_limit) {
-                /* byte count limit must be even if this case */
-                if (byte_count_limit & 1)
-                    byte_count_limit--;
-                size = byte_count_limit;
-            }
-            s->lcyl = size;
-            s->hcyl = size >> 8;
-            s->elementary_transfer_size = size;
-            /* we cannot transmit more than one sector at a time */
-            if (s->lba != -1) {
-                if (size > (s->cd_sector_size - s->io_buffer_index))
-                    size = (s->cd_sector_size - s->io_buffer_index);
-            }
-            s->packet_transfer_size -= size;
-            s->elementary_transfer_size -= size;
-            s->io_buffer_index += size;
-            ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
-                               size, ide_atapi_cmd_reply_end);
-            ide_set_irq(s->bus);
-#ifdef DEBUG_IDE_ATAPI
-            printf("status=0x%x\n", s->status);
-#endif
-        }
-    }
-}
-
-/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */
-static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
-{
-    if (size > max_size)
-        size = max_size;
-    s->lba = -1; /* no sector read */
-    s->packet_transfer_size = size;
-    s->io_buffer_size = size;    /* dma: send the reply data as one chunk */
-    s->elementary_transfer_size = 0;
-    s->io_buffer_index = 0;
-
-    if (s->atapi_dma) {
-    	s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
-        s->bus->dma->ops->start_dma(s->bus->dma, s,
-                                   ide_atapi_cmd_read_dma_cb);
-    } else {
-    	s->status = READY_STAT | SEEK_STAT;
-    	ide_atapi_cmd_reply_end(s);
-    }
-}
-
-/* start a CD-CDROM read command */
-static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
-                                   int sector_size)
-{
-    s->lba = lba;
-    s->packet_transfer_size = nb_sectors * sector_size;
-    s->elementary_transfer_size = 0;
-    s->io_buffer_index = sector_size;
-    s->cd_sector_size = sector_size;
-
-    s->status = READY_STAT | SEEK_STAT;
-    ide_atapi_cmd_reply_end(s);
-}
-
-/* ATAPI DMA support */
-
-/* XXX: handle read errors */
-static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
-{
-    IDEState *s = opaque;
-    int data_offset, n;
-
-    if (ret < 0) {
-        ide_atapi_io_error(s, ret);
-        goto eot;
-    }
-
-    if (s->io_buffer_size > 0) {
-	/*
-	 * For a cdrom read sector command (s->lba != -1),
-	 * adjust the lba for the next s->io_buffer_size chunk
-	 * and dma the current chunk.
-	 * For a command != read (s->lba == -1), just transfer
-	 * the reply data.
-	 */
-	if (s->lba != -1) {
-	    if (s->cd_sector_size == 2352) {
-		n = 1;
-		cd_data_to_raw(s->io_buffer, s->lba);
-	    } else {
-		n = s->io_buffer_size >> 11;
-	    }
-	    s->lba += n;
-	}
-        s->packet_transfer_size -= s->io_buffer_size;
-        if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0)
-            goto eot;
-    }
-
-    if (s->packet_transfer_size <= 0) {
-        s->status = READY_STAT | SEEK_STAT;
-        s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
-        ide_set_irq(s->bus);
-    eot:
-        s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
-        ide_set_inactive(s);
-        return;
-    }
-
-    s->io_buffer_index = 0;
-    if (s->cd_sector_size == 2352) {
-        n = 1;
-        s->io_buffer_size = s->cd_sector_size;
-        data_offset = 16;
-    } else {
-        n = s->packet_transfer_size >> 11;
-        if (n > (IDE_DMA_BUF_SECTORS / 4))
-            n = (IDE_DMA_BUF_SECTORS / 4);
-        s->io_buffer_size = n * 2048;
-        data_offset = 0;
-    }
-#ifdef DEBUG_AIO
-    printf("aio_read_cd: lba=%u n=%d\n", s->lba, n);
-#endif
-    s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset);
-    s->bus->dma->iov.iov_len = n * 4 * 512;
-    qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
-    s->bus->dma->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2,
-                                       &s->bus->dma->qiov, n * 4,
-                                       ide_atapi_cmd_read_dma_cb, s);
-    if (!s->bus->dma->aiocb) {
-        /* Note: media not present is the most likely case */
-        ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                            ASC_MEDIUM_NOT_PRESENT);
-        goto eot;
-    }
-}
-
-/* start a CD-CDROM read command with DMA */
-/* XXX: test if DMA is available */
-static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors,
-                                   int sector_size)
-{
-    s->lba = lba;
-    s->packet_transfer_size = nb_sectors * sector_size;
-    s->io_buffer_index = 0;
-    s->io_buffer_size = 0;
-    s->cd_sector_size = sector_size;
-
-    /* XXX: check if BUSY_STAT should be set */
-    s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
-    s->bus->dma->ops->start_dma(s->bus->dma, s,
-                               ide_atapi_cmd_read_dma_cb);
-}
-
-static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
-                               int sector_size)
-{
-#ifdef DEBUG_IDE_ATAPI
-    printf("read %s: LBA=%d nb_sectors=%d\n", s->atapi_dma ? "dma" : "pio",
-	lba, nb_sectors);
-#endif
-    if (s->atapi_dma) {
-        ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size);
-    } else {
-        ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size);
-    }
-}
-
-static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index,
-                                            uint16_t profile)
-{
-    uint8_t *buf_profile = buf + 12; /* start of profiles */
-
-    buf_profile += ((*index) * 4); /* start of indexed profile */
-    cpu_to_ube16 (buf_profile, profile);
-    buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7]));
-
-    /* each profile adds 4 bytes to the response */
-    (*index)++;
-    buf[11] += 4; /* Additional Length */
-
-    return 4;
-}
-
-static int ide_dvd_read_structure(IDEState *s, int format,
-                                  const uint8_t *packet, uint8_t *buf)
-{
-    switch (format) {
-        case 0x0: /* Physical format information */
-            {
-                int layer = packet[6];
-                uint64_t total_sectors;
-
-                if (layer != 0)
-                    return -ASC_INV_FIELD_IN_CMD_PACKET;
-
-                bdrv_get_geometry(s->bs, &total_sectors);
-                total_sectors >>= 2;
-                if (total_sectors == 0)
-                    return -ASC_MEDIUM_NOT_PRESENT;
-
-                buf[4] = 1;   /* DVD-ROM, part version 1 */
-                buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
-                buf[6] = 1;   /* one layer, read-only (per MMC-2 spec) */
-                buf[7] = 0;   /* default densities */
-
-                /* FIXME: 0x30000 per spec? */
-                cpu_to_ube32(buf + 8, 0); /* start sector */
-                cpu_to_ube32(buf + 12, total_sectors - 1); /* end sector */
-                cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */
-
-                /* Size of buffer, not including 2 byte size field */
-                cpu_to_be16wu((uint16_t *)buf, 2048 + 2);
-
-                /* 2k data + 4 byte header */
-                return (2048 + 4);
-            }
-
-        case 0x01: /* DVD copyright information */
-            buf[4] = 0; /* no copyright data */
-            buf[5] = 0; /* no region restrictions */
-
-            /* Size of buffer, not including 2 byte size field */
-            cpu_to_be16wu((uint16_t *)buf, 4 + 2);
-
-            /* 4 byte header + 4 byte data */
-            return (4 + 4);
-
-        case 0x03: /* BCA information - invalid field for no BCA info */
-            return -ASC_INV_FIELD_IN_CMD_PACKET;
-
-        case 0x04: /* DVD disc manufacturing information */
-            /* Size of buffer, not including 2 byte size field */
-            cpu_to_be16wu((uint16_t *)buf, 2048 + 2);
-
-            /* 2k data + 4 byte header */
-            return (2048 + 4);
-
-        case 0xff:
-            /*
-             * This lists all the command capabilities above.  Add new ones
-             * in order and update the length and buffer return values.
-             */
-
-            buf[4] = 0x00; /* Physical format */
-            buf[5] = 0x40; /* Not writable, is readable */
-            cpu_to_be16wu((uint16_t *)(buf + 6), 2048 + 4);
-
-            buf[8] = 0x01; /* Copyright info */
-            buf[9] = 0x40; /* Not writable, is readable */
-            cpu_to_be16wu((uint16_t *)(buf + 10), 4 + 4);
-
-            buf[12] = 0x03; /* BCA info */
-            buf[13] = 0x40; /* Not writable, is readable */
-            cpu_to_be16wu((uint16_t *)(buf + 14), 188 + 4);
-
-            buf[16] = 0x04; /* Manufacturing info */
-            buf[17] = 0x40; /* Not writable, is readable */
-            cpu_to_be16wu((uint16_t *)(buf + 18), 2048 + 4);
-
-            /* Size of buffer, not including 2 byte size field */
-            cpu_to_be16wu((uint16_t *)buf, 16 + 2);
-
-            /* data written + 4 byte header */
-            return (16 + 4);
-
-        default: /* TODO: formats beyond DVD-ROM requires */
-            return -ASC_INV_FIELD_IN_CMD_PACKET;
-    }
-}
-
-static unsigned int event_status_media(IDEState *s,
-                                       uint8_t *buf)
-{
-    enum media_event_code {
-        MEC_NO_CHANGE = 0,       /* Status unchanged */
-        MEC_EJECT_REQUESTED,     /* received a request from user to eject */
-        MEC_NEW_MEDIA,           /* new media inserted and ready for access */
-        MEC_MEDIA_REMOVAL,       /* only for media changers */
-        MEC_MEDIA_CHANGED,       /* only for media changers */
-        MEC_BG_FORMAT_COMPLETED, /* MRW or DVD+RW b/g format completed */
-        MEC_BG_FORMAT_RESTARTED, /* MRW or DVD+RW b/g format restarted */
-    };
-    enum media_status {
-        MS_TRAY_OPEN = 1,
-        MS_MEDIA_PRESENT = 2,
-    };
-    uint8_t event_code, media_status;
-
-    media_status = 0;
-    if (s->bs->tray_open) {
-        media_status = MS_TRAY_OPEN;
-    } else if (bdrv_is_inserted(s->bs)) {
-        media_status = MS_MEDIA_PRESENT;
-    }
-
-    /* Event notification descriptor */
-    event_code = MEC_NO_CHANGE;
-    if (media_status != MS_TRAY_OPEN && s->events.new_media) {
-        event_code = MEC_NEW_MEDIA;
-        s->events.new_media = false;
-    }
-
-    buf[4] = event_code;
-    buf[5] = media_status;
-
-    /* These fields are reserved, just clear them. */
-    buf[6] = 0;
-    buf[7] = 0;
-
-    return 8; /* We wrote to 4 extra bytes from the header */
-}
-
-static void handle_get_event_status_notification(IDEState *s,
-                                                 uint8_t *buf,
-                                                 const uint8_t *packet)
-{
-    struct {
-        uint8_t opcode;
-        uint8_t polled;        /* lsb bit is polled; others are reserved */
-        uint8_t reserved2[2];
-        uint8_t class;
-        uint8_t reserved3[2];
-        uint16_t len;
-        uint8_t control;
-    } __attribute__((packed)) *gesn_cdb;
-
-    struct {
-        uint16_t len;
-        uint8_t notification_class;
-        uint8_t supported_events;
-    } __attribute((packed)) *gesn_event_header;
-
-    enum notification_class_request_type {
-        NCR_RESERVED1 = 1 << 0,
-        NCR_OPERATIONAL_CHANGE = 1 << 1,
-        NCR_POWER_MANAGEMENT = 1 << 2,
-        NCR_EXTERNAL_REQUEST = 1 << 3,
-        NCR_MEDIA = 1 << 4,
-        NCR_MULTI_HOST = 1 << 5,
-        NCR_DEVICE_BUSY = 1 << 6,
-        NCR_RESERVED2 = 1 << 7,
-    };
-    enum event_notification_class_field {
-        ENC_NO_EVENTS = 0,
-        ENC_OPERATIONAL_CHANGE,
-        ENC_POWER_MANAGEMENT,
-        ENC_EXTERNAL_REQUEST,
-        ENC_MEDIA,
-        ENC_MULTIPLE_HOSTS,
-        ENC_DEVICE_BUSY,
-        ENC_RESERVED,
-    };
-    unsigned int max_len, used_len;
-
-    gesn_cdb = (void *)packet;
-    gesn_event_header = (void *)buf;
-
-    max_len = be16_to_cpu(gesn_cdb->len);
-
-    /* It is fine by the MMC spec to not support async mode operations */
-    if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */
-        /* Only polling is supported, asynchronous mode is not. */
-        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                            ASC_INV_FIELD_IN_CMD_PACKET);
-        return;
-    }
-
-    /* polling mode operation */
-
-    /*
-     * These are the supported events.
-     *
-     * We currently only support requests of the 'media' type.
-     */
-    gesn_event_header->supported_events = NCR_MEDIA;
-
-    /*
-     * We use |= below to set the class field; other bits in this byte
-     * are reserved now but this is useful to do if we have to use the
-     * reserved fields later.
-     */
-    gesn_event_header->notification_class = 0;
-
-    /*
-     * Responses to requests are to be based on request priority.  The
-     * notification_class_request_type enum above specifies the
-     * priority: upper elements are higher prio than lower ones.
-     */
-    if (gesn_cdb->class & NCR_MEDIA) {
-        gesn_event_header->notification_class |= ENC_MEDIA;
-        used_len = event_status_media(s, buf);
-    } else {
-        gesn_event_header->notification_class = 0x80; /* No event available */
-        used_len = sizeof(*gesn_event_header);
-    }
-    gesn_event_header->len = cpu_to_be16(used_len
-                                         - sizeof(*gesn_event_header));
-    ide_atapi_cmd_reply(s, used_len, max_len);
-}
-
-static void ide_atapi_cmd(IDEState *s)
-{
-    const uint8_t *packet;
-    uint8_t *buf;
-    int max_len;
-
-    packet = s->io_buffer;
-    buf = s->io_buffer;
-#ifdef DEBUG_IDE_ATAPI
-    {
-        int i;
-        printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8));
-        for(i = 0; i < ATAPI_PACKET_SIZE; i++) {
-            printf(" %02x", packet[i]);
-        }
-        printf("\n");
-    }
-#endif
-    /*
-     * If there's a UNIT_ATTENTION condition pending, only
-     * REQUEST_SENSE, INQUIRY, GET_CONFIGURATION and
-     * GET_EVENT_STATUS_NOTIFICATION commands are allowed to complete.
-     * MMC-5, section 4.1.6.1 lists only these commands being allowed
-     * to complete, with other commands getting a CHECK condition
-     * response unless a higher priority status, defined by the drive
-     * here, is pending.
-     */
-    if (s->sense_key == SENSE_UNIT_ATTENTION &&
-        s->io_buffer[0] != GPCMD_REQUEST_SENSE &&
-        s->io_buffer[0] != GPCMD_INQUIRY &&
-        s->io_buffer[0] != GPCMD_GET_EVENT_STATUS_NOTIFICATION) {
-        ide_atapi_cmd_check_status(s);
-        return;
-    }
-    if (bdrv_is_inserted(s->bs) && s->cdrom_changed) {
-        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-
-        s->cdrom_changed = 0;
-        s->sense_key = SENSE_UNIT_ATTENTION;
-        s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
-        return;
-    }
-    switch(s->io_buffer[0]) {
-    case GPCMD_TEST_UNIT_READY:
-        if (bdrv_is_inserted(s->bs)) {
-            ide_atapi_cmd_ok(s);
-        } else {
-            ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                ASC_MEDIUM_NOT_PRESENT);
-        }
-        break;
-    case GPCMD_MODE_SENSE_6:
-    case GPCMD_MODE_SENSE_10:
-        {
-            int action, code;
-            if (packet[0] == GPCMD_MODE_SENSE_10)
-                max_len = ube16_to_cpu(packet + 7);
-            else
-                max_len = packet[4];
-            action = packet[2] >> 6;
-            code = packet[2] & 0x3f;
-            switch(action) {
-            case 0: /* current values */
-                switch(code) {
-                case GPMODE_R_W_ERROR_PAGE: /* error recovery */
-                    cpu_to_ube16(&buf[0], 16 + 6);
-                    buf[2] = 0x70;
-                    buf[3] = 0;
-                    buf[4] = 0;
-                    buf[5] = 0;
-                    buf[6] = 0;
-                    buf[7] = 0;
-
-                    buf[8] = 0x01;
-                    buf[9] = 0x06;
-                    buf[10] = 0x00;
-                    buf[11] = 0x05;
-                    buf[12] = 0x00;
-                    buf[13] = 0x00;
-                    buf[14] = 0x00;
-                    buf[15] = 0x00;
-                    ide_atapi_cmd_reply(s, 16, max_len);
-                    break;
-                case GPMODE_AUDIO_CTL_PAGE:
-                    cpu_to_ube16(&buf[0], 24 + 6);
-                    buf[2] = 0x70;
-                    buf[3] = 0;
-                    buf[4] = 0;
-                    buf[5] = 0;
-                    buf[6] = 0;
-                    buf[7] = 0;
-
-                    /* Fill with CDROM audio volume */
-                    buf[17] = 0;
-                    buf[19] = 0;
-                    buf[21] = 0;
-                    buf[23] = 0;
-
-                    ide_atapi_cmd_reply(s, 24, max_len);
-                    break;
-                case GPMODE_CAPABILITIES_PAGE:
-                    cpu_to_ube16(&buf[0], 28 + 6);
-                    buf[2] = 0x70;
-                    buf[3] = 0;
-                    buf[4] = 0;
-                    buf[5] = 0;
-                    buf[6] = 0;
-                    buf[7] = 0;
-
-                    buf[8] = 0x2a;
-                    buf[9] = 0x12;
-                    buf[10] = 0x00;
-                    buf[11] = 0x00;
-
-                    /* Claim PLAY_AUDIO capability (0x01) since some Linux
-                       code checks for this to automount media. */
-                    buf[12] = 0x71;
-                    buf[13] = 3 << 5;
-                    buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
-                    if (bdrv_is_locked(s->bs))
-                        buf[6] |= 1 << 1;
-                    buf[15] = 0x00;
-                    cpu_to_ube16(&buf[16], 706);
-                    buf[18] = 0;
-                    buf[19] = 2;
-                    cpu_to_ube16(&buf[20], 512);
-                    cpu_to_ube16(&buf[22], 706);
-                    buf[24] = 0;
-                    buf[25] = 0;
-                    buf[26] = 0;
-                    buf[27] = 0;
-                    ide_atapi_cmd_reply(s, 28, max_len);
-                    break;
-                default:
-                    goto error_cmd;
-                }
-                break;
-            case 1: /* changeable values */
-                goto error_cmd;
-            case 2: /* default values */
-                goto error_cmd;
-            default:
-            case 3: /* saved values */
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
-                break;
-            }
-        }
-        break;
-    case GPCMD_REQUEST_SENSE:
-        max_len = packet[4];
-        memset(buf, 0, 18);
-        buf[0] = 0x70 | (1 << 7);
-        buf[2] = s->sense_key;
-        buf[7] = 10;
-        buf[12] = s->asc;
-        if (s->sense_key == SENSE_UNIT_ATTENTION)
-            s->sense_key = SENSE_NONE;
-        ide_atapi_cmd_reply(s, 18, max_len);
-        break;
-    case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
-        bdrv_set_locked(s->bs, packet[4] & 1);
-        ide_atapi_cmd_ok(s);
-        break;
-    case GPCMD_READ_10:
-    case GPCMD_READ_12:
-        {
-            int nb_sectors, lba;
-
-            if (packet[0] == GPCMD_READ_10)
-                nb_sectors = ube16_to_cpu(packet + 7);
-            else
-                nb_sectors = ube32_to_cpu(packet + 6);
-            lba = ube32_to_cpu(packet + 2);
-            if (nb_sectors == 0) {
-                ide_atapi_cmd_ok(s);
-                break;
-            }
-            ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
-        }
-        break;
-    case GPCMD_READ_CD:
-        {
-            int nb_sectors, lba, transfer_request;
-
-            nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8];
-            lba = ube32_to_cpu(packet + 2);
-            if (nb_sectors == 0) {
-                ide_atapi_cmd_ok(s);
-                break;
-            }
-            transfer_request = packet[9];
-            switch(transfer_request & 0xf8) {
-            case 0x00:
-                /* nothing */
-                ide_atapi_cmd_ok(s);
-                break;
-            case 0x10:
-                /* normal read */
-                ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
-                break;
-            case 0xf8:
-                /* read all data */
-                ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
-                break;
-            default:
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_INV_FIELD_IN_CMD_PACKET);
-                break;
-            }
-        }
-        break;
-    case GPCMD_SEEK:
-        {
-            unsigned int lba;
-            uint64_t total_sectors;
-
-            bdrv_get_geometry(s->bs, &total_sectors);
-            total_sectors >>= 2;
-            if (total_sectors == 0) {
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-            lba = ube32_to_cpu(packet + 2);
-            if (lba >= total_sectors) {
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_LOGICAL_BLOCK_OOR);
-                break;
-            }
-            ide_atapi_cmd_ok(s);
-        }
-        break;
-    case GPCMD_START_STOP_UNIT:
-        {
-            int start, eject, sense, err = 0;
-            start = packet[4] & 1;
-            eject = (packet[4] >> 1) & 1;
-
-            if (eject) {
-                err = bdrv_eject(s->bs, !start);
-            }
-
-            switch (err) {
-            case 0:
-                ide_atapi_cmd_ok(s);
-                break;
-            case -EBUSY:
-                sense = SENSE_NOT_READY;
-                if (bdrv_is_inserted(s->bs)) {
-                    sense = SENSE_ILLEGAL_REQUEST;
-                }
-                ide_atapi_cmd_error(s, sense,
-                                    ASC_MEDIA_REMOVAL_PREVENTED);
-                break;
-            default:
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-        }
-        break;
-    case GPCMD_MECHANISM_STATUS:
-        {
-            max_len = ube16_to_cpu(packet + 8);
-            cpu_to_ube16(buf, 0);
-            /* no current LBA */
-            buf[2] = 0;
-            buf[3] = 0;
-            buf[4] = 0;
-            buf[5] = 1;
-            cpu_to_ube16(buf + 6, 0);
-            ide_atapi_cmd_reply(s, 8, max_len);
-        }
-        break;
-    case GPCMD_READ_TOC_PMA_ATIP:
-        {
-            int format, msf, start_track, len;
-            uint64_t total_sectors;
-
-            bdrv_get_geometry(s->bs, &total_sectors);
-            total_sectors >>= 2;
-            if (total_sectors == 0) {
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-            max_len = ube16_to_cpu(packet + 7);
-            format = packet[9] >> 6;
-            msf = (packet[1] >> 1) & 1;
-            start_track = packet[6];
-            switch(format) {
-            case 0:
-                len = cdrom_read_toc(total_sectors, buf, msf, start_track);
-                if (len < 0)
-                    goto error_cmd;
-                ide_atapi_cmd_reply(s, len, max_len);
-                break;
-            case 1:
-                /* multi session : only a single session defined */
-                memset(buf, 0, 12);
-                buf[1] = 0x0a;
-                buf[2] = 0x01;
-                buf[3] = 0x01;
-                ide_atapi_cmd_reply(s, 12, max_len);
-                break;
-            case 2:
-                len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track);
-                if (len < 0)
-                    goto error_cmd;
-                ide_atapi_cmd_reply(s, len, max_len);
-                break;
-            default:
-            error_cmd:
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_INV_FIELD_IN_CMD_PACKET);
-                break;
-            }
-        }
-        break;
-    case GPCMD_READ_CDVD_CAPACITY:
-        {
-            uint64_t total_sectors;
-
-            bdrv_get_geometry(s->bs, &total_sectors);
-            total_sectors >>= 2;
-            if (total_sectors == 0) {
-                ide_atapi_cmd_error(s, SENSE_NOT_READY,
-                                    ASC_MEDIUM_NOT_PRESENT);
-                break;
-            }
-            /* NOTE: it is really the number of sectors minus 1 */
-            cpu_to_ube32(buf, total_sectors - 1);
-            cpu_to_ube32(buf + 4, 2048);
-            ide_atapi_cmd_reply(s, 8, 8);
-        }
-        break;
-    case GPCMD_READ_DVD_STRUCTURE:
-        {
-            int media = packet[1];
-            int format = packet[7];
-            int ret;
-
-            max_len = ube16_to_cpu(packet + 8);
-
-            if (format < 0xff) {
-                if (media_is_cd(s)) {
-                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                        ASC_INCOMPATIBLE_FORMAT);
-                    break;
-                } else if (!media_present(s)) {
-                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                        ASC_INV_FIELD_IN_CMD_PACKET);
-                    break;
-                }
-            }
-
-            memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ?
-                   IDE_DMA_BUF_SECTORS * 512 + 4 : max_len);
-
-            switch (format) {
-                case 0x00 ... 0x7f:
-                case 0xff:
-                    if (media == 0) {
-                        ret = ide_dvd_read_structure(s, format, packet, buf);
-
-                        if (ret < 0)
-                            ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
-                        else
-                            ide_atapi_cmd_reply(s, ret, max_len);
-
-                        break;
-                    }
-                    /* TODO: BD support, fall through for now */
-
-                /* Generic disk structures */
-                case 0x80: /* TODO: AACS volume identifier */
-                case 0x81: /* TODO: AACS media serial number */
-                case 0x82: /* TODO: AACS media identifier */
-                case 0x83: /* TODO: AACS media key block */
-                case 0x90: /* TODO: List of recognized format layers */
-                case 0xc0: /* TODO: Write protection status */
-                default:
-                    ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                        ASC_INV_FIELD_IN_CMD_PACKET);
-                    break;
-            }
-        }
-        break;
-    case GPCMD_SET_SPEED:
-        ide_atapi_cmd_ok(s);
-        break;
-    case GPCMD_INQUIRY:
-        max_len = packet[4];
-        buf[0] = 0x05; /* CD-ROM */
-        buf[1] = 0x80; /* removable */
-        buf[2] = 0x00; /* ISO */
-        buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
-        buf[4] = 31; /* additional length */
-        buf[5] = 0; /* reserved */
-        buf[6] = 0; /* reserved */
-        buf[7] = 0; /* reserved */
-        padstr8(buf + 8, 8, "QEMU");
-        padstr8(buf + 16, 16, "QEMU DVD-ROM");
-        padstr8(buf + 32, 4, s->version);
-        ide_atapi_cmd_reply(s, 36, max_len);
-        break;
-    case GPCMD_GET_CONFIGURATION:
-        {
-            uint32_t len;
-            uint8_t index = 0;
-
-            /* only feature 0 is supported */
-            if (packet[2] != 0 || packet[3] != 0) {
-                ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                                    ASC_INV_FIELD_IN_CMD_PACKET);
-                break;
-            }
-
-            /* XXX: could result in alignment problems in some architectures */
-            max_len = ube16_to_cpu(packet + 7);
-
-            /*
-             * XXX: avoid overflow for io_buffer if max_len is bigger than
-             *      the size of that buffer (dimensioned to max number of
-             *      sectors to transfer at once)
-             *
-             *      Only a problem if the feature/profiles grow.
-             */
-            if (max_len > 512) /* XXX: assume 1 sector */
-                max_len = 512;
-
-            memset(buf, 0, max_len);
-            /* 
-             * the number of sectors from the media tells us which profile
-             * to use as current.  0 means there is no media
-             */
-            if (media_is_dvd(s))
-                cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM);
-            else if (media_is_cd(s))
-                cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM);
-
-            buf[10] = 0x02 | 0x01; /* persistent and current */
-            len = 12; /* headers: 8 + 4 */
-            len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
-            len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
-            cpu_to_ube32(buf, len - 4); /* data length */
-
-            ide_atapi_cmd_reply(s, len, max_len);
-            break;
-        }
-    case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
-        handle_get_event_status_notification(s, buf, packet);
-        break;
-    default:
-        ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
-                            ASC_ILLEGAL_OPCODE);
-        break;
-    }
-}
-
 static void ide_cfata_metadata_inquiry(IDEState *s)
 {
     uint16_t *p;
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index ba7e9a8..aa198b6 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -9,6 +9,7 @@
 #include <hw/ide.h>
 #include "block_int.h"
 #include "iorange.h"
+#include "dma.h"
 
 /* debug IDE devices */
 //#define DEBUG_IDE
@@ -570,6 +571,15 @@ void ide_sector_write(IDEState *s);
 void ide_sector_read(IDEState *s);
 void ide_flush_cache(IDEState *s);
 
+void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
+                        EndTransferFunc *end_transfer_func);
+void ide_transfer_stop(IDEState *s);
+void ide_set_inactive(IDEState *s);
+
+/* hw/ide/atapi.c */
+void ide_atapi_cmd(IDEState *s);
+void ide_atapi_cmd_reply_end(IDEState *s);
+
 /* hw/ide/qdev.c */
 void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id);
 IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive);
commit ff5c52a379b9fddaf512907e9ffdc275a722e65a
Author: Avishay Traeger <AVISHAY at il.ibm.com>
Date:   Sun Apr 3 11:31:45 2011 +0300

    Improve accuracy of block migration bandwidth calculation
    
    block_mig_state.total_time is currently the sum of the read request
    latencies.  This is not very accurate because block migration uses aio and
    so several requests can be submitted at once.  Bandwidth should be computed
    with wall-clock time, not by adding the latencies.  In this case,
    "total_time" has a higher value than it should, and so the computed
    bandwidth is lower than it is in reality.  This means that migration can
    take longer than it needs to.
    However, we don't want to use pure wall-clock time here.  We are computing
    bandwidth in the asynchronous phase, where the migration repeatedly wakes
    up and sends some aio requests.  The computed bandwidth will be used for
    synchronous transfer.
    
    Signed-off-by: Avishay Traeger <avishay at il.ibm.com>
    Reviewed-by: Michael Roth <mdroth at linux.vnet.ibm.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/block-migration.c b/block-migration.c
index 576e55a..8d06a23 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -62,7 +62,6 @@ typedef struct BlkMigBlock {
     QEMUIOVector qiov;
     BlockDriverAIOCB *aiocb;
     int ret;
-    int64_t time;
     QSIMPLEQ_ENTRY(BlkMigBlock) entry;
 } BlkMigBlock;
 
@@ -78,6 +77,7 @@ typedef struct BlkMigState {
     int prev_progress;
     int bulk_completed;
     long double total_time;
+    long double prev_time_offset;
     int reads;
 } BlkMigState;
 
@@ -131,12 +131,6 @@ uint64_t blk_mig_bytes_total(void)
     return sum << BDRV_SECTOR_BITS;
 }
 
-static inline void add_avg_read_time(int64_t time)
-{
-    block_mig_state.reads++;
-    block_mig_state.total_time += time;
-}
-
 static inline long double compute_read_bwidth(void)
 {
     assert(block_mig_state.total_time != 0);
@@ -191,13 +185,14 @@ static void alloc_aio_bitmap(BlkMigDevState *bmds)
 
 static void blk_mig_read_cb(void *opaque, int ret)
 {
+    long double curr_time = qemu_get_clock_ns(rt_clock);
     BlkMigBlock *blk = opaque;
 
     blk->ret = ret;
 
-    blk->time = qemu_get_clock_ns(rt_clock) - blk->time;
-
-    add_avg_read_time(blk->time);
+    block_mig_state.reads++;
+    block_mig_state.total_time += (curr_time - block_mig_state.prev_time_offset);
+    block_mig_state.prev_time_offset = curr_time;
 
     QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry);
     bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0);
@@ -250,7 +245,9 @@ static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
     blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
     qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
 
-    blk->time = qemu_get_clock_ns(rt_clock);
+    if (block_mig_state.submitted == 0) {
+        block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
+    }
 
     blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
                                 nr_sectors, blk_mig_read_cb, blk);
@@ -409,7 +406,9 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
                 blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
                 qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
 
-                blk->time = qemu_get_clock_ns(rt_clock);
+                if (block_mig_state.submitted == 0) {
+                    block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
+                }
 
                 blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
                                             nr_sectors, blk_mig_read_cb, blk);
commit 4b9b7092b4cbef084138a446b8247ba89fd474f4
Author: Amit Shah <amit.shah at redhat.com>
Date:   Mon Apr 18 17:15:46 2011 +0530

    atapi: Add 'medium ready' to 'medium not ready' transition on cd change
    
    MMC-5 Table F.1 lists errors that can be thrown for the TEST_UNIT_READY
    command.  Going from medium not ready to medium ready states is
    communicated by throwing an error.
    
    This adds the missing 'tray opened' event that we fail to report to
    guests.  After doing this, older Linux guests properly revalidate a disc
    on the change command.  HSM violation errors, which caused Linux guests
    to do a soft-reset of the link, also go away:
    
    ata2.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6
    sr 1:0:0:0: CDB: Test Unit Ready: 00 00 00 00 00 00
    ata2.00: cmd a0/00:00:00:00:00/00:00:00:00:00/a0 tag 0
             res 01/60:00:00:00:00/00:00:00:00:00/a0 Emask 0x3 (HSM violation)
    ata2.00: status: { ERR }
    ata2: soft resetting link
    ata2.00: configured for MWDMA2
    ata2: EH complete
    
    Signed-off-by: Amit Shah <amit.shah at redhat.com>
    Acked-by: Jes Sorensen <Jes.Sorensen at redhat.com>
    Tested-by: Markus Armbruster <armbru at redhat.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/hw/ide/core.c b/hw/ide/core.c
index f028ddb..d8c613a 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1248,12 +1248,19 @@ static void ide_atapi_cmd(IDEState *s)
         ide_atapi_cmd_check_status(s);
         return;
     }
+    if (bdrv_is_inserted(s->bs) && s->cdrom_changed) {
+        ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+
+        s->cdrom_changed = 0;
+        s->sense_key = SENSE_UNIT_ATTENTION;
+        s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
+        return;
+    }
     switch(s->io_buffer[0]) {
     case GPCMD_TEST_UNIT_READY:
-        if (bdrv_is_inserted(s->bs) && !s->cdrom_changed) {
+        if (bdrv_is_inserted(s->bs)) {
             ide_atapi_cmd_ok(s);
         } else {
-            s->cdrom_changed = 0;
             ide_atapi_cmd_error(s, SENSE_NOT_READY,
                                 ASC_MEDIUM_NOT_PRESENT);
         }
@@ -1734,8 +1741,13 @@ static void cdrom_change_cb(void *opaque, int reason)
     bdrv_get_geometry(s->bs, &nb_sectors);
     s->nb_sectors = nb_sectors;
 
-    s->sense_key = SENSE_UNIT_ATTENTION;
-    s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
+    /*
+     * First indicate to the guest that a CD has been removed.  That's
+     * done on the next command the guest sends us.
+     *
+     * Then we set SENSE_UNIT_ATTENTION, by which the guest will
+     * detect a new CD in the drive.  See ide_atapi_cmd() for details.
+     */
     s->cdrom_changed = 1;
     s->events.new_media = true;
     ide_set_irq(s->bus);
commit 2f9cba0c148af32fad6813480f5c92efe17c2d49
Author: Stefan Weil <weil at mail.berlios.de>
Date:   Tue Apr 5 18:34:21 2011 +0200

    qemu-timer: Fix timers for w32
    
    Commit 68c23e5520e8286d79d96ab47c0ea722ceb75041 removed the
    multimedia timer, but this timer is needed for certain
    Linux kernels. Otherwise Linux boot stops with this error:
    
        MP-BIOS bug: 8254 timer not connected to IO-APIC
    
    So the multimedia timer is added again here.
    
    Cc: Paolo Bonzini <pbonzini at redhat.com>
    Signed-off-by: Stefan Weil <weil at mail.berlios.de>

diff --git a/qemu-timer.c b/qemu-timer.c
index e8e5e15..4141b6e 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -215,6 +215,10 @@ static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
 
 #ifdef _WIN32
 
+static int mm_start_timer(struct qemu_alarm_timer *t);
+static void mm_stop_timer(struct qemu_alarm_timer *t);
+static void mm_rearm_timer(struct qemu_alarm_timer *t);
+
 static int win32_start_timer(struct qemu_alarm_timer *t);
 static void win32_stop_timer(struct qemu_alarm_timer *t);
 static void win32_rearm_timer(struct qemu_alarm_timer *t);
@@ -307,6 +311,8 @@ static struct qemu_alarm_timer alarm_timers[] = {
 #endif
     {"unix", unix_start_timer, unix_stop_timer, NULL},
 #else
+    {"mmtimer", mm_start_timer, mm_stop_timer, NULL},
+    {"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer},
     {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer},
     {"win32", win32_start_timer, win32_stop_timer, NULL},
 #endif
@@ -1040,6 +1046,96 @@ static void unix_stop_timer(struct qemu_alarm_timer *t)
 
 #ifdef _WIN32
 
+static MMRESULT mm_timer;
+static unsigned mm_period;
+
+static void CALLBACK mm_alarm_handler(UINT uTimerID, UINT uMsg,
+                                      DWORD_PTR dwUser, DWORD_PTR dw1,
+                                      DWORD_PTR dw2)
+{
+    struct qemu_alarm_timer *t = alarm_timer;
+    if (!t) {
+        return;
+    }
+    if (alarm_has_dynticks(t) || qemu_next_alarm_deadline() <= 0) {
+        t->expired = alarm_has_dynticks(t);
+        t->pending = 1;
+        qemu_notify_event();
+    }
+}
+
+static int mm_start_timer(struct qemu_alarm_timer *t)
+{
+    TIMECAPS tc;
+    UINT flags;
+
+    memset(&tc, 0, sizeof(tc));
+    timeGetDevCaps(&tc, sizeof(tc));
+
+    mm_period = tc.wPeriodMin;
+    timeBeginPeriod(mm_period);
+
+    flags = TIME_CALLBACK_FUNCTION;
+    if (alarm_has_dynticks(t)) {
+        flags |= TIME_ONESHOT;
+    } else {
+        flags |= TIME_PERIODIC;
+    }
+
+    mm_timer = timeSetEvent(1,                  /* interval (ms) */
+                            mm_period,          /* resolution */
+                            mm_alarm_handler,   /* function */
+                            (DWORD_PTR)t,       /* parameter */
+                            flags);
+
+    if (!mm_timer) {
+        fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n",
+                GetLastError());
+        timeEndPeriod(mm_period);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void mm_stop_timer(struct qemu_alarm_timer *t)
+{
+    timeKillEvent(mm_timer);
+    timeEndPeriod(mm_period);
+}
+
+static void mm_rearm_timer(struct qemu_alarm_timer *t)
+{
+    int nearest_delta_ms;
+
+    assert(alarm_has_dynticks(t));
+    if (!active_timers[QEMU_CLOCK_REALTIME] &&
+        !active_timers[QEMU_CLOCK_VIRTUAL] &&
+        !active_timers[QEMU_CLOCK_HOST]) {
+        return;
+    }
+
+    timeKillEvent(mm_timer);
+
+    nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000;
+    if (nearest_delta_ms < 1) {
+        nearest_delta_ms = 1;
+    }
+    mm_timer = timeSetEvent(nearest_delta_ms,
+                            mm_period,
+                            mm_alarm_handler,
+                            (DWORD_PTR)t,
+                            TIME_ONESHOT | TIME_CALLBACK_FUNCTION);
+
+    if (!mm_timer) {
+        fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n",
+                GetLastError());
+
+        timeEndPeriod(mm_period);
+        exit(1);
+    }
+}
+
 static int win32_start_timer(struct qemu_alarm_timer *t)
 {
     HANDLE hTimer;
commit cd0544ee550cb125d752f765e9d9e7c3fdf464b2
Author: Stefan Weil <weil at mail.berlios.de>
Date:   Sun Apr 10 20:15:09 2011 +0200

    qemu-timer: Avoid type casts
    
    The type casts are no longer needed after some small changes
    in struct qemu_alarm_timer. This also improves readability
    of the code.
    
    Signed-off-by: Stefan Weil <weil at mail.berlios.de>

diff --git a/qemu-timer.c b/qemu-timer.c
index f771697..e8e5e15 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -175,8 +175,12 @@ struct qemu_alarm_timer {
     int (*start)(struct qemu_alarm_timer *t);
     void (*stop)(struct qemu_alarm_timer *t);
     void (*rearm)(struct qemu_alarm_timer *t);
-    void *priv;
-
+#if defined(__linux__)
+    int fd;
+    timer_t timer;
+#elif defined(_WIN32)
+    HANDLE timer;
+#endif
     char expired;
     char pending;
 };
@@ -295,18 +299,16 @@ static struct qemu_alarm_timer alarm_timers[] = {
 #ifndef _WIN32
 #ifdef __linux__
     {"dynticks", dynticks_start_timer,
-     dynticks_stop_timer, dynticks_rearm_timer, NULL},
+     dynticks_stop_timer, dynticks_rearm_timer},
     /* HPET - if available - is preferred */
-    {"hpet", hpet_start_timer, hpet_stop_timer, NULL, NULL},
+    {"hpet", hpet_start_timer, hpet_stop_timer, NULL},
     /* ...otherwise try RTC */
-    {"rtc", rtc_start_timer, rtc_stop_timer, NULL, NULL},
+    {"rtc", rtc_start_timer, rtc_stop_timer, NULL},
 #endif
-    {"unix", unix_start_timer, unix_stop_timer, NULL, NULL},
+    {"unix", unix_start_timer, unix_stop_timer, NULL},
 #else
-    {"dynticks", win32_start_timer,
-     win32_stop_timer, win32_rearm_timer, NULL},
-    {"win32", win32_start_timer,
-     win32_stop_timer, NULL, NULL},
+    {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer},
+    {"win32", win32_start_timer, win32_stop_timer, NULL},
 #endif
     {NULL, }
 };
@@ -864,7 +866,7 @@ static int hpet_start_timer(struct qemu_alarm_timer *t)
         goto fail;
 
     enable_sigio_timer(fd);
-    t->priv = (void *)(long)fd;
+    t->fd = fd;
 
     return 0;
 fail:
@@ -874,7 +876,7 @@ fail:
 
 static void hpet_stop_timer(struct qemu_alarm_timer *t)
 {
-    int fd = (long)t->priv;
+    int fd = t->fd;
 
     close(fd);
 }
@@ -903,14 +905,14 @@ static int rtc_start_timer(struct qemu_alarm_timer *t)
 
     enable_sigio_timer(rtc_fd);
 
-    t->priv = (void *)(long)rtc_fd;
+    t->fd = rtc_fd;
 
     return 0;
 }
 
 static void rtc_stop_timer(struct qemu_alarm_timer *t)
 {
-    int rtc_fd = (long)t->priv;
+    int rtc_fd = t->fd;
 
     close(rtc_fd);
 }
@@ -945,21 +947,21 @@ static int dynticks_start_timer(struct qemu_alarm_timer *t)
         return -1;
     }
 
-    t->priv = (void *)(long)host_timer;
+    t->timer = host_timer;
 
     return 0;
 }
 
 static void dynticks_stop_timer(struct qemu_alarm_timer *t)
 {
-    timer_t host_timer = (timer_t)(long)t->priv;
+    timer_t host_timer = t->timer;
 
     timer_delete(host_timer);
 }
 
 static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
 {
-    timer_t host_timer = (timer_t)(long)t->priv;
+    timer_t host_timer = t->timer;
     struct itimerspec timeout;
     int64_t nearest_delta_ns = INT64_MAX;
     int64_t current_ns;
@@ -1061,13 +1063,13 @@ static int win32_start_timer(struct qemu_alarm_timer *t)
         return -1;
     }
 
-    t->priv = (PVOID) hTimer;
+    t->timer = hTimer;
     return 0;
 }
 
 static void win32_stop_timer(struct qemu_alarm_timer *t)
 {
-    HANDLE hTimer = t->priv;
+    HANDLE hTimer = t->timer;
 
     if (hTimer) {
         DeleteTimerQueueTimer(NULL, hTimer, NULL);
@@ -1076,7 +1078,7 @@ static void win32_stop_timer(struct qemu_alarm_timer *t)
 
 static void win32_rearm_timer(struct qemu_alarm_timer *t)
 {
-    HANDLE hTimer = t->priv;
+    HANDLE hTimer = t->timer;
     int nearest_delta_ms;
     BOOLEAN success;
 
commit 2821d0f3abe408ea656898828efb8573ac60f4f4
Author: Stefan Weil <weil at mail.berlios.de>
Date:   Wed Apr 6 22:22:48 2011 +0200

    qemu-timer: Remove unneeded include statement (w32)
    
    mmsystem.h is not needed in qemu-timer.h, so remove it.
    
    Signed-off-by: Stefan Weil <weil at mail.berlios.de>

diff --git a/qemu-timer.h b/qemu-timer.h
index 2cacf65..06cbe20 100644
--- a/qemu-timer.h
+++ b/qemu-timer.h
@@ -7,7 +7,6 @@
 
 #ifdef _WIN32
 #include <windows.h>
-#include <mmsystem.h>
 #endif
 
 /* timers */
commit 45c7b37fb9c452bcc6fce0ac429ea1d1aa1f9e51
Author: Stefan Weil <weil at mail.berlios.de>
Date:   Thu Mar 24 21:31:24 2011 +0100

    qemu-timer: Add and use new function qemu_timer_expired_ns
    
    This simply moves code which is used three times
    into a new function thus improving readability.
    
    Signed-off-by: Stefan Weil <weil at mail.berlios.de>

diff --git a/qemu-timer.c b/qemu-timer.c
index b8c0c88..f771697 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -183,6 +183,11 @@ struct qemu_alarm_timer {
 
 static struct qemu_alarm_timer *alarm_timer;
 
+static bool qemu_timer_expired_ns(QEMUTimer *timer_head, int64_t current_time)
+{
+    return timer_head && (timer_head->expire_time <= current_time);
+}
+
 int qemu_alarm_pending(void)
 {
     return alarm_timer->pending;
@@ -528,10 +533,9 @@ static void qemu_mod_timer_ns(QEMUTimer *ts, int64_t expire_time)
     pt = &active_timers[ts->clock->type];
     for(;;) {
         t = *pt;
-        if (!t)
-            break;
-        if (t->expire_time > expire_time)
+        if (!qemu_timer_expired_ns(t, expire_time)) {
             break;
+        }
         pt = &t->next;
     }
     ts->expire_time = expire_time;
@@ -570,9 +574,7 @@ int qemu_timer_pending(QEMUTimer *ts)
 
 int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time)
 {
-    if (!timer_head)
-        return 0;
-    return (timer_head->expire_time <= current_time * timer_head->scale);
+    return qemu_timer_expired_ns(timer_head, current_time * timer_head->scale);
 }
 
 static void qemu_run_timers(QEMUClock *clock)
@@ -587,8 +589,9 @@ static void qemu_run_timers(QEMUClock *clock)
     ptimer_head = &active_timers[clock->type];
     for(;;) {
         ts = *ptimer_head;
-        if (!ts || ts->expire_time > current_time)
+        if (!qemu_timer_expired_ns(ts, current_time)) {
             break;
+        }
         /* remove timer from the list before calling the callback */
         *ptimer_head = ts->next;
         ts->next = NULL;
commit 9a9d9dba3ea45b2bd1539db021ff7c2d7bfc2a98
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Wed Apr 13 15:51:47 2011 +0100

    qemu-img: allow rebase to a NULL backing file when unsafe
    
    QEMU can drop a backing file so that an image file no longer depends on
    the backing file, but this feature has not been exposed in qemu-img.
    This is useful in an image streaming usecase or when an image file has
    been fully allocated and no reads can hit the backing file anymore.
    
    Since the dropping the backing file can make the image unusable, only
    allow this when the unsafe flag has been set.
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>
    Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
    Signed-off-by: Kevin Wolf <kwolf at redhat.com>

diff --git a/qemu-img.c b/qemu-img.c
index d9c2c12..ed5ba91 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1240,7 +1240,7 @@ static int img_rebase(int argc, char **argv)
         }
     }
 
-    if ((optind >= argc) || !out_baseimg) {
+    if ((optind >= argc) || (!unsafe && !out_baseimg)) {
         help();
     }
     filename = argv[optind++];


More information about the Spice-commits mailing list