summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile19
-rw-r--r--cpu-exec.c6
-rw-r--r--docs/usb-storage.txt38
-rw-r--r--event_notifier.c30
-rw-r--r--event_notifier.h8
-rw-r--r--exec.c8
-rw-r--r--hmp-commands.hx6
-rw-r--r--hmp.c18
-rw-r--r--hmp.h2
-rw-r--r--hw/apic.c27
-rw-r--r--hw/apic.h1
-rw-r--r--hw/apic_internal.h1
-rw-r--r--hw/ivshmem.c67
-rw-r--r--hw/qdev-properties.c2
-rw-r--r--hw/qdev.h2
-rw-r--r--hw/scsi-bus.c5
-rw-r--r--hw/scsi.h1
-rw-r--r--hw/usb/Makefile.objs1
-rw-r--r--hw/usb/dev-uas.c779
-rw-r--r--hw/usb/hcd-ehci.c91
-rw-r--r--hw/usb/hcd-uhci.c12
-rw-r--r--hw/vhost.c4
-rw-r--r--hw/virtio-pci.c77
-rw-r--r--hw/virtio.c46
-rw-r--r--hw/virtio.h3
-rw-r--r--hw/xen_pt.c2
-rw-r--r--kvm-all.c31
-rw-r--r--kvm-stub.c10
-rw-r--r--kvm.h2
-rw-r--r--memory.c18
-rw-r--r--memory.h9
-rw-r--r--monitor.c32
-rw-r--r--qapi-schema.json67
-rw-r--r--qapi/qapi-visit-core.c2
-rw-r--r--qemu-doc.texi2
-rw-r--r--qemu-options.hx30
-rw-r--r--qemu-tech.texi2
-rw-r--r--qmp-commands.hx14
-rw-r--r--roms/Makefile17
-rw-r--r--roms/config.vga.cirrus3
-rw-r--r--roms/config.vga.isavga3
-rw-r--r--roms/config.vga.qxl6
-rw-r--r--roms/config.vga.stdvga3
-rw-r--r--roms/config.vga.vmware6
-rwxr-xr-xscripts/make-release24
-rw-r--r--target-i386/cpu.c2
-rw-r--r--target-i386/cpu.h5
-rw-r--r--target-i386/kvm.c9
-rw-r--r--trace-events16
-rw-r--r--xen-all.c6
50 files changed, 1337 insertions, 238 deletions
diff --git a/Makefile b/Makefile
index 6eeabd0b74..ab82ef3dcb 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,7 @@ Makefile: ;
configure: ;
.PHONY: all clean cscope distclean dvi html info install install-doc \
- pdf recurse-all speed tar tarbin test
+ pdf recurse-all speed test dist
$(call set-vpath, $(SRC_PATH))
@@ -233,6 +233,13 @@ clean:
rm -f $$d/qemu-options.def; \
done
+VERSION ?= $(shell cat VERSION)
+
+dist: qemu-$(VERSION).tar.bz2
+
+qemu-%.tar.bz2:
+ $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
+
distclean: clean
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
rm -f config-all-devices.mak
@@ -391,16 +398,6 @@ qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
qemu-img.texi qemu-nbd.texi qemu-options.texi \
qemu-monitor.texi qemu-img-cmds.texi
-VERSION ?= $(shell cat VERSION)
-FILE = qemu-$(VERSION)
-
-# tar release (use 'make -k tar' on a checkouted tree)
-tar:
- rm -rf /tmp/$(FILE)
- cp -r . /tmp/$(FILE)
- cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn
- rm -rf /tmp/$(FILE)
-
# Add a dependency on the generated files, so that they are always
# rebuilt before other object files
Makefile: $(GENERATED_HEADERS)
diff --git a/cpu-exec.c b/cpu-exec.c
index 08c35f72d4..fc185a4f04 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -288,6 +288,12 @@ int cpu_exec(CPUArchState *env)
}
#endif
#if defined(TARGET_I386)
+#if !defined(CONFIG_USER_ONLY)
+ if (interrupt_request & CPU_INTERRUPT_POLL) {
+ env->interrupt_request &= ~CPU_INTERRUPT_POLL;
+ apic_poll_irq(env->apic_state);
+ }
+#endif
if (interrupt_request & CPU_INTERRUPT_INIT) {
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT,
0);
diff --git a/docs/usb-storage.txt b/docs/usb-storage.txt
new file mode 100644
index 0000000000..ff9755920d
--- /dev/null
+++ b/docs/usb-storage.txt
@@ -0,0 +1,38 @@
+
+qemu usb storage emulation
+--------------------------
+
+Qemu has two emulations for usb storage devices.
+
+Number one emulates the classic bulk-only transport protocol which is
+used by 99% of the usb sticks on the marked today and is called
+"usb-storage". Usage (hooking up to xhci, other host controllers work
+too):
+
+ qemu ${other_vm_args} \
+ -drive if=none,id=stick,file=/path/to/file.img \
+ -device nec-usb-xhci,id=xhci \
+ -device usb-storage,bus=xhci.0,drive=stick
+
+
+Number two is the newer usb attached scsi transport. This one doesn't
+automagically create a scsi disk, so you have to explicitly attach one
+manually. Multiple logical units are supported. Here is an example
+with tree logical units:
+
+ qemu ${other_vm_args} \
+ -drive if=none,id=uas-disk1,file=/path/to/file1.img \
+ -drive if=none,id=uas-disk2,file=/path/to/file2.img \
+ -drive if=none,id=uas-cdrom,media=cdrom,file=/path/to/image.iso \
+ -device nec-usb-xhci,id=xhci \
+ -device usb-uas,id=uas,bus=xhci.0 \
+ -device scsi-hd,bus=uas.0,scsi-id=0,lun=0,drive=uas-disk1 \
+ -device scsi-hd,bus=uas.0,scsi-id=0,lun=1,drive=uas-disk2 \
+ -device scsi-cd,bus=uas.0,scsi-id=0,lun=5,drive=uas-cdrom
+
+
+enjoy,
+ Gerd
+
+--
+Gerd Hoffmann <kraxel@redhat.com>
diff --git a/event_notifier.c b/event_notifier.c
index 0b829813d3..2c207e1399 100644
--- a/event_notifier.c
+++ b/event_notifier.c
@@ -10,11 +10,19 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu-common.h"
#include "event_notifier.h"
+#include "qemu-char.h"
+
#ifdef CONFIG_EVENTFD
#include <sys/eventfd.h>
#endif
+void event_notifier_init_fd(EventNotifier *e, int fd)
+{
+ e->fd = fd;
+}
+
int event_notifier_init(EventNotifier *e, int active)
{
#ifdef CONFIG_EVENTFD
@@ -38,24 +46,22 @@ int event_notifier_get_fd(EventNotifier *e)
return e->fd;
}
-int event_notifier_test_and_clear(EventNotifier *e)
+int event_notifier_set_handler(EventNotifier *e,
+ EventNotifierHandler *handler)
{
- uint64_t value;
- int r = read(e->fd, &value, sizeof(value));
+ return qemu_set_fd_handler(e->fd, (IOHandler *)handler, NULL, e);
+}
+
+int event_notifier_set(EventNotifier *e)
+{
+ uint64_t value = 1;
+ int r = write(e->fd, &value, sizeof(value));
return r == sizeof(value);
}
-int event_notifier_test(EventNotifier *e)
+int event_notifier_test_and_clear(EventNotifier *e)
{
uint64_t value;
int r = read(e->fd, &value, sizeof(value));
- if (r == sizeof(value)) {
- /* restore previous value. */
- int s = write(e->fd, &value, sizeof(value));
- /* never blocks because we use EFD_SEMAPHORE.
- * If we didn't we'd get EAGAIN on overflow
- * and we'd have to write code to ignore it. */
- assert(s == sizeof(value));
- }
return r == sizeof(value);
}
diff --git a/event_notifier.h b/event_notifier.h
index 886222cb36..f0ec2f2171 100644
--- a/event_notifier.h
+++ b/event_notifier.h
@@ -16,13 +16,17 @@
#include "qemu-common.h"
struct EventNotifier {
- int fd;
+ int fd;
};
+typedef void EventNotifierHandler(EventNotifier *);
+
+void event_notifier_init_fd(EventNotifier *, int fd);
int event_notifier_init(EventNotifier *, int active);
void event_notifier_cleanup(EventNotifier *);
int event_notifier_get_fd(EventNotifier *);
+int event_notifier_set(EventNotifier *);
int event_notifier_test_and_clear(EventNotifier *);
-int event_notifier_test(EventNotifier *);
+int event_notifier_set_handler(EventNotifier *, EventNotifierHandler *);
#endif
diff --git a/exec.c b/exec.c
index c9fa17de6f..feb4795525 100644
--- a/exec.c
+++ b/exec.c
@@ -3210,13 +3210,13 @@ static void core_log_global_stop(MemoryListener *listener)
static void core_eventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data, EventNotifier *e)
{
}
static void core_eventfd_del(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data, EventNotifier *e)
{
}
@@ -3276,13 +3276,13 @@ static void io_log_global_stop(MemoryListener *listener)
static void io_eventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data, EventNotifier *e)
{
}
static void io_eventfd_del(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data, EventNotifier *e)
{
}
diff --git a/hmp-commands.hx b/hmp-commands.hx
index f5d9d91de8..eea8b32894 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1236,8 +1236,7 @@ ETEXI
.args_type = "fdname:s",
.params = "getfd name",
.help = "receive a file descriptor via SCM rights and assign it a name",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_getfd,
+ .mhandler.cmd = hmp_getfd,
},
STEXI
@@ -1253,8 +1252,7 @@ ETEXI
.args_type = "fdname:s",
.params = "closefd name",
.help = "close a file descriptor previously passed via SCM rights",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_closefd,
+ .mhandler.cmd = hmp_closefd,
},
STEXI
diff --git a/hmp.c b/hmp.c
index 4c6d4ae942..6b72a64d99 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1002,3 +1002,21 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
qmp_netdev_del(id, &err);
hmp_handle_error(mon, &err);
}
+
+void hmp_getfd(Monitor *mon, const QDict *qdict)
+{
+ const char *fdname = qdict_get_str(qdict, "fdname");
+ Error *errp = NULL;
+
+ qmp_getfd(fdname, &errp);
+ hmp_handle_error(mon, &errp);
+}
+
+void hmp_closefd(Monitor *mon, const QDict *qdict)
+{
+ const char *fdname = qdict_get_str(qdict, "fdname");
+ Error *errp = NULL;
+
+ qmp_closefd(fdname, &errp);
+ hmp_handle_error(mon, &errp);
+}
diff --git a/hmp.h b/hmp.h
index 79d138d3ee..8d2b0d76da 100644
--- a/hmp.h
+++ b/hmp.h
@@ -64,5 +64,7 @@ void hmp_device_del(Monitor *mon, const QDict *qdict);
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
void hmp_netdev_add(Monitor *mon, const QDict *qdict);
void hmp_netdev_del(Monitor *mon, const QDict *qdict);
+void hmp_getfd(Monitor *mon, const QDict *qdict);
+void hmp_closefd(Monitor *mon, const QDict *qdict);
#endif
diff --git a/hw/apic.c b/hw/apic.c
index 60552df619..385555eb43 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -16,6 +16,7 @@
* 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/>
*/
+#include "qemu-thread.h"
#include "apic_internal.h"
#include "apic.h"
#include "ioapic.h"
@@ -361,11 +362,10 @@ static void apic_update_irq(APICCommonState *s)
if (!(s->spurious_vec & APIC_SV_ENABLE)) {
return;
}
- if (apic_irq_pending(s) > 0) {
+ if (!qemu_cpu_is_self(s->cpu_env)) {
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_POLL);
+ } else if (apic_irq_pending(s) > 0) {
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
- } else if (apic_accept_pic_intr(&s->busdev.qdev) &&
- pic_get_output(isa_pic)) {
- apic_deliver_pic_intr(&s->busdev.qdev, 1);
}
}
@@ -535,6 +535,15 @@ static void apic_deliver(DeviceState *d, uint8_t dest, uint8_t dest_mode,
apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode);
}
+static bool apic_check_pic(APICCommonState *s)
+{
+ if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) {
+ return false;
+ }
+ apic_deliver_pic_intr(&s->busdev.qdev, 1);
+ return true;
+}
+
int apic_get_interrupt(DeviceState *d)
{
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
@@ -560,7 +569,12 @@ int apic_get_interrupt(DeviceState *d)
reset_bit(s->irr, intno);
set_bit(s->isr, intno);
apic_sync_vapic(s, SYNC_TO_VAPIC);
+
+ /* re-inject if there is still a pending PIC interrupt */
+ apic_check_pic(s);
+
apic_update_irq(s);
+
return intno;
}
@@ -800,8 +814,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
int n = index - 0x32;
s->lvt[n] = val;
- if (n == APIC_LVT_TIMER)
+ if (n == APIC_LVT_TIMER) {
apic_timer_update(s, qemu_get_clock_ns(vm_clock));
+ } else if (n == APIC_LVT_LINT0 && apic_check_pic(s)) {
+ apic_update_irq(s);
+ }
}
break;
case 0x38:
diff --git a/hw/apic.h b/hw/apic.h
index 62179cebee..a89542b231 100644
--- a/hw/apic.h
+++ b/hw/apic.h
@@ -20,6 +20,7 @@ void apic_init_reset(DeviceState *s);
void apic_sipi(DeviceState *s);
void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
TPRAccess access);
+void apic_poll_irq(DeviceState *d);
/* pc.c */
int cpu_is_bsp(CPUX86State *env);
diff --git a/hw/apic_internal.h b/hw/apic_internal.h
index 60a6a8bdae..4d8ff490ce 100644
--- a/hw/apic_internal.h
+++ b/hw/apic_internal.h
@@ -141,7 +141,6 @@ void apic_report_irq_delivered(int delivered);
bool apic_next_timer(APICCommonState *s, int64_t current_time);
void apic_enable_tpr_access_reporting(DeviceState *d, bool enable);
void apic_enable_vapic(DeviceState *d, target_phys_addr_t paddr);
-void apic_poll_irq(DeviceState *d);
void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip,
TPRAccess access);
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
index 05559b639c..bba21c55e2 100644
--- a/hw/ivshmem.c
+++ b/hw/ivshmem.c
@@ -23,6 +23,7 @@
#include "kvm.h"
#include "migration.h"
#include "qerror.h"
+#include "event_notifier.h"
#include <sys/mman.h>
#include <sys/types.h>
@@ -45,7 +46,7 @@
typedef struct Peer {
int nb_eventfds;
- int *eventfds;
+ EventNotifier *eventfds;
} Peer;
typedef struct EventfdEntry {
@@ -63,7 +64,6 @@ typedef struct IVShmemState {
CharDriverState *server_chr;
MemoryRegion ivshmem_mmio;
- pcibus_t mmio_addr;
/* We might need to register the BAR before we actually have the memory.
* So prepare a container MemoryRegion for the BAR immediately and
* add a subregion when we have the memory.
@@ -168,7 +168,6 @@ static void ivshmem_io_write(void *opaque, target_phys_addr_t addr,
{
IVShmemState *s = opaque;
- uint64_t write_one = 1;
uint16_t dest = val >> 16;
uint16_t vector = val & 0xff;
@@ -194,12 +193,8 @@ static void ivshmem_io_write(void *opaque, target_phys_addr_t addr,
/* check doorbell range */
if (vector < s->peers[dest].nb_eventfds) {
- IVSHMEM_DPRINTF("Writing %" PRId64 " to VM %d on vector %d\n",
- write_one, dest, vector);
- if (write(s->peers[dest].eventfds[vector],
- &(write_one), 8) != 8) {
- IVSHMEM_DPRINTF("error writing to eventfd\n");
- }
+ IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
+ event_notifier_set(&s->peers[dest].eventfds[vector]);
}
break;
default:
@@ -279,12 +274,13 @@ static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
msix_notify(pdev, entry->vector);
}
-static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd,
- int vector)
+static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
+ int vector)
{
/* create a event character device based on the passed eventfd */
IVShmemState *s = opaque;
CharDriverState * chr;
+ int eventfd = event_notifier_get_fd(n);
chr = qemu_chr_open_eventfd(eventfd);
@@ -347,16 +343,39 @@ static void create_shared_memory_BAR(IVShmemState *s, int fd) {
pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
}
+static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
+{
+ memory_region_add_eventfd(&s->ivshmem_mmio,
+ DOORBELL,
+ 4,
+ true,
+ (posn << 16) | i,
+ &s->peers[posn].eventfds[i]);
+}
+
+static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
+{
+ memory_region_del_eventfd(&s->ivshmem_mmio,
+ DOORBELL,
+ 4,
+ true,
+ (posn << 16) | i,
+ &s->peers[posn].eventfds[i]);
+}
+
static void close_guest_eventfds(IVShmemState *s, int posn)
{
int i, guest_curr_max;
guest_curr_max = s->peers[posn].nb_eventfds;
+ memory_region_transaction_begin();
+ for (i = 0; i < guest_curr_max; i++) {
+ ivshmem_del_eventfd(s, posn, i);
+ }
+ memory_region_transaction_commit();
for (i = 0; i < guest_curr_max; i++) {
- kvm_set_ioeventfd_mmio(s->peers[posn].eventfds[i],
- s->mmio_addr + DOORBELL, (posn << 16) | i, 0, 4);
- close(s->peers[posn].eventfds[i]);
+ event_notifier_cleanup(&s->peers[posn].eventfds[i]);
}
g_free(s->peers[posn].eventfds);
@@ -369,12 +388,7 @@ static void setup_ioeventfds(IVShmemState *s) {
for (i = 0; i <= s->max_peer; i++) {
for (j = 0; j < s->peers[i].nb_eventfds; j++) {
- memory_region_add_eventfd(&s->ivshmem_mmio,
- DOORBELL,
- 4,
- true,
- (i << 16) | j,
- s->peers[i].eventfds[j]);
+ ivshmem_add_eventfd(s, i, j);
}
}
}
@@ -476,14 +490,14 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
if (guest_max_eventfd == 0) {
/* one eventfd per MSI vector */
- s->peers[incoming_posn].eventfds = (int *) g_malloc(s->vectors *
- sizeof(int));
+ s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors);
}
/* this is an eventfd for a particular guest VM */
IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
guest_max_eventfd, incoming_fd);
- s->peers[incoming_posn].eventfds[guest_max_eventfd] = incoming_fd;
+ event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd],
+ incoming_fd);
/* increment count for particular guest */
s->peers[incoming_posn].nb_eventfds++;
@@ -495,15 +509,12 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
if (incoming_posn == s->vm_id) {
s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s,
- s->peers[s->vm_id].eventfds[guest_max_eventfd],
+ &s->peers[s->vm_id].eventfds[guest_max_eventfd],
guest_max_eventfd);
}
if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
- if (kvm_set_ioeventfd_mmio(incoming_fd, s->mmio_addr + DOORBELL,
- (incoming_posn << 16) | guest_max_eventfd, 1, 4) < 0) {
- fprintf(stderr, "ivshmem: ioeventfd not available\n");
- }
+ ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd);
}
return;
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index 01c378f534..24b39e8db4 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -1139,7 +1139,7 @@ void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
assert_no_error(errp);
}
-void qdev_prop_set_string(DeviceState *dev, const char *name, char *value)
+void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
{
Error *errp = NULL;
object_property_set_str(OBJECT(dev), value, name, &errp);
diff --git a/hw/qdev.h b/hw/qdev.h
index 9be35d47e5..247dd1eb39 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -319,7 +319,7 @@ void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value);
void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value);
void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value);
void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value);
-void qdev_prop_set_string(DeviceState *dev, const char *name, char *value);
+void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value);
void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value);
void qdev_prop_set_netdev(DeviceState *dev, const char *name, VLANClientState *value);
void qdev_prop_set_vlan(DeviceState *dev, const char *name, VLANState *value);
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 5ad1013be1..dc7406389d 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -1354,6 +1354,7 @@ static const char *scsi_command_name(uint8_t cmd)
SCSIRequest *scsi_req_ref(SCSIRequest *req)
{
+ assert(req->refcount > 0);
req->refcount++;
return req;
}
@@ -1362,6 +1363,10 @@ void scsi_req_unref(SCSIRequest *req)
{
assert(req->refcount > 0);
if (--req->refcount == 0) {
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, req->dev->qdev.parent_bus);
+ if (bus->info->free_request && req->hba_private) {
+ bus->info->free_request(bus, req->hba_private);
+ }
if (req->ops->free_req) {
req->ops->free_req(req);
}
diff --git a/hw/scsi.h b/hw/scsi.h
index d90e9702fb..ea8a15567d 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -135,6 +135,7 @@ struct SCSIBusInfo {
void (*save_request)(QEMUFile *f, SCSIRequest *req);
void *(*load_request)(QEMUFile *f, SCSIRequest *req);
+ void (*free_request)(SCSIBus *bus, void *priv);
};
#define TYPE_SCSI_BUS "SCSI"
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 9c7ddf5cb2..4225136d0f 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -11,3 +11,4 @@ common-obj-y += core.o bus.o desc.o dev-hub.o
common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
common-obj-y += dev-serial.o dev-network.o dev-audio.o
+common-obj-y += dev-uas.o
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
new file mode 100644
index 0000000000..9b02ff48fa
--- /dev/null
+++ b/hw/usb/dev-uas.c
@@ -0,0 +1,779 @@
+/*
+ * UAS (USB Attached SCSI) emulation
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "trace.h"
+
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/scsi.h"
+#include "hw/scsi-defs.h"
+
+/* --------------------------------------------------------------------- */
+
+#define UAS_UI_COMMAND 0x01
+#define UAS_UI_SENSE 0x03
+#define UAS_UI_RESPONSE 0x04
+#define UAS_UI_TASK_MGMT 0x05
+#define UAS_UI_READ_READY 0x06
+#define UAS_UI_WRITE_READY 0x07
+
+#define UAS_RC_TMF_COMPLETE 0x00
+#define UAS_RC_INVALID_INFO_UNIT 0x02
+#define UAS_RC_TMF_NOT_SUPPORTED 0x04
+#define UAS_RC_TMF_FAILED 0x05
+#define UAS_RC_TMF_SUCCEEDED 0x08
+#define UAS_RC_INCORRECT_LUN 0x09
+#define UAS_RC_OVERLAPPED_TAG 0x0a
+
+#define UAS_TMF_ABORT_TASK 0x01
+#define UAS_TMF_ABORT_TASK_SET 0x02
+#define UAS_TMF_CLEAR_TASK_SET 0x04
+#define UAS_TMF_LOGICAL_UNIT_RESET 0x08
+#define UAS_TMF_I_T_NEXUS_RESET 0x10
+#define UAS_TMF_CLEAR_ACA 0x40
+#define UAS_TMF_QUERY_TASK 0x80
+#define UAS_TMF_QUERY_TASK_SET 0x81
+#define UAS_TMF_QUERY_ASYNC_EVENT 0x82
+
+#define UAS_PIPE_ID_COMMAND 0x01
+#define UAS_PIPE_ID_STATUS 0x02
+#define UAS_PIPE_ID_DATA_IN 0x03
+#define UAS_PIPE_ID_DATA_OUT 0x04
+
+typedef struct {
+ uint8_t id;
+ uint8_t reserved;
+ uint16_t tag;
+} QEMU_PACKED uas_ui_header;
+
+typedef struct {
+ uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */
+ uint8_t reserved_1;
+ uint8_t add_cdb_length; /* 7:2 additional adb length (dwords) */
+ uint8_t reserved_2;
+ uint64_t lun;
+ uint8_t cdb[16];
+ uint8_t add_cdb[];
+} QEMU_PACKED uas_ui_command;
+
+typedef struct {
+ uint16_t status_qualifier;
+ uint8_t status;
+ uint8_t reserved[7];
+ uint16_t sense_length;
+ uint8_t sense_data[18];
+} QEMU_PACKED uas_ui_sense;
+
+typedef struct {
+ uint16_t add_response_info;
+ uint8_t response_code;
+} QEMU_PACKED uas_ui_response;
+
+typedef struct {
+ uint8_t function;
+ uint8_t reserved;
+ uint16_t task_tag;
+ uint64_t lun;
+} QEMU_PACKED uas_ui_task_mgmt;
+
+typedef struct {
+ uas_ui_header hdr;
+ union {
+ uas_ui_command command;
+ uas_ui_sense sense;
+ uas_ui_task_mgmt task;
+ uas_ui_response response;
+ };
+} QEMU_PACKED uas_ui;
+
+/* --------------------------------------------------------------------- */
+
+typedef struct UASDevice UASDevice;
+typedef struct UASRequest UASRequest;
+typedef struct UASStatus UASStatus;
+
+struct UASDevice {
+ USBDevice dev;
+ SCSIBus bus;
+ UASRequest *datain;
+ UASRequest *dataout;
+ USBPacket *status;
+ QEMUBH *status_bh;
+ QTAILQ_HEAD(, UASStatus) results;
+ QTAILQ_HEAD(, UASRequest) requests;
+};
+
+struct UASRequest {
+ uint16_t tag;
+ uint64_t lun;
+ UASDevice *uas;
+ SCSIDevice *dev;
+ SCSIRequest *req;
+ USBPacket *data;
+ bool data_async;
+ bool active;
+ bool complete;
+ uint32_t buf_off;
+ uint32_t buf_size;
+ uint32_t data_off;
+ uint32_t data_size;
+ QTAILQ_ENTRY(UASRequest) next;
+};
+
+struct UASStatus {
+ uas_ui status;
+ uint32_t length;
+ QTAILQ_ENTRY(UASStatus) next;
+};
+
+/* --------------------------------------------------------------------- */
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT,
+ STR_SERIALNUMBER,
+ STR_CONFIG_HIGH,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU",
+ [STR_PRODUCT] = "USB Attached SCSI HBA",
+ [STR_SERIALNUMBER] = "27842",
+ [STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
+};
+
+static const USBDescIface desc_iface_high = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 4,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = 0x06, /* SCSI */
+ .bInterfaceProtocol = 0x62, /* UAS */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_COMMAND,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_STATUS,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_IN,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_OUT,
+ 0x00, /* u8 bReserved */
+ },
+ },
+ }
+};
+
+static const USBDescDevice desc_device_high = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_HIGH,
+ .bmAttributes = 0xc0,
+ .nif = 1,
+ .ifs = &desc_iface_high,
+ },
+ },
+};
+
+static const USBDesc desc = {
+ .id = {
+ .idVendor = 0x46f4, /* CRC16() of "QEMU" */
+ .idProduct = 0x0002,
+ .bcdDevice = 0,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .high = &desc_device_high,
+ .str = desc_strings,
+};
+
+/* --------------------------------------------------------------------- */
+
+static UASStatus *usb_uas_alloc_status(uint8_t id, uint16_t tag)
+{
+ UASStatus *st = g_new0(UASStatus, 1);
+
+ st->status.hdr.id = id;
+ st->status.hdr.tag = cpu_to_be16(tag);
+ st->length = sizeof(uas_ui_header);
+ return st;
+}
+
+static void usb_uas_send_status_bh(void *opaque)
+{
+ UASDevice *uas = opaque;
+ UASStatus *st = QTAILQ_FIRST(&uas->results);
+ USBPacket *p = uas->status;
+
+ assert(p != NULL);
+ assert(st != NULL);
+
+ uas->status = NULL;
+ usb_packet_copy(p, &st->status, st->length);
+ p->result = st->length;
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
+
+ usb_packet_complete(&uas->dev, p);
+}
+
+static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
+{
+ st->length += length;
+ QTAILQ_INSERT_TAIL(&uas->results, st, next);
+ if (uas->status) {
+ /*
+ * Just schedule bh make sure any in-flight data transaction
+ * is finished before completing (sending) the status packet.
+ */
+ qemu_bh_schedule(uas->status_bh);
+ } else {
+ USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
+ UAS_PIPE_ID_STATUS);
+ usb_wakeup(ep);
+ }
+}
+
+static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
+ uint8_t code, uint16_t add_info)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_RESPONSE, tag);
+
+ trace_usb_uas_response(uas->dev.addr, tag, code);
+ st->status.response.response_code = code;
+ st->status.response.add_response_info = cpu_to_be16(add_info);
+ usb_uas_queue_status(uas, st, sizeof(uas_ui_response));
+}
+
+static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_SENSE, req->tag);
+ int len, slen = 0;
+
+ trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
+ st->status.sense.status = status;
+ st->status.sense.status_qualifier = cpu_to_be16(0);
+ if (status != GOOD) {
+ slen = scsi_req_get_sense(req->req, st->status.sense.sense_data,
+ sizeof(st->status.sense.sense_data));
+ st->status.sense.sense_length = cpu_to_be16(slen);
+ }
+ len = sizeof(uas_ui_sense) - sizeof(st->status.sense.sense_data) + slen;
+ usb_uas_queue_status(req->uas, st, len);
+}
+
+static void usb_uas_queue_read_ready(UASRequest *req)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_READ_READY, req->tag);
+
+ trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
+ usb_uas_queue_status(req->uas, st, 0);
+}
+
+static void usb_uas_queue_write_ready(UASRequest *req)
+{
+ UASStatus *st = usb_uas_alloc_status(UAS_UI_WRITE_READY, req->tag);
+
+ trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
+ usb_uas_queue_status(req->uas, st, 0);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int usb_uas_get_lun(uint64_t lun64)
+{
+ return (lun64 >> 48) & 0xff;
+}
+
+static SCSIDevice *usb_uas_get_dev(UASDevice *uas, uint64_t lun64)
+{
+ if ((lun64 >> 56) != 0x00) {
+ return NULL;
+ }
+ return scsi_device_find(&uas->bus, 0, 0, usb_uas_get_lun(lun64));
+}
+
+static void usb_uas_complete_data_packet(UASRequest *req)
+{
+ USBPacket *p;
+
+ if (!req->data_async) {
+ return;
+ }
+ p = req->data;
+ req->data = NULL;
+ req->data_async = false;
+ usb_packet_complete(&req->uas->dev, p);
+}
+
+static void usb_uas_copy_data(UASRequest *req)
+{
+ uint32_t length;
+
+ length = MIN(req->buf_size - req->buf_off,
+ req->data->iov.size - req->data->result);
+ trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length,
+ req->data->result, req->data->iov.size,
+ req->buf_off, req->buf_size);
+ usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
+ length);
+ req->buf_off += length;
+ req->data_off += length;
+
+ if (req->data->result == req->data->iov.size) {
+ usb_uas_complete_data_packet(req);
+ }
+ if (req->buf_size && req->buf_off == req->buf_size) {
+ req->buf_off = 0;
+ req->buf_size = 0;
+ scsi_req_continue(req->req);
+ }
+}
+
+static void usb_uas_start_next_transfer(UASDevice *uas)
+{
+ UASRequest *req;
+
+ QTAILQ_FOREACH(req, &uas->requests, next) {
+ if (req->active || req->complete) {
+ continue;
+ }
+ if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) {
+ uas->datain = req;
+ usb_uas_queue_read_ready(req);
+ req->active = true;
+ return;
+ }
+ if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout == NULL) {
+ uas->dataout = req;
+ usb_uas_queue_write_ready(req);
+ req->active = true;
+ return;
+ }
+ }
+}
+
+static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_ui *ui)
+{
+ UASRequest *req;
+
+ req = g_new0(UASRequest, 1);
+ req->uas = uas;
+ req->tag = be16_to_cpu(ui->hdr.tag);
+ req->lun = be64_to_cpu(ui->command.lun);
+ req->dev = usb_uas_get_dev(req->uas, req->lun);
+ return req;
+}
+
+static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
+{
+ UASRequest *req = priv;
+ UASDevice *uas = req->uas;
+
+ if (req == uas->datain) {
+ uas->datain = NULL;
+ }
+ if (req == uas->dataout) {
+ uas->dataout = NULL;
+ }
+ QTAILQ_REMOVE(&uas->requests, req, next);
+ g_free(req);
+}
+
+static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
+{
+ UASRequest *req;
+
+ QTAILQ_FOREACH(req, &uas->requests, next) {
+ if (req->tag == tag) {
+ return req;
+ }
+ }
+ return NULL;
+}
+
+static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len)
+{
+ UASRequest *req = r->hba_private;
+
+ trace_usb_uas_scsi_data(req->uas->dev.addr, req->tag, len);
+ req->buf_off = 0;
+ req->buf_size = len;
+ if (req->data) {
+ usb_uas_copy_data(req);
+ } else {
+ usb_uas_start_next_transfer(req->uas);
+ }
+}
+
+static void usb_uas_scsi_command_complete(SCSIRequest *r,
+ uint32_t status, size_t resid)
+{
+ UASRequest *req = r->hba_private;
+ UASDevice *uas = req->uas;
+
+ trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
+ req->complete = true;
+ if (req->data) {
+ usb_uas_complete_data_packet(req);
+ }
+ usb_uas_queue_sense(req, status);
+ scsi_req_unref(req->req);
+ usb_uas_start_next_transfer(uas);
+}
+
+static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
+{
+ UASRequest *req = r->hba_private;
+
+ /* FIXME: queue notification to status pipe? */
+ scsi_req_unref(req->req);
+}
+
+static const struct SCSIBusInfo usb_uas_scsi_info = {
+ .tcq = true,
+ .max_target = 0,
+ .max_lun = 255,
+
+ .transfer_data = usb_uas_scsi_transfer_data,
+ .complete = usb_uas_scsi_command_complete,
+ .cancel = usb_uas_scsi_request_cancelled,
+ .free_request = usb_uas_scsi_free_request,
+};
+
+/* --------------------------------------------------------------------- */
+
+static void usb_uas_handle_reset(USBDevice *dev)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASRequest *req, *nreq;
+ UASStatus *st, *nst;
+
+ trace_usb_uas_reset(dev->addr);
+ QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
+ scsi_req_cancel(req->req);
+ }
+ QTAILQ_FOREACH_SAFE(st, &uas->results, next, nst) {
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
+ }
+}
+
+static int usb_uas_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
+{
+ int ret;
+
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+ if (ret >= 0) {
+ return ret;
+ }
+ fprintf(stderr, "%s: unhandled control request\n", __func__);
+ return USB_RET_STALL;
+}
+
+static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASRequest *req, *nreq;
+
+ if (uas->status == p) {
+ uas->status = NULL;
+ qemu_bh_cancel(uas->status_bh);
+ return;
+ }
+ QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
+ if (req->data == p) {
+ req->data = NULL;
+ return;
+ }
+ }
+ assert(!"canceled usb packet not found");
+}
+
+static void usb_uas_command(UASDevice *uas, uas_ui *ui)
+{
+ UASRequest *req;
+ uint32_t len;
+
+ req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
+ if (req) {
+ goto overlapped_tag;
+ }
+ req = usb_uas_alloc_request(uas, ui);
+ if (req->dev == NULL) {
+ goto bad_target;
+ }
+
+ trace_usb_uas_command(uas->dev.addr, req->tag,
+ usb_uas_get_lun(req->lun),
+ req->lun >> 32, req->lun & 0xffffffff);
+ QTAILQ_INSERT_TAIL(&uas->requests, req, next);
+ req->req = scsi_req_new(req->dev, req->tag,
+ usb_uas_get_lun(req->lun),
+ ui->command.cdb, req);
+ len = scsi_req_enqueue(req->req);
+ if (len) {
+ req->data_size = len;
+ scsi_req_continue(req->req);
+ }
+ return;
+
+overlapped_tag:
+ usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
+ return;
+
+bad_target:
+ /*
+ * FIXME: Seems to upset linux, is this wrong?
+ * NOTE: Happens only with no scsi devices at the bus, not sure
+ * this is a valid UAS setup in the first place.
+ */
+ usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0);
+ g_free(req);
+ return;
+}
+
+static void usb_uas_task(UASDevice *uas, uas_ui *ui)
+{
+ uint16_t tag = be16_to_cpu(ui->hdr.tag);
+ uint64_t lun64 = be64_to_cpu(ui->task.lun);
+ SCSIDevice *dev = usb_uas_get_dev(uas, lun64);
+ int lun = usb_uas_get_lun(lun64);
+ UASRequest *req;
+ uint16_t task_tag;
+
+ req = usb_uas_find_request(uas, be16_to_cpu(ui->hdr.tag));
+ if (req) {
+ goto overlapped_tag;
+ }
+
+ switch (ui->task.function) {
+ case UAS_TMF_ABORT_TASK:
+ task_tag = be16_to_cpu(ui->task.task_tag);
+ trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag);
+ if (dev == NULL) {
+ goto bad_target;
+ }
+ if (dev->lun != lun) {
+ goto incorrect_lun;
+ }
+ req = usb_uas_find_request(uas, task_tag);
+ if (req && req->dev == dev) {
+ scsi_req_cancel(req->req);
+ }
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
+ break;
+
+ case UAS_TMF_LOGICAL_UNIT_RESET:
+ trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun);
+ if (dev == NULL) {
+ goto bad_target;
+ }
+ if (dev->lun != lun) {
+ goto incorrect_lun;
+ }
+ qdev_reset_all(&dev->qdev);
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE, 0);
+ break;
+
+ default:
+ trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, ui->task.function);
+ usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED, 0);
+ break;
+ }
+ return;
+
+overlapped_tag:
+ usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG, 0);
+ return;
+
+bad_target:
+ /* FIXME: correct? [see long comment in usb_uas_command()] */
+ usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT, 0);
+ return;
+
+incorrect_lun:
+ usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
+ return;
+}
+
+static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ uas_ui ui;
+ UASStatus *st;
+ UASRequest *req;
+ int length, ret = 0;
+
+ switch (p->ep->nr) {
+ case UAS_PIPE_ID_COMMAND:
+ length = MIN(sizeof(ui), p->iov.size);
+ usb_packet_copy(p, &ui, length);
+ switch (ui.hdr.id) {
+ case UAS_UI_COMMAND:
+ usb_uas_command(uas, &ui);
+ ret = length;
+ break;
+ case UAS_UI_TASK_MGMT:
+ usb_uas_task(uas, &ui);
+ ret = length;
+ break;
+ default:
+ fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
+ __func__, ui.hdr.id);
+ ret = USB_RET_STALL;
+ break;
+ }
+ break;
+ case UAS_PIPE_ID_STATUS:
+ st = QTAILQ_FIRST(&uas->results);
+ if (st == NULL) {
+ assert(uas->status == NULL);
+ uas->status = p;
+ ret = USB_RET_ASYNC;
+ break;
+ }
+ usb_packet_copy(p, &st->status, st->length);
+ ret = st->length;
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
+ break;
+ case UAS_PIPE_ID_DATA_IN:
+ case UAS_PIPE_ID_DATA_OUT:
+ req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout;
+ if (req == NULL) {
+ fprintf(stderr, "%s: no inflight request\n", __func__);
+ ret = USB_RET_STALL;
+ break;
+ }
+ scsi_req_ref(req->req);
+ req->data = p;
+ usb_uas_copy_data(req);
+ if (p->result == p->iov.size || req->complete) {
+ req->data = NULL;
+ ret = p->result;
+ } else {
+ req->data_async = true;
+ ret = USB_RET_ASYNC;
+ }
+ scsi_req_unref(req->req);
+ usb_uas_start_next_transfer(uas);
+ break;
+ default:
+ fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static void usb_uas_handle_destroy(USBDevice *dev)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+
+ qemu_bh_delete(uas->status_bh);
+}
+
+static int usb_uas_init(USBDevice *dev)
+{
+ UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+
+ usb_desc_create_serial(dev);
+ usb_desc_init(dev);
+
+ QTAILQ_INIT(&uas->results);
+ QTAILQ_INIT(&uas->requests);
+ uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas);
+
+ scsi_bus_new(&uas->bus, &uas->dev.qdev, &usb_uas_scsi_info);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_usb_uas = {
+ .name = "usb-uas",
+ .unmigratable = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_USB_DEVICE(dev, UASDevice),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void usb_uas_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->init = usb_uas_init;
+ uc->product_desc = desc_strings[STR_PRODUCT];
+ uc->usb_desc = &desc;
+ uc->cancel_packet = usb_uas_cancel_io;
+ uc->handle_attach = usb_desc_attach;
+ uc->handle_reset = usb_uas_handle_reset;
+ uc->handle_control = usb_uas_handle_control;
+ uc->handle_data = usb_uas_handle_data;
+ uc->handle_destroy = usb_uas_handle_destroy;
+ dc->fw_name = "storage";
+ dc->vmsd = &vmstate_usb_uas;
+}
+
+static TypeInfo uas_info = {
+ .name = "usb-uas",
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(UASDevice),
+ .class_init = usb_uas_class_initfn,
+};
+
+static void usb_uas_register_types(void)
+{
+ type_register_static(&uas_info);
+}
+
+type_init(usb_uas_register_types)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 080f62c00d..b043e7c23e 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -420,6 +420,7 @@ struct EHCIState {
USBPort ports[NB_PORTS];
USBPort *companion_ports[NB_PORTS];
uint32_t usbsts_pending;
+ uint32_t usbsts_frindex;
EHCIQueueHead aqueues;
EHCIQueueHead pqueues;
@@ -558,34 +559,45 @@ static inline void ehci_clear_usbsts(EHCIState *s, int mask)
s->usbsts &= ~mask;
}
-static inline void ehci_set_interrupt(EHCIState *s, int intr)
+/* update irq line */
+static inline void ehci_update_irq(EHCIState *s)
{
int level = 0;
- // TODO honour interrupt threshold requests
-
- ehci_set_usbsts(s, intr);
-
if ((s->usbsts & USBINTR_MASK) & s->usbintr) {
level = 1;
}
- trace_usb_ehci_interrupt(level, s->usbsts, s->usbintr);
+ trace_usb_ehci_irq(level, s->frindex, s->usbsts, s->usbintr);
qemu_set_irq(s->irq, level);
}
-static inline void ehci_record_interrupt(EHCIState *s, int intr)
+/* flag interrupt condition */
+static inline void ehci_raise_irq(EHCIState *s, int intr)
{
s->usbsts_pending |= intr;
}
-static inline void ehci_commit_interrupt(EHCIState *s)
+/*
+ * Commit pending interrupts (added via ehci_raise_irq),
+ * at the rate allowed by "Interrupt Threshold Control".
+ */
+static inline void ehci_commit_irq(EHCIState *s)
{
+ uint32_t itc;
+
if (!s->usbsts_pending) {
return;
}
- ehci_set_interrupt(s, s->usbsts_pending);
+ if (s->usbsts_frindex > s->frindex) {
+ return;
+ }
+
+ itc = (s->usbcmd >> 16) & 0xff;
+ s->usbsts |= s->usbsts_pending;
s->usbsts_pending = 0;
+ s->usbsts_frindex = s->frindex + itc;
+ ehci_update_irq(s);
}
static void ehci_update_halt(EHCIState *s)
@@ -849,7 +861,8 @@ static void ehci_attach(USBPort *port)
*portsc |= PORTSC_CONNECT;
*portsc |= PORTSC_CSC;
- ehci_set_interrupt(s, USBSTS_PCD);
+ ehci_raise_irq(s, USBSTS_PCD);
+ ehci_commit_irq(s);
}
static void ehci_detach(USBPort *port)
@@ -878,7 +891,8 @@ static void ehci_detach(USBPort *port)
*portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
*portsc |= PORTSC_CSC;
- ehci_set_interrupt(s, USBSTS_PCD);
+ ehci_raise_irq(s, USBSTS_PCD);
+ ehci_commit_irq(s);
}
static void ehci_child_detach(USBPort *port, USBDevice *child)
@@ -997,6 +1011,8 @@ static void ehci_reset(void *opaque)
s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
s->usbsts = USBSTS_HALT;
+ s->usbsts_pending = 0;
+ s->usbsts_frindex = 0;
s->astate = EST_INACTIVE;
s->pstate = EST_INACTIVE;
@@ -1188,7 +1204,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
val &= USBSTS_RO_MASK; // bits 6 through 31 are RO
ehci_clear_usbsts(s, val); // bits 0 through 5 are R/WC
val = s->usbsts;
- ehci_set_interrupt(s, 0);
+ ehci_update_irq(s);
break;
case USBINTR:
@@ -1419,18 +1435,18 @@ static void ehci_execute_complete(EHCIQueue *q)
case USB_RET_NODEV:
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
- ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+ ehci_raise_irq(q->ehci, USBSTS_ERRINT);
break;
case USB_RET_STALL:
q->qh.token |= QTD_TOKEN_HALT;
- ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+ ehci_raise_irq(q->ehci, USBSTS_ERRINT);
break;
case USB_RET_NAK:
set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
return; /* We're not done yet with this transaction */
case USB_RET_BABBLE:
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
- ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+ ehci_raise_irq(q->ehci, USBSTS_ERRINT);
break;
default:
/* should not be triggerable */
@@ -1441,7 +1457,7 @@ static void ehci_execute_complete(EHCIQueue *q)
} else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) {
p->usb_status = USB_RET_BABBLE;
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
- ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
+ ehci_raise_irq(q->ehci, USBSTS_ERRINT);
} else {
// TODO check 4.12 for splits
@@ -1462,7 +1478,7 @@ static void ehci_execute_complete(EHCIQueue *q)
q->qh.token &= ~QTD_TOKEN_ACTIVE;
if (q->qh.token & QTD_TOKEN_IOC) {
- ehci_record_interrupt(q->ehci, USBSTS_INT);
+ ehci_raise_irq(q->ehci, USBSTS_INT);
}
}
@@ -1597,12 +1613,12 @@ static int ehci_process_itd(EHCIState *ehci,
/* 3.3.2: XACTERR is only allowed on IN transactions */
if (dir) {
itd->transact[i] |= ITD_XACT_XACTERR;
- ehci_record_interrupt(ehci, USBSTS_ERRINT);
+ ehci_raise_irq(ehci, USBSTS_ERRINT);
}
break;
case USB_RET_BABBLE:
itd->transact[i] |= ITD_XACT_BABBLE;
- ehci_record_interrupt(ehci, USBSTS_ERRINT);
+ ehci_raise_irq(ehci, USBSTS_ERRINT);
break;
case USB_RET_NAK:
/* no data for us, so do a zero-length transfer */
@@ -1620,7 +1636,7 @@ static int ehci_process_itd(EHCIState *ehci,
}
}
if (itd->transact[i] & ITD_XACT_IOC) {
- ehci_record_interrupt(ehci, USBSTS_INT);
+ ehci_raise_irq(ehci, USBSTS_INT);
}
itd->transact[i] &= ~ITD_XACT_ACTIVE;
}
@@ -2208,8 +2224,6 @@ static void ehci_advance_state(EHCIState *ehci, int async)
}
}
while (again);
-
- ehci_commit_interrupt(ehci);
}
static void ehci_advance_async_state(EHCIState *ehci)
@@ -2255,7 +2269,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
ehci_queues_tag_unused_async(ehci);
DPRINTF("ASYNC: doorbell request acknowledged\n");
ehci->usbcmd &= ~USBCMD_IAAD;
- ehci_set_interrupt(ehci, USBSTS_IAA);
+ ehci_raise_irq(ehci, USBSTS_IAA);
}
break;
@@ -2328,12 +2342,17 @@ static void ehci_update_frindex(EHCIState *ehci, int frames)
ehci->frindex += 8;
if (ehci->frindex == 0x00002000) {
- ehci_set_interrupt(ehci, USBSTS_FLR);
+ ehci_raise_irq(ehci, USBSTS_FLR);
}
if (ehci->frindex == 0x00004000) {
- ehci_set_interrupt(ehci, USBSTS_FLR);
+ ehci_raise_irq(ehci, USBSTS_FLR);
ehci->frindex = 0;
+ if (ehci->usbsts_frindex > 0x00004000) {
+ ehci->usbsts_frindex -= 0x00004000;
+ } else {
+ ehci->usbsts_frindex = 0;
+ }
}
}
}
@@ -2341,7 +2360,7 @@ static void ehci_update_frindex(EHCIState *ehci, int frames)
static void ehci_frame_timer(void *opaque)
{
EHCIState *ehci = opaque;
- int schedules = 0;
+ int need_timer = 0;
int64_t expire_time, t_now;
uint64_t ns_elapsed;
int frames, skipped_frames;
@@ -2352,8 +2371,8 @@ static void ehci_frame_timer(void *opaque)
frames = ns_elapsed / FRAME_TIMER_NS;
if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
- schedules++;
- expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ need_timer++;
+ ehci->async_stepdown = 0;
if (frames > ehci->maxframes) {
skipped_frames = frames - ehci->maxframes;
@@ -2372,8 +2391,6 @@ static void ehci_frame_timer(void *opaque)
if (ehci->async_stepdown < ehci->maxframes / 2) {
ehci->async_stepdown++;
}
- expire_time = t_now + (get_ticks_per_sec()
- * ehci->async_stepdown / FRAME_TIMER_FREQ);
ehci_update_frindex(ehci, frames);
ehci->last_run_ns += FRAME_TIMER_NS * frames;
}
@@ -2382,11 +2399,19 @@ static void ehci_frame_timer(void *opaque)
* called
*/
if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) {
- schedules++;
- qemu_bh_schedule(ehci->async_bh);
+ need_timer++;
+ ehci_advance_async_state(ehci);
+ }
+
+ ehci_commit_irq(ehci);
+ if (ehci->usbsts_pending) {
+ need_timer++;
+ ehci->async_stepdown = 0;
}
- if (schedules) {
+ if (need_timer) {
+ expire_time = t_now + (get_ticks_per_sec()
+ * (ehci->async_stepdown+1) / FRAME_TIMER_FREQ);
qemu_mod_timer(ehci->frame_timer, expire_time);
}
}
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 8f652d2f4a..2aac8a2505 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -388,11 +388,23 @@ static const VMStateDescription vmstate_uhci_port = {
}
};
+static int uhci_post_load(void *opaque, int version_id)
+{
+ UHCIState *s = opaque;
+
+ if (version_id < 2) {
+ s->expire_time = qemu_get_clock_ns(vm_clock) +
+ (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ }
+ return 0;
+}
+
static const VMStateDescription vmstate_uhci = {
.name = "uhci",
.version_id = 2,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .post_load = uhci_post_load,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, UHCIState),
VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
diff --git a/hw/vhost.c b/hw/vhost.c
index 43664e7f4d..0fd8da84e2 100644
--- a/hw/vhost.c
+++ b/hw/vhost.c
@@ -737,13 +737,13 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev,
static void vhost_eventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data, EventNotifier *e)
{
}
static void vhost_eventfd_del(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data, EventNotifier *e)
{
}
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 557d1d3b13..4e03f0b42c 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -173,46 +173,18 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
__func__, r);
return r;
}
+ virtio_queue_set_host_notifier_fd_handler(vq, true);
memory_region_add_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
- true, n, event_notifier_get_fd(notifier));
+ true, n, notifier);
} else {
memory_region_del_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
- true, n, event_notifier_get_fd(notifier));
- /* Handle the race condition where the guest kicked and we deassigned
- * before we got around to handling the kick.
- */
- if (event_notifier_test_and_clear(notifier)) {
- virtio_queue_notify_vq(vq);
- }
-
+ true, n, notifier);
+ virtio_queue_set_host_notifier_fd_handler(vq, false);
event_notifier_cleanup(notifier);
}
return r;
}
-static void virtio_pci_host_notifier_read(void *opaque)
-{
- VirtQueue *vq = opaque;
- EventNotifier *n = virtio_queue_get_host_notifier(vq);
- if (event_notifier_test_and_clear(n)) {
- virtio_queue_notify_vq(vq);
- }
-}
-
-static void virtio_pci_set_host_notifier_fd_handler(VirtIOPCIProxy *proxy,
- int n, bool assign)
-{
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
- EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
- if (assign) {
- qemu_set_fd_handler(event_notifier_get_fd(notifier),
- virtio_pci_host_notifier_read, NULL, vq);
- } else {
- qemu_set_fd_handler(event_notifier_get_fd(notifier),
- NULL, NULL, NULL);
- }
-}
-
static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
{
int n, r;
@@ -232,8 +204,6 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
if (r < 0) {
goto assign_error;
}
-
- virtio_pci_set_host_notifier_fd_handler(proxy, n, true);
}
proxy->ioeventfd_started = true;
return;
@@ -244,7 +214,6 @@ assign_error:
continue;
}
- virtio_pci_set_host_notifier_fd_handler(proxy, n, false);
r = virtio_pci_set_host_notifier_internal(proxy, n, false);
assert(r >= 0);
}
@@ -266,7 +235,6 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
continue;
}
- virtio_pci_set_host_notifier_fd_handler(proxy, n, false);
r = virtio_pci_set_host_notifier_internal(proxy, n, false);
assert(r >= 0);
}
@@ -528,25 +496,15 @@ static unsigned virtio_pci_get_features(void *opaque)
return proxy->host_features;
}
-static void virtio_pci_guest_notifier_read(void *opaque)
-{
- VirtQueue *vq = opaque;
- EventNotifier *n = virtio_queue_get_guest_notifier(vq);
- if (event_notifier_test_and_clear(n)) {
- virtio_irq(vq);
- }
-}
-
static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
unsigned int queue_no,
unsigned int vector,
MSIMessage msg)
{
VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
- int fd, ret;
-
- fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vq));
+ int ret;
if (irqfd->users == 0) {
ret = kvm_irqchip_add_msi_route(kvm_state, msg);
@@ -557,7 +515,7 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
}
irqfd->users++;
- ret = kvm_irqchip_add_irqfd(kvm_state, fd, irqfd->virq);
+ ret = kvm_irqchip_add_irq_notifier(kvm_state, n, irqfd->virq);
if (ret < 0) {
if (--irqfd->users == 0) {
kvm_irqchip_release_virq(kvm_state, irqfd->virq);
@@ -565,8 +523,7 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
return ret;
}
- qemu_set_fd_handler(fd, NULL, NULL, NULL);
-
+ virtio_queue_set_guest_notifier_fd_handler(vq, true, true);
return 0;
}
@@ -575,19 +532,18 @@ static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy,
unsigned int vector)
{
VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
- int fd, ret;
-
- fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vq));
+ int ret;
- ret = kvm_irqchip_remove_irqfd(kvm_state, fd, irqfd->virq);
+ ret = kvm_irqchip_remove_irq_notifier(kvm_state, n, irqfd->virq);
assert(ret == 0);
if (--irqfd->users == 0) {
kvm_irqchip_release_virq(kvm_state, irqfd->virq);
}
- qemu_set_fd_handler(fd, virtio_pci_guest_notifier_read, NULL, vq);
+ virtio_queue_set_guest_notifier_fd_handler(vq, true, false);
}
static int kvm_virtio_pci_vector_use(PCIDevice *dev, unsigned vector,
@@ -649,14 +605,9 @@ static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign)
if (r < 0) {
return r;
}
- qemu_set_fd_handler(event_notifier_get_fd(notifier),
- virtio_pci_guest_notifier_read, NULL, vq);
+ virtio_queue_set_guest_notifier_fd_handler(vq, true, false);
} else {
- qemu_set_fd_handler(event_notifier_get_fd(notifier),
- NULL, NULL, NULL);
- /* Test and clear notifier before closing it,
- * in case poll callback didn't have time to run. */
- virtio_pci_guest_notifier_read(vq);
+ virtio_queue_set_guest_notifier_fd_handler(vq, false, false);
event_notifier_cleanup(notifier);
}
diff --git a/hw/virtio.c b/hw/virtio.c
index 168abe4864..d146f86f13 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -984,10 +984,56 @@ VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n)
return vdev->vq + n;
}
+static void virtio_queue_guest_notifier_read(EventNotifier *n)
+{
+ VirtQueue *vq = container_of(n, VirtQueue, guest_notifier);
+ if (event_notifier_test_and_clear(n)) {
+ virtio_irq(vq);
+ }
+}
+
+void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign,
+ bool with_irqfd)
+{
+ if (assign && !with_irqfd) {
+ event_notifier_set_handler(&vq->guest_notifier,
+ virtio_queue_guest_notifier_read);
+ } else {
+ event_notifier_set_handler(&vq->guest_notifier, NULL);
+ }
+ if (!assign) {
+ /* Test and clear notifier before closing it,
+ * in case poll callback didn't have time to run. */
+ virtio_queue_guest_notifier_read(&vq->guest_notifier);
+ }
+}
+
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq)
{
return &vq->guest_notifier;
}
+
+static void virtio_queue_host_notifier_read(EventNotifier *n)
+{
+ VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
+ if (event_notifier_test_and_clear(n)) {
+ virtio_queue_notify_vq(vq);
+ }
+}
+
+void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign)
+{
+ if (assign) {
+ event_notifier_set_handler(&vq->host_notifier,
+ virtio_queue_host_notifier_read);
+ } else {
+ event_notifier_set_handler(&vq->host_notifier, NULL);
+ /* Test and clear notifier before after disabling event,
+ * in case poll callback didn't have time to run. */
+ virtio_queue_host_notifier_read(&vq->host_notifier);
+ }
+}
+
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq)
{
return &vq->host_notifier;
diff --git a/hw/virtio.h b/hw/virtio.h
index 42a7762999..f8b5535db1 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -230,7 +230,10 @@ void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx);
VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
int virtio_queue_get_id(VirtQueue *vq);
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
+void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign,
+ bool with_irqfd);
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
+void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign);
void virtio_queue_notify_vq(VirtQueue *vq);
void virtio_irq(VirtQueue *vq);
#endif
diff --git a/hw/xen_pt.c b/hw/xen_pt.c
index 3b6d1867ab..fdf68aa564 100644
--- a/hw/xen_pt.c
+++ b/hw/xen_pt.c
@@ -634,7 +634,7 @@ static void xen_pt_log_global_fns(MemoryListener *l)
}
static void xen_pt_eventfd_fns(MemoryListener *l, MemoryRegionSection *s,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data, EventNotifier *n)
{
}
diff --git a/kvm-all.c b/kvm-all.c
index 1016ca49b1..2148b20bdb 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -32,6 +32,7 @@
#include "bswap.h"
#include "memory.h"
#include "exec-memory.h"
+#include "event_notifier.h"
/* This check must be after config-host.h is included */
#ifdef CONFIG_EVENTFD
@@ -800,23 +801,29 @@ static void kvm_io_ioeventfd_del(MemoryRegionSection *section,
static void kvm_eventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data,
+ EventNotifier *e)
{
if (section->address_space == get_system_memory()) {
- kvm_mem_ioeventfd_add(section, match_data, data, fd);
+ kvm_mem_ioeventfd_add(section, match_data, data,
+ event_notifier_get_fd(e));
} else {
- kvm_io_ioeventfd_add(section, match_data, data, fd);
+ kvm_io_ioeventfd_add(section, match_data, data,
+ event_notifier_get_fd(e));
}
}
static void kvm_eventfd_del(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data,
+ EventNotifier *e)
{
if (section->address_space == get_system_memory()) {
- kvm_mem_ioeventfd_del(section, match_data, data, fd);
+ kvm_mem_ioeventfd_del(section, match_data, data,
+ event_notifier_get_fd(e));
} else {
- kvm_io_ioeventfd_del(section, match_data, data, fd);
+ kvm_io_ioeventfd_del(section, match_data, data,
+ event_notifier_get_fd(e));
}
}
@@ -1142,7 +1149,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg)
int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg)
{
- abort();
+ return -ENOSYS;
}
static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign)
@@ -1156,11 +1163,21 @@ int kvm_irqchip_add_irqfd(KVMState *s, int fd, int virq)
return kvm_irqchip_assign_irqfd(s, fd, virq, true);
}
+int kvm_irqchip_add_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return kvm_irqchip_add_irqfd(s, event_notifier_get_fd(n), virq);
+}
+
int kvm_irqchip_remove_irqfd(KVMState *s, int fd, int virq)
{
return kvm_irqchip_assign_irqfd(s, fd, virq, false);
}
+int kvm_irqchip_remove_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return kvm_irqchip_remove_irqfd(s, event_notifier_get_fd(n), virq);
+}
+
static int kvm_irqchip_create(KVMState *s)
{
QemuOptsList *list = qemu_find_opts("machine");
diff --git a/kvm-stub.c b/kvm-stub.c
index ec9a36454d..d23b11c020 100644
--- a/kvm-stub.c
+++ b/kvm-stub.c
@@ -147,7 +147,17 @@ int kvm_irqchip_add_irqfd(KVMState *s, int fd, int virq)
return -ENOSYS;
}
+int kvm_irqchip_add_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return -ENOSYS;
+}
+
int kvm_irqchip_remove_irqfd(KVMState *s, int fd, int virq)
{
return -ENOSYS;
}
+
+int kvm_irqchip_remove_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return -ENOSYS;
+}
diff --git a/kvm.h b/kvm.h
index ddc7c53cb0..2617dd5acd 100644
--- a/kvm.h
+++ b/kvm.h
@@ -220,4 +220,6 @@ void kvm_irqchip_release_virq(KVMState *s, int virq);
int kvm_irqchip_add_irqfd(KVMState *s, int fd, int virq);
int kvm_irqchip_remove_irqfd(KVMState *s, int fd, int virq);
+int kvm_irqchip_add_irq_notifier(KVMState *s, EventNotifier *n, int virq);
+int kvm_irqchip_remove_irq_notifier(KVMState *s, EventNotifier *n, int virq);
#endif
diff --git a/memory.c b/memory.c
index aab4a31323..643871bafa 100644
--- a/memory.c
+++ b/memory.c
@@ -156,7 +156,7 @@ struct MemoryRegionIoeventfd {
AddrRange addr;
bool match_data;
uint64_t data;
- int fd;
+ EventNotifier *e;
};
static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd a,
@@ -181,9 +181,9 @@ static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd a,
return false;
}
}
- if (a.fd < b.fd) {
+ if (a.e < b.e) {
return true;
- } else if (a.fd > b.fd) {
+ } else if (a.e > b.e) {
return false;
}
return false;
@@ -597,7 +597,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
.size = int128_get64(fd->addr.size),
};
MEMORY_LISTENER_CALL(eventfd_del, Forward, &section,
- fd->match_data, fd->data, fd->fd);
+ fd->match_data, fd->data, fd->e);
++iold;
} else if (inew < fds_new_nb
&& (iold == fds_old_nb
@@ -610,7 +610,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
.size = int128_get64(fd->addr.size),
};
MEMORY_LISTENER_CALL(eventfd_add, Reverse, &section,
- fd->match_data, fd->data, fd->fd);
+ fd->match_data, fd->data, fd->e);
++inew;
} else {
++iold;
@@ -1195,14 +1195,14 @@ void memory_region_add_eventfd(MemoryRegion *mr,
unsigned size,
bool match_data,
uint64_t data,
- int fd)
+ EventNotifier *e)
{
MemoryRegionIoeventfd mrfd = {
.addr.start = int128_make64(addr),
.addr.size = int128_make64(size),
.match_data = match_data,
.data = data,
- .fd = fd,
+ .e = e,
};
unsigned i;
@@ -1225,14 +1225,14 @@ void memory_region_del_eventfd(MemoryRegion *mr,
unsigned size,
bool match_data,
uint64_t data,
- int fd)
+ EventNotifier *e)
{
MemoryRegionIoeventfd mrfd = {
.addr.start = int128_make64(addr),
.addr.size = int128_make64(size),
.match_data = match_data,
.data = data,
- .fd = fd,
+ .e = e,
};
unsigned i;
diff --git a/memory.h b/memory.h
index 740c48e8e5..bd1bbaeabe 100644
--- a/memory.h
+++ b/memory.h
@@ -198,9 +198,9 @@ struct MemoryListener {
void (*log_global_start)(MemoryListener *listener);
void (*log_global_stop)(MemoryListener *listener);
void (*eventfd_add)(MemoryListener *listener, MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd);
+ bool match_data, uint64_t data, EventNotifier *e);
void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd);
+ bool match_data, uint64_t data, EventNotifier *e);
/* Lower = earlier (during add), later (during del) */
unsigned priority;
MemoryRegion *address_space_filter;
@@ -541,7 +541,7 @@ void memory_region_add_eventfd(MemoryRegion *mr,
unsigned size,
bool match_data,
uint64_t data,
- int fd);
+ EventNotifier *e);
/**
* memory_region_del_eventfd: Cancel an eventfd.
@@ -561,7 +561,8 @@ void memory_region_del_eventfd(MemoryRegion *mr,
unsigned size,
bool match_data,
uint64_t data,
- int fd);
+ EventNotifier *e);
+
/**
* memory_region_add_subregion: Add a subregion to a container.
*
diff --git a/monitor.c b/monitor.c
index 188c03d5b1..09aa3cdf52 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2307,48 +2307,45 @@ static void do_inject_mce(Monitor *mon, const QDict *qdict)
}
#endif
-static int do_getfd(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_getfd(const char *fdname, Error **errp)
{
- const char *fdname = qdict_get_str(qdict, "fdname");
mon_fd_t *monfd;
int fd;
- fd = qemu_chr_fe_get_msgfd(mon->chr);
+ fd = qemu_chr_fe_get_msgfd(cur_mon->chr);
if (fd == -1) {
- qerror_report(QERR_FD_NOT_SUPPLIED);
- return -1;
+ error_set(errp, QERR_FD_NOT_SUPPLIED);
+ return;
}
if (qemu_isdigit(fdname[0])) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "fdname",
- "a name not starting with a digit");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "fdname",
+ "a name not starting with a digit");
+ return;
}
- QLIST_FOREACH(monfd, &mon->fds, next) {
+ QLIST_FOREACH(monfd, &cur_mon->fds, next) {
if (strcmp(monfd->name, fdname) != 0) {
continue;
}
close(monfd->fd);
monfd->fd = fd;
- return 0;
+ return;
}
monfd = g_malloc0(sizeof(mon_fd_t));
monfd->name = g_strdup(fdname);
monfd->fd = fd;
- QLIST_INSERT_HEAD(&mon->fds, monfd, next);
- return 0;
+ QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next);
}
-static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_closefd(const char *fdname, Error **errp)
{
- const char *fdname = qdict_get_str(qdict, "fdname");
mon_fd_t *monfd;
- QLIST_FOREACH(monfd, &mon->fds, next) {
+ QLIST_FOREACH(monfd, &cur_mon->fds, next) {
if (strcmp(monfd->name, fdname) != 0) {
continue;
}
@@ -2357,11 +2354,10 @@ static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data)
close(monfd->fd);
g_free(monfd->name);
g_free(monfd);
- return 0;
+ return;
}
- qerror_report(QERR_FD_NOT_FOUND, fdname);
- return -1;
+ error_set(errp, QERR_FD_NOT_FOUND, fdname);
}
static void do_loadvm(Monitor *mon, const QDict *qdict)
diff --git a/qapi-schema.json b/qapi-schema.json
index 1ab5dbd5ff..a92adb1d5b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1789,34 +1789,36 @@
#
# Dump guest's memory to vmcore. It is a synchronous operation that can take
# very long depending on the amount of guest memory. This command is only
-# supported only on i386 and x86_64
-#
-# @paging: if true, do paging to get guest's memory mapping. The @paging's
-# default value of @paging is false, If you want to use gdb to process the
-# core, please set @paging to true. The reason why the @paging's value is
-# false:
-# 1. guest machine in a catastrophic state can have corrupted memory,
-# which we cannot trust.
-# 2. The guest machine can be in read-mode even if paging is enabled.
-# For example: the guest machine uses ACPI to sleep, and ACPI sleep
-# state goes in real-mode
+# supported on i386 and x86_64.
+#
+# @paging: if true, do paging to get guest's memory mapping. This allows
+# using gdb to process the core file. However, setting @paging to false
+# may be desirable because of two reasons:
+#
+# 1. The guest may be in a catastrophic state or can have corrupted
+# memory, which cannot be trusted
+# 2. The guest can be in real-mode even if paging is enabled. For example,
+# the guest uses ACPI to sleep, and ACPI sleep state goes in real-mode
+#
# @protocol: the filename or file descriptor of the vmcore. The supported
-# protocol can be file or fd:
+# protocols are:
+#
# 1. file: the protocol starts with "file:", and the following string is
# the file's path.
# 2. fd: the protocol starts with "fd:", and the following string is the
# fd's name.
+#
# @begin: #optional if specified, the starting physical address.
+#
# @length: #optional if specified, the memory size, in bytes. If you don't
-# want to dump all guest's memory, please specify the start @begin and
-# @length
+# want to dump all guest's memory, please specify the start @begin and @length
#
# Returns: nothing on success
# If @begin contains an invalid address, InvalidParameter
# If only one of @begin and @length is specified, MissingParameter
# If @protocol stats with "fd:", and the fd cannot be found, FdNotFound
# If @protocol starts with "file:", and the file cannot be
-# opened, OpenFileFailed
+# opened, OpenFileFailed
# If @protocol does not start with "fd:" or "file:", InvalidParameter
# If an I/O error occurs while writing the file, IOError
# If the target does not support this command, Unsupported
@@ -1868,3 +1870,38 @@
# Since: 0.14.0
##
{ 'command': 'netdev_del', 'data': {'id': 'str'} }
+
+##
+# @getfd:
+#
+# Receive a file descriptor via SCM rights and assign it a name
+#
+# @fdname: file descriptor name
+#
+# Returns: Nothing on success
+# If file descriptor was not received, FdNotSupplied
+# If @fdname is not valid, InvalidParameterType
+#
+# Since: 0.14.0
+#
+# Notes: If @fdname already exists, the file descriptor assigned to
+# it will be closed and replaced by the received file
+# descriptor.
+# The 'closefd' command can be used to explicitly close the
+# file descriptor when it is no longer needed.
+##
+{ 'command': 'getfd', 'data': {'fdname': 'str'} }
+
+##
+# @closefd:
+#
+# Close a file descriptor previously passed via SCM rights
+#
+# @fdname: file descriptor name
+#
+# Returns: Nothing on success
+# If @fdname is not found, FdNotFound
+#
+# Since: 0.14.0
+##
+{ 'command': 'closefd', 'data': {'fdname': 'str'} }
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index ffffbf79aa..705eca90aa 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -298,7 +298,7 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[],
}
if (strings[value] == NULL) {
- error_set(errp, QERR_INVALID_PARAMETER, name ? name : "null");
+ error_set(errp, QERR_INVALID_PARAMETER, enum_str);
g_free(enum_str);
return;
}
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 0af0ff45c2..84dad19579 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -78,7 +78,7 @@ to ease cross-compilation and cross-debugging.
@end itemize
-QEMU can run without an host kernel driver and yet gives acceptable
+QEMU can run without a host kernel driver and yet gives acceptable
performance.
For system emulation, the following hardware targets are supported:
diff --git a/qemu-options.hx b/qemu-options.hx
index ecf7ca12d7..97245a335c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1030,8 +1030,21 @@ is a TCP port number, not a display number.
@item password
Require that password based authentication is used for client connections.
-The password must be set separately using the @code{change} command in the
-@ref{pcsys_monitor}
+
+The password must be set separately using the @code{set_password} command in
+the @ref{pcsys_monitor}. The syntax to change your password is:
+@code{set_password <protocol> <password>} where <protocol> could be either
+"vnc" or "spice".
+
+If you would like to change <protocol> password expiration, you should use
+@code{expire_password <protocol> <expiration-time>} where expiration time could
+be one of the following options: now, never, +seconds or UNIX time of
+expiration, e.g. +60 to make password expire in 60 seconds, or 1335196800
+to make password expire on "Mon Apr 23 12:00:00 EDT 2012" (UNIX time for this
+date and time).
+
+You can also use keywords "now" or "never" for the expiration time to
+allow <protocol> password to expire immediately or never expire.
@item tls
@@ -2641,7 +2654,10 @@ DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \
STEXI
@item -nodefaults
@findex -nodefaults
-Don't create default devices.
+Don't create default devices. Normally, QEMU sets the default devices like serial
+port, parallel port, virtual console, monitor device, VGA adapter, floppy and
+CD-ROM drive and others. The @code{-nodefaults} option will disable all those
+default devices.
ETEXI
#ifndef _WIN32
@@ -2697,7 +2713,9 @@ DEF("readconfig", HAS_ARG, QEMU_OPTION_readconfig,
STEXI
@item -readconfig @var{file}
@findex -readconfig
-Read device configuration from @var{file}.
+Read device configuration from @var{file}. This approach is useful when you want to spawn
+QEMU process with many command line options but you don't want to exceed the command line
+character limit.
ETEXI
DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig,
"-writeconfig <file>\n"
@@ -2705,7 +2723,9 @@ DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig,
STEXI
@item -writeconfig @var{file}
@findex -writeconfig
-Write device configuration to @var{file}.
+Write device configuration to @var{file}. The @var{file} can be either filename to save
+command line and device configuration into file or dash @code{-}) character to print the
+output to stdout. This can be later used as input file for @code{-readconfig} option.
ETEXI
DEF("nodefconfig", 0, QEMU_OPTION_nodefconfig,
"-nodefconfig\n"
diff --git a/qemu-tech.texi b/qemu-tech.texi
index b51a58abba..d73dda8e35 100644
--- a/qemu-tech.texi
+++ b/qemu-tech.texi
@@ -536,7 +536,7 @@ timers, especially together with the use of bottom halves (BHs).
@node Hardware interrupts
@section Hardware interrupts
-In order to be faster, QEMU does not check at every basic block if an
+In order to be faster, QEMU does not check at every basic block if a
hardware interrupt is pending. Instead, the user must asynchronously
call a specific function to tell that an interrupt is pending. This
function resets the chaining of the currently executing basic
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 2e1a38e695..e3cf3c5a1a 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -873,8 +873,7 @@ EQMP
.args_type = "fdname:s",
.params = "getfd name",
.help = "receive a file descriptor via SCM rights and assign it a name",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_getfd,
+ .mhandler.cmd_new = qmp_marshal_input_getfd,
},
SQMP
@@ -892,6 +891,14 @@ Example:
-> { "execute": "getfd", "arguments": { "fdname": "fd1" } }
<- { "return": {} }
+Notes:
+
+(1) If the name specified by the "fdname" argument already exists,
+ the file descriptor assigned to it will be closed and replaced
+ by the received file descriptor.
+(2) The 'closefd' command can be used to explicitly close the file
+ descriptor when it is no longer needed.
+
EQMP
{
@@ -899,8 +906,7 @@ EQMP
.args_type = "fdname:s",
.params = "closefd name",
.help = "close a file descriptor previously passed via SCM rights",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_closefd,
+ .mhandler.cmd_new = qmp_marshal_input_closefd,
},
SQMP
diff --git a/roms/Makefile b/roms/Makefile
index 0114e6f33f..feb9c2b145 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -1,10 +1,27 @@
+vgabios_variants := stdvga cirrus vmware qxl
+
default:
@echo "nothing is build by default"
@echo "available build targets:"
@echo " bios -- update bios.bin (seabios)"
+ @echo " seavgabios -- update vgabios binaries (seabios)"
+ @echo " lgplvgabios -- update vgabios binaries (lgpl)"
bios: config.seabios
sh configure-seabios.sh $<
make -C seabios out/bios.bin
cp seabios/out/bios.bin ../pc-bios/bios.bin
+
+seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants))
+
+seavgabios-%: config.vga.%
+ sh configure-seabios.sh $<
+ make -C seabios out/vgabios.bin
+ cp seabios/out/vgabios.bin ../pc-bios/vgabios-$*.bin
+
+lgplvgabios: $(patsubst %,lgplvgabios-%,$(vgabios_variants))
+
+lgplvgabios-%:
+ make -C vgabios vgabios-$*.bin
+ cp vgabios/VGABIOS-lgpl-latest.$*.bin ../pc-bios/vgabios-$*.bin
diff --git a/roms/config.vga.cirrus b/roms/config.vga.cirrus
new file mode 100644
index 0000000000..c8fe58239f
--- /dev/null
+++ b/roms/config.vga.cirrus
@@ -0,0 +1,3 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_CIRRUS=y
+CONFIG_VGA_PCI=y
diff --git a/roms/config.vga.isavga b/roms/config.vga.isavga
new file mode 100644
index 0000000000..e55e294a0c
--- /dev/null
+++ b/roms/config.vga.isavga
@@ -0,0 +1,3 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=n
diff --git a/roms/config.vga.qxl b/roms/config.vga.qxl
new file mode 100644
index 0000000000..d393f0c34f
--- /dev/null
+++ b/roms/config.vga.qxl
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x1b36
+CONFIG_VGA_DID=0x0100
diff --git a/roms/config.vga.stdvga b/roms/config.vga.stdvga
new file mode 100644
index 0000000000..7d063b787c
--- /dev/null
+++ b/roms/config.vga.stdvga
@@ -0,0 +1,3 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
diff --git a/roms/config.vga.vmware b/roms/config.vga.vmware
new file mode 100644
index 0000000000..eb10427afd
--- /dev/null
+++ b/roms/config.vga.vmware
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x15ad
+CONFIG_VGA_DID=0x0405
diff --git a/scripts/make-release b/scripts/make-release
new file mode 100755
index 0000000000..196c755f57
--- /dev/null
+++ b/scripts/make-release
@@ -0,0 +1,24 @@
+#!/bin/bash -e
+#
+# QEMU Release Script
+#
+# Copyright IBM, Corp. 2012
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2 or later.
+# See the COPYING file in the top-level directory.
+
+src="$1"
+version="$2"
+destination=qemu-${version}
+
+git clone "${src}" ${destination}
+pushd ${destination}
+git checkout "v${version}"
+git submodule update --init
+rm -rf .git roms/*/.git
+popd
+tar cfj ${destination}.tar.bz2 ${destination}
+rm -rf ${destination}
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 5521709240..b3bcbacd76 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -77,7 +77,7 @@ static const char *ext3_feature_name[] = {
};
static const char *kvm_feature_name[] = {
- "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", "kvm_asyncpf", NULL, NULL, NULL,
+ "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", "kvm_asyncpf", NULL, "kvm_pv_eoi", 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,
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index f257c972fb..2a61c810bb 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -400,6 +400,7 @@
#define CPUID_EXT_X2APIC (1 << 21)
#define CPUID_EXT_MOVBE (1 << 22)
#define CPUID_EXT_POPCNT (1 << 23)
+#define CPUID_EXT_TSC_DEADLINE_TIMER (1 << 24)
#define CPUID_EXT_XSAVE (1 << 26)
#define CPUID_EXT_OSXSAVE (1 << 27)
#define CPUID_EXT_HYPERVISOR (1 << 31)
@@ -477,6 +478,7 @@
for syscall instruction */
/* i386-specific interrupt pending bits. */
+#define CPU_INTERRUPT_POLL CPU_INTERRUPT_TGT_EXT_1
#define CPU_INTERRUPT_SMI CPU_INTERRUPT_TGT_EXT_2
#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
#define CPU_INTERRUPT_MCE CPU_INTERRUPT_TGT_EXT_4
@@ -1047,7 +1049,8 @@ static inline void cpu_clone_regs(CPUX86State *env, target_ulong newsp)
static inline bool cpu_has_work(CPUX86State *env)
{
- return ((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ return ((env->interrupt_request & (CPU_INTERRUPT_HARD |
+ CPU_INTERRUPT_POLL)) &&
(env->eflags & IF_MASK)) ||
(env->interrupt_request & (CPU_INTERRUPT_NMI |
CPU_INTERRUPT_INIT |
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 0d0d8f69d3..e53c2f6bdf 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -361,8 +361,13 @@ int kvm_arch_init_vcpu(CPUX86State *env)
env->cpuid_features &= kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX);
i = env->cpuid_ext_features & CPUID_EXT_HYPERVISOR;
+ j = env->cpuid_ext_features & CPUID_EXT_TSC_DEADLINE_TIMER;
env->cpuid_ext_features &= kvm_arch_get_supported_cpuid(s, 1, 0, R_ECX);
env->cpuid_ext_features |= i;
+ if (j && kvm_irqchip_in_kernel() &&
+ kvm_check_extension(s, KVM_CAP_TSC_DEADLINE_TIMER)) {
+ env->cpuid_ext_features |= CPUID_EXT_TSC_DEADLINE_TIMER;
+ }
env->cpuid_ext2_features &= kvm_arch_get_supported_cpuid(s, 0x80000001,
0, R_EDX);
@@ -1727,6 +1732,10 @@ int kvm_arch_process_async_events(CPUX86State *env)
return 0;
}
+ if (env->interrupt_request & CPU_INTERRUPT_POLL) {
+ env->interrupt_request &= ~CPU_INTERRUPT_POLL;
+ apic_poll_irq(env->apic_state);
+ }
if (((env->interrupt_request & CPU_INTERRUPT_HARD) &&
(env->eflags & IF_MASK)) ||
(env->interrupt_request & CPU_INTERRUPT_NMI)) {
diff --git a/trace-events b/trace-events
index 8b1fb24c57..2a5f074137 100644
--- a/trace-events
+++ b/trace-events
@@ -262,7 +262,7 @@ usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d"
usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
-usb_ehci_interrupt(uint32_t level, uint32_t sts, uint32_t mask) "level %d, sts 0x%x, mask 0x%x"
+usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x"
# hw/usb/hcd-uhci.c
usb_uhci_reset(void) "=== RESET ==="
@@ -351,6 +351,20 @@ usb_hub_clear_port_feature(int addr, int nr, const char *f) "dev %d, port %d, fe
usb_hub_attach(int addr, int nr) "dev %d, port %d"
usb_hub_detach(int addr, int nr) "dev %d, port %d"
+# hw/usb/dev-uas.c
+usb_uas_reset(int addr) "dev %d"
+usb_uas_command(int addr, uint16_t tag, int lun, uint32_t lun64_1, uint32_t lun64_2) "dev %d, tag 0x%x, lun %d, lun64 %08x-%08x"
+usb_uas_response(int addr, uint16_t tag, uint8_t code) "dev %d, tag 0x%x, code 0x%x"
+usb_uas_sense(int addr, uint16_t tag, uint8_t status) "dev %d, tag 0x%x, status 0x%x"
+usb_uas_read_ready(int addr, uint16_t tag) "dev %d, tag 0x%x"
+usb_uas_write_ready(int addr, uint16_t tag) "dev %d, tag 0x%x"
+usb_uas_xfer_data(int addr, uint16_t tag, uint32_t copy, uint32_t uoff, uint32_t usize, uint32_t soff, uint32_t ssize) "dev %d, tag 0x%x, copy %d, usb-pkt %d/%d, scsi-buf %d/%d"
+usb_uas_scsi_data(int addr, uint16_t tag, uint32_t bytes) "dev %d, tag 0x%x, bytes %d"
+usb_uas_scsi_complete(int addr, uint16_t tag, uint32_t status, uint32_t resid) "dev %d, tag 0x%x, status 0x%x, residue %d"
+usb_uas_tmf_abort_task(int addr, uint16_t tag, uint16_t task_tag) "dev %d, tag 0x%x, task-tag 0x%x"
+usb_uas_tmf_logical_unit_reset(int addr, uint16_t tag, int lun) "dev %d, tag 0x%x, lun %d"
+usb_uas_tmf_unsupported(int addr, uint16_t tag, uint32_t function) "dev %d, tag 0x%x, function 0x%x"
+
# hw/usb/host-linux.c
usb_host_open_started(int bus, int addr) "dev %d:%d"
usb_host_open_success(int bus, int addr) "dev %d:%d"
diff --git a/xen-all.c b/xen-all.c
index 59f232395e..61def2ec8f 100644
--- a/xen-all.c
+++ b/xen-all.c
@@ -560,13 +560,15 @@ static void xen_log_global_stop(MemoryListener *listener)
static void xen_eventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data,
+ EventNotifier *e)
{
}
static void xen_eventfd_del(MemoryListener *listener,
MemoryRegionSection *section,
- bool match_data, uint64_t data, int fd)
+ bool match_data, uint64_t data,
+ EventNotifier *e)
{
}