]> xenbits.xensource.com Git - xenclient/ioemu.git/commitdiff
disable qemu PCI devices in HVM domains - protocol document
authorIan Jackson <ian.jackson@eu.citrix.com>
Wed, 31 Dec 2008 16:02:18 +0000 (16:02 +0000)
committerIan Jackson <Ian.Jackson@eu.citrix.com>
Wed, 31 Dec 2008 16:02:18 +0000 (16:02 +0000)
This is the protocol specification for the IO port 0x10 feature as
posted to xen-devel by Steven Smith.

Contributed-By: Steven Smith <steven.smith@eu.citrix.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
i386-dm/README.hvm-pv-magic-ioport-disable [new file with mode: 0644]

diff --git a/i386-dm/README.hvm-pv-magic-ioport-disable b/i386-dm/README.hvm-pv-magic-ioport-disable
new file mode 100644 (file)
index 0000000..656f3c6
--- /dev/null
@@ -0,0 +1,861 @@
+Message-ID: <20081215171059.GA2574@weybridge.uk.xensource.com>
+From: Steven Smith <steven.smith@eu.citrix.com>
+To: Keir Fraser <keir.fraser@eu.citrix.com>
+Cc: James Harper <james.harper@bendigoit.com.au>,
+       Ian Jackson <Ian.Jackson@eu.citrix.com>,
+       xen-devel@lists.xensource.com
+Subject: Re: [Xen-devel] disable qemu PCI devices in HVM domains
+Date: Mon, 15 Dec 2008 17:10:59 +0000
+
+> >> I like the principle of disabling the drivers via an instruction to
+> >> qemu rather than by attempting to wrestle with the Windows driver
+> >> machinery to try to hide the devices.  But couldn't we simulate a PCI
+> >> unplug or a medium change or something instead ?  Then you could do it
+> >> later in the boot after your own drivers have properly bound.
+> >> 
+> > 
+> > Is the 'pci unplug' as simple as making a call somewhere like
+> > pci_unplug(id of ide adapter)? I'm concerned that Windows may not like
+> > this.
+> My own opinion is that the ioports are fine, but they should be offsets from
+> the xen-platform-pci device's ioport bar. Also we ought to document the
+> ports in xen-platform-pci's source file, as it's going to start getting
+> messy in there.
+> 
+> I'm not sure if the approach taken by the Citrix drivers could be at all
+> useful. Cc'ing Steven Smith in case he has any comments to make.
+I can't see any reason why the approach we take in our closed-source
+drivers wouldn't work here as well.  I've attached the appropriate
+patches from our product qemu patchqueue, tidied up and stripped of
+the most obviously XenServer-specific bits, and made to apply to
+current ioemu-remote.
+
+
+The protocol covers three basic things:
+
+-- Disconnecting emulated devices.
+-- Getting log messages out of the drivers and into dom0.
+-- Allowing dom0 to block the loading of specific drivers.  This is
+   intended as a backwards-compatibility thing: if we discover a bug
+   in some old version of the drivers, then rather than working around
+   it in Xen, we have the option of just making those drivers fall
+   back to emulated mode.
+
+The current protocol works like this (from the point of view of
+drivers):
+
+1) When the drivers first come up, they check whether the unplug logic
+   is available by reading a two-byte magic number from IO port 0x10.
+   These should be 0x49d2.  If the magic number doesn't match, the
+   drivers don't do anything.
+
+2) The drivers read a one-byte protocol version from IO port 0x12.  If
+   this is 0, skip to 6.
+
+3) The drivers write a two-byte product number to IO port 0x12.  At
+   the moment, the only drivers using this protocol are our
+   closed-source ones, which use product number 1.
+
+4) The drivers write a four-byte build number to IO port 0x10.
+
+5) The drivers check the magic number by reading two bytes from 0x10
+   again.  If it's changed from 0x49d2, the drivers are blacklisted
+   and should not load.
+
+6) The drivers write a two-byte bitmask of devices to unplug to IO
+   port 0x10.  The defined fields are:
+
+   1 -- All IDE disks (not including CD drives)
+   2 -- All emulated NICs
+   4 -- All IDE disks except for the primary master (not including CD
+       drives)
+
+   The relevant emulated devices then disappear from the relevant
+   buses.  For most guest operating systems, you want to do this
+   before device enumeration happens.
+
+...) Once the drivers have checked the magic number (and the
+     blacklist, if appropriate), they can send log messages to qemu
+     which will be logged to wherever qemu's logs go
+     (/var/log/xen/qemu-dm.log on normal Xen, dom0 syslog on
+     XenServer).  These messages are written to IO port 0x12 a byte at
+     a time, and are terminated by newlines.  There's a fairly
+     aggressive rate limiter on these messages, so they shouldn't be
+     used for anything even vaguely high-volume, but they're rather
+     useful for debugging and support.
+
+This isn't exactly a pretty protocol, but it does solve the problem.
+
+
+The blacklist is, from qemu's point of view, handled mostly through
+xenstore.  A driver version is considered to be blacklisted if
+/mh/driver-blacklist/{product_name}/{build_number} exists and is
+readable, where {build_number} is the build number from step 4 as a
+decimal number.  {product_name} is a string corresponding to the
+product number in step 3; at present, the only product number is 1,
+which has a product_name of xensource-windows.
+
+
+A previous version of the protocol put the IO ports on the PCI
+platform device.  Unfortunately, that makes it difficult to get at
+them before PCI bus enumeration happens, which complicates removal of
+the emulated NICs.  It is possible to work around these but (at least
+on Windows) it's complicated and messy, and generally best avoided.
+
+Steven.
+support-hvm-pv-drivers-ioemu-support
+hvm-log-to-dom0
+rate_limit_guest_syslog
+pv-driver-version
+Index: ioemu-remote/hw/ide.c
+===================================================================
+--- ioemu-remote.orig/hw/ide.c 2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/hw/ide.c      2008-12-15 16:02:35.000000000 +0000
+@@ -484,6 +484,7 @@
+     int type; /* see IDE_TYPE_xxx */
+ } PCIIDEState;
++static PCIIDEState *principal_ide_controller;
+ #if defined(__ia64__)
+ #include <xen/hvm/ioreq.h>
+@@ -2778,6 +2779,47 @@
+     s->media_changed = 0;
+ }
++/* Unplug all of the IDE hard disks, starting at index @start in the
++   table. */
++static void _ide_unplug_harddisks(int start)
++{
++    IDEState *s;
++    int i, j;
++
++    if (!principal_ide_controller) {
++        fprintf(stderr, "No principal controller?\n");
++        return;
++    }
++    for (i = start; i < 4; i++) {
++        s = principal_ide_controller->ide_if + i;
++        if (!s->bs)
++            continue; /* drive not present */
++        if (s->is_cdrom)
++            continue; /* cdrom */
++        /* Is a hard disk, unplug it. */
++        for (j = 0; j < nb_drives; j++)
++            if (drives_table[j].bdrv == s->bs)
++                drives_table[j].bdrv = NULL;
++        bdrv_close(s->bs);
++        s->bs = NULL;
++        ide_reset(s);
++    }
++}
++
++/* Unplug all hard disks except for the primary master (which will
++   almost always be the boot device). */
++void ide_unplug_aux_harddisks(void)
++{
++    _ide_unplug_harddisks(1);
++}
++
++/* Unplug all hard disks, including the boot device. */
++void ide_unplug_harddisks(void)
++{
++    _ide_unplug_harddisks(0);
++}
++
++
+ struct partition {
+       uint8_t boot_ind;               /* 0x80 - active */
+       uint8_t head;           /* starting head */
+@@ -3290,6 +3332,9 @@
+                                            sizeof(PCIIDEState),
+                                            -1,
+                                            NULL, NULL);
++    if (principal_ide_controller)
++      abort();
++    principal_ide_controller = d;
+     d->type = IDE_TYPE_CMD646;
+     pci_conf = d->dev.config;
+     pci_conf[0x00] = 0x95; // CMD646
+@@ -3419,6 +3464,9 @@
+                                            sizeof(PCIIDEState),
+                                            devfn,
+                                            NULL, NULL);
++    if (principal_ide_controller)
++      abort();
++    principal_ide_controller = d;
+     d->type = IDE_TYPE_PIIX3;
+     pci_conf = d->dev.config;
+Index: ioemu-remote/hw/pci.c
+===================================================================
+--- ioemu-remote.orig/hw/pci.c 2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/hw/pci.c      2008-12-15 16:02:22.000000000 +0000
+@@ -26,6 +26,9 @@
+ #include "console.h"
+ #include "net.h"
++#include "exec-all.h"
++#include "qemu-xen.h"
++
+ //#define DEBUG_PCI
+ struct PCIBus {
+@@ -648,6 +651,46 @@
+     }
+ }
++void pci_unplug_netifs(void)
++{
++    PCIBus *bus;
++    PCIDevice *dev;
++    PCIIORegion *region;
++    int x;
++    int i;
++
++    /* We only support one PCI bus */
++    for (bus = first_bus; bus; bus = NULL) {
++       for (x = 0; x < 256; x++) {
++           dev = bus->devices[x];
++           if (dev &&
++               dev->config[0xa] == 0 &&
++               dev->config[0xb] == 2) {
++               /* Found a netif.  Remove it from the bus.  Note that
++                  we don't free it here, since there could still be
++                  references to it floating around.  There are only
++                  ever one or two structures leaked, and it's not
++                  worth finding them all. */
++               bus->devices[x] = NULL;
++               for (i = 0; i < PCI_NUM_REGIONS; i++) {
++                   region = &dev->io_regions[i];
++                   if (region->addr == (uint32_t)-1 ||
++                       region->size == 0)
++                       continue;
++                   fprintf(logfile, "region type %d at [%x,%x).\n",
++                           region->type, region->addr,
++                           region->addr+region->size);
++                   if (region->type == PCI_ADDRESS_SPACE_IO) {
++                       isa_unassign_ioport(region->addr, region->size);
++                   } else if (region->type == PCI_ADDRESS_SPACE_MEM) {
++                       unregister_iomem(region->addr);
++                   }
++               }
++           }
++       }
++    }
++}
++
+ typedef struct {
+     PCIDevice dev;
+     PCIBus *bus;
+Index: ioemu-remote/qemu-xen.h
+===================================================================
+--- ioemu-remote.orig/qemu-xen.h       2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/qemu-xen.h    2008-12-15 16:02:22.000000000 +0000
+@@ -26,8 +26,11 @@
+ void xen_vga_vram_map(uint64_t vram_addr, int copy);
+ #endif
+-
++void ide_unplug_harddisks(void);
++void net_tap_shutdown_all(void);
++void pci_unplug_netifs(void);
+ void destroy_hvm_domain(void);
++void unregister_iomem(target_phys_addr_t start);
+ #ifdef __ia64__
+ static inline void xc_domain_shutdown_hook(int xc_handle, uint32_t domid)
+Index: ioemu-remote/vl.c
+===================================================================
+--- ioemu-remote.orig/vl.c     2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/vl.c  2008-12-15 16:02:22.000000000 +0000
+@@ -262,6 +262,20 @@
+ #include "xen-vl-extra.c"
++typedef struct IOHandlerRecord {
++    int fd;
++    IOCanRWHandler *fd_read_poll;
++    IOHandler *fd_read;
++    IOHandler *fd_write;
++    int deleted;
++    void *opaque;
++    /* temporary data */
++    struct pollfd *ufd;
++    struct IOHandlerRecord *next;
++} IOHandlerRecord;
++
++static IOHandlerRecord *first_io_handler;
++
+ /***********************************************************/
+ /* x86 ISA bus support */
+@@ -4055,6 +4069,7 @@
+ typedef struct TAPState {
+     VLANClientState *vc;
+     int fd;
++    struct TAPState *next;
+     char down_script[1024];
+     char script_arg[1024];
+ } TAPState;
+@@ -4092,6 +4107,34 @@
+     }
+ }
++static TAPState *head_net_tap;
++
++void net_tap_shutdown_all(void)
++{
++    struct IOHandlerRecord **pioh, *ioh;
++
++    while (head_net_tap) {
++       pioh = &first_io_handler;
++       for (;;) {
++           ioh = *pioh;
++           if (ioh == NULL)
++               break;
++           if (ioh->fd == head_net_tap->fd) {
++               *pioh = ioh->next;
++               qemu_free(ioh);
++               break;
++           }
++           pioh = &ioh->next;
++       }
++       if (!ioh)
++           fprintf(stderr,
++                   "warning: can't find iohandler for %d to close it properly.\n",
++                   head_net_tap->fd);
++       close(head_net_tap->fd);
++       head_net_tap = head_net_tap->next;
++    }
++}
++
+ /* fd support */
+ static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
+@@ -4103,6 +4146,8 @@
+         return NULL;
+     s->fd = fd;
+     s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
++    s->next = head_net_tap;
++    head_net_tap = s;
+     qemu_set_fd_handler(s->fd, tap_send, NULL, s);
+     snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd);
+     return s;
+@@ -5666,20 +5711,6 @@
+ #define MAX_IO_HANDLERS 64
+-typedef struct IOHandlerRecord {
+-    int fd;
+-    IOCanRWHandler *fd_read_poll;
+-    IOHandler *fd_read;
+-    IOHandler *fd_write;
+-    int deleted;
+-    void *opaque;
+-    /* temporary data */
+-    struct pollfd *ufd;
+-    struct IOHandlerRecord *next;
+-} IOHandlerRecord;
+-
+-static IOHandlerRecord *first_io_handler;
+-
+ /* XXX: fd_read_poll should be suppressed, but an API change is
+    necessary in the character devices to suppress fd_can_read(). */
+ int qemu_set_fd_handler2(int fd,
+Index: ioemu-remote/block-raw-posix.c
+===================================================================
+--- ioemu-remote.orig/block-raw-posix.c        2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/block-raw-posix.c     2008-12-15 16:02:22.000000000 +0000
+@@ -55,6 +55,7 @@
+ #include <sys/ioctl.h>
+ #include <linux/cdrom.h>
+ #include <linux/fd.h>
++#include <sys/mount.h>
+ #endif
+ #ifdef __FreeBSD__
+ #include <sys/disk.h>
+@@ -125,6 +126,10 @@
+         return ret;
+     }
+     s->fd = fd;
++#ifndef CONFIG_STUBDOM
++    /* Invalidate buffer cache for this device. */
++    ioctl(s->fd, BLKFLSBUF, 0);
++#endif
+     return 0;
+ }
+@@ -505,6 +510,10 @@
+ {
+     BDRVRawState *s = bs->opaque;
+     if (s->fd >= 0) {
++#ifndef CONFIG_STUBDOM
++        /* Invalidate buffer cache for this device. */
++        ioctl(s->fd, BLKFLSBUF, 0);
++#endif
+         close(s->fd);
+         s->fd = -1;
+     }
+Index: ioemu-remote/i386-dm/exec-dm.c
+===================================================================
+--- ioemu-remote.orig/i386-dm/exec-dm.c        2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/i386-dm/exec-dm.c     2008-12-15 16:02:22.000000000 +0000
+@@ -267,7 +267,7 @@
+ /* XXX: Simple implementation. Fix later */
+ #define MAX_MMIO 32
+-struct mmio_space {
++static struct mmio_space {
+         target_phys_addr_t start;
+         unsigned long size;
+         unsigned long io_index;
+@@ -413,6 +413,17 @@
+         return 0;
+ }
++void unregister_iomem(target_phys_addr_t start)
++{
++    int index = iomem_index(start);
++    if (index) {
++        fprintf(logfile, "squash iomem [%lx, %lx).\n", mmio[index].start,
++                mmio[index].start + mmio[index].size);
++        mmio[index].start = mmio[index].size = 0;
++    }
++}
++
++
+ #if defined(__i386__) || defined(__x86_64__)
+ #define phys_ram_addr(x) (qemu_map_cache(x))
+ #elif defined(__ia64__)
+Index: ioemu-remote/hw/xen_platform.c
+===================================================================
+--- ioemu-remote.orig/hw/xen_platform.c        2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/hw/xen_platform.c     2008-12-15 16:02:35.000000000 +0000
+@@ -24,6 +24,7 @@
+  */
+ #include "hw.h"
++#include "pc.h"
+ #include "pci.h"
+ #include "irq.h"
+ #include "qemu-xen.h"
+@@ -163,6 +164,52 @@
+     cpu_register_physical_memory(addr, 0x1000000, mmio_io_addr);
+ }
++#define UNPLUG_ALL_IDE_DISKS 1
++#define UNPLUG_ALL_NICS 2
++#define UNPLUG_AUX_IDE_DISKS 4
++
++static void platform_fixed_ioport_write2(void *opaque, uint32_t addr, uint32_t val)
++{
++    switch (addr - 0x10) {
++    case 0:
++        /* Unplug devices.  Value is a bitmask of which devices to
++           unplug, with bit 0 the IDE devices, bit 1 the network
++           devices, and bit 2 the non-primary-master IDE devices. */
++        if (val & UNPLUG_ALL_IDE_DISKS)
++            ide_unplug_harddisks();
++        if (val & UNPLUG_ALL_NICS) {
++            pci_unplug_netifs();
++            net_tap_shutdown_all();
++        }
++        if (val & UNPLUG_AUX_IDE_DISKS) {
++            ide_unplug_aux_harddisks();
++        }
++        break;
++    }
++}
++
++static uint32_t platform_fixed_ioport_read2(void *opaque, uint32_t addr)
++{
++    switch (addr - 0x10) {
++    case 0:
++        return 0x49d2; /* Magic value so that you can identify the
++                          interface. */
++    default:
++        return 0xffff;
++    }
++}
++
++static uint32_t platform_fixed_ioport_read1(void *opaque, uint32_t addr)
++{
++    switch (addr - 0x10) {
++    case 2:
++        /* Version number */
++        return 0;
++    default:
++        return 0xff;
++    }
++}
++
+ struct pci_config_header {
+     uint16_t vendor_id;
+     uint16_t device_id;
+@@ -255,4 +302,7 @@
+     register_savevm("platform", 0, 2, xen_pci_save, xen_pci_load, d);
+     printf("Done register platform.\n");
++    register_ioport_write(0x10, 16, 2, platform_fixed_ioport_write2, NULL);
++    register_ioport_read(0x10, 16, 2, platform_fixed_ioport_read2, NULL);
++    register_ioport_read(0x10, 16, 1, platform_fixed_ioport_read1, NULL);
+ }
+Index: ioemu-remote/hw/pc.h
+===================================================================
+--- ioemu-remote.orig/hw/pc.h  2008-12-15 16:02:19.000000000 +0000
++++ ioemu-remote/hw/pc.h       2008-12-15 16:02:35.000000000 +0000
+@@ -146,6 +146,8 @@
+                         qemu_irq *pic);
+ void pci_piix4_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
+                         qemu_irq *pic);
++void ide_unplug_harddisks(void);
++void ide_unplug_aux_harddisks(void);
+ /* ne2000.c */
+Index: ioemu-remote/hw/xen_platform.c
+===================================================================
+--- ioemu-remote.orig/hw/xen_platform.c        2008-12-15 15:57:04.000000000 +0000
++++ ioemu-remote/hw/xen_platform.c     2008-12-15 16:00:19.000000000 +0000
+@@ -31,6 +31,8 @@
+ #include <xenguest.h>
+ extern FILE *logfile;
++static char log_buffer[4096];
++static int log_buffer_off;
+ #define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */
+@@ -68,6 +70,18 @@
+             d->platform_flags = val & PFFLAG_ROM_LOCK;
+         break;
+     }
++    case 8:
++        {
++            if (val == '\n' || log_buffer_off == sizeof(log_buffer) - 1) {
++                /* Flush buffer */
++                log_buffer[log_buffer_off] = 0;
++                fprintf(logfile, "%s\n", log_buffer);
++                log_buffer_off = 0;
++                break;
++            }
++            log_buffer[log_buffer_off++] = val;
++        }
++        break;
+     default:
+         break;
+     }
+@@ -180,6 +194,24 @@
+     }
+ }
++
++static void platform_fixed_ioport_write1(void *opaque, uint32_t addr, uint32_t val)
++{
++    switch (addr - 0x10) {
++    case 2:
++        /* Send bytes to syslog */
++        if (val == '\n' || log_buffer_off == sizeof(log_buffer) - 1) {
++            /* Flush buffer */
++            log_buffer[log_buffer_off] = 0;
++            fprintf(logfile, "%s\n", log_buffer);
++            log_buffer_off = 0;
++            break;
++        }
++        log_buffer[log_buffer_off++] = val;
++        break;
++    }
++}
++
+ static uint32_t platform_fixed_ioport_read2(void *opaque, uint32_t addr)
+ {
+     switch (addr - 0x10) {
+@@ -295,6 +327,7 @@
+     register_savevm("platform", 0, 2, xen_pci_save, xen_pci_load, d);
+     printf("Done register platform.\n");
+     register_ioport_write(0x10, 16, 2, platform_fixed_ioport_write2, NULL);
++    register_ioport_write(0x10, 16, 1, platform_fixed_ioport_write1, NULL);
+     register_ioport_read(0x10, 16, 2, platform_fixed_ioport_read2, NULL);
+     register_ioport_read(0x10, 16, 1, platform_fixed_ioport_read1, NULL);
+ }
+Index: ioemu-remote/hw/xen_platform.c
+===================================================================
+--- ioemu-remote.orig/hw/xen_platform.c        2008-12-15 15:02:53.000000000 +0000
++++ ioemu-remote/hw/xen_platform.c     2008-12-15 15:03:34.000000000 +0000
+@@ -29,8 +29,10 @@
+ #include "irq.h"
+ #include "qemu-xen.h"
++#include <assert.h>
+ #include <xenguest.h>
++static int throttling_disabled;
+ extern FILE *logfile;
+ static char log_buffer[4096];
+ static int log_buffer_off;
+@@ -44,6 +46,88 @@
+   uint64_t   vga_stolen_ram;
+ } PCIXenPlatformState;
++/* We throttle access to dom0 syslog, to avoid DOS attacks.  This is
++   modelled as a token bucket, with one token for every byte of log.
++   The bucket size is 128KB (->1024 lines of 128 bytes each) and
++   refills at 256B/s.  It starts full.  The guest is blocked if no
++   tokens are available when it tries to generate a log message. */
++#define BUCKET_MAX_SIZE (128*1024)
++#define BUCKET_FILL_RATE 256
++
++static void throttle(unsigned count)
++{
++    static unsigned available;
++    static struct timespec last_refil;
++    static int started;
++    static int warned;
++
++    struct timespec waiting_for, now;
++    double delay;
++    struct timespec ts;
++
++    if (throttling_disabled)
++        return;
++
++    if (!started) {
++        clock_gettime(CLOCK_MONOTONIC, &last_refil);
++        available = BUCKET_MAX_SIZE;
++        started = 1;
++    }
++
++    if (count > BUCKET_MAX_SIZE) {
++        fprintf(logfile, "tried to get %d tokens, but bucket size is %d\n",
++                BUCKET_MAX_SIZE, count);
++        exit(1);
++    }
++
++    if (available < count) {
++        /* The bucket is empty.  Refil it */
++
++        /* When will it be full enough to handle this request? */
++        delay = (double)(count - available) / BUCKET_FILL_RATE;
++        waiting_for = last_refil;
++        waiting_for.tv_sec += delay;
++        waiting_for.tv_nsec += (delay - (int)delay) * 1e9;
++        if (waiting_for.tv_nsec >= 1000000000) {
++            waiting_for.tv_nsec -= 1000000000;
++            waiting_for.tv_sec++;
++        }
++
++        /* How long do we have to wait? (might be negative) */
++        clock_gettime(CLOCK_MONOTONIC, &now);
++        ts.tv_sec = waiting_for.tv_sec - now.tv_sec;
++        ts.tv_nsec = waiting_for.tv_nsec - now.tv_nsec;
++        if (ts.tv_nsec < 0) {
++            ts.tv_sec--;
++            ts.tv_nsec += 1000000000;
++        }
++
++        /* Wait for it. */
++        if (ts.tv_sec > 0 ||
++            (ts.tv_sec == 0 && ts.tv_nsec > 0)) {
++            if (!warned) {
++                fprintf(logfile, "throttling guest access to syslog");
++                warned = 1;
++            }
++            while (nanosleep(&ts, &ts) < 0 && errno == EINTR)
++                ;
++        }
++
++        /* Refil */
++        clock_gettime(CLOCK_MONOTONIC, &now);
++        delay = (now.tv_sec - last_refil.tv_sec) +
++            (now.tv_nsec - last_refil.tv_nsec) * 1.0e-9;
++        available += BUCKET_FILL_RATE * delay;
++        if (available > BUCKET_MAX_SIZE)
++            available = BUCKET_MAX_SIZE;
++        last_refil = now;
++    }
++
++    assert(available >= count);
++
++    available -= count;
++}
++
+ static uint32_t xen_platform_ioport_readb(void *opaque, uint32_t addr)
+ {
+     PCIXenPlatformState *s = opaque;
+@@ -76,6 +160,7 @@
+             if (val == '\n' || log_buffer_off == sizeof(log_buffer) - 1) {
+                 /* Flush buffer */
+                 log_buffer[log_buffer_off] = 0;
++                throttle(log_buffer_off);
+                 fprintf(logfile, "%s\n", log_buffer);
+                 log_buffer_off = 0;
+                 break;
+@@ -278,6 +363,7 @@
+         if (val == '\n' || log_buffer_off == sizeof(log_buffer) - 1) {
+             /* Flush buffer */
+             log_buffer[log_buffer_off] = 0;
++            throttle(log_buffer_off);
+             fprintf(logfile, "%s\n", log_buffer);
+             log_buffer_off = 0;
+             break;
+@@ -302,6 +388,7 @@
+ {
+     PCIXenPlatformState *d;
+     struct pci_config_header *pch;
++    struct stat stbuf;
+     printf("Register xen platform.\n");
+     d = (PCIXenPlatformState *)pci_register_device(
+@@ -337,4 +424,8 @@
+     register_ioport_write(0x10, 16, 1, platform_fixed_ioport_write1, NULL);
+     register_ioport_read(0x10, 16, 2, platform_fixed_ioport_read2, NULL);
+     register_ioport_read(0x10, 16, 1, platform_fixed_ioport_read1, NULL);
++
++    if (stat("/etc/disable-guest-log-throttle", &stbuf) == 0)
++        throttling_disabled = 1;
++
+ }
+Index: ioemu-remote/hw/xen_platform.c
+===================================================================
+--- ioemu-remote.orig/hw/xen_platform.c        2008-12-15 15:03:34.000000000 +0000
++++ ioemu-remote/hw/xen_platform.c     2008-12-15 15:06:08.000000000 +0000
+@@ -32,6 +32,8 @@
+ #include <assert.h>
+ #include <xenguest.h>
++static int drivers_blacklisted;
++static uint16_t driver_product_version;
+ static int throttling_disabled;
+ extern FILE *logfile;
+ static char log_buffer[4096];
+@@ -341,6 +343,42 @@
+             ide_unplug_aux_harddisks();
+         }
+         break;
++    case 2:
++        switch (val) {
++        case 1:
++            fprintf(logfile, "Citrix Windows PV drivers loaded in guest\n");
++            break;
++        case 0:
++            fprintf(logfile, "Guest claimed to be running PV product 0?\n");
++            break;
++        default:
++            fprintf(logfile, "Unknown PV product %d loaded in guest\n", val);
++            break;
++        }
++        driver_product_version = val;
++        break;
++    }
++}
++
++static void platform_fixed_ioport_write4(void *opaque, uint32_t addr,
++                                         uint32_t val)
++{
++    switch (addr - 0x10) {
++    case 0:
++        /* PV driver version */
++        if (driver_product_version == 0) {
++            fprintf(logfile,
++                    "Drivers tried to set their version number (%d) before setting the product number?\n",
++                    val);
++            return;
++        }
++        fprintf(logfile, "PV driver build %d\n", val);
++        if (xenstore_pv_driver_build_blacklisted(driver_product_version,
++                                                 val)) {
++            fprintf(logfile, "Drivers are blacklisted!\n");
++            drivers_blacklisted = 1;
++        }
++        break;
+     }
+ }
+@@ -348,8 +386,14 @@
+ {
+     switch (addr - 0x10) {
+     case 0:
+-        return 0x49d2; /* Magic value so that you can identify the
+-                          interface. */
++        if (drivers_blacklisted) {
++            /* The drivers will recognise this magic number and refuse
++             * to do anything. */
++            return 0xd249;
++        } else {
++            /* Magic value so that you can identify the interface. */
++            return 0x49d2;
++        }
+     default:
+         return 0xffff;
+     }
+@@ -378,7 +422,7 @@
+     switch (addr - 0x10) {
+     case 2:
+         /* Version number */
+-        return 0;
++        return 1;
+     default:
+         return 0xff;
+     }
+@@ -420,6 +464,7 @@
+     register_savevm("platform", 0, 2, xen_pci_save, xen_pci_load, d);
+     printf("Done register platform.\n");
++    register_ioport_write(0x10, 16, 4, platform_fixed_ioport_write4, NULL);
+     register_ioport_write(0x10, 16, 2, platform_fixed_ioport_write2, NULL);
+     register_ioport_write(0x10, 16, 1, platform_fixed_ioport_write1, NULL);
+     register_ioport_read(0x10, 16, 2, platform_fixed_ioport_read2, NULL);
+Index: ioemu-remote/qemu-xen.h
+===================================================================
+--- ioemu-remote.orig/qemu-xen.h       2008-12-15 15:02:53.000000000 +0000
++++ ioemu-remote/qemu-xen.h    2008-12-15 15:06:39.000000000 +0000
+@@ -91,6 +91,8 @@
+ char *xenstore_device_model_read(int domid, char *key, unsigned int *len);
+ char *xenstore_read_battery_data(int battery_status);
+ int xenstore_refresh_battery_status(void);
++int xenstore_pv_driver_build_blacklisted(uint16_t product_number,
++                                         uint32_t build_nr);
+ /* xenfbfront.c */
+ int xenfb_pv_display_init(DisplayState *ds);
+Index: ioemu-remote/xenstore.c
+===================================================================
+--- ioemu-remote.orig/xenstore.c       2008-12-15 14:30:47.000000000 +0000
++++ ioemu-remote/xenstore.c    2008-12-15 15:06:08.000000000 +0000
+@@ -782,6 +782,34 @@
+     free(path);
+ }
++int
++xenstore_pv_driver_build_blacklisted(uint16_t product_nr,
++                                     uint32_t build_nr)
++{
++    char *buf = NULL;
++    char *tmp;
++    const char *product;
++
++    switch (product_nr) {
++    case 1:
++        product = "xensource-windows";
++        break;
++    default:
++        /* Don't know what product this is -> we can't blacklist
++         * it. */
++        return 0;
++    }
++    if (asprintf(&buf, "/mh/driver-blacklist/%s/%d", product, build_nr) < 0)
++        return 0;
++    tmp = xs_read(xsh, XBT_NULL, buf, NULL);
++    free(tmp);
++    free(buf);
++    if (tmp == NULL)
++        return 0;
++    else
++        return 1;
++}
++
+ void xenstore_record_dm_state(char *state)
+ {
+     xenstore_record_dm("state", state);
+application/pgp-signature           [Press RETURN to save to a file]