From c8dfc65f533430e541ecb37031f7d31ae2967504 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sep 07 2012 15:20:05 +0000 Subject: New upstream release 1.2.0 final - Add support for Spice seamless migration - Add support for Spice dynamic monitors - Add support for usb-redir live migration --- diff --git a/.gitignore b/.gitignore index 0ad020f..0314ae9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ qemu-kvm-0.13.0-25fdf4a.tar.gz /qemu-1.2-0.1.20120806git3e430569.fc18.src.rpm /qemu-kvm-1.2-3e430569.tar.gz /qemu-kvm-1.2.0-rc1.tar.gz +/qemu-kvm-1.2.0.tar.gz diff --git a/0201-spice-abort-on-invalid-streaming-cmdline-params.patch b/0201-spice-abort-on-invalid-streaming-cmdline-params.patch new file mode 100644 index 0000000..fa5c5b1 --- /dev/null +++ b/0201-spice-abort-on-invalid-streaming-cmdline-params.patch @@ -0,0 +1,38 @@ +From 46851363e2aed89fc535803c8a23f6bed9934312 Mon Sep 17 00:00:00 2001 +From: Christophe Fergeau +Date: Mon, 13 Aug 2012 10:32:32 +0200 +Subject: [PATCH 201/215] spice: abort on invalid streaming cmdline params + +When parsing its command line parameters, spice aborts when it +finds unexpected values, except for the 'streaming-video' option. +This happens because the parsing of the parameters for this option +is done using the 'name2enum' helper, which does not error out +on unknown values. Using the 'parse_name' helper makes sure we +error out in this case. Looking at git history, the use of +'name2enum' instead of 'parse_name' seems to have been an oversight, +so let's change to that now. + +Fixes rhbz#831708 + +Signed-off-by: Gerd Hoffmann +--- + ui/spice-core.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ui/spice-core.c b/ui/spice-core.c +index 4fc48f8..bb4f585 100644 +--- a/ui/spice-core.c ++++ b/ui/spice-core.c +@@ -344,7 +344,8 @@ static const char *stream_video_names[] = { + [ SPICE_STREAM_VIDEO_FILTER ] = "filter", + }; + #define parse_stream_video(_name) \ +- name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names)) ++ parse_name(_name, "stream video control", \ ++ stream_video_names, ARRAY_SIZE(stream_video_names)) + + static const char *compression_names[] = { + [ SPICE_IMAGE_COMPRESS_OFF ] = "off", +-- +1.7.12 + diff --git a/0202-spice-notify-spice-server-on-vm-start-stop.patch b/0202-spice-notify-spice-server-on-vm-start-stop.patch new file mode 100644 index 0000000..150bb5c --- /dev/null +++ b/0202-spice-notify-spice-server-on-vm-start-stop.patch @@ -0,0 +1,52 @@ +From 2d2ccb50223c16fbf08140b9dd59275657de2a61 Mon Sep 17 00:00:00 2001 +From: Yonit Halperin +Date: Tue, 21 Aug 2012 11:51:55 +0300 +Subject: [PATCH 202/215] spice: notify spice server on vm start/stop + +Spice server needs to know about the vm state in order to prevent +attempts to write to devices when they are stopped, mainly during +the non-live stage of migration. +Instead, spice will take care of restoring this writes, on the migration +target side, after migration completes. + +Signed-off-by: Yonit Halperin +Signed-off-by: Gerd Hoffmann +--- + ui/spice-core.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/ui/spice-core.c b/ui/spice-core.c +index bb4f585..a515c94 100644 +--- a/ui/spice-core.c ++++ b/ui/spice-core.c +@@ -546,6 +546,18 @@ static int add_channel(const char *name, const char *value, void *opaque) + return 0; + } + ++static void vm_change_state_handler(void *opaque, int running, ++ RunState state) ++{ ++#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */ ++ if (running) { ++ spice_server_vm_start(spice_server); ++ } else { ++ spice_server_vm_stop(spice_server); ++ } ++#endif ++} ++ + void qemu_spice_init(void) + { + QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); +@@ -719,6 +731,8 @@ void qemu_spice_init(void) + qemu_spice_input_init(); + qemu_spice_audio_init(); + ++ qemu_add_vm_change_state_handler(vm_change_state_handler, &spice_server); ++ + g_free(x509_key_file); + g_free(x509_cert_file); + g_free(x509_cacert_file); +-- +1.7.12 + diff --git a/0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch b/0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch new file mode 100644 index 0000000..e0b42d0 --- /dev/null +++ b/0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch @@ -0,0 +1,170 @@ +From f7d481b35b63f2df58aaef5afcac5ca1e67ce233 Mon Sep 17 00:00:00 2001 +From: Yonit Halperin +Date: Tue, 21 Aug 2012 11:51:56 +0300 +Subject: [PATCH 203/215] spice: notify on vm state change only via + spice_server_vm_start/stop + +QXLWorker->start/stop are deprecated since spice-server 0.11.2 + +Signed-off-by: Yonit Halperin +Signed-off-by: Gerd Hoffmann +--- + hw/qxl.c | 7 ++++--- + ui/spice-core.c | 4 ++++ + ui/spice-display.c | 32 ++++++++++++++++++++++++++++++-- + ui/spice-display.h | 9 +++++++-- + 4 files changed, 45 insertions(+), 7 deletions(-) + +diff --git a/hw/qxl.c b/hw/qxl.c +index c2dd3b4..95bbc03 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -958,9 +958,10 @@ static void qxl_update_irq(PCIQXLDevice *d) + static void qxl_check_state(PCIQXLDevice *d) + { + QXLRam *ram = d->ram; ++ int spice_display_running = qemu_spice_display_is_running(&d->ssd); + +- assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); +- assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); ++ assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); ++ assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); + } + + static void qxl_reset_state(PCIQXLDevice *d) +@@ -1538,7 +1539,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events) + uint32_t old_pending; + uint32_t le_events = cpu_to_le32(events); + +- assert(d->ssd.running); ++ assert(qemu_spice_display_is_running(&d->ssd)); + old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events); + if ((old_pending & le_events) == le_events) { + return; +diff --git a/ui/spice-core.c b/ui/spice-core.c +index a515c94..1a7a773 100644 +--- a/ui/spice-core.c ++++ b/ui/spice-core.c +@@ -37,6 +37,7 @@ + #include "migration.h" + #include "monitor.h" + #include "hw/hw.h" ++#include "spice-display.h" + + /* core bits */ + +@@ -551,9 +552,11 @@ static void vm_change_state_handler(void *opaque, int running, + { + #if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */ + if (running) { ++ qemu_spice_display_start(); + spice_server_vm_start(spice_server); + } else { + spice_server_vm_stop(spice_server); ++ qemu_spice_display_stop(); + } + #endif + } +@@ -755,6 +758,7 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin) + spice_server = spice_server_new(); + spice_server_init(spice_server, &core_interface); + } ++ + return spice_server_add_interface(spice_server, sin); + } + +diff --git a/ui/spice-display.c b/ui/spice-display.c +index 3e8f0b3..1c31418 100644 +--- a/ui/spice-display.c ++++ b/ui/spice-display.c +@@ -126,18 +126,44 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd) + ssd->worker->wakeup(ssd->worker); + } + +-void qemu_spice_start(SimpleSpiceDisplay *ssd) ++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */ ++static void qemu_spice_start(SimpleSpiceDisplay *ssd) + { + trace_qemu_spice_start(ssd->qxl.id); + ssd->worker->start(ssd->worker); + } + +-void qemu_spice_stop(SimpleSpiceDisplay *ssd) ++static void qemu_spice_stop(SimpleSpiceDisplay *ssd) + { + trace_qemu_spice_stop(ssd->qxl.id); + ssd->worker->stop(ssd->worker); + } + ++#else ++ ++static int spice_display_is_running; ++ ++void qemu_spice_display_start(void) ++{ ++ spice_display_is_running = true; ++} ++ ++void qemu_spice_display_stop(void) ++{ ++ spice_display_is_running = false; ++} ++ ++#endif ++ ++int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd) ++{ ++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */ ++ return ssd->running; ++#else ++ return spice_display_is_running; ++#endif ++} ++ + static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) + { + SimpleSpiceUpdate *update; +@@ -272,6 +298,7 @@ void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) + void qemu_spice_vm_change_state_handler(void *opaque, int running, + RunState state) + { ++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */ + SimpleSpiceDisplay *ssd = opaque; + + if (running) { +@@ -281,6 +308,7 @@ void qemu_spice_vm_change_state_handler(void *opaque, int running, + qemu_spice_stop(ssd); + ssd->running = false; + } ++#endif + } + + void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds) +diff --git a/ui/spice-display.h b/ui/spice-display.h +index 12e50b6..672d65e 100644 +--- a/ui/spice-display.h ++++ b/ui/spice-display.h +@@ -82,7 +82,9 @@ struct SimpleSpiceDisplay { + + QXLRect dirty; + int notify; ++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */ + int running; ++#endif + + /* + * All struct members below this comment can be accessed from +@@ -129,5 +131,8 @@ void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, + void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, + uint32_t id, qxl_async_io async); + void qemu_spice_wakeup(SimpleSpiceDisplay *ssd); +-void qemu_spice_start(SimpleSpiceDisplay *ssd); +-void qemu_spice_stop(SimpleSpiceDisplay *ssd); ++#if SPICE_SERVER_VERSION >= 0x000b02 /* before 0.11.2 */ ++void qemu_spice_display_start(void); ++void qemu_spice_display_stop(void); ++#endif ++int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd); +-- +1.7.12 + diff --git a/0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch b/0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch new file mode 100644 index 0000000..631271a --- /dev/null +++ b/0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch @@ -0,0 +1,90 @@ +From d8543fcc36b38c76a638d15ed95d8b5acf27d93a Mon Sep 17 00:00:00 2001 +From: Yonit Halperin +Date: Tue, 21 Aug 2012 11:51:57 +0300 +Subject: [PATCH 204/215] spice migration: add QEVENT_SPICE_MIGRATE_COMPLETED + +When migrating, libvirt queries the migration status, and upon migration +completions, it closes the migration src. On the other hand, when +migration is completed, spice transfers data from the src to destination +via the client. This data is required for keeping the spice session +after migration, without suffering from data loss and inconsistencies. +In order to allow this data transfer, we add QEVENT for signaling +libvirt that spice migration has completed, and libvirt needs to wait +for this event before quitting the src process. + +Signed-off-by: Yonit Halperin +Signed-off-by: Gerd Hoffmann +--- + monitor.c | 1 + + monitor.h | 1 + + ui/spice-core.c | 9 ++++++++- + 3 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/monitor.c b/monitor.c +index c14698d..99eee98 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -455,6 +455,7 @@ static const char *monitor_event_names[] = { + [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK", + [QEVENT_WAKEUP] = "WAKEUP", + [QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE", ++ [QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED", + }; + QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX) + +diff --git a/monitor.h b/monitor.h +index 47d556b..5fc2983 100644 +--- a/monitor.h ++++ b/monitor.h +@@ -43,6 +43,7 @@ typedef enum MonitorEvent { + QEVENT_SUSPEND_DISK, + QEVENT_WAKEUP, + QEVENT_BALLOON_CHANGE, ++ QEVENT_SPICE_MIGRATE_COMPLETED, + + /* Add to 'monitor_event_names' array in monitor.c when + * defining new events here */ +diff --git a/ui/spice-core.c b/ui/spice-core.c +index 1a7a773..851e869 100644 +--- a/ui/spice-core.c ++++ b/ui/spice-core.c +@@ -285,6 +285,7 @@ typedef struct SpiceMigration { + } SpiceMigration; + + static void migrate_connect_complete_cb(SpiceMigrateInstance *sin); ++static void migrate_end_complete_cb(SpiceMigrateInstance *sin); + + static const SpiceMigrateInterface migrate_interface = { + .base.type = SPICE_INTERFACE_MIGRATION, +@@ -292,7 +293,7 @@ static const SpiceMigrateInterface migrate_interface = { + .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR, + .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR, + .migrate_connect_complete = migrate_connect_complete_cb, +- .migrate_end_complete = NULL, ++ .migrate_end_complete = migrate_end_complete_cb, + }; + + static SpiceMigration spice_migrate; +@@ -305,6 +306,11 @@ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin) + } + sm->connect_complete.cb = NULL; + } ++ ++static void migrate_end_complete_cb(SpiceMigrateInstance *sin) ++{ ++ monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL); ++} + #endif + + /* config string parsing */ +@@ -489,6 +495,7 @@ static void migration_state_notifier(Notifier *notifier, void *data) + } else if (migration_has_finished(s)) { + #ifndef SPICE_INTERFACE_MIGRATION + spice_server_migrate_switch(spice_server); ++ monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL); + #else + spice_server_migrate_end(spice_server, true); + } else if (migration_has_failed(s)) { +-- +1.7.12 + diff --git a/0205-spice-add-migrated-flag-to-spice-info.patch b/0205-spice-add-migrated-flag-to-spice-info.patch new file mode 100644 index 0000000..0e63c64 --- /dev/null +++ b/0205-spice-add-migrated-flag-to-spice-info.patch @@ -0,0 +1,94 @@ +From ad7734d7a3cb4560dcc0bef2794adeddc793af75 Mon Sep 17 00:00:00 2001 +From: Yonit Halperin +Date: Tue, 21 Aug 2012 11:51:58 +0300 +Subject: [PATCH 205/215] spice: add 'migrated' flag to spice info + +The flag is 'true' when spice migration has completed on the src side. +It is needed for a case where libvirt dies before migration completes +and it misses the event QEVENT_SPICE_MIGRATE_COMPLETED. +When libvirt is restored and queries the migration status, it also needs +to query spice and check if its migration has completed. + +Signed-off-by: Yonit Halperin +Signed-off-by: Gerd Hoffmann +--- + hmp.c | 2 ++ + qapi-schema.json | 5 ++++- + ui/spice-core.c | 4 ++++ + 3 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/hmp.c b/hmp.c +index 81c8acb..ec4274b 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -413,6 +413,8 @@ void hmp_info_spice(Monitor *mon) + monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n", + info->host, info->tls_port); + } ++ monitor_printf(mon, " migrated: %s\n", ++ info->migrated ? "true" : "false"); + monitor_printf(mon, " auth: %s\n", info->auth); + monitor_printf(mon, " compiled: %s\n", info->compiled_version); + monitor_printf(mon, " mouse-mode: %s\n", +diff --git a/qapi-schema.json b/qapi-schema.json +index bd8ad74..8ddde12 100644 +--- a/qapi-schema.json ++++ b/qapi-schema.json +@@ -808,6 +808,9 @@ + # + # @enabled: true if the SPICE server is enabled, false otherwise + # ++# @migrated: true if the last guest migration completed and spice ++# migration had completed as well. false otherwise. ++# + # @host: #optional The hostname the SPICE server is bound to. This depends on + # the name resolution on the host and may be an IP address. + # +@@ -833,7 +836,7 @@ + # Since: 0.14.0 + ## + { 'type': 'SpiceInfo', +- 'data': {'enabled': 'bool', '*host': 'str', '*port': 'int', ++ 'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int', + '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str', + 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} } + +diff --git a/ui/spice-core.c b/ui/spice-core.c +index 851e869..ab069c5 100644 +--- a/ui/spice-core.c ++++ b/ui/spice-core.c +@@ -46,6 +46,7 @@ static Notifier migration_state; + static const char *auth = "spice"; + static char *auth_passwd; + static time_t auth_expires = TIME_MAX; ++static int spice_migration_completed; + int using_spice = 0; + + static QemuThread me; +@@ -310,6 +311,7 @@ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin) + static void migrate_end_complete_cb(SpiceMigrateInstance *sin) + { + monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL); ++ spice_migration_completed = true; + } + #endif + +@@ -443,6 +445,7 @@ SpiceInfo *qmp_query_spice(Error **errp) + } + + info->enabled = true; ++ info->migrated = spice_migration_completed; + + addr = qemu_opt_get(opts, "addr"); + port = qemu_opt_get_number(opts, "port", 0); +@@ -496,6 +499,7 @@ static void migration_state_notifier(Notifier *notifier, void *data) + #ifndef SPICE_INTERFACE_MIGRATION + spice_server_migrate_switch(spice_server); + monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL); ++ spice_migration_completed = true; + #else + spice_server_migrate_end(spice_server, true); + } else if (migration_has_failed(s)) { +-- +1.7.12 + diff --git a/0206-spice-adding-seamless-migration-option-to-the-comman.patch b/0206-spice-adding-seamless-migration-option-to-the-comman.patch new file mode 100644 index 0000000..ac0e9a7 --- /dev/null +++ b/0206-spice-adding-seamless-migration-option-to-the-comman.patch @@ -0,0 +1,77 @@ +From 978a72a62c36e932a4b7ae18a5cf23d2d30e9755 Mon Sep 17 00:00:00 2001 +From: Yonit Halperin +Date: Tue, 21 Aug 2012 11:51:59 +0300 +Subject: [PATCH 206/215] spice: adding seamless-migration option to the + command line + +The seamless-migration flag is required in order to identify +whether libvirt supports the new QEVENT_SPICE_MIGRATE_COMPLETED or not +(by default the flag is off). +New libvirt versions that wait for QEVENT_SPICE_MIGRATE_COMPLETED should turn on this flag. +When this flag is off, spice fallbacks to its old migration method, which +can result in data loss. + +Signed-off-by: Yonit Halperin +Signed-off-by: Gerd Hoffmann +--- + qemu-config.c | 3 +++ + qemu-options.hx | 3 +++ + ui/spice-core.c | 7 +++++++ + 3 files changed, 13 insertions(+) + +diff --git a/qemu-config.c b/qemu-config.c +index 238390e..3eaee48 100644 +--- a/qemu-config.c ++++ b/qemu-config.c +@@ -541,6 +541,9 @@ QemuOptsList qemu_spice_opts = { + },{ + .name = "playback-compression", + .type = QEMU_OPT_BOOL, ++ }, { ++ .name = "seamless-migration", ++ .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +diff --git a/qemu-options.hx b/qemu-options.hx +index ea06324..dd7aa63 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -920,6 +920,9 @@ Enable/disable passing mouse events via vdagent. Default is on. + @item playback-compression=[on|off] + Enable/disable audio stream compression (using celt 0.5.1). Default is on. + ++@item seamless-migration=[on|off] ++Enable/disable spice seamless migration. Default is off. ++ + @end table + ETEXI + +diff --git a/ui/spice-core.c b/ui/spice-core.c +index ab069c5..ba0d0bd 100644 +--- a/ui/spice-core.c ++++ b/ui/spice-core.c +@@ -585,6 +585,9 @@ void qemu_spice_init(void) + int port, tls_port, len, addr_flags; + spice_image_compression_t compression; + spice_wan_compression_t wan_compr; ++#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */ ++ bool seamless_migration; ++#endif + + qemu_thread_get_self(&me); + +@@ -728,6 +731,10 @@ void qemu_spice_init(void) + spice_server_set_uuid(spice_server, qemu_uuid); + #endif + ++#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */ ++ seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0); ++ spice_server_set_seamless_migration(spice_server, seamless_migration); ++#endif + if (0 != spice_server_init(spice_server, &core_interface)) { + error_report("failed to initialize spice server"); + exit(1); +-- +1.7.12 + diff --git a/0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch b/0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch new file mode 100644 index 0000000..6440642 --- /dev/null +++ b/0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch @@ -0,0 +1,47 @@ +From 54704702dbbb1d55f2aaecf5c837a583d9e209a5 Mon Sep 17 00:00:00 2001 +From: Yonit Halperin +Date: Tue, 21 Aug 2012 13:54:20 +0300 +Subject: [PATCH 207/215] spice: increase the verbosity of spice section in + "qemu --help" + +Added all spice options to the help string. This can be used by libvirt +to determine which spice related features are supported by qemu. + +Signed-off-by: Yonit Halperin +Signed-off-by: Gerd Hoffmann +--- + qemu-options.hx | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/qemu-options.hx b/qemu-options.hx +index dd7aa63..1af4fec 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -838,7 +838,23 @@ Enable SDL. + ETEXI + + DEF("spice", HAS_ARG, QEMU_OPTION_spice, +- "-spice enable spice\n", QEMU_ARCH_ALL) ++ "-spice [port=port][,tls-port=secured-port][,x509-dir=]\n" ++ " [,x509-key-file=][,x509-key-password=]\n" ++ " [,x509-cert-file=][,x509-cacert-file=]\n" ++ " [,x509-dh-key-file=][,addr=addr][,ipv4|ipv6]\n" ++ " [,tls-ciphers=]\n" ++ " [,tls-channel=[main|display|cursor|inputs|record|playback]]\n" ++ " [,plaintext-channel=[main|display|cursor|inputs|record|playback]]\n" ++ " [,sasl][,password=][,disable-ticketing]\n" ++ " [,image-compression=[auto_glz|auto_lz|quic|glz|lz|off]]\n" ++ " [,jpeg-wan-compression=[auto|never|always]]\n" ++ " [,zlib-glz-wan-compression=[auto|never|always]]\n" ++ " [,streaming-video=[off|all|filter]][,disable-copy-paste]\n" ++ " [,agent-mouse=[on|off]][,playback-compression=[on|off]]\n" ++ " [,seamless-migration=[on|off]]\n" ++ " enable spice\n" ++ " at least one of {port, tls-port} is mandatory\n", ++ QEMU_ARCH_ALL) + STEXI + @item -spice @var{option}[,@var{option}[,...]] + @findex -spice +-- +1.7.12 + diff --git a/0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch b/0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch new file mode 100644 index 0000000..beefe40 --- /dev/null +++ b/0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch @@ -0,0 +1,37 @@ +From c3236169b390617d13ede20ff0aed5d0bc1aba66 Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Tue, 21 Aug 2012 13:51:31 +0300 +Subject: [PATCH 208/215] qxl/update_area_io: guest_bug on invalid parameters + +Signed-off-by: Alon Levy +Signed-off-by: Gerd Hoffmann +--- + hw/qxl.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/hw/qxl.c b/hw/qxl.c +index 95bbc03..baf9bb4 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -1386,6 +1386,18 @@ async_common: + QXLCookie *cookie = NULL; + QXLRect update = d->ram->update_area; + ++ if (d->ram->update_surface > NUM_SURFACES) { ++ qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n", ++ d->ram->update_surface); ++ return; ++ } ++ if (update.left >= update.right || update.top >= update.bottom) { ++ qxl_set_guest_bug(d, ++ "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n", ++ update.left, update.top, update.right, update.bottom); ++ return; ++ } ++ + if (async == QXL_ASYNC) { + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_UPDATE_AREA_ASYNC); +-- +1.7.12 + diff --git a/0209-qxl-disallow-unknown-revisions.patch b/0209-qxl-disallow-unknown-revisions.patch new file mode 100644 index 0000000..630be61 --- /dev/null +++ b/0209-qxl-disallow-unknown-revisions.patch @@ -0,0 +1,33 @@ +From 31052a357ad124c9ed17fbc39a0db5c8d6d0d9c5 Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Tue, 21 Aug 2012 13:51:32 +0300 +Subject: [PATCH 209/215] qxl: disallow unknown revisions + +Signed-off-by: Alon Levy +Signed-off-by: Gerd Hoffmann +--- + hw/qxl.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/hw/qxl.c b/hw/qxl.c +index baf9bb4..d134a70 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -1798,10 +1798,13 @@ static int qxl_init_common(PCIQXLDevice *qxl) + io_size = 16; + break; + case 3: /* qxl-3 */ +- default: + pci_device_rev = QXL_DEFAULT_REVISION; + io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); + break; ++ default: ++ error_report("Invalid revision %d for qxl device (max %d)", ++ qxl->revision, QXL_DEFAULT_REVISION); ++ return -1; + } + + pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); +-- +1.7.12 + diff --git a/0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch b/0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch new file mode 100644 index 0000000..a628f52 --- /dev/null +++ b/0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch @@ -0,0 +1,321 @@ +From cc8b6a27e848cb88a4df98d48517c5b658d70efc Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Wed, 22 Aug 2012 11:16:25 +0300 +Subject: [PATCH 210/215] qxl: add QXL_IO_MONITORS_CONFIG_ASYNC + +Revision bumped to 4 for new IO support, enabled for spice-server >= +0.11.1. New io enabled if revision is 4. Revision can be set to 4. + +[ kraxel: 3 continues to be the default revision. Once we have a new + stable spice-server release and the qemu patches to enable + the new bits merged we'll go flip the switch and make rev4 + the default ] + +This io calls the corresponding new spice api +spice_qxl_monitors_config_async to let spice-server read a new guest set +monitors config and notify the client. + +On migration reissue spice_qxl_monitors_config_async. + +RHBZ: 770842 + +Signed-off-by: Alon Levy +Signed-off-by: Gerd Hoffmann + +fixup + +Signed-off-by: Gerd Hoffmann +--- + configure | 7 ++++ + hw/qxl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++--- + hw/qxl.h | 7 ++++ + trace-events | 1 + + ui/spice-display.h | 1 + + 5 files changed, 109 insertions(+), 4 deletions(-) + +diff --git a/configure b/configure +index 9fc4fb5..c57d1c1 100755 +--- a/configure ++++ b/configure +@@ -2711,6 +2711,9 @@ EOF + spice="yes" + libs_softmmu="$libs_softmmu $spice_libs" + QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags" ++ if $pkg_config --atleast-version=0.12.0 spice-protocol >/dev/null 2>&1; then ++ spice_qxl_io_monitors_config_async="yes" ++ fi + else + if test "$spice" = "yes" ; then + feature_not_found "spice" +@@ -3448,6 +3451,10 @@ if test "$spice" = "yes" ; then + echo "CONFIG_SPICE=y" >> $config_host_mak + fi + ++if test "$spice_qxl_io_monitors_config_async" = "yes" ; then ++ echo "CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC=y" >> $config_host_mak ++fi ++ + if test "$smartcard" = "yes" ; then + echo "CONFIG_SMARTCARD=y" >> $config_host_mak + fi +diff --git a/hw/qxl.c b/hw/qxl.c +index d134a70..adf17fd 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -27,6 +27,11 @@ + + #include "qxl.h" + ++#ifndef CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC ++/* spice-protocol is too old, add missing definitions */ ++#define QXL_IO_MONITORS_CONFIG_ASYNC (QXL_IO_FLUSH_RELEASE + 1) ++#endif ++ + /* + * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as + * such can be changed by the guest, so to avoid a guest trigerrable +@@ -249,6 +254,39 @@ static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) + } + } + ++static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) ++{ ++ trace_qxl_spice_monitors_config(qxl->id); ++/* 0x000b01 == 0.11.1 */ ++#if SPICE_SERVER_VERSION >= 0x000b01 && \ ++ defined(CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC) ++ if (replay) { ++ /* ++ * don't use QXL_COOKIE_TYPE_IO: ++ * - we are not running yet (post_load), we will assert ++ * in send_events ++ * - this is not a guest io, but a reply, so async_io isn't set. ++ */ ++ spice_qxl_monitors_config_async(&qxl->ssd.qxl, ++ qxl->guest_monitors_config, ++ MEMSLOT_GROUP_GUEST, ++ (uintptr_t)qxl_cookie_new( ++ QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, ++ 0)); ++ } else { ++ qxl->guest_monitors_config = qxl->ram->monitors_config; ++ spice_qxl_monitors_config_async(&qxl->ssd.qxl, ++ qxl->ram->monitors_config, ++ MEMSLOT_GROUP_GUEST, ++ (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, ++ QXL_IO_MONITORS_CONFIG_ASYNC)); ++ } ++#else ++ fprintf(stderr, "qxl: too old spice-protocol/spice-server for " ++ "QXL_IO_MONITORS_CONFIG_ASYNC\n"); ++#endif ++} ++ + void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) + { + trace_qxl_spice_reset_image_cache(qxl->id); +@@ -538,6 +576,7 @@ static const char *io_port_to_string(uint32_t io_port) + = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", + [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", + [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", ++ [QXL_IO_MONITORS_CONFIG_ASYNC] = "QXL_IO_MONITORS_CONFIG_ASYNC", + }; + return io_port_to_string[io_port]; + } +@@ -819,6 +858,7 @@ static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) + case QXL_IO_DESTROY_PRIMARY_ASYNC: + case QXL_IO_UPDATE_AREA_ASYNC: + case QXL_IO_FLUSH_SURFACES_ASYNC: ++ case QXL_IO_MONITORS_CONFIG_ASYNC: + break; + case QXL_IO_CREATE_PRIMARY_ASYNC: + qxl_create_guest_primary_complete(qxl); +@@ -894,6 +934,8 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) + case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA: + qxl_render_update_area_done(qxl, cookie); + break; ++ case QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG: ++ break; + default: + fprintf(stderr, "qxl: %s: unexpected cookie type %d\n", + __func__, cookie->type); +@@ -1315,6 +1357,13 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, + return; + } + ++ if (d->revision <= QXL_REVISION_STABLE_V10 && ++ io_port >= QXL_IO_FLUSH_SURFACES_ASYNC) { ++ qxl_set_guest_bug(d, "unsupported io %d for revision %d\n", ++ io_port, d->revision); ++ return; ++ } ++ + switch (io_port) { + case QXL_IO_RESET: + case QXL_IO_SET_MODE: +@@ -1334,7 +1383,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, + io_port, io_port_to_string(io_port)); + /* be nice to buggy guest drivers */ + if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && +- io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) { ++ io_port < QXL_IO_RANGE_SIZE) { + qxl_send_events(d, QXL_INTERRUPT_IO_CMD); + } + return; +@@ -1362,6 +1411,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, + io_port = QXL_IO_DESTROY_ALL_SURFACES; + goto async_common; + case QXL_IO_FLUSH_SURFACES_ASYNC: ++ case QXL_IO_MONITORS_CONFIG_ASYNC: + async_common: + async = QXL_ASYNC; + qemu_mutex_lock(&d->async_lock); +@@ -1503,6 +1553,9 @@ async_common: + d->mode = QXL_MODE_UNDEFINED; + qxl_spice_destroy_surfaces(d, async); + break; ++ case QXL_IO_MONITORS_CONFIG_ASYNC: ++ qxl_spice_monitors_config_async(d, 0); ++ break; + default: + qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port); + } +@@ -1798,9 +1851,17 @@ static int qxl_init_common(PCIQXLDevice *qxl) + io_size = 16; + break; + case 3: /* qxl-3 */ +- pci_device_rev = QXL_DEFAULT_REVISION; ++ pci_device_rev = QXL_REVISION_STABLE_V10; ++ io_size = 32; /* PCI region size must be pow2 */ ++ break; ++/* 0x000b01 == 0.11.1 */ ++#if SPICE_SERVER_VERSION >= 0x000b01 && \ ++ defined(CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC) ++ case 4: /* qxl-4 */ ++ pci_device_rev = QXL_REVISION_STABLE_V12; + io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); + break; ++#endif + default: + error_report("Invalid revision %d for qxl device (max %d)", + qxl->revision, QXL_DEFAULT_REVISION); +@@ -1999,7 +2060,9 @@ static int qxl_post_load(void *opaque, int version) + } + qxl_spice_loadvm_commands(d, cmds, out); + g_free(cmds); +- ++ if (d->guest_monitors_config) { ++ qxl_spice_monitors_config_async(d, 1); ++ } + break; + case QXL_MODE_COMPAT: + /* note: no need to call qxl_create_memslots, qxl_set_mode +@@ -2012,6 +2075,14 @@ static int qxl_post_load(void *opaque, int version) + + #define QXL_SAVE_VERSION 21 + ++static bool qxl_monitors_config_needed(void *opaque) ++{ ++ PCIQXLDevice *qxl = opaque; ++ ++ return qxl->guest_monitors_config != 0; ++} ++ ++ + static VMStateDescription qxl_memslot = { + .name = "qxl-memslot", + .version_id = QXL_SAVE_VERSION, +@@ -2042,6 +2113,16 @@ static VMStateDescription qxl_surface = { + } + }; + ++static VMStateDescription qxl_vmstate_monitors_config = { ++ .name = "qxl/monitors-config", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice), ++ VMSTATE_END_OF_LIST() ++ }, ++}; ++ + static VMStateDescription qxl_vmstate = { + .name = "qxl", + .version_id = QXL_SAVE_VERSION, +@@ -2049,7 +2130,7 @@ static VMStateDescription qxl_vmstate = { + .pre_save = qxl_pre_save, + .pre_load = qxl_pre_load, + .post_load = qxl_post_load, +- .fields = (VMStateField []) { ++ .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci, PCIQXLDevice), + VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState), + VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice), +@@ -2068,6 +2149,14 @@ static VMStateDescription qxl_vmstate = { + VMSTATE_UINT64(guest_cursor, PCIQXLDevice), + VMSTATE_END_OF_LIST() + }, ++ .subsections = (VMStateSubsection[]) { ++ { ++ .vmsd = &qxl_vmstate_monitors_config, ++ .needed = qxl_monitors_config_needed, ++ }, { ++ /* empty */ ++ } ++ } + }; + + static Property qxl_properties[] = { +diff --git a/hw/qxl.h b/hw/qxl.h +index 172baf6..9cfedb7 100644 +--- a/hw/qxl.h ++++ b/hw/qxl.h +@@ -71,6 +71,8 @@ typedef struct PCIQXLDevice { + } guest_surfaces; + QXLPHYSICAL guest_cursor; + ++ QXLPHYSICAL guest_monitors_config; ++ + QemuMutex track_lock; + + /* thread signaling */ +@@ -128,7 +130,12 @@ typedef struct PCIQXLDevice { + } \ + } while (0) + ++#if 0 ++/* spice-server 0.12 is still in development */ ++#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V12 ++#else + #define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10 ++#endif + + /* qxl.c */ + void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); +diff --git a/trace-events b/trace-events +index 0de70d9..a58b0b7 100644 +--- a/trace-events ++++ b/trace-events +@@ -967,6 +967,7 @@ qxl_spice_destroy_surfaces(int qid, int async) "%d async=%d" + qxl_spice_destroy_surface_wait_complete(int qid, uint32_t id) "%d sid=%d" + qxl_spice_destroy_surface_wait(int qid, uint32_t id, int async) "%d sid=%d async=%d" + qxl_spice_flush_surfaces_async(int qid, uint32_t surface_count, uint32_t num_free_res) "%d s#=%d, res#=%d" ++qxl_spice_monitors_config(int id) "%d" + qxl_spice_loadvm_commands(int qid, void *ext, uint32_t count) "%d ext=%p count=%d" + qxl_spice_oom(int qid) "%d" + qxl_spice_reset_cursor(int qid) "%d" +diff --git a/ui/spice-display.h b/ui/spice-display.h +index 672d65e..bcff114 100644 +--- a/ui/spice-display.h ++++ b/ui/spice-display.h +@@ -51,6 +51,7 @@ typedef enum qxl_async_io { + enum { + QXL_COOKIE_TYPE_IO, + QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, ++ QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, + }; + + typedef struct QXLCookie { +-- +1.7.12 + diff --git a/0211-configure-print-spice-protocol-and-spice-server-vers.patch b/0211-configure-print-spice-protocol-and-spice-server-vers.patch new file mode 100644 index 0000000..711256c --- /dev/null +++ b/0211-configure-print-spice-protocol-and-spice-server-vers.patch @@ -0,0 +1,37 @@ +From 1d2790682dfac06c2358ac99f5e0bad6a065702d Mon Sep 17 00:00:00 2001 +From: Alon Levy +Date: Wed, 22 Aug 2012 11:16:26 +0300 +Subject: [PATCH 211/215] configure: print spice-protocol and spice-server + versions + +Signed-off-by: Alon Levy +Signed-off-by: Gerd Hoffmann +--- + configure | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/configure b/configure +index c57d1c1..25c406f 100755 +--- a/configure ++++ b/configure +@@ -2711,6 +2711,8 @@ EOF + spice="yes" + libs_softmmu="$libs_softmmu $spice_libs" + QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags" ++ spice_protocol_version=$($pkg_config --modversion spice-protocol) ++ spice_server_version=$($pkg_config --modversion spice-server) + if $pkg_config --atleast-version=0.12.0 spice-protocol >/dev/null 2>&1; then + spice_qxl_io_monitors_config_async="yes" + fi +@@ -3169,7 +3171,7 @@ echo "libcap-ng support $cap_ng" + echo "vhost-net support $vhost_net" + echo "Trace backend $trace_backend" + echo "Trace output file $trace_file-" +-echo "spice support $spice" ++echo "spice support $spice ($spice_protocol_version/$spice_server_version)" + echo "rbd support $rbd" + echo "xfsctl support $xfs" + echo "nss used $smartcard_nss" +-- +1.7.12 + diff --git a/0212-spice-make-number-of-surfaces-runtime-configurable.patch b/0212-spice-make-number-of-surfaces-runtime-configurable.patch new file mode 100644 index 0000000..0101a20 --- /dev/null +++ b/0212-spice-make-number-of-surfaces-runtime-configurable.patch @@ -0,0 +1,201 @@ +From 72e437d9b775cb92f93c3acd0109239f1bb3e6e2 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 4 Sep 2012 11:39:41 +0200 +Subject: [PATCH 212/215] spice: make number of surfaces runtime-configurable. + +Signed-off-by: Gerd Hoffmann +--- + hw/qxl.c | 31 +++++++++++++++++-------------- + hw/qxl.h | 3 +-- + ui/spice-display.c | 5 ++++- + ui/spice-display.h | 3 +-- + 4 files changed, 23 insertions(+), 19 deletions(-) + +diff --git a/hw/qxl.c b/hw/qxl.c +index adf17fd..8725f67 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -236,7 +236,8 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) + { + trace_qxl_spice_destroy_surfaces_complete(qxl->id); + qemu_mutex_lock(&qxl->track_lock); +- memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds)); ++ memset(qxl->guest_surfaces.cmds, 0, ++ sizeof(qxl->guest_surfaces.cmds) * qxl->ssd.num_surfaces); + qxl->guest_surfaces.count = 0; + qemu_mutex_unlock(&qxl->track_lock); + } +@@ -345,7 +346,7 @@ static void init_qxl_rom(PCIQXLDevice *d) + rom->slot_id_bits = MEMSLOT_SLOT_BITS; + rom->slots_start = 1; + rom->slots_end = NUM_MEMSLOTS - 1; +- rom->n_surfaces = cpu_to_le32(NUM_SURFACES); ++ rom->n_surfaces = cpu_to_le32(d->ssd.num_surfaces); + + for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) { + fb = qxl_modes[i].y_res * qxl_modes[i].stride; +@@ -449,9 +450,9 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) + } + uint32_t id = le32_to_cpu(cmd->surface_id); + +- if (id >= NUM_SURFACES) { ++ if (id >= qxl->ssd.num_surfaces) { + qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id, +- NUM_SURFACES); ++ qxl->ssd.num_surfaces); + return 1; + } + qemu_mutex_lock(&qxl->track_lock); +@@ -527,7 +528,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) + info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; + info->internal_groupslot_id = 0; + info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS; +- info->n_surfaces = NUM_SURFACES; ++ info->n_surfaces = qxl->ssd.num_surfaces; + } + + static const char *qxl_mode_to_string(int mode) +@@ -1436,7 +1437,7 @@ async_common: + QXLCookie *cookie = NULL; + QXLRect update = d->ram->update_area; + +- if (d->ram->update_surface > NUM_SURFACES) { ++ if (d->ram->update_surface > d->ssd.num_surfaces) { + qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n", + d->ram->update_surface); + return; +@@ -1529,7 +1530,7 @@ async_common: + } + break; + case QXL_IO_DESTROY_SURFACE_WAIT: +- if (val >= NUM_SURFACES) { ++ if (val >= d->ssd.num_surfaces) { + qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):" + "%" PRIu64 " >= NUM_SURFACES", async, val); + goto cancel_async; +@@ -1707,7 +1708,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) + vram_start = (intptr_t)memory_region_get_ram_ptr(&qxl->vram_bar); + + /* dirty the off-screen surfaces */ +- for (i = 0; i < NUM_SURFACES; i++) { ++ for (i = 0; i < qxl->ssd.num_surfaces; i++) { + QXLSurfaceCmd *cmd; + intptr_t surface_offset; + int surface_size; +@@ -1835,7 +1836,6 @@ static int qxl_init_common(PCIQXLDevice *qxl) + qxl->mode = QXL_MODE_UNDEFINED; + qxl->generation = 1; + qxl->num_memslots = NUM_MEMSLOTS; +- qxl->num_surfaces = NUM_SURFACES; + qemu_mutex_init(&qxl->track_lock); + qemu_mutex_init(&qxl->async_lock); + qxl->current_async = QXL_UNDEFINED_IO; +@@ -1877,6 +1877,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) + init_qxl_rom(qxl); + init_qxl_ram(qxl); + ++ qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces); + memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size); + vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); + memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar, +@@ -2042,8 +2043,8 @@ static int qxl_post_load(void *opaque, int version) + qxl_create_guest_primary(d, 1, QXL_SYNC); + + /* replay surface-create and cursor-set commands */ +- cmds = g_malloc0(sizeof(QXLCommandExt) * (NUM_SURFACES + 1)); +- for (in = 0, out = 0; in < NUM_SURFACES; in++) { ++ cmds = g_malloc0(sizeof(QXLCommandExt) * (d->ssd.num_surfaces + 1)); ++ for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) { + if (d->guest_surfaces.cmds[in] == 0) { + continue; + } +@@ -2143,9 +2144,10 @@ static VMStateDescription qxl_vmstate = { + qxl_memslot, struct guest_slots), + VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0, + qxl_surface, QXLSurfaceCreate), +- VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice), +- VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0, +- vmstate_info_uint64, uint64_t), ++ VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice), ++ VMSTATE_VARRAY_INT32(guest_surfaces.cmds, PCIQXLDevice, ++ ssd.num_surfaces, 0, ++ vmstate_info_uint64, uint64_t), + VMSTATE_UINT64(guest_cursor, PCIQXLDevice), + VMSTATE_END_OF_LIST() + }, +@@ -2173,6 +2175,7 @@ static Property qxl_properties[] = { + DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1), + DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1), + DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16), ++ DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024), + DEFINE_PROP_END_OF_LIST(), + }; + +diff --git a/hw/qxl.h b/hw/qxl.h +index 9cfedb7..5553824 100644 +--- a/hw/qxl.h ++++ b/hw/qxl.h +@@ -40,7 +40,6 @@ typedef struct PCIQXLDevice { + uint32_t revision; + + int32_t num_memslots; +- int32_t num_surfaces; + + uint32_t current_async; + QemuMutex async_lock; +@@ -65,7 +64,7 @@ typedef struct PCIQXLDevice { + } guest_primary; + + struct surfaces { +- QXLPHYSICAL cmds[NUM_SURFACES]; ++ QXLPHYSICAL *cmds; + uint32_t count; + uint32_t max; + } guest_surfaces; +diff --git a/ui/spice-display.c b/ui/spice-display.c +index 1c31418..99bc665 100644 +--- a/ui/spice-display.c ++++ b/ui/spice-display.c +@@ -317,6 +317,9 @@ void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds) + qemu_mutex_init(&ssd->lock); + ssd->mouse_x = -1; + ssd->mouse_y = -1; ++ if (ssd->num_surfaces == 0) { ++ ssd->num_surfaces = 1024; ++ } + ssd->bufsize = (16 * 1024 * 1024); + ssd->buf = g_malloc(ssd->bufsize); + } +@@ -427,7 +430,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) + info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; + info->internal_groupslot_id = 0; + info->qxl_ram_size = ssd->bufsize; +- info->n_surfaces = NUM_SURFACES; ++ info->n_surfaces = ssd->num_surfaces; + } + + static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) +diff --git a/ui/spice-display.h b/ui/spice-display.h +index bcff114..512ab78 100644 +--- a/ui/spice-display.h ++++ b/ui/spice-display.h +@@ -32,8 +32,6 @@ + #define MEMSLOT_GROUP_GUEST 1 + #define NUM_MEMSLOTS_GROUPS 2 + +-#define NUM_SURFACES 1024 +- + /* + * Internal enum to differenciate between options for + * io calls that have a sync (old) version and an _async (new) +@@ -80,6 +78,7 @@ struct SimpleSpiceDisplay { + QXLInstance qxl; + uint32_t unique; + QemuPfConv *conv; ++ int32_t num_surfaces; + + QXLRect dirty; + int notify; +-- +1.7.12 + diff --git a/0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch b/0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch new file mode 100644 index 0000000..4427f78 --- /dev/null +++ b/0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch @@ -0,0 +1,65 @@ +From 44eab2c48c8b090fff8b7ded39b40dae6c6ce003 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=B8ren=20Sandmann=20Pedersen?= +Date: Tue, 4 Sep 2012 10:14:48 -0400 +Subject: [PATCH 213/215] qxl: Add set_client_capabilities() interface to + QXLInterface + +This new interface lets spice server inform the guest whether + +(a) a client is connected +(b) what capabilities the client has + +There is a fixed number (464) of bits reserved for capabilities, and +when the capabilities bits change, the QXL_INTERRUPT_CLIENT interrupt +is generated. + +Signed-off-by: Soren Sandmann +Signed-off-by: Gerd Hoffmann +--- + hw/qxl.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/hw/qxl.c b/hw/qxl.c +index 8725f67..2aa5848 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -944,6 +944,26 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) + } + } + ++#if SPICE_SERVER_VERSION >= 0x000b04 ++ ++/* called from spice server thread context only */ ++static void interface_set_client_capabilities(QXLInstance *sin, ++ uint8_t client_present, ++ uint8_t caps[58]) ++{ ++ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); ++ ++ qxl->shadow_rom.client_present = client_present; ++ memcpy(qxl->shadow_rom.client_capabilities, caps, sizeof(caps)); ++ qxl->rom->client_present = client_present; ++ memcpy(qxl->rom->client_capabilities, caps, sizeof(caps)); ++ qxl_rom_set_dirty(qxl); ++ ++ qxl_send_events(qxl, QXL_INTERRUPT_CLIENT); ++} ++ ++#endif ++ + static const QXLInterface qxl_interface = { + .base.type = SPICE_INTERFACE_QXL, + .base.description = "qxl gpu", +@@ -965,6 +985,9 @@ static const QXLInterface qxl_interface = { + .flush_resources = interface_flush_resources, + .async_complete = interface_async_complete, + .update_area_complete = interface_update_area_complete, ++#if SPICE_SERVER_VERSION >= 0x000b04 ++ .set_client_capabilities = interface_set_client_capabilities, ++#endif + }; + + static void qxl_enter_vga_mode(PCIQXLDevice *d) +-- +1.7.12 + diff --git a/0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch b/0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch new file mode 100644 index 0000000..cfae0f3 --- /dev/null +++ b/0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch @@ -0,0 +1,32 @@ +From 666f88952fe1bcf84ba857f417a5d25f86bbf38b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?S=C3=B8ren=20Sandmann=20Pedersen?= +Date: Tue, 4 Sep 2012 10:14:49 -0400 +Subject: [PATCH 214/215] Remove #ifdef QXL_COMMAND_FLAG_COMPAT_16BPP + +We require spice >= 0.8 now, so this flag is always present. + +Signed-off-by: Soren Sandmann +Signed-off-by: Gerd Hoffmann +--- + hw/qxl.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/hw/qxl.c b/hw/qxl.c +index 2aa5848..b726c19 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -1359,11 +1359,9 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) + + d->mode = QXL_MODE_COMPAT; + d->cmdflags = QXL_COMMAND_FLAG_COMPAT; +-#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */ + if (mode->bits == 16) { + d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP; + } +-#endif + d->shadow_rom.mode = cpu_to_le32(modenr); + d->rom->mode = cpu_to_le32(modenr); + qxl_rom_set_dirty(d); +-- +1.7.12 + diff --git a/0215-qxl-dont-update-invalid-area.patch b/0215-qxl-dont-update-invalid-area.patch new file mode 100644 index 0000000..8739272 --- /dev/null +++ b/0215-qxl-dont-update-invalid-area.patch @@ -0,0 +1,41 @@ +From 6f1652c4412ab60c7f456100143c519d124a895c Mon Sep 17 00:00:00 2001 +From: Dunrong Huang +Date: Fri, 31 Aug 2012 00:44:44 +0800 +Subject: [PATCH 215/215] qxl: dont update invalid area + +This patch fixes the following error: + +$ ~/usr/bin/qemu-system-x86_64 -enable-kvm -m 1024 -spice port=5900,disable-ticketing -vga qxl -cdrom ~/Images/linuxmint-13-mate-dvd-32bit.iso +(/home/mathslinux/usr/bin/qemu-system-x86_64:10068): SpiceWorker-CRITICAL **: red_worker.c:4599:red_update_area: condition `area->left >= 0 && area->top >= 0 && area->left < area->right && area->top < area->bottom' failed +Aborted + +spice server terminates QEMU process if we pass invalid area to it, +so dont update those invalid areas. + +Signed-off-by: Dunrong Huang +Signed-off-by: Gerd Hoffmann +--- + hw/qxl.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/hw/qxl.c b/hw/qxl.c +index b726c19..045432e 100644 +--- a/hw/qxl.c ++++ b/hw/qxl.c +@@ -1470,6 +1470,13 @@ async_common: + return; + } + ++ if (update.left < 0 || update.top < 0 || update.left >= update.right || ++ update.top >= update.bottom) { ++ qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: " ++ "invalid area(%d,%d,%d,%d)\n", update.left, ++ update.right, update.top, update.bottom); ++ break; ++ } + if (async == QXL_ASYNC) { + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_UPDATE_AREA_ASYNC); +-- +1.7.12 + diff --git a/0301-usb-controllers-do-not-need-to-check-for-babble-them.patch b/0301-usb-controllers-do-not-need-to-check-for-babble-them.patch new file mode 100644 index 0000000..a1cfa08 --- /dev/null +++ b/0301-usb-controllers-do-not-need-to-check-for-babble-them.patch @@ -0,0 +1,56 @@ +From d69c3f589874de55e2eae03110a0c696485b8fa7 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 17 Aug 2012 11:39:16 +0200 +Subject: [PATCH 301/366] usb: controllers do not need to check for babble + themselves + +If an (emulated) usb-device tries to write more data to a packet then +its iov len, this will trigger an assert in usb_packet_copy(), and if +a driver somehow circumvents that check and writes more data to the +iov then there is space, we have a much bigger problem then not correctly +reporting babble to the guest. + +In practice babble will only happen with (real) redirected devices, and there +both the usb-host os and the qemu usb-device code already check for it. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 4 ---- + hw/usb/hcd-uhci.c | 5 ----- + 2 files changed, 9 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 017342b..9523247 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -1481,10 +1481,6 @@ static void ehci_execute_complete(EHCIQueue *q) + assert(0); + break; + } +- } 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_raise_irq(q->ehci, USBSTS_ERRINT); + } else { + // TODO check 4.12 for splits + +diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c +index b0db921..c7c8786 100644 +--- a/hw/usb/hcd-uhci.c ++++ b/hw/usb/hcd-uhci.c +@@ -729,11 +729,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ + *int_mask |= 0x01; + + if (pid == USB_TOKEN_IN) { +- if (len > max_len) { +- ret = USB_RET_BABBLE; +- goto out; +- } +- + if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { + *int_mask |= 0x02; + /* short packet: do not update QH */ +-- +1.7.12 + diff --git a/0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch b/0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch new file mode 100644 index 0000000..e40f57d --- /dev/null +++ b/0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch @@ -0,0 +1,35 @@ +From ae5b888748b713d018ca7b339262b32bf88ec1be Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 12:33:44 +0200 +Subject: [PATCH 302/366] usb-core: Don't set packet state to complete on a + nak + +This way the hcd can re-use the same packet to retry without needing +to re-init it. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/core.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/hw/usb/core.c b/hw/usb/core.c +index 2da38e7..be6d936 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -399,8 +399,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) + * otherwise packets can complete out of order! + */ + assert(!p->ep->pipeline); +- p->result = ret; +- usb_packet_set_state(p, USB_PACKET_COMPLETE); ++ if (ret != USB_RET_NAK) { ++ p->result = ret; ++ usb_packet_set_state(p, USB_PACKET_COMPLETE); ++ } + } + } else { + ret = USB_RET_ASYNC; +-- +1.7.12 + diff --git a/0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch b/0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch new file mode 100644 index 0000000..c3ed784 --- /dev/null +++ b/0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch @@ -0,0 +1,52 @@ +From d42de93ca42a6d3b5101ec3db474f52a87a07a63 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 28 Aug 2012 09:43:18 +0200 +Subject: [PATCH 303/366] usb-core: Add a usb_ep_find_packet_by_id() helper + function + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb.h | 2 ++ + hw/usb/core.c | 15 +++++++++++++++ + 2 files changed, 17 insertions(+) + +diff --git a/hw/usb.h b/hw/usb.h +index b8fceec..684e3f4 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -377,6 +377,8 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, + uint16_t raw); + int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); + void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); ++USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, ++ uint64_t id); + + void usb_attach(USBPort *port); + void usb_detach(USBPort *port); +diff --git a/hw/usb/core.c b/hw/usb/core.c +index be6d936..fe431d0 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -726,3 +726,18 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + uep->pipeline = enabled; + } ++ ++USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, ++ uint64_t id) ++{ ++ struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); ++ USBPacket *p; ++ ++ while ((p = QTAILQ_FIRST(&uep->queue)) != NULL) { ++ if (p->id == id) { ++ return p; ++ } ++ } ++ ++ return NULL; ++} +-- +1.7.12 + diff --git a/0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch b/0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch new file mode 100644 index 0000000..ffcd31a --- /dev/null +++ b/0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch @@ -0,0 +1,32 @@ +From d0c16c3cd8dc1f2dc5ca6dae0d09e5f066332531 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 12:48:49 +0200 +Subject: [PATCH 304/366] usb-core: Allow the first packet of a pipelined ep + to complete immediately + +This can happen with usb-redir live-migration when the packet gets re-queued +after the migration and the original queuing from the migration source side +has already finished. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb/core.c b/hw/usb/core.c +index fe431d0..b9f1f7a 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -398,7 +398,7 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) + * When pipelining is enabled usb-devices must always return async, + * otherwise packets can complete out of order! + */ +- assert(!p->ep->pipeline); ++ assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue)); + if (ret != USB_RET_NAK) { + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); +-- +1.7.12 + diff --git a/0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch b/0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch new file mode 100644 index 0000000..6d8c7e9 --- /dev/null +++ b/0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch @@ -0,0 +1,121 @@ +From eccc0da01324939a52a74c763ade93e4a38d7328 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 29 Aug 2012 10:12:52 +0200 +Subject: [PATCH 305/366] Revert "ehci: don't flush cache on doorbell rings." + +This reverts commit 9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0, which got +added to fix an issue where the real, underlying cause was not stopping +the ep queue on an error. + +Now that the underlying cause is fixed by the "usb: Halt ep queue and +cancel pending packets on a packet error" patch, the "don't flush" fix +is no longer needed. + +Not only is it not needed, it causes us to see cancellations (unlinks) +done by the Linux EHCI driver too late, which in combination with the new +usb-core packet-id generation where qtd addresses are used as ids, causes +duplicate ids for in flight packets. + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 35 ++++++----------------------------- + 1 file changed, 6 insertions(+), 29 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 9523247..e7c36f4 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -365,7 +365,6 @@ struct EHCIQueue { + uint32_t seen; + uint64_t ts; + int async; +- int revalidate; + + /* cached data from guest - needs to be flushed + * when guest removes an entry (doorbell, handshake sequence) +@@ -805,18 +804,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, + return NULL; + } + +-static void ehci_queues_tag_unused_async(EHCIState *ehci) +-{ +- EHCIQueue *q; +- +- QTAILQ_FOREACH(q, &ehci->aqueues, next) { +- if (!q->seen) { +- q->revalidate = 1; +- } +- } +-} +- +-static void ehci_queues_rip_unused(EHCIState *ehci, int async) ++static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) + { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; +@@ -828,7 +816,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async) + q->ts = ehci->last_run_ns; + continue; + } +- if (ehci->last_run_ns < q->ts + maxage) { ++ if (!flush && ehci->last_run_ns < q->ts + maxage) { + continue; + } + ehci_free_queue(q); +@@ -1684,7 +1672,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) + ehci_set_usbsts(ehci, USBSTS_REC); + } + +- ehci_queues_rip_unused(ehci, async); ++ ehci_queues_rip_unused(ehci, async, 0); + + /* Find the head of the list (4.9.1.1) */ + for(i = 0; i < MAX_QH; i++) { +@@ -1769,7 +1757,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + EHCIPacket *p; + uint32_t entry, devaddr; + EHCIQueue *q; +- EHCIqh qh; + + entry = ehci_get_fetch_addr(ehci, async); + q = ehci_find_queue_by_qh(ehci, entry, async); +@@ -1787,17 +1774,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + } + + get_dwords(ehci, NLPTR_GET(q->qhaddr), +- (uint32_t *) &qh, sizeof(EHCIqh) >> 2); +- if (q->revalidate && (q->qh.epchar != qh.epchar || +- q->qh.epcap != qh.epcap || +- q->qh.current_qtd != qh.current_qtd)) { +- ehci_free_queue(q); +- q = ehci_alloc_queue(ehci, entry, async); +- q->seen++; +- p = NULL; +- } +- q->qh = qh; +- q->revalidate = 0; ++ (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); + ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); + + devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); +@@ -2306,7 +2283,7 @@ static void ehci_advance_async_state(EHCIState *ehci) + */ + if (ehci->usbcmd & USBCMD_IAAD) { + /* Remove all unseen qhs from the async qhs queue */ +- ehci_queues_tag_unused_async(ehci); ++ ehci_queues_rip_unused(ehci, async, 1); + DPRINTF("ASYNC: doorbell request acknowledged\n"); + ehci->usbcmd &= ~USBCMD_IAAD; + ehci_raise_irq(ehci, USBSTS_IAA); +@@ -2359,7 +2336,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) + ehci_set_fetch_addr(ehci, async,entry); + ehci_set_state(ehci, async, EST_FETCHENTRY); + ehci_advance_state(ehci, async); +- ehci_queues_rip_unused(ehci, async); ++ ehci_queues_rip_unused(ehci, async, 0); + break; + + default: +-- +1.7.12 + diff --git a/0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch b/0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch new file mode 100644 index 0000000..7af3a27 --- /dev/null +++ b/0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch @@ -0,0 +1,84 @@ +From 1ee48073d05a8a0dfe08ad2853be125a87f176de Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 29 Aug 2012 10:37:37 +0200 +Subject: [PATCH 306/366] ehci: Validate qh is not changed unexpectedly by the + guest + +-combine the qh check with the check for devaddr changes +-also ensure that p gets set to NULL when the queue gets cancelled on + devaddr change, which was not done properly before this patch + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 39 ++++++++++++++++++++++++++++----------- + 1 file changed, 28 insertions(+), 11 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index e7c36f4..35eb441 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -780,6 +780,14 @@ static void ehci_cancel_queue(EHCIQueue *q) + } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); + } + ++static void ehci_reset_queue(EHCIQueue *q) ++{ ++ trace_usb_ehci_queue_action(q, "reset"); ++ ehci_cancel_queue(q); ++ q->dev = NULL; ++ q->qtdaddr = 0; ++} ++ + static void ehci_free_queue(EHCIQueue *q) + { + EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; +@@ -1755,8 +1763,9 @@ out: + static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + { + EHCIPacket *p; +- uint32_t entry, devaddr; ++ uint32_t entry, devaddr, endp; + EHCIQueue *q; ++ EHCIqh qh; + + entry = ehci_get_fetch_addr(ehci, async); + q = ehci_find_queue_by_qh(ehci, entry, async); +@@ -1774,17 +1783,25 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + } + + get_dwords(ehci, NLPTR_GET(q->qhaddr), +- (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); +- ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); ++ (uint32_t *) &qh, sizeof(EHCIqh) >> 2); ++ ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh); ++ ++ /* ++ * The overlay area of the qh should never be changed by the guest, ++ * except when idle, in which case the reset is a nop. ++ */ ++ devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR); ++ endp = get_field(qh.epchar, QH_EPCHAR_EP); ++ if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) || ++ (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || ++ (memcmp(&qh.current_qtd, &q->qh.current_qtd, ++ 9 * sizeof(uint32_t)) != 0) || ++ (q->dev != NULL && q->dev->addr != devaddr)) { ++ ehci_reset_queue(q); ++ p = NULL; ++ } ++ q->qh = qh; + +- devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); +- if (q->dev != NULL && q->dev->addr != devaddr) { +- if (!QTAILQ_EMPTY(&q->packets)) { +- /* should not happen (guest bug) */ +- ehci_cancel_queue(q); +- } +- q->dev = NULL; +- } + if (q->dev == NULL) { + q->dev = ehci_find_device(q->ehci, devaddr); + } +-- +1.7.12 + diff --git a/0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch b/0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch new file mode 100644 index 0000000..edfb6e0 --- /dev/null +++ b/0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch @@ -0,0 +1,33 @@ +From faeefe2a1fdb5895bffb1380b318f3cc396cb057 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 28 Aug 2012 16:21:12 +0200 +Subject: [PATCH 307/366] ehci: Update copyright headers to reflect recent + work + +Update copyright headers to reflect all the work Gerd and I have been doing +on the EHCI emulation. + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 35eb441..78a248f 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -2,6 +2,11 @@ + * QEMU USB EHCI Emulation + * + * Copyright(c) 2008 Emutex Ltd. (address@hidden) ++ * Copyright(c) 2011-2012 Red Hat, Inc. ++ * ++ * Red Hat Authors: ++ * Gerd Hoffmann ++ * Hans de Goede + * + * EHCI project was started by Mark Burkley, with contributions by + * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, +-- +1.7.12 + diff --git a/0308-ehci-Properly-cleanup-packets-on-cancel.patch b/0308-ehci-Properly-cleanup-packets-on-cancel.patch new file mode 100644 index 0000000..03b9ce4 --- /dev/null +++ b/0308-ehci-Properly-cleanup-packets-on-cancel.patch @@ -0,0 +1,26 @@ +From 95a792a46e8daae4bfb0997f6340bcd8dddea06c Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 30 Aug 2012 15:00:33 +0200 +Subject: [PATCH 308/366] ehci: Properly cleanup packets on cancel + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 78a248f..4fe85c8 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -747,6 +747,8 @@ static void ehci_free_packet(EHCIPacket *p) + trace_usb_ehci_packet_action(p->queue, p, "free"); + if (p->async == EHCI_ASYNC_INFLIGHT) { + usb_cancel_packet(&p->packet); ++ usb_packet_unmap(&p->packet, &p->sgl); ++ qemu_sglist_destroy(&p->sgl); + } + QTAILQ_REMOVE(&p->queue->packets, p, next); + usb_packet_cleanup(&p->packet); +-- +1.7.12 + diff --git a/0309-ehci-Properly-report-completed-but-not-yet-processed.patch b/0309-ehci-Properly-report-completed-but-not-yet-processed.patch new file mode 100644 index 0000000..445b322 --- /dev/null +++ b/0309-ehci-Properly-report-completed-but-not-yet-processed.patch @@ -0,0 +1,49 @@ +From 584606864261092041b46806030e4889b747ad41 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 30 Aug 2012 15:18:24 +0200 +Subject: [PATCH 309/366] ehci: Properly report completed but not yet + processed packets to the guest + +Reported packets which have completed before being cancelled as such to the +host. Note that the new code path this patch adds is untested since it I've +been unable to actually trigger the race which needs this code path. + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 4fe85c8..0a6c9ef 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -489,6 +489,9 @@ static const char *ehci_mmio_names[] = { + [CONFIGFLAG] = "CONFIGFLAG", + }; + ++static int ehci_state_executing(EHCIQueue *q); ++static int ehci_state_writeback(EHCIQueue *q); ++ + static const char *nr2str(const char **n, size_t len, uint32_t nr) + { + if (nr < len && n[nr] != NULL) { +@@ -750,6 +753,16 @@ static void ehci_free_packet(EHCIPacket *p) + usb_packet_unmap(&p->packet, &p->sgl); + qemu_sglist_destroy(&p->sgl); + } ++ if (p->async == EHCI_ASYNC_FINISHED) { ++ int state = ehci_get_state(p->queue->ehci, p->queue->async); ++ /* This is a normal, but rare condition (cancel racing completion) */ ++ fprintf(stderr, "EHCI: Warning packet completed but not processed\n"); ++ ehci_state_executing(p->queue); ++ ehci_state_writeback(p->queue); ++ ehci_set_state(p->queue->ehci, p->queue->async, state); ++ /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */ ++ return; ++ } + QTAILQ_REMOVE(&p->queue->packets, p, next); + usb_packet_cleanup(&p->packet); + g_free(p); +-- +1.7.12 + diff --git a/0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch b/0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch new file mode 100644 index 0000000..bcb596c --- /dev/null +++ b/0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch @@ -0,0 +1,47 @@ +From 6f009493144d09e417997caa13b62a38798dc206 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 31 Aug 2012 10:31:54 +0200 +Subject: [PATCH 310/366] ehci: check for EHCI_ASYNC_FINISHED first in + ehci_free_packet + +Otherwise we'll see the packet free twice in the trace log even though +it actually happens only once. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 0a6c9ef..23221d0 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -747,12 +747,6 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) + + static void ehci_free_packet(EHCIPacket *p) + { +- trace_usb_ehci_packet_action(p->queue, p, "free"); +- if (p->async == EHCI_ASYNC_INFLIGHT) { +- usb_cancel_packet(&p->packet); +- usb_packet_unmap(&p->packet, &p->sgl); +- qemu_sglist_destroy(&p->sgl); +- } + if (p->async == EHCI_ASYNC_FINISHED) { + int state = ehci_get_state(p->queue->ehci, p->queue->async); + /* This is a normal, but rare condition (cancel racing completion) */ +@@ -763,6 +757,12 @@ static void ehci_free_packet(EHCIPacket *p) + /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */ + return; + } ++ trace_usb_ehci_packet_action(p->queue, p, "free"); ++ if (p->async == EHCI_ASYNC_INFLIGHT) { ++ usb_cancel_packet(&p->packet); ++ usb_packet_unmap(&p->packet, &p->sgl); ++ qemu_sglist_destroy(&p->sgl); ++ } + QTAILQ_REMOVE(&p->queue->packets, p, next); + usb_packet_cleanup(&p->packet); + g_free(p); +-- +1.7.12 + diff --git a/0311-ehci-trace-guest-bugs.patch b/0311-ehci-trace-guest-bugs.patch new file mode 100644 index 0000000..0abd96e --- /dev/null +++ b/0311-ehci-trace-guest-bugs.patch @@ -0,0 +1,106 @@ +From 044dcae94243d03739c309935a68b646424a4d4e Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 31 Aug 2012 10:44:21 +0200 +Subject: [PATCH 311/366] ehci: trace guest bugs + +make qemu_queue_{cancel,reset} return the number of packets released, +so the caller can figure whenever there have been active packets even +though there shouldn't have been any. Add tracepoint to log this. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 26 ++++++++++++++++++++------ + trace-events | 1 + + 2 files changed, 21 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 23221d0..4564615 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -716,6 +716,12 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr, + (bool)(sitd->results & SITD_RESULTS_ACTIVE)); + } + ++static void ehci_trace_guest_bug(EHCIState *s, const char *message) ++{ ++ trace_usb_ehci_guest_bug(message); ++ fprintf(stderr, "ehci warning: %s\n", message); ++} ++ + static inline bool ehci_enabled(EHCIState *s) + { + return s->usbcmd & USBCMD_RUNSTOP; +@@ -785,27 +791,33 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) + return q; + } + +-static void ehci_cancel_queue(EHCIQueue *q) ++static int ehci_cancel_queue(EHCIQueue *q) + { + EHCIPacket *p; ++ int packets = 0; + + p = QTAILQ_FIRST(&q->packets); + if (p == NULL) { +- return; ++ return 0; + } + + trace_usb_ehci_queue_action(q, "cancel"); + do { + ehci_free_packet(p); ++ packets++; + } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); ++ return packets; + } + +-static void ehci_reset_queue(EHCIQueue *q) ++static int ehci_reset_queue(EHCIQueue *q) + { ++ int packets; ++ + trace_usb_ehci_queue_action(q, "reset"); +- ehci_cancel_queue(q); ++ packets = ehci_cancel_queue(q); + q->dev = NULL; + q->qtdaddr = 0; ++ return packets; + } + + static void ehci_free_queue(EHCIQueue *q) +@@ -1817,7 +1829,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + (memcmp(&qh.current_qtd, &q->qh.current_qtd, + 9 * sizeof(uint32_t)) != 0) || + (q->dev != NULL && q->dev->addr != devaddr)) { +- ehci_reset_queue(q); ++ if (ehci_reset_queue(q) > 0) { ++ ehci_trace_guest_bug(ehci, "guest updated active QH"); ++ } + p = NULL; + } + q->qh = qh; +@@ -1979,8 +1993,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q) + (!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) || + (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) || + p->qtd.bufptr[0] != qtd.bufptr[0]) { +- /* guest bug: guest updated active QH or qTD underneath us */ + ehci_cancel_queue(q); ++ ehci_trace_guest_bug(q->ehci, "guest updated active QH or qTD"); + p = NULL; + } else { + p->qtd = qtd; +diff --git a/trace-events b/trace-events +index 8fcbc50..5112a47 100644 +--- a/trace-events ++++ b/trace-events +@@ -263,6 +263,7 @@ usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t l + 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_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" ++usb_ehci_guest_bug(const char *reason) "%s" + + # hw/usb/hcd-uhci.c + usb_uhci_reset(void) "=== RESET ===" +-- +1.7.12 + diff --git a/0312-ehci-add-doorbell-trace-events.patch b/0312-ehci-add-doorbell-trace-events.patch new file mode 100644 index 0000000..817132d --- /dev/null +++ b/0312-ehci-add-doorbell-trace-events.patch @@ -0,0 +1,48 @@ +From 77e3a7e6bf15a85040101e7c0990dd82abdc9e9c Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 31 Aug 2012 12:41:43 +0200 +Subject: [PATCH 312/366] ehci: add doorbell trace events + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 3 ++- + trace-events | 2 ++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 4564615..398f5e0 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -1241,6 +1241,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) + */ + s->async_stepdown = 0; + qemu_bh_schedule(s->async_bh); ++ trace_usb_ehci_doorbell_ring(); + } + + if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) != +@@ -2335,7 +2336,7 @@ static void ehci_advance_async_state(EHCIState *ehci) + if (ehci->usbcmd & USBCMD_IAAD) { + /* Remove all unseen qhs from the async qhs queue */ + ehci_queues_rip_unused(ehci, async, 1); +- DPRINTF("ASYNC: doorbell request acknowledged\n"); ++ trace_usb_ehci_doorbell_ack(); + ehci->usbcmd &= ~USBCMD_IAAD; + ehci_raise_irq(ehci, USBSTS_IAA); + } +diff --git a/trace-events b/trace-events +index 5112a47..10bc04e 100644 +--- a/trace-events ++++ b/trace-events +@@ -264,6 +264,8 @@ 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_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" + usb_ehci_guest_bug(const char *reason) "%s" ++usb_ehci_doorbell_ring(void) "" ++usb_ehci_doorbell_ack(void) "" + + # hw/usb/hcd-uhci.c + usb_uhci_reset(void) "=== RESET ===" +-- +1.7.12 + diff --git a/0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch b/0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch new file mode 100644 index 0000000..0587c98 --- /dev/null +++ b/0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch @@ -0,0 +1,86 @@ +From cbbfcc647e26708cdd78c8ddf28d99f8a1e1e6b0 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 10:22:16 +0200 +Subject: [PATCH 313/366] ehci: Add some additional ehci_trace_guest_bug() + calls + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 398f5e0..5a88268 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -820,12 +820,16 @@ static int ehci_reset_queue(EHCIQueue *q) + return packets; + } + +-static void ehci_free_queue(EHCIQueue *q) ++static void ehci_free_queue(EHCIQueue *q, const char *warn) + { + EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; ++ int cancelled; + + trace_usb_ehci_queue_action(q, "free"); +- ehci_cancel_queue(q); ++ cancelled = ehci_cancel_queue(q); ++ if (warn && cancelled > 0) { ++ ehci_trace_guest_bug(q->ehci, warn); ++ } + QTAILQ_REMOVE(head, q, next); + g_free(q); + } +@@ -847,6 +851,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, + static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) + { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; ++ const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL; + uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; + EHCIQueue *q, *tmp; + +@@ -859,7 +864,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) + if (!flush && ehci->last_run_ns < q->ts + maxage) { + continue; + } +- ehci_free_queue(q); ++ ehci_free_queue(q, warn); + } + } + +@@ -872,17 +877,18 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) + if (q->dev != dev) { + continue; + } +- ehci_free_queue(q); ++ ehci_free_queue(q, NULL); + } + } + + static void ehci_queues_rip_all(EHCIState *ehci, int async) + { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; ++ const char *warn = async ? "guest stopped busy async schedule" : NULL; + EHCIQueue *q, *tmp; + + QTAILQ_FOREACH_SAFE(q, head, next, tmp) { +- ehci_free_queue(q); ++ ehci_free_queue(q, warn); + } + } + +@@ -1549,7 +1555,8 @@ static int ehci_execute(EHCIPacket *p, const char *action) + + p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; + if (p->tbytes > BUFF_SIZE) { +- fprintf(stderr, "Request for more bytes than allowed\n"); ++ ehci_trace_guest_bug(p->queue->ehci, ++ "guest requested more bytes than allowed"); + return USB_RET_PROCERR; + } + +-- +1.7.12 + diff --git a/0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch b/0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch new file mode 100644 index 0000000..3c82869 --- /dev/null +++ b/0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch @@ -0,0 +1,117 @@ +From 68851693ee59d3f17c14983902076a5ef49e2e80 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 11:01:13 +0200 +Subject: [PATCH 314/366] ehci: Fix memory leak in handling of NAK-ed packets + +Currently each time we try to execute a NAK-ed packet we redo +ehci_init_transfer, and usb_packet_map, re-allocing (without freeing) the +sg list every time. + +This patch fixes this, it does this by introducing another async state, so +that we also properly cleanup a NAK-ed packet on cancel. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 38 +++++++++++++++++++++++++++----------- + 1 file changed, 27 insertions(+), 11 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 5a88268..d87aca8 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -345,6 +345,7 @@ typedef struct EHCIState EHCIState; + + enum async_state { + EHCI_ASYNC_NONE = 0, ++ EHCI_ASYNC_INITIALIZED, + EHCI_ASYNC_INFLIGHT, + EHCI_ASYNC_FINISHED, + }; +@@ -764,6 +765,10 @@ static void ehci_free_packet(EHCIPacket *p) + return; + } + trace_usb_ehci_packet_action(p->queue, p, "free"); ++ if (p->async == EHCI_ASYNC_INITIALIZED) { ++ usb_packet_unmap(&p->packet, &p->sgl); ++ qemu_sglist_destroy(&p->sgl); ++ } + if (p->async == EHCI_ASYNC_INFLIGHT) { + usb_cancel_packet(&p->packet); + usb_packet_unmap(&p->packet, &p->sgl); +@@ -1485,8 +1490,8 @@ static void ehci_execute_complete(EHCIQueue *q) + + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); +- assert(p->async != EHCI_ASYNC_INFLIGHT); +- p->async = EHCI_ASYNC_NONE; ++ assert(p->async == EHCI_ASYNC_INITIALIZED || ++ p->async == EHCI_ASYNC_FINISHED); + + DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", + q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); +@@ -1531,6 +1536,7 @@ static void ehci_execute_complete(EHCIQueue *q) + ehci_finish_transfer(q, p->usb_status); + usb_packet_unmap(&p->packet, &p->sgl); + qemu_sglist_destroy(&p->sgl); ++ p->async = EHCI_ASYNC_NONE; + + q->qh.token ^= QTD_TOKEN_DTOGGLE; + q->qh.token &= ~QTD_TOKEN_ACTIVE; +@@ -1548,6 +1554,9 @@ static int ehci_execute(EHCIPacket *p, const char *action) + int ret; + int endp; + ++ assert(p->async == EHCI_ASYNC_NONE || ++ p->async == EHCI_ASYNC_INITIALIZED); ++ + if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { + fprintf(stderr, "Attempting to execute inactive qtd\n"); + return USB_RET_PROCERR; +@@ -1576,15 +1585,18 @@ static int ehci_execute(EHCIPacket *p, const char *action) + break; + } + +- if (ehci_init_transfer(p) != 0) { +- return USB_RET_PROCERR; +- } +- + endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); + ep = usb_ep_get(p->queue->dev, p->pid, endp); + +- usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr); +- usb_packet_map(&p->packet, &p->sgl); ++ if (p->async == EHCI_ASYNC_NONE) { ++ if (ehci_init_transfer(p) != 0) { ++ return USB_RET_PROCERR; ++ } ++ ++ usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr); ++ usb_packet_map(&p->packet, &p->sgl); ++ p->async = EHCI_ASYNC_INITIALIZED; ++ } + + trace_usb_ehci_packet_action(p->queue, p, action); + ret = usb_handle_packet(p->queue->dev, &p->packet); +@@ -2021,11 +2033,15 @@ static int ehci_state_fetchqtd(EHCIQueue *q) + } else if (p != NULL) { + switch (p->async) { + case EHCI_ASYNC_NONE: ++ /* Should never happen packet should at least be initialized */ ++ assert(0); ++ break; ++ case EHCI_ASYNC_INITIALIZED: + /* Previously nacked packet (likely interrupt ep) */ +- ehci_set_state(q->ehci, q->async, EST_EXECUTE); +- break; ++ ehci_set_state(q->ehci, q->async, EST_EXECUTE); ++ break; + case EHCI_ASYNC_INFLIGHT: +- /* Unfinyshed async handled packet, go horizontal */ ++ /* Unfinished async handled packet, go horizontal */ + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); + break; + case EHCI_ASYNC_FINISHED: +-- +1.7.12 + diff --git a/0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch b/0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch new file mode 100644 index 0000000..a52d3db --- /dev/null +++ b/0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch @@ -0,0 +1,54 @@ +From 1599d0b01712fb16c3ad291b653a31f768f7f5ef Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 11:35:58 +0200 +Subject: [PATCH 315/366] ehci: Handle USB_RET_PROCERR in ehci_fill_queue + +USB_RET_PROCERR can be triggered by the guest (by for example requesting more +then BUFFSIZE bytes), so don't assert on it. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index d87aca8..2534394 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -2076,7 +2076,7 @@ static int ehci_state_horizqh(EHCIQueue *q) + return again; + } + +-static void ehci_fill_queue(EHCIPacket *p) ++static int ehci_fill_queue(EHCIPacket *p) + { + EHCIQueue *q = p->queue; + EHCIqtd qtd = p->qtd; +@@ -2100,9 +2100,13 @@ static void ehci_fill_queue(EHCIPacket *p) + p->qtdaddr = qtdaddr; + p->qtd = qtd; + p->usb_status = ehci_execute(p, "queue"); ++ if (p->usb_status == USB_RET_PROCERR) { ++ break; ++ } + assert(p->usb_status == USB_RET_ASYNC); + p->async = EHCI_ASYNC_INFLIGHT; + } ++ return p->usb_status; + } + + static int ehci_state_execute(EHCIQueue *q) +@@ -2144,8 +2148,7 @@ static int ehci_state_execute(EHCIQueue *q) + trace_usb_ehci_packet_action(p->queue, p, "async"); + p->async = EHCI_ASYNC_INFLIGHT; + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); +- again = 1; +- ehci_fill_queue(p); ++ again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; + goto out; + } + +-- +1.7.12 + diff --git a/0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch b/0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch new file mode 100644 index 0000000..8bd379d --- /dev/null +++ b/0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch @@ -0,0 +1,35 @@ +From d17b1ad80cba3354b3eca5b8464bf7bb3f8e95c1 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 12:17:48 +0200 +Subject: [PATCH 316/366] ehci: Correct a comment in fetchqtd packet + processing + +Since my previous comment said "Should never happen", I tried changing the +next line to an assert(0), which did not go well, which as the new comments +explains is logical if you think about it for a moment. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 2534394..2f3e9c0 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -2045,7 +2045,10 @@ static int ehci_state_fetchqtd(EHCIQueue *q) + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); + break; + case EHCI_ASYNC_FINISHED: +- /* Should never happen, as this case is caught by fetchqh */ ++ /* ++ * We get here when advqueue moves to a packet which is already ++ * finished, which can happen with packets queued up by fill_queue ++ */ + ehci_set_state(q->ehci, q->async, EST_EXECUTING); + break; + } +-- +1.7.12 + diff --git a/0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch b/0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch new file mode 100644 index 0000000..01ce1e1 --- /dev/null +++ b/0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch @@ -0,0 +1,44 @@ +From a15db9c6d428a756f6ad4055334e476cd90b31f6 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 17 Aug 2012 17:27:08 +0200 +Subject: [PATCH 317/366] usb-redir: Never return USB_RET_NAK for async + handled packets + +USB_RET_NAK is not a valid response for async handled packets (and will +trigger an assert as such). + +Also drop the warning when receiving a status of cancelled for packets not +cancelled by qemu itself, this can happen when a device gets unredirected +by the usbredir-host while transfers are pending. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 1460515..9ba964e 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -1055,11 +1055,14 @@ static int usbredir_handle_status(USBRedirDevice *dev, + case usb_redir_stall: + return USB_RET_STALL; + case usb_redir_cancelled: +- WARNING("returning cancelled packet to HC?\n"); +- return USB_RET_NAK; ++ /* ++ * When the usbredir-host unredirects a device, it will report a status ++ * of cancelled for all pending packets, followed by a disconnect msg. ++ */ ++ return USB_RET_IOERROR; + case usb_redir_inval: + WARNING("got invalid param error from usb-host?\n"); +- return USB_RET_NAK; ++ return USB_RET_IOERROR; + case usb_redir_babble: + return USB_RET_BABBLE; + case usb_redir_ioerror: +-- +1.7.12 + diff --git a/0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch b/0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch new file mode 100644 index 0000000..4994684 --- /dev/null +++ b/0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch @@ -0,0 +1,181 @@ +From acf269a7bbed7c969eb9be02943f7e4b52196a8f Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 23 Aug 2012 16:37:19 +0200 +Subject: [PATCH 318/366] usb-redir: Don't delay handling of open events to a + bottom half + +There is no need for this, and doing so means that a backend trying to +write immediately after an open event will see qemu_chr_be_can_write +returning 0, which not all backends handle well as there is no wakeup +mechanism to detect when the frontend does become writable. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 100 +++++++++++++++++++++++++++++------------------------- + 1 file changed, 53 insertions(+), 47 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 9ba964e..8ac8637 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -79,8 +79,8 @@ struct USBRedirDevice { + /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ + const uint8_t *read_buf; + int read_buf_size; +- /* For async handling of open/close */ +- QEMUBH *open_close_bh; ++ /* For async handling of close */ ++ QEMUBH *chardev_close_bh; + /* To delay the usb attach in case of quick chardev close + open */ + QEMUTimer *attach_timer; + int64_t next_attach_time; +@@ -794,18 +794,11 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + * from within the USBDevice data / control packet callbacks and doing a + * usb_detach from within these callbacks is not a good idea. + * +- * So we use a bh handler to take care of close events. We also handle +- * open events from this callback to make sure that a close directly followed +- * by an open gets handled in the right order. ++ * So we use a bh handler to take care of close events. + */ +-static void usbredir_open_close_bh(void *opaque) ++static void usbredir_chardev_close_bh(void *opaque) + { + USBRedirDevice *dev = opaque; +- uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; +- char version[32]; +- +- strcpy(version, "qemu usb-redir guest "); +- pstrcat(version, sizeof(version), qemu_get_version()); + + usbredir_device_disconnect(dev); + +@@ -813,36 +806,47 @@ static void usbredir_open_close_bh(void *opaque) + usbredirparser_destroy(dev->parser); + dev->parser = NULL; + } ++} + +- if (dev->cs->opened) { +- dev->parser = qemu_oom_check(usbredirparser_create()); +- dev->parser->priv = dev; +- dev->parser->log_func = usbredir_log; +- dev->parser->read_func = usbredir_read; +- dev->parser->write_func = usbredir_write; +- dev->parser->hello_func = usbredir_hello; +- dev->parser->device_connect_func = usbredir_device_connect; +- dev->parser->device_disconnect_func = usbredir_device_disconnect; +- dev->parser->interface_info_func = usbredir_interface_info; +- dev->parser->ep_info_func = usbredir_ep_info; +- dev->parser->configuration_status_func = usbredir_configuration_status; +- dev->parser->alt_setting_status_func = usbredir_alt_setting_status; +- dev->parser->iso_stream_status_func = usbredir_iso_stream_status; +- dev->parser->interrupt_receiving_status_func = +- usbredir_interrupt_receiving_status; +- dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; +- dev->parser->control_packet_func = usbredir_control_packet; +- dev->parser->bulk_packet_func = usbredir_bulk_packet; +- dev->parser->iso_packet_func = usbredir_iso_packet; +- dev->parser->interrupt_packet_func = usbredir_interrupt_packet; +- dev->read_buf = NULL; +- dev->read_buf_size = 0; ++static void usbredir_chardev_open(USBRedirDevice *dev) ++{ ++ uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; ++ char version[32]; + +- usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); +- usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); +- usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); +- usbredirparser_do_write(dev->parser); +- } ++ /* Make sure any pending closes are handled (no-op if none pending) */ ++ usbredir_chardev_close_bh(dev); ++ qemu_bh_cancel(dev->chardev_close_bh); ++ ++ strcpy(version, "qemu usb-redir guest "); ++ pstrcat(version, sizeof(version), qemu_get_version()); ++ ++ dev->parser = qemu_oom_check(usbredirparser_create()); ++ dev->parser->priv = dev; ++ dev->parser->log_func = usbredir_log; ++ dev->parser->read_func = usbredir_read; ++ dev->parser->write_func = usbredir_write; ++ dev->parser->hello_func = usbredir_hello; ++ dev->parser->device_connect_func = usbredir_device_connect; ++ dev->parser->device_disconnect_func = usbredir_device_disconnect; ++ dev->parser->interface_info_func = usbredir_interface_info; ++ dev->parser->ep_info_func = usbredir_ep_info; ++ dev->parser->configuration_status_func = usbredir_configuration_status; ++ dev->parser->alt_setting_status_func = usbredir_alt_setting_status; ++ dev->parser->iso_stream_status_func = usbredir_iso_stream_status; ++ dev->parser->interrupt_receiving_status_func = ++ usbredir_interrupt_receiving_status; ++ dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; ++ dev->parser->control_packet_func = usbredir_control_packet; ++ dev->parser->bulk_packet_func = usbredir_bulk_packet; ++ dev->parser->iso_packet_func = usbredir_iso_packet; ++ dev->parser->interrupt_packet_func = usbredir_interrupt_packet; ++ dev->read_buf = NULL; ++ dev->read_buf_size = 0; ++ ++ usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); ++ usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); ++ usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); ++ usbredirparser_do_write(dev->parser); + } + + static void usbredir_do_attach(void *opaque) +@@ -866,13 +870,13 @@ static int usbredir_chardev_can_read(void *opaque) + { + USBRedirDevice *dev = opaque; + +- if (dev->parser) { +- /* usbredir_parser_do_read will consume *all* data we give it */ +- return 1024 * 1024; +- } else { +- /* usbredir_open_close_bh hasn't handled the open event yet */ ++ if (!dev->parser) { ++ WARNING("chardev_can_read called on non open chardev!\n"); + return 0; + } ++ ++ /* usbredir_parser_do_read will consume *all* data we give it */ ++ return 1024 * 1024; + } + + static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) +@@ -896,8 +900,10 @@ static void usbredir_chardev_event(void *opaque, int event) + + switch (event) { + case CHR_EVENT_OPENED: ++ usbredir_chardev_open(dev); ++ break; + case CHR_EVENT_CLOSED: +- qemu_bh_schedule(dev->open_close_bh); ++ qemu_bh_schedule(dev->chardev_close_bh); + break; + } + } +@@ -945,7 +951,7 @@ static int usbredir_initfn(USBDevice *udev) + } + } + +- dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); ++ dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + + QTAILQ_INIT(&dev->asyncq); +@@ -984,7 +990,7 @@ static void usbredir_handle_destroy(USBDevice *udev) + qemu_chr_fe_close(dev->cs); + qemu_chr_delete(dev->cs); + /* Note must be done after qemu_chr_close, as that causes a close event */ +- qemu_bh_delete(dev->open_close_bh); ++ qemu_bh_delete(dev->chardev_close_bh); + + qemu_del_timer(dev->attach_timer); + qemu_free_timer(dev->attach_timer); +-- +1.7.12 + diff --git a/0319-usb-redir-Get-rid-of-async-struct-get-member.patch b/0319-usb-redir-Get-rid-of-async-struct-get-member.patch new file mode 100644 index 0000000..daf27ed --- /dev/null +++ b/0319-usb-redir-Get-rid-of-async-struct-get-member.patch @@ -0,0 +1,73 @@ +From 5f83bc6b7bf54271bc1e3460b2e6f790407964e0 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 27 Aug 2012 16:33:08 +0200 +Subject: [PATCH 319/366] usb-redir: Get rid of async-struct get member + +This is a preparation patch for completely getting rid of the async-packet +struct in usb-redir, instead relying on the (new) per ep queues in the +qemu usb core. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 8ac8637..4121983 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -1,7 +1,7 @@ + /* + * USB redirector usb-guest + * +- * Copyright (c) 2011 Red Hat, Inc. ++ * Copyright (c) 2011-2012 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede +@@ -99,7 +99,6 @@ struct AsyncURB { + USBRedirDevice *dev; + USBPacket *packet; + uint32_t packet_id; +- int get; + union { + struct usb_redir_control_packet_header control_packet; + struct usb_redir_bulk_packet_header bulk_packet; +@@ -682,7 +681,6 @@ static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) + + DPRINTF("get config id %u\n", aurb->packet_id); + +- aurb->get = 1; + usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +@@ -731,7 +729,6 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, + DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); + + get_alt.interface = interface; +- aurb->get = 1; + usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, + &get_alt); + usbredirparser_do_write(dev->parser); +@@ -1253,7 +1250,7 @@ static void usbredir_configuration_status(void *priv, uint32_t id, + return; + } + if (aurb->packet) { +- if (aurb->get) { ++ if (dev->dev.setup_buf[0] & USB_DIR_IN) { + dev->dev.data_buf[0] = config_status->configuration; + len = 1; + } +@@ -1281,7 +1278,7 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, + return; + } + if (aurb->packet) { +- if (aurb->get) { ++ if (dev->dev.setup_buf[0] & USB_DIR_IN) { + dev->dev.data_buf[0] = alt_setting_status->alt; + len = 1; + } +-- +1.7.12 + diff --git a/0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch b/0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch new file mode 100644 index 0000000..ea5b5ed --- /dev/null +++ b/0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch @@ -0,0 +1,107 @@ +From 3240adfcb041efcb11b4cbf4b08dc86be52b8ffc Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 28 Aug 2012 09:05:38 +0200 +Subject: [PATCH 320/366] usb-redir: Get rid of local shadow copy of packet + headers + +The shadow copy only serves as an extra check (besides the packet-id) to +ensure the packet we get back is a reply to the packet we think it is. + +This check has never triggered in all the time usb-redir is in use now, +and since the verified data in the returned packet-header is not used +otherwise, removing the check does not open any possibilities for the +usbredirhost to confuse us. + +This is a preparation patch for completely getting rid of the async-packet +struct in usb-redir, instead relying on the (new) per ep queues in the +qemu usb core. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 27 --------------------------- + 1 file changed, 27 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 4121983..816a19a 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -99,11 +99,6 @@ struct AsyncURB { + USBRedirDevice *dev; + USBPacket *packet; + uint32_t packet_id; +- union { +- struct usb_redir_control_packet_header control_packet; +- struct usb_redir_bulk_packet_header bulk_packet; +- struct usb_redir_interrupt_packet_header interrupt_packet; +- }; + QTAILQ_ENTRY(AsyncURB)next; + }; + +@@ -510,7 +505,6 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, + bulk_packet.endpoint = ep; + bulk_packet.length = p->iov.size; + bulk_packet.stream_id = 0; +- aurb->bulk_packet = bulk_packet; + + if (ep & USB_DIR_IN) { + usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, +@@ -591,7 +585,6 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + + interrupt_packet.endpoint = ep; + interrupt_packet.length = p->iov.size; +- aurb->interrupt_packet = interrupt_packet; + + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); +@@ -772,7 +765,6 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + control_packet.value = value; + control_packet.index = index; + control_packet.length = length; +- aurb->control_packet = control_packet; + + if (control_packet.requesttype & USB_DIR_IN) { + usbredirparser_send_control_packet(dev->parser, aurb->packet_id, +@@ -1353,14 +1345,6 @@ static void usbredir_control_packet(void *priv, uint32_t id, + return; + } + +- aurb->control_packet.status = control_packet->status; +- aurb->control_packet.length = control_packet->length; +- if (memcmp(&aurb->control_packet, control_packet, +- sizeof(*control_packet))) { +- ERROR("return control packet mismatch, please report this!\n"); +- len = USB_RET_NAK; +- } +- + if (aurb->packet) { + len = usbredir_handle_status(dev, control_packet->status, len); + if (len > 0) { +@@ -1398,12 +1382,6 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, + return; + } + +- if (aurb->bulk_packet.endpoint != bulk_packet->endpoint || +- aurb->bulk_packet.stream_id != bulk_packet->stream_id) { +- ERROR("return bulk packet mismatch, please report this!\n"); +- len = USB_RET_NAK; +- } +- + if (aurb->packet) { + len = usbredir_handle_status(dev, bulk_packet->status, len); + if (len > 0) { +@@ -1482,11 +1460,6 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, + return; + } + +- if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) { +- ERROR("return int packet mismatch, please report this!\n"); +- len = USB_RET_NAK; +- } +- + if (aurb->packet) { + aurb->packet->result = usbredir_handle_status(dev, + interrupt_packet->status, len); +-- +1.7.12 + diff --git a/0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch b/0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch new file mode 100644 index 0000000..3852c31 --- /dev/null +++ b/0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch @@ -0,0 +1,38 @@ +From 1b1c99626404328b571e1f6c18b50f9bed9c2e2c Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 28 Aug 2012 09:08:45 +0200 +Subject: [PATCH 321/366] usb-redir: Get rid of unused async-struct dev member + +This is a preparation patch for completely getting rid of the async-packet +struct in usb-redir, instead relying on the (new) per ep queues in the +qemu usb core. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 816a19a..4a23b82 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -96,7 +96,6 @@ struct USBRedirDevice { + }; + + struct AsyncURB { +- USBRedirDevice *dev; + USBPacket *packet; + uint32_t packet_id; + QTAILQ_ENTRY(AsyncURB)next; +@@ -255,7 +254,6 @@ static int usbredir_write(void *priv, uint8_t *data, int count) + static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) + { + AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB)); +- aurb->dev = dev; + aurb->packet = p; + aurb->packet_id = dev->packet_id; + QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); +-- +1.7.12 + diff --git a/0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch b/0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch new file mode 100644 index 0000000..0bf0d67 --- /dev/null +++ b/0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch @@ -0,0 +1,499 @@ +From 69642884acc4f5d5b92e16d94fdae50765d1eba7 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 28 Aug 2012 11:30:13 +0200 +Subject: [PATCH 322/366] usb-redir: Move to core packet id and queue handling + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 226 ++++++++++++++++++++++-------------------------------- + 1 file changed, 92 insertions(+), 134 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 4a23b82..1ce994c 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -43,7 +43,7 @@ + #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) + #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) + +-typedef struct AsyncURB AsyncURB; ++typedef struct Cancelled Cancelled; + typedef struct USBRedirDevice USBRedirDevice; + + /* Struct to hold buffered packets (iso or int input packets) */ +@@ -86,8 +86,7 @@ struct USBRedirDevice { + int64_t next_attach_time; + struct usbredirparser *parser; + struct endp_data endpoint[MAX_ENDPOINTS]; +- uint32_t packet_id; +- QTAILQ_HEAD(, AsyncURB) asyncq; ++ QTAILQ_HEAD(, Cancelled) cancelled; + /* Data for device filtering */ + struct usb_redir_device_connect_header device_info; + struct usb_redir_interface_info_header interface_info; +@@ -95,10 +94,9 @@ struct USBRedirDevice { + int filter_rules_count; + }; + +-struct AsyncURB { +- USBPacket *packet; +- uint32_t packet_id; +- QTAILQ_ENTRY(AsyncURB)next; ++struct Cancelled { ++ uint64_t id; ++ QTAILQ_ENTRY(Cancelled)next; + }; + + static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); +@@ -248,57 +246,58 @@ static int usbredir_write(void *priv, uint8_t *data, int count) + } + + /* +- * Async and buffered packets helpers ++ * Cancelled and buffered packets helpers + */ + +-static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) ++static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) + { +- AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB)); +- aurb->packet = p; +- aurb->packet_id = dev->packet_id; +- QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); +- dev->packet_id++; ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ Cancelled *c; + +- return aurb; +-} ++ DPRINTF("cancel packet id %"PRIu64"\n", p->id); + +-static void async_free(USBRedirDevice *dev, AsyncURB *aurb) +-{ +- QTAILQ_REMOVE(&dev->asyncq, aurb, next); +- g_free(aurb); ++ c = g_malloc0(sizeof(Cancelled)); ++ c->id = p->id; ++ QTAILQ_INSERT_TAIL(&dev->cancelled, c, next); ++ ++ usbredirparser_send_cancel_data_packet(dev->parser, p->id); ++ usbredirparser_do_write(dev->parser); + } + +-static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id) ++static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) + { +- AsyncURB *aurb; ++ Cancelled *c; ++ ++ if (!dev->dev.attached) { ++ return 1; /* Treat everything as cancelled after a disconnect */ ++ } + +- QTAILQ_FOREACH(aurb, &dev->asyncq, next) { +- if (aurb->packet_id == packet_id) { +- return aurb; ++ QTAILQ_FOREACH(c, &dev->cancelled, next) { ++ if (c->id == id) { ++ QTAILQ_REMOVE(&dev->cancelled, c, next); ++ g_free(c); ++ return 1; + } + } +- DPRINTF("could not find async urb for packet_id %u\n", packet_id); +- return NULL; ++ return 0; + } + +-static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) ++static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, ++ uint8_t ep, uint64_t id) + { +- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); +- AsyncURB *aurb; +- +- QTAILQ_FOREACH(aurb, &dev->asyncq, next) { +- if (p != aurb->packet) { +- continue; +- } ++ USBPacket *p; + +- DPRINTF("async cancel id %u\n", aurb->packet_id); +- usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id); +- usbredirparser_do_write(dev->parser); ++ if (usbredir_is_cancelled(dev, id)) { ++ return NULL; ++ } + +- /* Mark it as dead */ +- aurb->packet = NULL; +- break; ++ p = usb_ep_find_packet_by_id(&dev->dev, ++ (ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT, ++ ep & 0x0f, id); ++ if (p == NULL) { ++ ERROR("could not find packet with id %"PRIu64"\n", id); + } ++ return p; + } + + static void bufp_alloc(USBRedirDevice *dev, +@@ -494,24 +493,22 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) + static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, + uint8_t ep) + { +- AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_bulk_packet_header bulk_packet; + +- DPRINTF("bulk-out ep %02X len %zd id %u\n", ep, +- p->iov.size, aurb->packet_id); ++ DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id); + + bulk_packet.endpoint = ep; + bulk_packet.length = p->iov.size; + bulk_packet.stream_id = 0; + + if (ep & USB_DIR_IN) { +- usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, ++ usbredirparser_send_bulk_packet(dev->parser, p->id, + &bulk_packet, NULL, 0); + } else { + uint8_t buf[p->iov.size]; + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "bulk data out:", buf, p->iov.size); +- usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, ++ usbredirparser_send_bulk_packet(dev->parser, p->id, + &bulk_packet, buf, p->iov.size); + } + usbredirparser_do_write(dev->parser); +@@ -574,19 +571,18 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + return len; + } else { + /* Output interrupt endpoint, normal async operation */ +- AsyncURB *aurb = async_alloc(dev, p); + struct usb_redir_interrupt_packet_header interrupt_packet; + uint8_t buf[p->iov.size]; + +- DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size, +- aurb->packet_id); ++ DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep, ++ p->iov.size, p->id); + + interrupt_packet.endpoint = ep; + interrupt_packet.length = p->iov.size; + + usb_packet_copy(p, buf, p->iov.size); + usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); +- usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, ++ usbredirparser_send_interrupt_packet(dev->parser, p->id, + &interrupt_packet, buf, p->iov.size); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; +@@ -640,10 +636,9 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, + int config) + { + struct usb_redir_set_configuration_header set_config; +- AsyncURB *aurb = async_alloc(dev, p); + int i; + +- DPRINTF("set config %d id %u\n", config, aurb->packet_id); ++ DPRINTF("set config %d id %"PRIu64"\n", config, p->id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + switch (dev->endpoint[i].type) { +@@ -660,19 +655,16 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, + } + + set_config.configuration = config; +- usbredirparser_send_set_configuration(dev->parser, aurb->packet_id, +- &set_config); ++ usbredirparser_send_set_configuration(dev->parser, p->id, &set_config); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; + } + + static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) + { +- AsyncURB *aurb = async_alloc(dev, p); +- +- DPRINTF("get config id %u\n", aurb->packet_id); ++ DPRINTF("get config id %"PRIu64"\n", p->id); + +- usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); ++ usbredirparser_send_get_configuration(dev->parser, p->id); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; + } +@@ -681,11 +673,9 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, + int interface, int alt) + { + struct usb_redir_set_alt_setting_header set_alt; +- AsyncURB *aurb = async_alloc(dev, p); + int i; + +- DPRINTF("set interface %d alt %d id %u\n", interface, alt, +- aurb->packet_id); ++ DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id); + + for (i = 0; i < MAX_ENDPOINTS; i++) { + if (dev->endpoint[i].interface == interface) { +@@ -705,8 +695,7 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, + + set_alt.interface = interface; + set_alt.alt = alt; +- usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id, +- &set_alt); ++ usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; + } +@@ -715,13 +704,11 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, + int interface) + { + struct usb_redir_get_alt_setting_header get_alt; +- AsyncURB *aurb = async_alloc(dev, p); + +- DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); ++ DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id); + + get_alt.interface = interface; +- usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, +- &get_alt); ++ usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt); + usbredirparser_do_write(dev->parser); + return USB_RET_ASYNC; + } +@@ -731,7 +718,6 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + { + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + struct usb_redir_control_packet_header control_packet; +- AsyncURB *aurb; + + /* Special cases for certain standard device requests */ + switch (request) { +@@ -749,13 +735,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + return usbredir_get_interface(dev, p, index); + } + +- /* "Normal" ctrl requests */ +- aurb = async_alloc(dev, p); +- +- /* Note request is (bRequestType << 8) | bRequest */ +- DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n", +- request >> 8, request & 0xff, value, index, length, +- aurb->packet_id); ++ /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */ ++ DPRINTF( ++ "ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n", ++ request >> 8, request & 0xff, value, index, length, p->id); + + control_packet.request = request & 0xFF; + control_packet.requesttype = request >> 8; +@@ -765,11 +748,11 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + control_packet.length = length; + + if (control_packet.requesttype & USB_DIR_IN) { +- usbredirparser_send_control_packet(dev->parser, aurb->packet_id, ++ usbredirparser_send_control_packet(dev->parser, p->id, + &control_packet, NULL, 0); + } else { + usbredir_log_data(dev, "ctrl data out:", data, length); +- usbredirparser_send_control_packet(dev->parser, aurb->packet_id, ++ usbredirparser_send_control_packet(dev->parser, p->id, + &control_packet, data, length); + } + usbredirparser_do_write(dev->parser); +@@ -941,7 +924,7 @@ static int usbredir_initfn(USBDevice *udev) + dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + +- QTAILQ_INIT(&dev->asyncq); ++ QTAILQ_INIT(&dev->cancelled); + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } +@@ -959,11 +942,12 @@ static int usbredir_initfn(USBDevice *udev) + + static void usbredir_cleanup_device_queues(USBRedirDevice *dev) + { +- AsyncURB *aurb, *next_aurb; ++ Cancelled *c, *next_c; + int i; + +- QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) { +- async_free(dev, aurb); ++ QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) { ++ QTAILQ_REMOVE(&dev->cancelled, c, next); ++ g_free(c); + } + for (i = 0; i < MAX_ENDPOINTS; i++) { + usbredir_free_bufpq(dev, I2EP(i)); +@@ -1229,33 +1213,28 @@ static void usbredir_configuration_status(void *priv, uint32_t id, + struct usb_redir_configuration_status_header *config_status) + { + USBRedirDevice *dev = priv; +- AsyncURB *aurb; ++ USBPacket *p; + int len = 0; + + DPRINTF("set config status %d config %d id %u\n", config_status->status, + config_status->configuration, id); + +- aurb = async_find(dev, id); +- if (!aurb) { +- return; +- } +- if (aurb->packet) { ++ p = usbredir_find_packet_by_id(dev, 0, id); ++ if (p) { + if (dev->dev.setup_buf[0] & USB_DIR_IN) { + dev->dev.data_buf[0] = config_status->configuration; + len = 1; + } +- aurb->packet->result = +- usbredir_handle_status(dev, config_status->status, len); +- usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); ++ p->result = usbredir_handle_status(dev, config_status->status, len); ++ usb_generic_async_ctrl_complete(&dev->dev, p); + } +- async_free(dev, aurb); + } + + static void usbredir_alt_setting_status(void *priv, uint32_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status) + { + USBRedirDevice *dev = priv; +- AsyncURB *aurb; ++ USBPacket *p; + int len = 0; + + DPRINTF("alt status %d intf %d alt %d id: %u\n", +@@ -1263,20 +1242,16 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, + alt_setting_status->interface, + alt_setting_status->alt, id); + +- aurb = async_find(dev, id); +- if (!aurb) { +- return; +- } +- if (aurb->packet) { ++ p = usbredir_find_packet_by_id(dev, 0, id); ++ if (p) { + if (dev->dev.setup_buf[0] & USB_DIR_IN) { + dev->dev.data_buf[0] = alt_setting_status->alt; + len = 1; + } +- aurb->packet->result = ++ p->result = + usbredir_handle_status(dev, alt_setting_status->status, len); +- usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); ++ usb_generic_async_ctrl_complete(&dev->dev, p); + } +- async_free(dev, aurb); + } + + static void usbredir_iso_stream_status(void *priv, uint32_t id, +@@ -1331,19 +1306,14 @@ static void usbredir_control_packet(void *priv, uint32_t id, + uint8_t *data, int data_len) + { + USBRedirDevice *dev = priv; ++ USBPacket *p; + int len = control_packet->length; +- AsyncURB *aurb; + + DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, + len, id); + +- aurb = async_find(dev, id); +- if (!aurb) { +- free(data); +- return; +- } +- +- if (aurb->packet) { ++ p = usbredir_find_packet_by_id(dev, 0, id); ++ if (p) { + len = usbredir_handle_status(dev, control_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "ctrl data in:", data, data_len); +@@ -1355,10 +1325,9 @@ static void usbredir_control_packet(void *priv, uint32_t id, + len = USB_RET_STALL; + } + } +- aurb->packet->result = len; +- usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); ++ p->result = len; ++ usb_generic_async_ctrl_complete(&dev->dev, p); + } +- async_free(dev, aurb); + free(data); + } + +@@ -1369,33 +1338,27 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, + USBRedirDevice *dev = priv; + uint8_t ep = bulk_packet->endpoint; + int len = bulk_packet->length; +- AsyncURB *aurb; ++ USBPacket *p; + + DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, + ep, len, id); + +- aurb = async_find(dev, id); +- if (!aurb) { +- free(data); +- return; +- } +- +- if (aurb->packet) { ++ p = usbredir_find_packet_by_id(dev, ep, id); ++ if (p) { + len = usbredir_handle_status(dev, bulk_packet->status, len); + if (len > 0) { + usbredir_log_data(dev, "bulk data in:", data, data_len); +- if (data_len <= aurb->packet->iov.size) { +- usb_packet_copy(aurb->packet, data, data_len); ++ if (data_len <= p->iov.size) { ++ usb_packet_copy(p, data, data_len); + } else { + ERROR("bulk buffer too small (%d > %zd)\n", data_len, +- aurb->packet->iov.size); ++ p->iov.size); + len = USB_RET_STALL; + } + } +- aurb->packet->result = len; +- usb_packet_complete(&dev->dev, aurb->packet); ++ p->result = len; ++ usb_packet_complete(&dev->dev, p); + } +- async_free(dev, aurb); + free(data); + } + +@@ -1453,17 +1416,12 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, + } else { + int len = interrupt_packet->length; + +- AsyncURB *aurb = async_find(dev, id); +- if (!aurb) { +- return; +- } +- +- if (aurb->packet) { +- aurb->packet->result = usbredir_handle_status(dev, ++ USBPacket *p = usbredir_find_packet_by_id(dev, ep, id); ++ if (p) { ++ p->result = usbredir_handle_status(dev, + interrupt_packet->status, len); +- usb_packet_complete(&dev->dev, aurb->packet); ++ usb_packet_complete(&dev->dev, p); + } +- async_free(dev, aurb); + } + } + +-- +1.7.12 + diff --git a/0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch b/0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch new file mode 100644 index 0000000..20a9db2 --- /dev/null +++ b/0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch @@ -0,0 +1,34 @@ +From bd0bc4416beb7ecef0baf2424250c07b9ef15fb6 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 28 Aug 2012 11:33:47 +0200 +Subject: [PATCH 323/366] usb-redir: Return babble when getting more bulk data + then requested + +Babble is the appropriate error in this case (rather then signalling a stall). + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 1ce994c..60b8f3e 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -1351,9 +1351,9 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, + if (data_len <= p->iov.size) { + usb_packet_copy(p, data, data_len); + } else { +- ERROR("bulk buffer too small (%d > %zd)\n", data_len, +- p->iov.size); +- len = USB_RET_STALL; ++ ERROR("bulk got more data then requested (%d > %zd)\n", ++ data_len, p->iov.size); ++ len = USB_RET_BABBLE; + } + } + p->result = len; +-- +1.7.12 + diff --git a/0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch b/0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch new file mode 100644 index 0000000..e6296ec --- /dev/null +++ b/0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch @@ -0,0 +1,232 @@ +From 5ea5581f88d2e4bba876c2ee2544cf3641a64eca Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 31 Aug 2012 13:41:38 +0200 +Subject: [PATCH 324/366] usb-redir: Convert to new libusbredirparser 0.5 API + +This gives us support for 64 bit ids which is needed for using XHCI with +the new hcd generated ids. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + configure | 2 +- + hw/usb/redirect.c | 62 +++++++++++++++++++++++++++---------------------------- + 2 files changed, 32 insertions(+), 32 deletions(-) + +diff --git a/configure b/configure +index 0bfef84..25c406f 100755 +--- a/configure ++++ b/configure +@@ -2765,7 +2765,7 @@ fi + + # check for usbredirparser for usb network redirection support + if test "$usb_redir" != "no" ; then +- if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then ++ if $pkg_config --atleast-version=0.5 libusbredirparser >/dev/null 2>&1 ; then + usb_redir="yes" + usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) + usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 60b8f3e..789ef51 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -107,27 +107,27 @@ static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info); + static void usbredir_ep_info(void *priv, + struct usb_redir_ep_info_header *ep_info); +-static void usbredir_configuration_status(void *priv, uint32_t id, ++static void usbredir_configuration_status(void *priv, uint64_t id, + struct usb_redir_configuration_status_header *configuration_status); +-static void usbredir_alt_setting_status(void *priv, uint32_t id, ++static void usbredir_alt_setting_status(void *priv, uint64_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status); +-static void usbredir_iso_stream_status(void *priv, uint32_t id, ++static void usbredir_iso_stream_status(void *priv, uint64_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status); +-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, ++static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status); +-static void usbredir_bulk_streams_status(void *priv, uint32_t id, ++static void usbredir_bulk_streams_status(void *priv, uint64_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status); +-static void usbredir_control_packet(void *priv, uint32_t id, ++static void usbredir_control_packet(void *priv, uint64_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len); +-static void usbredir_bulk_packet(void *priv, uint32_t id, ++static void usbredir_bulk_packet(void *priv, uint64_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len); +-static void usbredir_iso_packet(void *priv, uint32_t id, ++static void usbredir_iso_packet(void *priv, uint64_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len); +-static void usbredir_interrupt_packet(void *priv, uint32_t id, ++static void usbredir_interrupt_packet(void *priv, uint64_t id, + struct usb_redir_interrupt_packet_header *interrupt_header, + uint8_t *data, int data_len); + +@@ -815,6 +815,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) + + usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); + usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); ++ usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); + usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); + usbredirparser_do_write(dev->parser); + } +@@ -1209,15 +1210,15 @@ static void usbredir_ep_info(void *priv, + } + } + +-static void usbredir_configuration_status(void *priv, uint32_t id, ++static void usbredir_configuration_status(void *priv, uint64_t id, + struct usb_redir_configuration_status_header *config_status) + { + USBRedirDevice *dev = priv; + USBPacket *p; + int len = 0; + +- DPRINTF("set config status %d config %d id %u\n", config_status->status, +- config_status->configuration, id); ++ DPRINTF("set config status %d config %d id %"PRIu64"\n", ++ config_status->status, config_status->configuration, id); + + p = usbredir_find_packet_by_id(dev, 0, id); + if (p) { +@@ -1230,16 +1231,15 @@ static void usbredir_configuration_status(void *priv, uint32_t id, + } + } + +-static void usbredir_alt_setting_status(void *priv, uint32_t id, ++static void usbredir_alt_setting_status(void *priv, uint64_t id, + struct usb_redir_alt_setting_status_header *alt_setting_status) + { + USBRedirDevice *dev = priv; + USBPacket *p; + int len = 0; + +- DPRINTF("alt status %d intf %d alt %d id: %u\n", +- alt_setting_status->status, +- alt_setting_status->interface, ++ DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n", ++ alt_setting_status->status, alt_setting_status->interface, + alt_setting_status->alt, id); + + p = usbredir_find_packet_by_id(dev, 0, id); +@@ -1254,13 +1254,13 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, + } + } + +-static void usbredir_iso_stream_status(void *priv, uint32_t id, ++static void usbredir_iso_stream_status(void *priv, uint64_t id, + struct usb_redir_iso_stream_status_header *iso_stream_status) + { + USBRedirDevice *dev = priv; + uint8_t ep = iso_stream_status->endpoint; + +- DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status, ++ DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status, + ep, id); + + if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) { +@@ -1274,14 +1274,14 @@ static void usbredir_iso_stream_status(void *priv, uint32_t id, + } + } + +-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, ++static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, + struct usb_redir_interrupt_receiving_status_header + *interrupt_receiving_status) + { + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_receiving_status->endpoint; + +- DPRINTF("interrupt recv status %d ep %02X id %u\n", ++ DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n", + interrupt_receiving_status->status, ep, id); + + if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) { +@@ -1296,12 +1296,12 @@ static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, + } + } + +-static void usbredir_bulk_streams_status(void *priv, uint32_t id, ++static void usbredir_bulk_streams_status(void *priv, uint64_t id, + struct usb_redir_bulk_streams_status_header *bulk_streams_status) + { + } + +-static void usbredir_control_packet(void *priv, uint32_t id, ++static void usbredir_control_packet(void *priv, uint64_t id, + struct usb_redir_control_packet_header *control_packet, + uint8_t *data, int data_len) + { +@@ -1309,7 +1309,7 @@ static void usbredir_control_packet(void *priv, uint32_t id, + USBPacket *p; + int len = control_packet->length; + +- DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, ++ DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status, + len, id); + + p = usbredir_find_packet_by_id(dev, 0, id); +@@ -1331,7 +1331,7 @@ static void usbredir_control_packet(void *priv, uint32_t id, + free(data); + } + +-static void usbredir_bulk_packet(void *priv, uint32_t id, ++static void usbredir_bulk_packet(void *priv, uint64_t id, + struct usb_redir_bulk_packet_header *bulk_packet, + uint8_t *data, int data_len) + { +@@ -1340,8 +1340,8 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, + int len = bulk_packet->length; + USBPacket *p; + +- DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, +- ep, len, id); ++ DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n", ++ bulk_packet->status, ep, len, id); + + p = usbredir_find_packet_by_id(dev, ep, id); + if (p) { +@@ -1362,15 +1362,15 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, + free(data); + } + +-static void usbredir_iso_packet(void *priv, uint32_t id, ++static void usbredir_iso_packet(void *priv, uint64_t id, + struct usb_redir_iso_packet_header *iso_packet, + uint8_t *data, int data_len) + { + USBRedirDevice *dev = priv; + uint8_t ep = iso_packet->endpoint; + +- DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep, +- data_len, id); ++ DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n", ++ iso_packet->status, ep, data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { + ERROR("received iso packet for non iso endpoint %02X\n", ep); +@@ -1388,14 +1388,14 @@ static void usbredir_iso_packet(void *priv, uint32_t id, + bufp_alloc(dev, data, data_len, iso_packet->status, ep); + } + +-static void usbredir_interrupt_packet(void *priv, uint32_t id, ++static void usbredir_interrupt_packet(void *priv, uint64_t id, + struct usb_redir_interrupt_packet_header *interrupt_packet, + uint8_t *data, int data_len) + { + USBRedirDevice *dev = priv; + uint8_t ep = interrupt_packet->endpoint; + +- DPRINTF("interrupt-in status %d ep %02X len %d id %u\n", ++ DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n", + interrupt_packet->status, ep, data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { +-- +1.7.12 + diff --git a/0325-usb-redir-Set-ep-max_packet_size-if-available.patch b/0325-usb-redir-Set-ep-max_packet_size-if-available.patch new file mode 100644 index 0000000..d9e15a5 --- /dev/null +++ b/0325-usb-redir-Set-ep-max_packet_size-if-available.patch @@ -0,0 +1,39 @@ +From 7c936f9e6b07f4468f49886b4e785e4120a08e11 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 11:49:07 +0200 +Subject: [PATCH 325/366] usb-redir: Set ep max_packet_size if available + +This is needed for usb-redir to work properly with the xhci emulation. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 789ef51..ac8bfd2 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -815,6 +815,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) + + usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); + usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); ++ usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); + usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); + usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); + usbredirparser_do_write(dev->parser); +@@ -1207,6 +1208,10 @@ static void usbredir_ep_info(void *priv, + i & 0x0f); + usb_ep->type = dev->endpoint[i].type; + usb_ep->ifnum = dev->endpoint[i].interface; ++ if (usbredirparser_peer_has_cap(dev->parser, ++ usb_redir_cap_ep_info_max_packet_size)) { ++ usb_ep->max_packet_size = ep_info->max_packet_size[i]; ++ } + } + } + +-- +1.7.12 + diff --git a/0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch b/0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch new file mode 100644 index 0000000..d4cde52 --- /dev/null +++ b/0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch @@ -0,0 +1,59 @@ +From 4ba71f5871be4d92f36097bc431fc303e1e42a7a Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 11:53:28 +0200 +Subject: [PATCH 326/366] usb-redir: Add a usbredir_reject_device helper + function + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index ac8bfd2..357f307 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -821,16 +821,21 @@ static void usbredir_chardev_open(USBRedirDevice *dev) + usbredirparser_do_write(dev->parser); + } + ++static void usbredir_reject_device(USBRedirDevice *dev) ++{ ++ usbredir_device_disconnect(dev); ++ if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { ++ usbredirparser_send_filter_reject(dev->parser); ++ usbredirparser_do_write(dev->parser); ++ } ++} ++ + static void usbredir_do_attach(void *opaque) + { + USBRedirDevice *dev = opaque; + + if (usb_device_attach(&dev->dev) != 0) { +- usbredir_device_disconnect(dev); +- if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { +- usbredirparser_send_filter_reject(dev->parser); +- usbredirparser_do_write(dev->parser); +- } ++ usbredir_reject_device(dev); + } + } + +@@ -1013,11 +1018,7 @@ static int usbredir_check_filter(USBRedirDevice *dev) + return 0; + + error: +- usbredir_device_disconnect(dev); +- if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { +- usbredirparser_send_filter_reject(dev->parser); +- usbredirparser_do_write(dev->parser); +- } ++ usbredir_reject_device(dev); + return -1; + } + +-- +1.7.12 + diff --git a/0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch b/0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch new file mode 100644 index 0000000..f3ea67a --- /dev/null +++ b/0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch @@ -0,0 +1,43 @@ +From af1ecda005c043dd8186725ae7a93ea66ebfe96f Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 12:04:49 +0200 +Subject: [PATCH 327/366] usb-redir: Ensure our peer has the necessary caps + when redirecting to XHCI + +In order for redirection to work properly when redirecting to an emulated +XHCI controller, the usb-redir-host must support both +usb_redir_cap_ep_info_max_packet_size and usb_redir_cap_64bits_ids, +reject any devices redirected to an XHCI controller when these are not +supported. + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 357f307..5b0f834 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -834,6 +834,17 @@ static void usbredir_do_attach(void *opaque) + { + USBRedirDevice *dev = opaque; + ++ /* In order to work properly with XHCI controllers we need these caps */ ++ if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !( ++ usbredirparser_peer_has_cap(dev->parser, ++ usb_redir_cap_ep_info_max_packet_size) && ++ usbredirparser_peer_has_cap(dev->parser, ++ usb_redir_cap_64bits_ids))) { ++ ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n"); ++ usbredir_reject_device(dev); ++ return; ++ } ++ + if (usb_device_attach(&dev->dev) != 0) { + usbredir_reject_device(dev); + } +-- +1.7.12 + diff --git a/0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch b/0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch new file mode 100644 index 0000000..48de611 --- /dev/null +++ b/0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch @@ -0,0 +1,28 @@ +From 30b6793253b39f347e1f74791118403451b9886c Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 3 Sep 2012 13:44:04 +0200 +Subject: [PATCH 328/366] usb-redir: Enable pipelining for bulk endpoints + +Signed-off-by: Hans de Goede +Signed-off-by: Gerd Hoffmann +--- + hw/usb/redirect.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 5b0f834..9cbcddb 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -1224,6 +1224,9 @@ static void usbredir_ep_info(void *priv, + usb_redir_cap_ep_info_max_packet_size)) { + usb_ep->max_packet_size = ep_info->max_packet_size[i]; + } ++ if (ep_info->type[i] == usb_redir_type_bulk) { ++ usb_ep->pipeline = true; ++ } + } + } + +-- +1.7.12 + diff --git a/0329-Better-name-usb-braille-device.patch b/0329-Better-name-usb-braille-device.patch new file mode 100644 index 0000000..b9cf4e6 --- /dev/null +++ b/0329-Better-name-usb-braille-device.patch @@ -0,0 +1,30 @@ +From f83e26f01f1489af484f69bdeb2d01d4896de6ec Mon Sep 17 00:00:00 2001 +From: Samuel Thibault +Date: Thu, 23 Aug 2012 09:59:27 +0200 +Subject: [PATCH 329/366] Better name usb braille device + +Windows users need to know that they have to use the Baum driver to make +the qemu braille device work. + +Signed-off-by: Samuel Thibault +Signed-off-by: Gerd Hoffmann +--- + hw/usb/dev-serial.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c +index 8fc9bdd..0ddfab6 100644 +--- a/hw/usb/dev-serial.c ++++ b/hw/usb/dev-serial.c +@@ -113,7 +113,7 @@ enum { + static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU", + [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", +- [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE", ++ [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE", + [STR_SERIALNUMBER] = "1", + }; + +-- +1.7.12 + diff --git a/0330-usb-audio-fix-usb-version.patch b/0330-usb-audio-fix-usb-version.patch new file mode 100644 index 0000000..c79c18f --- /dev/null +++ b/0330-usb-audio-fix-usb-version.patch @@ -0,0 +1,29 @@ +From 77824f2b7aba75a3f31a996634dc836fe26ee2c5 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 28 Aug 2012 16:43:34 +0200 +Subject: [PATCH 330/366] usb-audio: fix usb version + +usb-audio is a full speed (1.1) device, +but bcdUSB claims it is usb 2.0. Fix it. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/dev-audio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c +index 79b75fb..2594c78 100644 +--- a/hw/usb/dev-audio.c ++++ b/hw/usb/dev-audio.c +@@ -217,7 +217,7 @@ static const USBDescIface desc_iface[] = { + }; + + static const USBDescDevice desc_device = { +- .bcdUSB = 0x0200, ++ .bcdUSB = 0x0100, + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { +-- +1.7.12 + diff --git a/0331-xhci-rip-out-background-transfer-code.patch b/0331-xhci-rip-out-background-transfer-code.patch new file mode 100644 index 0000000..4ccdfd5 --- /dev/null +++ b/0331-xhci-rip-out-background-transfer-code.patch @@ -0,0 +1,324 @@ +From f019cdb4b17a0615e897ddac37f2ffa8866a4979 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 17 Aug 2012 14:05:21 +0200 +Subject: [PATCH 331/366] xhci: rip out background transfer code + +original xhci code (the one which used libusb directly) used to use +'background transfers' for iso streams. In upstream qemu the iso +stream buffering is handled by usb-host & usb-redir, so we will +never ever need this. It has been left in as reference, but is dead +code anyway. Rip it out. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 223 +----------------------------------------------------- + 1 file changed, 4 insertions(+), 219 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 3eb27fa..c0a2476 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -45,8 +45,6 @@ + #define MAXPORTS (USB2_PORTS+USB3_PORTS) + + #define TD_QUEUE 24 +-#define BG_XFERS 8 +-#define BG_PKTS 8 + + /* Very pessimistic, let's hope it's enough for all cases */ + #define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS) +@@ -311,13 +309,11 @@ typedef struct XHCITransfer { + bool running_retry; + bool cancelled; + bool complete; +- bool backgrounded; + unsigned int iso_pkts; + unsigned int slotid; + unsigned int epid; + bool in_xfer; + bool iso_xfer; +- bool bg_xfer; + + unsigned int trb_count; + unsigned int trb_alloced; +@@ -340,14 +336,9 @@ typedef struct XHCIEPContext { + unsigned int comp_xfer; + XHCITransfer transfers[TD_QUEUE]; + XHCITransfer *retry; +- bool bg_running; +- bool bg_updating; +- unsigned int next_bg; +- XHCITransfer bg_transfers[BG_XFERS]; + EPType type; + dma_addr_t pctx; + unsigned int max_psize; +- bool has_bg; + uint32_t state; + } XHCIEPContext; + +@@ -866,10 +857,6 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, + epctx->pctx = pctx; + epctx->max_psize = ctx[1]>>16; + epctx->max_psize *= 1+((ctx[1]>>8)&0xff); +- epctx->has_bg = false; +- if (epctx->type == ET_ISO_IN) { +- epctx->has_bg = true; +- } + DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n", + epid/2, epid%2, epctx->max_psize); + for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { +@@ -916,9 +903,6 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, + t->running_retry = 0; + epctx->retry = NULL; + } +- if (t->backgrounded) { +- t->backgrounded = 0; +- } + if (t->trbs) { + g_free(t->trbs); + } +@@ -932,25 +916,6 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, + t->data_length = t->data_alloced = 0; + xferi = (xferi + 1) % TD_QUEUE; + } +- if (epctx->has_bg) { +- xferi = epctx->next_bg; +- for (i = 0; i < BG_XFERS; i++) { +- XHCITransfer *t = &epctx->bg_transfers[xferi]; +- if (t->running_async) { +- usb_cancel_packet(&t->packet); +- t->running_async = 0; +- t->cancelled = 1; +- DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i); +- killed++; +- } +- if (t->data) { +- g_free(t->data); +- } +- +- t->data = NULL; +- xferi = (xferi + 1) % BG_XFERS; +- } +- } + return killed; + } + +@@ -1231,160 +1196,6 @@ static void xhci_stall_ep(XHCITransfer *xfer) + static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, + XHCIEPContext *epctx); + +-static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx) +-{ +- if (epctx->bg_updating) { +- return; +- } +- DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx); +- assert(epctx->has_bg); +- DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg); +- epctx->bg_updating = 1; +- while (epctx->transfers[epctx->comp_xfer].backgrounded && +- epctx->bg_transfers[epctx->next_bg].complete) { +- XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer]; +- XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg]; +-#if 0 +- DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n", +- epctx->comp_xfer, epctx->next_bg, bg->cur_pkt, +- bg->usbxfer->iso_packet_desc[bg->cur_pkt].status +- ); +-#endif +- assert(epctx->type == ET_ISO_IN); +- assert(bg->iso_xfer); +- assert(bg->in_xfer); +- uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize; +-#if 0 +- int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length; +- fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status); +-#else +- int len = 0; +- FIXME(); +-#endif +- fg->complete = 1; +- fg->backgrounded = 0; +- +- if (fg->status == CC_STALL_ERROR) { +- xhci_stall_ep(fg); +- } +- +- xhci_xfer_data(fg, p, len, 1, 0, 1); +- +- epctx->comp_xfer++; +- if (epctx->comp_xfer == TD_QUEUE) { +- epctx->comp_xfer = 0; +- } +- DPRINTF("next fg xfer: %d\n", epctx->comp_xfer); +- bg->cur_pkt++; +- if (bg->cur_pkt == bg->pkts) { +- bg->complete = 0; +- if (xhci_submit(xhci, bg, epctx) < 0) { +- fprintf(stderr, "xhci: bg resubmit failed\n"); +- } +- epctx->next_bg++; +- if (epctx->next_bg == BG_XFERS) { +- epctx->next_bg = 0; +- } +- DPRINTF("next bg xfer: %d\n", epctx->next_bg); +- +- xhci_kick_ep(xhci, fg->slotid, fg->epid); +- } +- } +- epctx->bg_updating = 0; +-} +- +-#if 0 +-static void xhci_xfer_cb(struct libusb_transfer *transfer) +-{ +- XHCIState *xhci; +- XHCITransfer *xfer; +- +- xfer = (XHCITransfer *)transfer->user_data; +- xhci = xfer->xhci; +- +- DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid, +- xfer->epid, transfer->status); +- +- assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS); +- assert(xfer->epid >= 1 && xfer->epid <= 31); +- +- if (xfer->cancelled) { +- DPRINTF("xhci: transfer cancelled, not reporting anything\n"); +- xfer->running = 0; +- return; +- } +- +- XHCIEPContext *epctx; +- XHCISlot *slot; +- slot = &xhci->slots[xfer->slotid-1]; +- assert(slot->eps[xfer->epid-1]); +- epctx = slot->eps[xfer->epid-1]; +- +- if (xfer->bg_xfer) { +- DPRINTF("xhci: background transfer, updating\n"); +- xfer->complete = 1; +- xfer->running = 0; +- xhci_bg_update(xhci, epctx); +- return; +- } +- +- if (xfer->iso_xfer) { +- transfer->status = transfer->iso_packet_desc[0].status; +- transfer->actual_length = transfer->iso_packet_desc[0].actual_length; +- } +- +- xfer->status = libusb_to_ccode(transfer->status); +- +- xfer->complete = 1; +- xfer->running = 0; +- +- if (transfer->status == LIBUSB_TRANSFER_STALL) +- xhci_stall_ep(xhci, epctx, xfer); +- +- DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length); +- +- if (xfer->in_xfer) { +- if (xfer->epid == 1) { +- xhci_xfer_data(xhci, xfer, xfer->data + 8, +- transfer->actual_length, 1, 0, 1); +- } else { +- xhci_xfer_data(xhci, xfer, xfer->data, +- transfer->actual_length, 1, 0, 1); +- } +- } else { +- xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1); +- } +- +- xhci_kick_ep(xhci, xfer->slotid, xfer->epid); +-} +- +-static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer, +- uint8_t bmRequestType, uint8_t bRequest, +- uint16_t wValue, uint16_t wIndex, uint16_t wLength) +-{ +- uint16_t type_req = (bmRequestType << 8) | bRequest; +- +- switch (type_req) { +- case 0x0000 | USB_REQ_SET_CONFIGURATION: +- DPRINTF("xhci: HLE switch configuration\n"); +- return xhci_switch_config(xhci, xfer->slotid, wValue) == 0; +- case 0x0100 | USB_REQ_SET_INTERFACE: +- DPRINTF("xhci: HLE set interface altsetting\n"); +- return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0; +- case 0x0200 | USB_REQ_CLEAR_FEATURE: +- if (wValue == 0) { // endpoint halt +- DPRINTF("xhci: HLE clear halt\n"); +- return xhci_clear_halt(xhci, xfer->slotid, wIndex); +- } +- case 0x0000 | USB_REQ_SET_ADDRESS: +- fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n"); +- return 0; +- default: +- return 0; +- } +-} +-#endif +- + static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) + { + USBEndpoint *ep; +@@ -1559,9 +1370,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx + xfer->data_alloced = xfer->data_length; + } + if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { +- if (!xfer->bg_xfer) { +- xfer->pkts = 1; +- } ++ xfer->pkts = 1; + } else { + xfer->pkts = 0; + } +@@ -1620,32 +1429,8 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext + + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length); + +- if (!epctx->has_bg) { +- xfer->data_length = length; +- xfer->backgrounded = 0; +- return xhci_submit(xhci, xfer, epctx); +- } else { +- if (!epctx->bg_running) { +- for (i = 0; i < BG_XFERS; i++) { +- XHCITransfer *t = &epctx->bg_transfers[i]; +- t->xhci = xhci; +- t->epid = xfer->epid; +- t->slotid = xfer->slotid; +- t->pkts = BG_PKTS; +- t->pktsize = epctx->max_psize; +- t->data_length = t->pkts * t->pktsize; +- t->bg_xfer = 1; +- if (xhci_submit(xhci, t, epctx) < 0) { +- fprintf(stderr, "xhci: bg submit failed\n"); +- return -1; +- } +- } +- epctx->bg_running = 1; +- } +- xfer->backgrounded = 1; +- xhci_bg_update(xhci, epctx); +- return 0; +- } ++ xfer->data_length = length; ++ return xhci_submit(xhci, xfer, epctx); + } + + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid) +@@ -1695,7 +1480,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid + + while (1) { + XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; +- if (xfer->running_async || xfer->running_retry || xfer->backgrounded) { ++ if (xfer->running_async || xfer->running_retry) { + break; + } + length = xhci_ring_chain_length(xhci, &epctx->ring); +-- +1.7.12 + diff --git a/0332-xhci-drop-buffering.patch b/0332-xhci-drop-buffering.patch new file mode 100644 index 0000000..1097664 --- /dev/null +++ b/0332-xhci-drop-buffering.patch @@ -0,0 +1,372 @@ +From ad14386b3f1ccd445afd30d30a343cb9fcf6c0a7 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 17 Aug 2012 11:04:36 +0200 +Subject: [PATCH 332/366] xhci: drop buffering + +This patch splits the xhci_xfer_data function into three. +The xhci_xfer_data function used to do does two things: + + (1) copy transfer data between guest memory and a temporary buffer. + (2) report transfer results to the guest using events. + +Now we three functions to handle this: + + (1) xhci_xfer_map creates a scatter list for the transfer and + uses that (instead of the temporary buffer) to build a + USBPacket. + (2) xhci_xfer_unmap undoes the mapping. + (3) xhci_xfer_report sends out events. + +The patch also fixes reporting of transaction errors which must be +reported unconditinally, not only in case the guest asks for it +using the ISP flag. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 183 +++++++++++++++++++++--------------------------------- + trace-events | 2 +- + 2 files changed, 72 insertions(+), 113 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index c0a2476..c858b6d 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -305,6 +305,7 @@ typedef struct XHCIState XHCIState; + typedef struct XHCITransfer { + XHCIState *xhci; + USBPacket packet; ++ QEMUSGList sgl; + bool running_async; + bool running_retry; + bool cancelled; +@@ -319,10 +320,6 @@ typedef struct XHCITransfer { + unsigned int trb_alloced; + XHCITRB *trbs; + +- unsigned int data_length; +- unsigned int data_alloced; +- uint8_t *data; +- + TRBCCode status; + + unsigned int pkts; +@@ -906,14 +903,9 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, + if (t->trbs) { + g_free(t->trbs); + } +- if (t->data) { +- g_free(t->data); +- } + + t->trbs = NULL; +- t->data = NULL; + t->trb_count = t->trb_alloced = 0; +- t->data_length = t->data_alloced = 0; + xferi = (xferi + 1) % TD_QUEUE; + } + return killed; +@@ -1072,24 +1064,13 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, + return CC_SUCCESS; + } + +-static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, +- unsigned int length, bool in_xfer, bool out_xfer, +- bool report) ++static int xhci_xfer_map(XHCITransfer *xfer) + { +- int i; +- uint32_t edtla = 0; +- unsigned int transferred = 0; +- unsigned int left = length; +- bool reported = 0; +- bool shortpkt = 0; +- XHCIEvent event = {ER_TRANSFER, CC_SUCCESS}; ++ int in_xfer = (xfer->packet.pid == USB_TOKEN_IN); + XHCIState *xhci = xfer->xhci; ++ int i; + +- DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n", +- length, in_xfer, out_xfer, report); +- +- assert(!(in_xfer && out_xfer)); +- ++ pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count); + for (i = 0; i < xfer->trb_count; i++) { + XHCITRB *trb = &xfer->trbs[i]; + dma_addr_t addr; +@@ -1099,54 +1080,70 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, + case TR_DATA: + if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { + fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n"); +- xhci_die(xhci); +- return transferred; ++ goto err; + } + /* fallthrough */ + case TR_NORMAL: + case TR_ISOCH: + addr = xhci_mask64(trb->parameter); + chunk = trb->status & 0x1ffff; ++ if (trb->control & TRB_TR_IDT) { ++ if (chunk > 8 || in_xfer) { ++ fprintf(stderr, "xhci: invalid immediate data TRB\n"); ++ goto err; ++ } ++ qemu_sglist_add(&xfer->sgl, trb->addr, chunk); ++ } else { ++ qemu_sglist_add(&xfer->sgl, addr, chunk); ++ } ++ break; ++ } ++ } ++ ++ usb_packet_map(&xfer->packet, &xfer->sgl); ++ return 0; ++ ++err: ++ qemu_sglist_destroy(&xfer->sgl); ++ xhci_die(xhci); ++ return -1; ++} ++ ++static void xhci_xfer_unmap(XHCITransfer *xfer) ++{ ++ usb_packet_unmap(&xfer->packet, &xfer->sgl); ++ qemu_sglist_destroy(&xfer->sgl); ++} ++ ++static void xhci_xfer_report(XHCITransfer *xfer) ++{ ++ uint32_t edtla = 0; ++ unsigned int left; ++ bool reported = 0; ++ bool shortpkt = 0; ++ XHCIEvent event = {ER_TRANSFER, CC_SUCCESS}; ++ XHCIState *xhci = xfer->xhci; ++ int i; ++ ++ left = xfer->packet.result < 0 ? 0 : xfer->packet.result; ++ ++ for (i = 0; i < xfer->trb_count; i++) { ++ XHCITRB *trb = &xfer->trbs[i]; ++ unsigned int chunk = 0; ++ ++ switch (TRB_TYPE(*trb)) { ++ case TR_DATA: ++ case TR_NORMAL: ++ case TR_ISOCH: ++ chunk = trb->status & 0x1ffff; + if (chunk > left) { + chunk = left; +- shortpkt = 1; +- } +- if (in_xfer || out_xfer) { +- if (trb->control & TRB_TR_IDT) { +- uint64_t idata; +- if (chunk > 8 || in_xfer) { +- fprintf(stderr, "xhci: invalid immediate data TRB\n"); +- xhci_die(xhci); +- return transferred; +- } +- idata = le64_to_cpu(trb->parameter); +- memcpy(data, &idata, chunk); +- } else { +- DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at " +- DMA_ADDR_FMT "\n", in_xfer, chunk, addr); +- if (in_xfer) { +- pci_dma_write(&xhci->pci_dev, addr, data, chunk); +- } else { +- pci_dma_read(&xhci->pci_dev, addr, data, chunk); +- } +-#ifdef DEBUG_DATA +- unsigned int count = chunk; +- int i; +- if (count > 16) { +- count = 16; +- } +- DPRINTF(" ::"); +- for (i = 0; i < count; i++) { +- DPRINTF(" %02x", data[i]); +- } +- DPRINTF("\n"); +-#endif ++ if (xfer->status == CC_SUCCESS) { ++ shortpkt = 1; + } + } + left -= chunk; +- data += chunk; + edtla += chunk; +- transferred += chunk; + break; + case TR_STATUS: + reported = 0; +@@ -1154,8 +1151,9 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, + break; + } + +- if (report && !reported && (trb->control & TRB_TR_IOC || +- (shortpkt && (trb->control & TRB_TR_ISP)))) { ++ if (!reported && ((trb->control & TRB_TR_IOC) || ++ (shortpkt && (trb->control & TRB_TR_ISP)) || ++ (xfer->status != CC_SUCCESS))) { + event.slotid = xfer->slotid; + event.epid = xfer->epid; + event.length = (trb->status & 0x1ffff) - chunk; +@@ -1175,9 +1173,11 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, + } + xhci_event(xhci, &event); + reported = 1; ++ if (xfer->status != CC_SUCCESS) { ++ return; ++ } + } + } +- return transferred; + } + + static void xhci_stall_ep(XHCITransfer *xfer) +@@ -1204,7 +1204,7 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) + dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; + ep = usb_ep_get(dev, dir, xfer->epid >> 1); + usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr); +- usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length); ++ xhci_xfer_map(xfer); + DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", + xfer->packet.pid, dev->addr, ep->nr); + return 0; +@@ -1230,12 +1230,13 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) + xfer->running_async = 0; + xfer->running_retry = 0; + xfer->complete = 1; ++ xhci_xfer_unmap(xfer); + } + + if (ret >= 0) { +- xfer->status = CC_SUCCESS; +- xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1); + trace_usb_xhci_xfer_success(xfer, ret); ++ xfer->status = CC_SUCCESS; ++ xhci_xfer_report(xfer); + return 0; + } + +@@ -1244,12 +1245,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) + switch (ret) { + case USB_RET_NODEV: + xfer->status = CC_USB_TRANSACTION_ERROR; +- xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1); ++ xhci_xfer_report(xfer); + xhci_stall_ep(xfer); + break; + case USB_RET_STALL: + xfer->status = CC_STALL_ERROR; +- xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1); ++ xhci_xfer_report(xfer); + xhci_stall_ep(xfer); + break; + default: +@@ -1279,8 +1280,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + trb_setup = &xfer->trbs[0]; + trb_status = &xfer->trbs[xfer->trb_count-1]; + +- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, +- trb_setup->parameter >> 48); ++ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); + + /* at most one Event Data TRB allowed after STATUS */ + if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) { +@@ -1311,18 +1311,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + bmRequestType = trb_setup->parameter; + wLength = trb_setup->parameter >> 48; + +- if (xfer->data && xfer->data_alloced < wLength) { +- xfer->data_alloced = 0; +- g_free(xfer->data); +- xfer->data = NULL; +- } +- if (!xfer->data) { +- DPRINTF("xhci: alloc %d bytes data\n", wLength); +- xfer->data = g_malloc(wLength+1); +- xfer->data_alloced = wLength; +- } +- xfer->data_length = wLength; +- + port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; + dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); + if (!dev) { +@@ -1336,9 +1324,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + + xhci_setup_packet(xfer, dev); + xfer->packet.parameter = trb_setup->parameter; +- if (!xfer->in_xfer) { +- xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0); +- } + + ret = usb_handle_packet(dev, &xfer->packet); + +@@ -1359,16 +1344,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx + + xfer->in_xfer = epctx->type>>2; + +- if (xfer->data && xfer->data_alloced < xfer->data_length) { +- xfer->data_alloced = 0; +- g_free(xfer->data); +- xfer->data = NULL; +- } +- if (!xfer->data && xfer->data_length) { +- DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length); +- xfer->data = g_malloc(xfer->data_length); +- xfer->data_alloced = xfer->data_length; +- } + if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { + xfer->pkts = 1; + } else { +@@ -1402,9 +1377,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx + return -1; + } + +- if (!xfer->in_xfer) { +- xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0); +- } + ret = usb_handle_packet(dev, &xfer->packet); + + xhci_complete_packet(xfer, ret); +@@ -1416,20 +1388,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx + + static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) + { +- int i; +- unsigned int length = 0; +- XHCITRB *trb; +- +- for (i = 0; i < xfer->trb_count; i++) { +- trb = &xfer->trbs[i]; +- if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) { +- length += trb->status & 0x1ffff; +- } +- } +- +- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length); +- +- xfer->data_length = length; ++ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); + return xhci_submit(xhci, xfer, epctx); + } + +diff --git a/trace-events b/trace-events +index 10bc04e..c83d65e 100644 +--- a/trace-events ++++ b/trace-events +@@ -326,7 +326,7 @@ usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" + usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" + usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" + usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +-usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t length) "%p: slotid %d, epid %d, length %d" ++usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d" + usb_xhci_xfer_async(void *xfer) "%p" + usb_xhci_xfer_nak(void *xfer) "%p" + usb_xhci_xfer_retry(void *xfer) "%p" +-- +1.7.12 + diff --git a/0333-xhci-move-device-lookup-into-xhci_setup_packet.patch b/0333-xhci-move-device-lookup-into-xhci_setup_packet.patch new file mode 100644 index 0000000..3cde29a --- /dev/null +++ b/0333-xhci-move-device-lookup-into-xhci_setup_packet.patch @@ -0,0 +1,155 @@ +From 76d49f5c5abc30f33f5b9288df68d53dba9e10f0 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 24 Aug 2012 14:21:39 +0200 +Subject: [PATCH 333/366] xhci: move device lookup into xhci_setup_packet + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 74 ++++++++++++++++++++++++++++--------------------------- + 1 file changed, 38 insertions(+), 36 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index c858b6d..303e1ac 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -1196,13 +1196,38 @@ static void xhci_stall_ep(XHCITransfer *xfer) + static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, + XHCIEPContext *epctx); + +-static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) ++static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) + { ++ if (!(port->portsc & PORTSC_PED)) { ++ return NULL; ++ } ++ return usb_find_device(&port->port, addr); ++} ++ ++static int xhci_setup_packet(XHCITransfer *xfer) ++{ ++ XHCIState *xhci = xfer->xhci; ++ XHCIPort *port; ++ USBDevice *dev; + USBEndpoint *ep; + int dir; + + dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; +- ep = usb_ep_get(dev, dir, xfer->epid >> 1); ++ ++ if (xfer->packet.ep) { ++ ep = xfer->packet.ep; ++ dev = ep->dev; ++ } else { ++ port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; ++ dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); ++ if (!dev) { ++ fprintf(stderr, "xhci: slot %d port %d has no device\n", ++ xfer->slotid, xhci->slots[xfer->slotid-1].port); ++ return -1; ++ } ++ ep = usb_ep_get(dev, dir, xfer->epid >> 1); ++ } ++ + usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr); + xhci_xfer_map(xfer); + DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", +@@ -1260,21 +1285,11 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) + return 0; + } + +-static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) +-{ +- if (!(port->portsc & PORTSC_PED)) { +- return NULL; +- } +- return usb_find_device(&port->port, addr); +-} +- + static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + { + XHCITRB *trb_setup, *trb_status; + uint8_t bmRequestType; + uint16_t wLength; +- XHCIPort *port; +- USBDevice *dev; + int ret; + + trb_setup = &xfer->trbs[0]; +@@ -1311,21 +1326,15 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + bmRequestType = trb_setup->parameter; + wLength = trb_setup->parameter >> 48; + +- port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; +- dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); +- if (!dev) { +- fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, +- xhci->slots[xfer->slotid-1].port); +- return -1; +- } +- + xfer->in_xfer = bmRequestType & USB_DIR_IN; + xfer->iso_xfer = false; + +- xhci_setup_packet(xfer, dev); ++ if (xhci_setup_packet(xfer) < 0) { ++ return -1; ++ } + xfer->packet.parameter = trb_setup->parameter; + +- ret = usb_handle_packet(dev, &xfer->packet); ++ ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); + + xhci_complete_packet(xfer, ret); + if (!xfer->running_async && !xfer->running_retry) { +@@ -1336,8 +1345,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + + static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) + { +- XHCIPort *port; +- USBDevice *dev; + int ret; + + DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); +@@ -1350,16 +1357,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx + xfer->pkts = 0; + } + +- port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; +- dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); +- if (!dev) { +- fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, +- xhci->slots[xfer->slotid-1].port); +- return -1; +- } +- +- xhci_setup_packet(xfer, dev); +- + switch(epctx->type) { + case ET_INTR_OUT: + case ET_INTR_IN: +@@ -1377,7 +1374,10 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx + return -1; + } + +- ret = usb_handle_packet(dev, &xfer->packet); ++ if (xhci_setup_packet(xfer) < 0) { ++ return -1; ++ } ++ ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); + + xhci_complete_packet(xfer, ret); + if (!xfer->running_async && !xfer->running_retry) { +@@ -1420,7 +1420,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid + + trace_usb_xhci_xfer_retry(xfer); + assert(xfer->running_retry); +- xhci_setup_packet(xfer, xfer->packet.ep->dev); ++ if (xhci_setup_packet(xfer) < 0) { ++ return; ++ } + result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); + if (result == USB_RET_NAK) { + return; +-- +1.7.12 + diff --git a/0334-xhci-implement-mfindex.patch b/0334-xhci-implement-mfindex.patch new file mode 100644 index 0000000..9f00140 --- /dev/null +++ b/0334-xhci-implement-mfindex.patch @@ -0,0 +1,142 @@ +From 6a1c694cd009fb7e4656e1c9a18756da6f89be14 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 21 Aug 2012 12:32:58 +0200 +Subject: [PATCH 334/366] xhci: implement mfindex + +Implement mfindex register and mfindex wrap event. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 46 insertions(+), 7 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 303e1ac..9077cb3 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -380,8 +380,6 @@ struct XHCIState { + XHCISlot slots[MAXSLOTS]; + + /* Runtime Registers */ +- uint32_t mfindex; +- /* note: we only support one interrupter */ + uint32_t iman; + uint32_t imod; + uint32_t erstsz; +@@ -390,6 +388,9 @@ struct XHCIState { + uint32_t erdp_low; + uint32_t erdp_high; + ++ int64_t mfindex_start; ++ QEMUTimer *mfwrap_timer; ++ + dma_addr_t er_start; + uint32_t er_size; + bool er_pcs; +@@ -410,6 +411,11 @@ typedef struct XHCIEvRingSeg { + uint32_t rsvd; + } XHCIEvRingSeg; + ++static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, ++ unsigned int epid); ++static void xhci_event(XHCIState *xhci, XHCIEvent *event); ++static void xhci_write_event(XHCIState *xhci, XHCIEvent *event); ++ + static const char *TRBType_names[] = { + [TRB_RESERVED] = "TRB_RESERVED", + [TR_NORMAL] = "TR_NORMAL", +@@ -462,8 +468,36 @@ static const char *trb_name(XHCITRB *trb) + ARRAY_SIZE(TRBType_names)); + } + +-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, +- unsigned int epid); ++static uint64_t xhci_mfindex_get(XHCIState *xhci) ++{ ++ int64_t now = qemu_get_clock_ns(vm_clock); ++ return (now - xhci->mfindex_start) / 125000; ++} ++ ++static void xhci_mfwrap_update(XHCIState *xhci) ++{ ++ const uint32_t bits = USBCMD_RS | USBCMD_EWE; ++ uint32_t mfindex, left; ++ int64_t now; ++ ++ if ((xhci->usbcmd & bits) == bits) { ++ now = qemu_get_clock_ns(vm_clock); ++ mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff; ++ left = 0x4000 - mfindex; ++ qemu_mod_timer(xhci->mfwrap_timer, now + left * 125000); ++ } else { ++ qemu_del_timer(xhci->mfwrap_timer); ++ } ++} ++ ++static void xhci_mfwrap_timer(void *opaque) ++{ ++ XHCIState *xhci = opaque; ++ XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS }; ++ ++ xhci_event(xhci, &wrap); ++ xhci_mfwrap_update(xhci); ++} + + static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high) + { +@@ -793,6 +827,7 @@ static void xhci_run(XHCIState *xhci) + { + trace_usb_xhci_run(); + xhci->usbsts &= ~USBSTS_HCH; ++ xhci->mfindex_start = qemu_get_clock_ns(vm_clock); + } + + static void xhci_stop(XHCIState *xhci) +@@ -2050,7 +2085,6 @@ static void xhci_reset(DeviceState *dev) + xhci_update_port(xhci, xhci->ports + i, 0); + } + +- xhci->mfindex = 0; + xhci->iman = 0; + xhci->imod = 0; + xhci->erstsz = 0; +@@ -2064,6 +2098,9 @@ static void xhci_reset(DeviceState *dev) + xhci->er_full = 0; + xhci->ev_buffer_put = 0; + xhci->ev_buffer_get = 0; ++ ++ xhci->mfindex_start = qemu_get_clock_ns(vm_clock); ++ xhci_mfwrap_update(xhci); + } + + static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) +@@ -2266,6 +2303,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) + xhci_stop(xhci); + } + xhci->usbcmd = val & 0xc0f; ++ xhci_mfwrap_update(xhci); + if (val & USBCMD_HCRST) { + xhci_reset(&xhci->pci_dev.qdev); + } +@@ -2317,8 +2355,7 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + + switch (reg) { + case 0x00: /* MFINDEX */ +- fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n"); +- ret = xhci->mfindex; ++ ret = xhci_mfindex_get(xhci) & 0x3fff; + break; + case 0x20: /* IMAN */ + ret = xhci->iman; +@@ -2618,6 +2655,8 @@ static int usb_xhci_initfn(struct PCIDevice *dev) + + usb_xhci_init(xhci, &dev->qdev); + ++ xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci); ++ + xhci->irq = xhci->pci_dev.irq[0]; + + memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci, +-- +1.7.12 + diff --git a/0335-xhci-iso-xfer-support.patch b/0335-xhci-iso-xfer-support.patch new file mode 100644 index 0000000..ae88244 --- /dev/null +++ b/0335-xhci-iso-xfer-support.patch @@ -0,0 +1,238 @@ +From 9f7a9361a816479ca109e1747e45d40135fad3b0 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 24 Aug 2012 14:13:08 +0200 +Subject: [PATCH 335/366] xhci: iso xfer support + +Add support for iso transfers. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 101 insertions(+), 16 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 9077cb3..3c61bb8 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -325,9 +325,15 @@ typedef struct XHCITransfer { + unsigned int pkts; + unsigned int pktsize; + unsigned int cur_pkt; ++ ++ uint64_t mfindex_kick; + } XHCITransfer; + + typedef struct XHCIEPContext { ++ XHCIState *xhci; ++ unsigned int slotid; ++ unsigned int epid; ++ + XHCIRing ring; + unsigned int next_xfer; + unsigned int comp_xfer; +@@ -337,6 +343,11 @@ typedef struct XHCIEPContext { + dma_addr_t pctx; + unsigned int max_psize; + uint32_t state; ++ ++ /* iso xfer scheduling */ ++ unsigned int interval; ++ int64_t mfindex_last; ++ QEMUTimer *kick_timer; + } XHCIEPContext; + + typedef struct XHCISlot { +@@ -856,6 +867,12 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, + epctx->state = state; + } + ++static void xhci_ep_kick_timer(void *opaque) ++{ ++ XHCIEPContext *epctx = opaque; ++ xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid); ++} ++ + static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid, dma_addr_t pctx, + uint32_t *ctx) +@@ -877,6 +894,9 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, + + epctx = g_malloc(sizeof(XHCIEPContext)); + memset(epctx, 0, sizeof(XHCIEPContext)); ++ epctx->xhci = xhci; ++ epctx->slotid = slotid; ++ epctx->epid = epid; + + slot->eps[epid-1] = epctx; + +@@ -895,6 +915,10 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, + usb_packet_init(&epctx->transfers[i].packet); + } + ++ epctx->interval = 1 << (ctx[0] >> 16) & 0xff; ++ epctx->mfindex_last = 0; ++ epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx); ++ + epctx->state = EP_RUNNING; + ctx[0] &= ~EP_STATE_MASK; + ctx[0] |= EP_RUNNING; +@@ -934,6 +958,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, + if (t->running_retry) { + t->running_retry = 0; + epctx->retry = NULL; ++ qemu_del_timer(epctx->kick_timer); + } + if (t->trbs) { + g_free(t->trbs); +@@ -969,6 +994,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, + + xhci_set_ep_state(xhci, epctx, EP_DISABLED); + ++ qemu_free_timer(epctx->kick_timer); + g_free(epctx); + slot->eps[epid-1] = NULL; + +@@ -1378,29 +1404,70 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + return 0; + } + ++static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer, ++ XHCIEPContext *epctx, uint64_t mfindex) ++{ ++ if (xfer->trbs[0].control & TRB_TR_SIA) { ++ uint64_t asap = ((mfindex + epctx->interval - 1) & ++ ~(epctx->interval-1)); ++ if (asap >= epctx->mfindex_last && ++ asap <= epctx->mfindex_last + epctx->interval * 4) { ++ xfer->mfindex_kick = epctx->mfindex_last + epctx->interval; ++ } else { ++ xfer->mfindex_kick = asap; ++ } ++ } else { ++ xfer->mfindex_kick = (xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT) ++ & TRB_TR_FRAMEID_MASK; ++ xfer->mfindex_kick |= mfindex & ~0x3fff; ++ if (xfer->mfindex_kick < mfindex) { ++ xfer->mfindex_kick += 0x4000; ++ } ++ } ++} ++ ++static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer, ++ XHCIEPContext *epctx, uint64_t mfindex) ++{ ++ if (xfer->mfindex_kick > mfindex) { ++ qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock) + ++ (xfer->mfindex_kick - mfindex) * 125000); ++ xfer->running_retry = 1; ++ } else { ++ epctx->mfindex_last = xfer->mfindex_kick; ++ qemu_del_timer(epctx->kick_timer); ++ xfer->running_retry = 0; ++ } ++} ++ ++ + static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) + { ++ uint64_t mfindex; + int ret; + + DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); + + xfer->in_xfer = epctx->type>>2; + +- if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { +- xfer->pkts = 1; +- } else { +- xfer->pkts = 0; +- } +- + switch(epctx->type) { + case ET_INTR_OUT: + case ET_INTR_IN: + case ET_BULK_OUT: + case ET_BULK_IN: ++ xfer->pkts = 0; ++ xfer->iso_xfer = false; + break; + case ET_ISO_OUT: + case ET_ISO_IN: +- FIXME(); ++ xfer->pkts = 1; ++ xfer->iso_xfer = true; ++ mfindex = xhci_mfindex_get(xhci); ++ xhci_calc_iso_kick(xhci, xfer, epctx, mfindex); ++ xhci_check_iso_kick(xhci, xfer, epctx, mfindex); ++ if (xfer->running_retry) { ++ return -1; ++ } + break; + default: + fprintf(stderr, "xhci: unknown or unhandled EP " +@@ -1430,6 +1497,7 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid) + { + XHCIEPContext *epctx; ++ uint64_t mfindex; + int length; + int i; + +@@ -1449,20 +1517,35 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid + } + + if (epctx->retry) { +- /* retry nak'ed transfer */ + XHCITransfer *xfer = epctx->retry; + int result; + + trace_usb_xhci_xfer_retry(xfer); + assert(xfer->running_retry); +- if (xhci_setup_packet(xfer) < 0) { +- return; +- } +- result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); +- if (result == USB_RET_NAK) { +- return; ++ if (xfer->iso_xfer) { ++ /* retry delayed iso transfer */ ++ mfindex = xhci_mfindex_get(xhci); ++ xhci_check_iso_kick(xhci, xfer, epctx, mfindex); ++ if (xfer->running_retry) { ++ return; ++ } ++ if (xhci_setup_packet(xfer) < 0) { ++ return; ++ } ++ result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); ++ assert(result != USB_RET_NAK); ++ xhci_complete_packet(xfer, result); ++ } else { ++ /* retry nak'ed transfer */ ++ if (xhci_setup_packet(xfer) < 0) { ++ return; ++ } ++ result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); ++ if (result == USB_RET_NAK) { ++ return; ++ } ++ xhci_complete_packet(xfer, result); + } +- xhci_complete_packet(xfer, result); + assert(!xfer->running_retry); + epctx->retry = NULL; + } +@@ -1514,7 +1597,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid + if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { + epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; + } else { +- fprintf(stderr, "xhci: error firing data transfer\n"); ++ if (!xfer->iso_xfer) { ++ fprintf(stderr, "xhci: error firing data transfer\n"); ++ } + } + } + +-- +1.7.12 + diff --git a/0336-xhci-trace-cc-codes-in-cleartext.patch b/0336-xhci-trace-cc-codes-in-cleartext.patch new file mode 100644 index 0000000..096d194 --- /dev/null +++ b/0336-xhci-trace-cc-codes-in-cleartext.patch @@ -0,0 +1,100 @@ +From 530efbfebc3b33f6b109e52d57a25e9b7fe2b588 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 27 Aug 2012 16:09:20 +0200 +Subject: [PATCH 336/366] xhci: trace cc codes in cleartext + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- + trace-events | 2 +- + 2 files changed, 48 insertions(+), 2 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 3c61bb8..ab32a7b 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -465,6 +465,45 @@ static const char *TRBType_names[] = { + [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE", + }; + ++static const char *TRBCCode_names[] = { ++ [CC_INVALID] = "CC_INVALID", ++ [CC_SUCCESS] = "CC_SUCCESS", ++ [CC_DATA_BUFFER_ERROR] = "CC_DATA_BUFFER_ERROR", ++ [CC_BABBLE_DETECTED] = "CC_BABBLE_DETECTED", ++ [CC_USB_TRANSACTION_ERROR] = "CC_USB_TRANSACTION_ERROR", ++ [CC_TRB_ERROR] = "CC_TRB_ERROR", ++ [CC_STALL_ERROR] = "CC_STALL_ERROR", ++ [CC_RESOURCE_ERROR] = "CC_RESOURCE_ERROR", ++ [CC_BANDWIDTH_ERROR] = "CC_BANDWIDTH_ERROR", ++ [CC_NO_SLOTS_ERROR] = "CC_NO_SLOTS_ERROR", ++ [CC_INVALID_STREAM_TYPE_ERROR] = "CC_INVALID_STREAM_TYPE_ERROR", ++ [CC_SLOT_NOT_ENABLED_ERROR] = "CC_SLOT_NOT_ENABLED_ERROR", ++ [CC_EP_NOT_ENABLED_ERROR] = "CC_EP_NOT_ENABLED_ERROR", ++ [CC_SHORT_PACKET] = "CC_SHORT_PACKET", ++ [CC_RING_UNDERRUN] = "CC_RING_UNDERRUN", ++ [CC_RING_OVERRUN] = "CC_RING_OVERRUN", ++ [CC_VF_ER_FULL] = "CC_VF_ER_FULL", ++ [CC_PARAMETER_ERROR] = "CC_PARAMETER_ERROR", ++ [CC_BANDWIDTH_OVERRUN] = "CC_BANDWIDTH_OVERRUN", ++ [CC_CONTEXT_STATE_ERROR] = "CC_CONTEXT_STATE_ERROR", ++ [CC_NO_PING_RESPONSE_ERROR] = "CC_NO_PING_RESPONSE_ERROR", ++ [CC_EVENT_RING_FULL_ERROR] = "CC_EVENT_RING_FULL_ERROR", ++ [CC_INCOMPATIBLE_DEVICE_ERROR] = "CC_INCOMPATIBLE_DEVICE_ERROR", ++ [CC_MISSED_SERVICE_ERROR] = "CC_MISSED_SERVICE_ERROR", ++ [CC_COMMAND_RING_STOPPED] = "CC_COMMAND_RING_STOPPED", ++ [CC_COMMAND_ABORTED] = "CC_COMMAND_ABORTED", ++ [CC_STOPPED] = "CC_STOPPED", ++ [CC_STOPPED_LENGTH_INVALID] = "CC_STOPPED_LENGTH_INVALID", ++ [CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR] ++ = "CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR", ++ [CC_ISOCH_BUFFER_OVERRUN] = "CC_ISOCH_BUFFER_OVERRUN", ++ [CC_EVENT_LOST_ERROR] = "CC_EVENT_LOST_ERROR", ++ [CC_UNDEFINED_ERROR] = "CC_UNDEFINED_ERROR", ++ [CC_INVALID_STREAM_ID_ERROR] = "CC_INVALID_STREAM_ID_ERROR", ++ [CC_SECONDARY_BANDWIDTH_ERROR] = "CC_SECONDARY_BANDWIDTH_ERROR", ++ [CC_SPLIT_TRANSACTION_ERROR] = "CC_SPLIT_TRANSACTION_ERROR", ++}; ++ + static const char *lookup_name(uint32_t index, const char **list, uint32_t llen) + { + if (index >= llen || list[index] == NULL) { +@@ -479,6 +518,12 @@ static const char *trb_name(XHCITRB *trb) + ARRAY_SIZE(TRBType_names)); + } + ++static const char *event_name(XHCIEvent *event) ++{ ++ return lookup_name(event->ccode, TRBCCode_names, ++ ARRAY_SIZE(TRBCCode_names)); ++} ++ + static uint64_t xhci_mfindex_get(XHCIState *xhci) + { + int64_t now = qemu_get_clock_ns(vm_clock); +@@ -574,7 +619,8 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) + ev_trb.control = cpu_to_le32(ev_trb.control); + + trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb), +- ev_trb.parameter, ev_trb.status, ev_trb.control); ++ event_name(event), ev_trb.parameter, ++ ev_trb.status, ev_trb.control); + + addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx; + pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE); +diff --git a/trace-events b/trace-events +index c83d65e..27d59cd 100644 +--- a/trace-events ++++ b/trace-events +@@ -313,7 +313,7 @@ usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" + usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" + usb_xhci_irq_intx(uint32_t level) "level %d" + usb_xhci_irq_msi(uint32_t nr) "nr %d" +-usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x" ++usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x" + usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" + usb_xhci_slot_enable(uint32_t slotid) "slotid %d" + usb_xhci_slot_disable(uint32_t slotid) "slotid %d" +-- +1.7.12 + diff --git a/0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch b/0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch new file mode 100644 index 0000000..c36d7d8 --- /dev/null +++ b/0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch @@ -0,0 +1,39 @@ +From 53adf697ca70ee8298b6abeb05ebf5a0ebebdc1c Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 29 Aug 2012 12:54:59 +0200 +Subject: [PATCH 337/366] xhci: add trace_usb_xhci_ep_set_dequeue + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 2 +- + trace-events | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index ab32a7b..5cdaf76 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -1145,7 +1145,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, + return CC_TRB_ERROR; + } + +- DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue); ++ trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue); + dequeue = xhci_mask64(pdequeue); + + slot = &xhci->slots[slotid-1]; +diff --git a/trace-events b/trace-events +index 27d59cd..a894689 100644 +--- a/trace-events ++++ b/trace-events +@@ -323,6 +323,7 @@ usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d" + usb_xhci_slot_reset(uint32_t slotid) "slotid %d" + usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" + usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" ++usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64 + usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" + usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" + usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +-- +1.7.12 + diff --git a/0338-xhci-fix-runtime-write-tracepoint.patch b/0338-xhci-fix-runtime-write-tracepoint.patch new file mode 100644 index 0000000..a2eebfd --- /dev/null +++ b/0338-xhci-fix-runtime-write-tracepoint.patch @@ -0,0 +1,26 @@ +From 9c1e6303c5c86f20ed083a0ed9c829c08fd2ac06 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 30 Aug 2012 12:42:32 +0200 +Subject: [PATCH 338/366] xhci: fix runtime write tracepoint + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 5cdaf76..e8d2372 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -2520,7 +2520,7 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + + static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) + { +- trace_usb_xhci_runtime_read(reg, val); ++ trace_usb_xhci_runtime_write(reg, val); + + switch (reg) { + case 0x20: /* IMAN */ +-- +1.7.12 + diff --git a/0339-xhci-update-register-layout.patch b/0339-xhci-update-register-layout.patch new file mode 100644 index 0000000..7fc8af1 --- /dev/null +++ b/0339-xhci-update-register-layout.patch @@ -0,0 +1,63 @@ +From 56a4ea65ecc929a355b0cc243099d2e3a9862843 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 23 Aug 2012 13:26:25 +0200 +Subject: [PATCH 339/366] xhci: update register layout + +Change the register layout to be a bit more sparse and also not depend +on the number of ports. Useful when for making the number of ports +runtime-configurable. +--- + hw/usb/hcd-xhci.c | 21 +++++++++++++-------- + 1 file changed, 13 insertions(+), 8 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index e8d2372..414b633 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -36,13 +36,12 @@ + #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \ + __func__, __LINE__); abort(); } while (0) + +-#define MAXSLOTS 8 +-#define MAXINTRS 1 +- + #define USB2_PORTS 4 + #define USB3_PORTS 4 + + #define MAXPORTS (USB2_PORTS+USB3_PORTS) ++#define MAXSLOTS MAXPORTS ++#define MAXINTRS 1 /* MAXPORTS */ + + #define TD_QUEUE 24 + +@@ -53,16 +52,22 @@ + #define ER_FULL_HACK + + #define LEN_CAP 0x40 +-#define OFF_OPER LEN_CAP + #define LEN_OPER (0x400 + 0x10 * MAXPORTS) +-#define OFF_RUNTIME ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f) +-#define LEN_RUNTIME (0x20 + MAXINTRS * 0x20) +-#define OFF_DOORBELL (OFF_RUNTIME + LEN_RUNTIME) ++#define LEN_RUNTIME ((MAXINTRS + 1) * 0x20) + #define LEN_DOORBELL ((MAXSLOTS + 1) * 0x20) + ++#define OFF_OPER LEN_CAP ++#define OFF_RUNTIME 0x1000 ++#define OFF_DOORBELL 0x2000 + /* must be power of 2 */ +-#define LEN_REGS 0x2000 ++#define LEN_REGS 0x4000 + ++#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME ++#error Increase OFF_RUNTIME ++#endif ++#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL ++#error Increase OFF_DOORBELL ++#endif + #if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS + # error Increase LEN_REGS + #endif +-- +1.7.12 + diff --git a/0340-xhci-update-port-handling.patch b/0340-xhci-update-port-handling.patch new file mode 100644 index 0000000..289fa52 --- /dev/null +++ b/0340-xhci-update-port-handling.patch @@ -0,0 +1,352 @@ +From ed16ece865cb40c9ad8ddf28905c0af3ad80e118 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 28 Aug 2012 13:38:01 +0200 +Subject: [PATCH 340/366] xhci: update port handling + +This patch changes the way xhci ports are linked to USBPorts. The fixed +1:1 relationship between xhci ports and USBPorts is gone. Now each +USBPort represents a physical plug which has usually two xhci ports +assigned: one usb2 and ond usb3 port. usb devices show up at one or the +other, depending on whenever they support superspeed or not. + +This patch also makes the number of usb2 and usb3 ports runtime +configurable by adding 'p2' and 'p3' properties. It is allowed to +have different numbers of usb2 and usb3 ports. Specifying p2=4,p3=2 +will give you an xhci adapter which supports all speeds on physical +ports 1+2 and usb2 only on ports 3+4. +--- + hw/usb/hcd-xhci.c | 137 ++++++++++++++++++++++++++++++++++++++---------------- + 1 file changed, 97 insertions(+), 40 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 414b633..e08312e 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -36,10 +36,10 @@ + #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \ + __func__, __LINE__); abort(); } while (0) + +-#define USB2_PORTS 4 +-#define USB3_PORTS 4 ++#define MAXPORTS_2 8 ++#define MAXPORTS_3 8 + +-#define MAXPORTS (USB2_PORTS+USB3_PORTS) ++#define MAXPORTS (MAXPORTS_2+MAXPORTS_3) + #define MAXSLOTS MAXPORTS + #define MAXINTRS 1 /* MAXPORTS */ + +@@ -300,8 +300,10 @@ typedef struct XHCIRing { + } XHCIRing; + + typedef struct XHCIPort { +- USBPort port; + uint32_t portsc; ++ uint32_t portnr; ++ USBPort *uport; ++ uint32_t speedmask; + } XHCIPort; + + struct XHCIState; +@@ -379,9 +381,13 @@ struct XHCIState { + qemu_irq irq; + MemoryRegion mem; + const char *name; +- uint32_t msi; + unsigned int devaddr; + ++ /* properties */ ++ uint32_t numports_2; ++ uint32_t numports_3; ++ uint32_t msi; ++ + /* Operational Registers */ + uint32_t usbcmd; + uint32_t usbsts; +@@ -392,8 +398,10 @@ struct XHCIState { + uint32_t dcbaap_high; + uint32_t config; + ++ USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)]; + XHCIPort ports[MAXPORTS]; + XHCISlot slots[MAXSLOTS]; ++ uint32_t numports; + + /* Runtime Registers */ + uint32_t iman; +@@ -578,6 +586,28 @@ static inline dma_addr_t xhci_mask64(uint64_t addr) + } + } + ++static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) ++{ ++ int index; ++ ++ if (!uport->dev) { ++ return NULL; ++ } ++ switch (uport->dev->speed) { ++ case USB_SPEED_LOW: ++ case USB_SPEED_FULL: ++ case USB_SPEED_HIGH: ++ index = uport->index; ++ break; ++ case USB_SPEED_SUPER: ++ index = uport->index + xhci->numports_2; ++ break; ++ default: ++ return NULL; ++ } ++ return &xhci->ports[index]; ++} ++ + static void xhci_irq_update(XHCIState *xhci) + { + int level = 0; +@@ -1126,7 +1156,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, + ep |= 0x80; + } + +- dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev; ++ dev = xhci->ports[xhci->slots[slotid-1].port-1].uport->dev; + if (!dev) { + return CC_USB_TRANSACTION_ERROR; + } +@@ -1313,7 +1343,7 @@ static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) + if (!(port->portsc & PORTSC_PED)) { + return NULL; + } +- return usb_find_device(&port->port, addr); ++ return usb_find_device(port->uport, addr); + } + + static int xhci_setup_packet(XHCITransfer *xfer) +@@ -1736,9 +1766,9 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, + ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); + + port = (slot_ctx[1]>>16) & 0xFF; +- dev = xhci->ports[port-1].port.dev; ++ dev = xhci->ports[port-1].uport->dev; + +- if (port < 1 || port > MAXPORTS) { ++ if (port < 1 || port > xhci->numports) { + fprintf(stderr, "xhci: bad port %d\n", port); + return CC_TRB_ERROR; + } else if (!dev) { +@@ -1987,7 +2017,7 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr + static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) + { + dma_addr_t ctx; +- uint8_t bw_ctx[MAXPORTS+1]; ++ uint8_t bw_ctx[xhci->numports+1]; + + DPRINTF("xhci_get_port_bandwidth()\n"); + +@@ -1997,7 +2027,7 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) + + /* TODO: actually implement real values here */ + bw_ctx[0] = 0; +- memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */ ++ memset(&bw_ctx[1], 80, xhci->numports); /* 80% */ + pci_dma_write(&xhci->pci_dev, ctx, bw_ctx, sizeof(bw_ctx)); + + return CC_SUCCESS; +@@ -2167,12 +2197,11 @@ static void xhci_process_commands(XHCIState *xhci) + + static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) + { +- int nr = port->port.index + 1; +- + port->portsc = PORTSC_PP; +- if (port->port.dev && port->port.dev->attached && !is_detach) { ++ if (port->uport->dev && port->uport->dev->attached && !is_detach && ++ (1 << port->uport->dev->speed) & port->speedmask) { + port->portsc |= PORTSC_CCS; +- switch (port->port.dev->speed) { ++ switch (port->uport->dev->speed) { + case USB_SPEED_LOW: + port->portsc |= PORTSC_SPEED_LOW; + break; +@@ -2182,14 +2211,18 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) + case USB_SPEED_HIGH: + port->portsc |= PORTSC_SPEED_HIGH; + break; ++ case USB_SPEED_SUPER: ++ port->portsc |= PORTSC_SPEED_SUPER; ++ break; + } + } + + if (xhci_running(xhci)) { + port->portsc |= PORTSC_CSC; +- XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24}; ++ XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, ++ port->portnr << 24}; + xhci_event(xhci, &ev); +- DPRINTF("xhci: port change event for port %d\n", nr); ++ DPRINTF("xhci: port change event for port %d\n", port->portnr); + } + } + +@@ -2217,7 +2250,7 @@ static void xhci_reset(DeviceState *dev) + xhci_disable_slot(xhci, i+1); + } + +- for (i = 0; i < MAXPORTS; i++) { ++ for (i = 0; i < xhci->numports; i++) { + xhci_update_port(xhci, xhci->ports + i, 0); + } + +@@ -2248,7 +2281,8 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) + ret = 0x01000000 | LEN_CAP; + break; + case 0x04: /* HCSPARAMS 1 */ +- ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS; ++ ret = ((xhci->numports_2+xhci->numports_3)<<24) ++ | (MAXINTRS<<8) | MAXSLOTS; + break; + case 0x08: /* HCSPARAMS 2 */ + ret = 0x0000000f; +@@ -2278,7 +2312,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) + ret = 0x20425455; /* "USB " */ + break; + case 0x28: /* Supported Protocol:08 */ +- ret = 0x00000001 | (USB2_PORTS<<8); ++ ret = 0x00000001 | (xhci->numports_2<<8); + break; + case 0x2c: /* Supported Protocol:0c */ + ret = 0x00000000; /* reserved */ +@@ -2290,7 +2324,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) + ret = 0x20425455; /* "USB " */ + break; + case 0x38: /* Supported Protocol:08 */ +- ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8); ++ ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8); + break; + case 0x3c: /* Supported Protocol:0c */ + ret = 0x00000000; /* reserved */ +@@ -2309,7 +2343,7 @@ static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg) + uint32_t port = reg >> 4; + uint32_t ret; + +- if (port >= MAXPORTS) { ++ if (port >= xhci->numports) { + fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); + ret = 0; + goto out; +@@ -2342,7 +2376,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) + + trace_usb_xhci_port_write(port, reg & 0x0f, val); + +- if (port >= MAXPORTS) { ++ if (port >= xhci->numports) { + fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); + return; + } +@@ -2364,7 +2398,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) + /* write-1-to-start bits */ + if (val & PORTSC_PR) { + DPRINTF("xhci: port %d reset\n", port); +- usb_device_reset(xhci->ports[port].port.dev); ++ usb_device_reset(xhci->ports[port].uport->dev); + portsc |= PORTSC_PRC | PORTSC_PED; + } + xhci->ports[port].portsc = portsc; +@@ -2659,7 +2693,7 @@ static const MemoryRegionOps xhci_mem_ops = { + static void xhci_attach(USBPort *usbport) + { + XHCIState *xhci = usbport->opaque; +- XHCIPort *port = &xhci->ports[usbport->index]; ++ XHCIPort *port = xhci_lookup_port(xhci, usbport); + + xhci_update_port(xhci, port, 0); + } +@@ -2667,7 +2701,7 @@ static void xhci_attach(USBPort *usbport) + static void xhci_detach(USBPort *usbport) + { + XHCIState *xhci = usbport->opaque; +- XHCIPort *port = &xhci->ports[usbport->index]; ++ XHCIPort *port = xhci_lookup_port(xhci, usbport); + + xhci_update_port(xhci, port, 1); + } +@@ -2675,9 +2709,9 @@ static void xhci_detach(USBPort *usbport) + static void xhci_wakeup(USBPort *usbport) + { + XHCIState *xhci = usbport->opaque; +- XHCIPort *port = &xhci->ports[usbport->index]; +- int nr = port->port.index + 1; +- XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24}; ++ XHCIPort *port = xhci_lookup_port(xhci, usbport); ++ XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, ++ port->portnr << 24}; + uint32_t pls; + + pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; +@@ -2759,22 +2793,43 @@ static USBBusOps xhci_bus_ops = { + + static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) + { +- int i; ++ XHCIPort *port; ++ int i, usbports, speedmask; + + xhci->usbsts = USBSTS_HCH; + ++ if (xhci->numports_2 > MAXPORTS_2) { ++ xhci->numports_2 = MAXPORTS_2; ++ } ++ if (xhci->numports_3 > MAXPORTS_3) { ++ xhci->numports_3 = MAXPORTS_3; ++ } ++ usbports = MAX(xhci->numports_2, xhci->numports_3); ++ xhci->numports = xhci->numports_2 + xhci->numports_3; ++ + usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev); + +- for (i = 0; i < MAXPORTS; i++) { +- memset(&xhci->ports[i], 0, sizeof(xhci->ports[i])); +- usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i, +- &xhci_port_ops, +- USB_SPEED_MASK_LOW | +- USB_SPEED_MASK_FULL | +- USB_SPEED_MASK_HIGH); +- } +- for (i = 0; i < MAXSLOTS; i++) { +- xhci->slots[i].enabled = 0; ++ for (i = 0; i < usbports; i++) { ++ speedmask = 0; ++ if (i < xhci->numports_2) { ++ port = &xhci->ports[i]; ++ port->portnr = i + 1; ++ port->uport = &xhci->uports[i]; ++ port->speedmask = ++ USB_SPEED_MASK_LOW | ++ USB_SPEED_MASK_FULL | ++ USB_SPEED_MASK_HIGH; ++ speedmask |= port->speedmask; ++ } ++ if (i < xhci->numports_3) { ++ port = &xhci->ports[i + xhci->numports_2]; ++ port->portnr = i + 1 + xhci->numports_2; ++ port->uport = &xhci->uports[i]; ++ port->speedmask = USB_SPEED_MASK_SUPER; ++ speedmask |= port->speedmask; ++ } ++ usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i, ++ &xhci_port_ops, speedmask); + } + } + +@@ -2830,6 +2885,8 @@ static const VMStateDescription vmstate_xhci = { + + static Property xhci_properties[] = { + DEFINE_PROP_UINT32("msi", XHCIState, msi, 0), ++ DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), ++ DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), + DEFINE_PROP_END_OF_LIST(), + }; + +-- +1.7.12 + diff --git a/0341-usb3-superspeed-descriptors.patch b/0341-usb3-superspeed-descriptors.patch new file mode 100644 index 0000000..05591fe --- /dev/null +++ b/0341-usb3-superspeed-descriptors.patch @@ -0,0 +1,64 @@ +From f25f31e864756f27f6a94ab7e66b20061291ffa5 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 28 Aug 2012 17:28:50 +0200 +Subject: [PATCH 341/366] usb3: superspeed descriptors + +Add superspeed descriptor entry to USBDesc, +advertise superspeed support when present. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/desc.c | 10 +++++++++- + hw/usb/desc.h | 1 + + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/desc.c b/hw/usb/desc.c +index 0a9d3c9..3e8c6cb 100644 +--- a/hw/usb/desc.c ++++ b/hw/usb/desc.c +@@ -359,6 +359,9 @@ static void usb_desc_setdefaults(USBDevice *dev) + case USB_SPEED_HIGH: + dev->device = desc->high; + break; ++ case USB_SPEED_SUPER: ++ dev->device = desc->super; ++ break; + } + usb_desc_set_config(dev, 0); + } +@@ -376,6 +379,9 @@ void usb_desc_init(USBDevice *dev) + if (desc->high) { + dev->speedmask |= USB_SPEED_MASK_HIGH; + } ++ if (desc->super) { ++ dev->speedmask |= USB_SPEED_MASK_SUPER; ++ } + usb_desc_setdefaults(dev); + } + +@@ -384,7 +390,9 @@ void usb_desc_attach(USBDevice *dev) + const USBDesc *desc = usb_device_get_usb_desc(dev); + + assert(desc != NULL); +- if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) { ++ if (desc->super && (dev->port->speedmask & USB_SPEED_MASK_SUPER)) { ++ dev->speed = USB_SPEED_SUPER; ++ } else if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) { + dev->speed = USB_SPEED_HIGH; + } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) { + dev->speed = USB_SPEED_FULL; +diff --git a/hw/usb/desc.h b/hw/usb/desc.h +index 7cf5442..d89fa41 100644 +--- a/hw/usb/desc.h ++++ b/hw/usb/desc.h +@@ -152,6 +152,7 @@ struct USBDesc { + USBDescID id; + const USBDescDevice *full; + const USBDescDevice *high; ++ const USBDescDevice *super; + const char* const *str; + }; + +-- +1.7.12 + diff --git a/0342-usb3-superspeed-endpoint-companion.patch b/0342-usb3-superspeed-endpoint-companion.patch new file mode 100644 index 0000000..7949f8f --- /dev/null +++ b/0342-usb3-superspeed-endpoint-companion.patch @@ -0,0 +1,248 @@ +From 5a02a74430f4628a78f43242afbb1377deea4c80 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 28 Aug 2012 17:28:03 +0200 +Subject: [PATCH 342/366] usb3: superspeed endpoint companion + +Add support for building superspeed endpoint companion descriptors, +create them for superspeed usb devices. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb.h | 1 + + hw/usb/desc.c | 55 ++++++++++++++++++++++++++++++++++++++++--------------- + hw/usb/desc.h | 26 +++++++++++++++++++++----- + 3 files changed, 62 insertions(+), 20 deletions(-) + +diff --git a/hw/usb.h b/hw/usb.h +index 684e3f4..78ffdf4 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -137,6 +137,7 @@ + #define USB_DT_INTERFACE_ASSOC 0x0B + #define USB_DT_CS_INTERFACE 0x24 + #define USB_DT_CS_ENDPOINT 0x25 ++#define USB_DT_ENDPOINT_COMPANION 0x30 + + #define USB_ENDPOINT_XFER_CONTROL 0 + #define USB_ENDPOINT_XFER_ISOC 1 +diff --git a/hw/usb/desc.c b/hw/usb/desc.c +index 3e8c6cb..8f5a8e5 100644 +--- a/hw/usb/desc.c ++++ b/hw/usb/desc.c +@@ -76,7 +76,8 @@ int usb_desc_device_qualifier(const USBDescDevice *dev, + return bLength; + } + +-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) ++int usb_desc_config(const USBDescConfig *conf, int flags, ++ uint8_t *dest, size_t len) + { + uint8_t bLength = 0x09; + uint16_t wTotalLength = 0; +@@ -99,7 +100,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) + + /* handle grouped interfaces if any */ + for (i = 0; i < conf->nif_groups; i++) { +- rc = usb_desc_iface_group(&(conf->if_groups[i]), ++ rc = usb_desc_iface_group(&(conf->if_groups[i]), flags, + dest + wTotalLength, + len - wTotalLength); + if (rc < 0) { +@@ -110,7 +111,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) + + /* handle normal (ungrouped / no IAD) interfaces if any */ + for (i = 0; i < conf->nif; i++) { +- rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength); ++ rc = usb_desc_iface(conf->ifs + i, flags, ++ dest + wTotalLength, len - wTotalLength); + if (rc < 0) { + return rc; + } +@@ -122,8 +124,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) + return wTotalLength; + } + +-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, +- size_t len) ++int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, ++ uint8_t *dest, size_t len) + { + int pos = 0; + int i = 0; +@@ -147,7 +149,7 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, + + /* handle associated interfaces in this group */ + for (i = 0; i < iad->nif; i++) { +- int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos); ++ int rc = usb_desc_iface(&(iad->ifs[i]), flags, dest + pos, len - pos); + if (rc < 0) { + return rc; + } +@@ -157,7 +159,8 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, + return pos; + } + +-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) ++int usb_desc_iface(const USBDescIface *iface, int flags, ++ uint8_t *dest, size_t len) + { + uint8_t bLength = 0x09; + int i, rc, pos = 0; +@@ -188,7 +191,7 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) + } + + for (i = 0; i < iface->bNumEndpoints; i++) { +- rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos); ++ rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos); + if (rc < 0) { + return rc; + } +@@ -198,13 +201,15 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) + return pos; + } + +-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len) ++int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, ++ uint8_t *dest, size_t len) + { + uint8_t bLength = ep->is_audio ? 0x09 : 0x07; + uint8_t extralen = ep->extra ? ep->extra[0] : 0; ++ uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0; + USBDescriptor *d = (void *)dest; + +- if (len < bLength + extralen) { ++ if (len < bLength + extralen + superlen) { + return -1; + } + +@@ -224,7 +229,21 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len) + memcpy(dest + bLength, ep->extra, extralen); + } + +- return bLength + extralen; ++ if (superlen) { ++ USBDescriptor *d = (void *)(dest + bLength + extralen); ++ ++ d->bLength = 0x06; ++ d->bDescriptorType = USB_DT_ENDPOINT_COMPANION; ++ ++ d->u.super_endpoint.bMaxBurst = ep->bMaxBurst; ++ d->u.super_endpoint.bmAttributes = ep->bmAttributes_super; ++ d->u.super_endpoint.wBytesPerInterval_lo = ++ usb_lo(ep->wBytesPerInterval); ++ d->u.super_endpoint.wBytesPerInterval_hi = ++ usb_hi(ep->wBytesPerInterval); ++ } ++ ++ return bLength + extralen + superlen; + } + + int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) +@@ -509,7 +528,7 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len + uint8_t buf[256]; + uint8_t type = value >> 8; + uint8_t index = value & 0xff; +- int ret = -1; ++ int flags, ret = -1; + + if (dev->speed == USB_SPEED_HIGH) { + other_dev = usb_device_get_usb_desc(dev)->full; +@@ -517,6 +536,11 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len + other_dev = usb_device_get_usb_desc(dev)->high; + } + ++ flags = 0; ++ if (dev->device->bcdUSB >= 0x0300) { ++ flags |= USB_DESC_FLAG_SUPER; ++ } ++ + switch(type) { + case USB_DT_DEVICE: + ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf)); +@@ -524,7 +548,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len + break; + case USB_DT_CONFIG: + if (index < dev->device->bNumConfigurations) { +- ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf)); ++ ret = usb_desc_config(dev->device->confs + index, flags, ++ buf, sizeof(buf)); + } + trace_usb_desc_config(dev->addr, index, len, ret); + break; +@@ -532,7 +557,6 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len + ret = usb_desc_string(dev, index, buf, sizeof(buf)); + trace_usb_desc_string(dev->addr, index, len, ret); + break; +- + case USB_DT_DEVICE_QUALIFIER: + if (other_dev != NULL) { + ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf)); +@@ -541,7 +565,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (other_dev != NULL && index < other_dev->bNumConfigurations) { +- ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf)); ++ ret = usb_desc_config(other_dev->confs + index, flags, ++ buf, sizeof(buf)); + buf[0x01] = USB_DT_OTHER_SPEED_CONFIG; + } + trace_usb_desc_other_speed_config(dev->addr, index, len, ret); +diff --git a/hw/usb/desc.h b/hw/usb/desc.h +index d89fa41..4b5e88d 100644 +--- a/hw/usb/desc.h ++++ b/hw/usb/desc.h +@@ -63,6 +63,12 @@ typedef struct USBDescriptor { + uint8_t bRefresh; /* only audio ep */ + uint8_t bSynchAddress; /* only audio ep */ + } endpoint; ++ struct { ++ uint8_t bMaxBurst; ++ uint8_t bmAttributes; ++ uint8_t wBytesPerInterval_lo; ++ uint8_t wBytesPerInterval_hi; ++ } super_endpoint; + } u; + } QEMU_PACKED USBDescriptor; + +@@ -139,6 +145,11 @@ struct USBDescEndpoint { + + uint8_t is_audio; /* has bRefresh + bSynchAddress */ + uint8_t *extra; ++ ++ /* superspeed endpoint companion */ ++ uint8_t bMaxBurst; ++ uint8_t bmAttributes_super; ++ uint16_t wBytesPerInterval; + }; + + struct USBDescOther { +@@ -156,16 +167,21 @@ struct USBDesc { + const char* const *str; + }; + ++#define USB_DESC_FLAG_SUPER (1 << 1) ++ + /* generate usb packages from structs */ + int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, + uint8_t *dest, size_t len); + int usb_desc_device_qualifier(const USBDescDevice *dev, + uint8_t *dest, size_t len); +-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len); +-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, +- size_t len); +-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len); +-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len); ++int usb_desc_config(const USBDescConfig *conf, int flags, ++ uint8_t *dest, size_t len); ++int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, ++ uint8_t *dest, size_t len); ++int usb_desc_iface(const USBDescIface *iface, int flags, ++ uint8_t *dest, size_t len); ++int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, ++ uint8_t *dest, size_t len); + int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len); + + /* control message emulation helpers */ +-- +1.7.12 + diff --git a/0343-usb3-bos-decriptor.patch b/0343-usb3-bos-decriptor.patch new file mode 100644 index 0000000..3158fd0 --- /dev/null +++ b/0343-usb3-bos-decriptor.patch @@ -0,0 +1,215 @@ +From 52666569c1aa34531c101e005feccec0899c14e2 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 28 Aug 2012 17:46:29 +0200 +Subject: [PATCH 343/366] usb3: bos decriptor + +Add support for creating BOS descriptor and +device cappability descriptors. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb.h | 6 ++++ + hw/usb/desc.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + hw/usb/desc.h | 25 ++++++++++++++ + trace-events | 1 + + 4 files changed, 141 insertions(+) + +diff --git a/hw/usb.h b/hw/usb.h +index 78ffdf4..48c8926 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -135,10 +135,16 @@ + #define USB_DT_OTHER_SPEED_CONFIG 0x07 + #define USB_DT_DEBUG 0x0A + #define USB_DT_INTERFACE_ASSOC 0x0B ++#define USB_DT_BOS 0x0F ++#define USB_DT_DEVICE_CAPABILITY 0x10 + #define USB_DT_CS_INTERFACE 0x24 + #define USB_DT_CS_ENDPOINT 0x25 + #define USB_DT_ENDPOINT_COMPANION 0x30 + ++#define USB_DEV_CAP_WIRELESS 0x01 ++#define USB_DEV_CAP_USB2_EXT 0x02 ++#define USB_DEV_CAP_SUPERSPEED 0x03 ++ + #define USB_ENDPOINT_XFER_CONTROL 0 + #define USB_ENDPOINT_XFER_ISOC 1 + #define USB_ENDPOINT_XFER_BULK 2 +diff --git a/hw/usb/desc.c b/hw/usb/desc.c +index 8f5a8e5..1f12eae 100644 +--- a/hw/usb/desc.c ++++ b/hw/usb/desc.c +@@ -258,6 +258,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) + return bLength; + } + ++static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len) ++{ ++ uint8_t bLength = 0x07; ++ USBDescriptor *d = (void *)dest; ++ ++ if (len < bLength) { ++ return -1; ++ } ++ ++ d->bLength = bLength; ++ d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; ++ d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT; ++ ++ d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */ ++ d->u.cap.u.usb2_ext.bmAttributes_2 = 0; ++ d->u.cap.u.usb2_ext.bmAttributes_3 = 0; ++ d->u.cap.u.usb2_ext.bmAttributes_4 = 0; ++ ++ return bLength; ++} ++ ++static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len) ++{ ++ uint8_t bLength = 0x0a; ++ USBDescriptor *d = (void *)dest; ++ ++ if (len < bLength) { ++ return -1; ++ } ++ ++ d->bLength = bLength; ++ d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; ++ d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED; ++ ++ d->u.cap.u.super.bmAttributes = 0; ++ d->u.cap.u.super.wSpeedsSupported_lo = 0; ++ d->u.cap.u.super.wSpeedsSupported_hi = 0; ++ d->u.cap.u.super.bFunctionalitySupport = 0; ++ d->u.cap.u.super.bU1DevExitLat = 0x0a; ++ d->u.cap.u.super.wU2DevExitLat_lo = 0x20; ++ d->u.cap.u.super.wU2DevExitLat_hi = 0; ++ ++ if (desc->full) { ++ d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1); ++ d->u.cap.u.super.bFunctionalitySupport = 1; ++ } ++ if (desc->high) { ++ d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2); ++ if (!d->u.cap.u.super.bFunctionalitySupport) { ++ d->u.cap.u.super.bFunctionalitySupport = 2; ++ } ++ } ++ if (desc->super) { ++ d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3); ++ if (!d->u.cap.u.super.bFunctionalitySupport) { ++ d->u.cap.u.super.bFunctionalitySupport = 3; ++ } ++ } ++ ++ return bLength; ++} ++ ++static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len) ++{ ++ uint8_t bLength = 0x05; ++ uint16_t wTotalLength = 0; ++ uint8_t bNumDeviceCaps = 0; ++ USBDescriptor *d = (void *)dest; ++ int rc; ++ ++ if (len < bLength) { ++ return -1; ++ } ++ ++ d->bLength = bLength; ++ d->bDescriptorType = USB_DT_BOS; ++ ++ wTotalLength += bLength; ++ ++ if (desc->high != NULL) { ++ rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength, ++ len - wTotalLength); ++ if (rc < 0) { ++ return rc; ++ } ++ wTotalLength += rc; ++ bNumDeviceCaps++; ++ } ++ ++ if (desc->super != NULL) { ++ rc = usb_desc_cap_super(desc, dest + wTotalLength, ++ len - wTotalLength); ++ if (rc < 0) { ++ return rc; ++ } ++ wTotalLength += rc; ++ bNumDeviceCaps++; ++ } ++ ++ d->u.bos.wTotalLength_lo = usb_lo(wTotalLength); ++ d->u.bos.wTotalLength_hi = usb_hi(wTotalLength); ++ d->u.bos.bNumDeviceCaps = bNumDeviceCaps; ++ return wTotalLength; ++} ++ + /* ------------------------------------------------------------------ */ + + static void usb_desc_ep_init(USBDevice *dev) +@@ -571,6 +676,10 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len + } + trace_usb_desc_other_speed_config(dev->addr, index, len, ret); + break; ++ case USB_DT_BOS: ++ ret = usb_desc_bos(desc, buf, sizeof(buf)); ++ trace_usb_desc_bos(dev->addr, len, ret); ++ break; + + case USB_DT_DEBUG: + /* ignore silently */ +diff --git a/hw/usb/desc.h b/hw/usb/desc.h +index 4b5e88d..68bb570 100644 +--- a/hw/usb/desc.h ++++ b/hw/usb/desc.h +@@ -69,6 +69,31 @@ typedef struct USBDescriptor { + uint8_t wBytesPerInterval_lo; + uint8_t wBytesPerInterval_hi; + } super_endpoint; ++ struct { ++ uint8_t wTotalLength_lo; ++ uint8_t wTotalLength_hi; ++ uint8_t bNumDeviceCaps; ++ } bos; ++ struct { ++ uint8_t bDevCapabilityType; ++ union { ++ struct { ++ uint8_t bmAttributes_1; ++ uint8_t bmAttributes_2; ++ uint8_t bmAttributes_3; ++ uint8_t bmAttributes_4; ++ } usb2_ext; ++ struct { ++ uint8_t bmAttributes; ++ uint8_t wSpeedsSupported_lo; ++ uint8_t wSpeedsSupported_hi; ++ uint8_t bFunctionalitySupport; ++ uint8_t bU1DevExitLat; ++ uint8_t wU2DevExitLat_lo; ++ uint8_t wU2DevExitLat_hi; ++ } super; ++ } u; ++ } cap; + } u; + } QEMU_PACKED USBDescriptor; + +diff --git a/trace-events b/trace-events +index a894689..5bc591a 100644 +--- a/trace-events ++++ b/trace-events +@@ -340,6 +340,7 @@ usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device quali + usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" + usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" + usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d" ++usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d" + usb_set_addr(int addr) "dev %d" + usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" + usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d" +-- +1.7.12 + diff --git a/0344-usb-storage-usb3-support.patch b/0344-usb-storage-usb3-support.patch new file mode 100644 index 0000000..b50fcdd --- /dev/null +++ b/0344-usb-storage-usb3-support.patch @@ -0,0 +1,94 @@ +From 4a6f1fdf12d8b03633dad54dadc12781a77fbf6b Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 28 Aug 2012 17:29:15 +0200 +Subject: [PATCH 344/366] usb-storage: usb3 support + +Add usb3 descriptors to usb-storage, so it shows up as superspeed +device when connected to xhci. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/dev-storage.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 43 insertions(+), 3 deletions(-) + +diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c +index ff48d91..e732191 100644 +--- a/hw/usb/dev-storage.c ++++ b/hw/usb/dev-storage.c +@@ -78,6 +78,7 @@ enum { + STR_SERIALNUMBER, + STR_CONFIG_FULL, + STR_CONFIG_HIGH, ++ STR_CONFIG_SUPER, + }; + + static const USBDescStrings desc_strings = { +@@ -86,6 +87,7 @@ static const USBDescStrings desc_strings = { + [STR_SERIALNUMBER] = "1", + [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", + [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", ++ [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", + }; + + static const USBDescIface desc_iface_full = { +@@ -158,6 +160,43 @@ static const USBDescDevice desc_device_high = { + }, + }; + ++static const USBDescIface desc_iface_super = { ++ .bInterfaceNumber = 0, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_MASS_STORAGE, ++ .bInterfaceSubClass = 0x06, /* SCSI */ ++ .bInterfaceProtocol = 0x50, /* Bulk */ ++ .eps = (USBDescEndpoint[]) { ++ { ++ .bEndpointAddress = USB_DIR_IN | 0x01, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = 1024, ++ .bMaxBurst = 15, ++ },{ ++ .bEndpointAddress = USB_DIR_OUT | 0x02, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = 1024, ++ .bMaxBurst = 15, ++ }, ++ } ++}; ++ ++static const USBDescDevice desc_device_super = { ++ .bcdUSB = 0x0300, ++ .bMaxPacketSize0 = 9, ++ .bNumConfigurations = 1, ++ .confs = (USBDescConfig[]) { ++ { ++ .bNumInterfaces = 1, ++ .bConfigurationValue = 1, ++ .iConfiguration = STR_CONFIG_SUPER, ++ .bmAttributes = 0xc0, ++ .nif = 1, ++ .ifs = &desc_iface_super, ++ }, ++ }, ++}; ++ + static const USBDesc desc = { + .id = { + .idVendor = 0x46f4, /* CRC16() of "QEMU" */ +@@ -167,9 +206,10 @@ static const USBDesc desc = { + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, +- .full = &desc_device_full, +- .high = &desc_device_high, +- .str = desc_strings, ++ .full = &desc_device_full, ++ .high = &desc_device_high, ++ .super = &desc_device_super, ++ .str = desc_strings, + }; + + static void usb_msd_copy_data(MSDState *s, USBPacket *p) +-- +1.7.12 + diff --git a/0345-xhci-fix-cleanup-msi.patch b/0345-xhci-fix-cleanup-msi.patch new file mode 100644 index 0000000..5f25cbd --- /dev/null +++ b/0345-xhci-fix-cleanup-msi.patch @@ -0,0 +1,96 @@ +From 3ed11ea2c3b6d5db29246b4da105902aa0346d65 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 30 Aug 2012 10:57:12 +0200 +Subject: [PATCH 345/366] xhci: fix & cleanup msi. + +Drop custom write_config function which isn't needed any more. +Make the msi property a bit property so it accepts 'on' & 'off'. +Enable MSI by default. + +TODO: add compat property to disable on old machine types. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 27 +++++++++------------------ + 1 file changed, 9 insertions(+), 18 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index e08312e..e1d5d2a 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -386,7 +386,7 @@ struct XHCIState { + /* properties */ + uint32_t numports_2; + uint32_t numports_3; +- uint32_t msi; ++ uint32_t flags; + + /* Operational Registers */ + uint32_t usbcmd; +@@ -435,6 +435,10 @@ typedef struct XHCIEvRingSeg { + uint32_t rsvd; + } XHCIEvRingSeg; + ++enum xhci_flags { ++ XHCI_FLAG_USE_MSI = 1, ++}; ++ + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid); + static void xhci_event(XHCIState *xhci, XHCIEvent *event); +@@ -617,7 +621,7 @@ static void xhci_irq_update(XHCIState *xhci) + level = 1; + } + +- if (xhci->msi && msi_enabled(&xhci->pci_dev)) { ++ if (msi_enabled(&xhci->pci_dev)) { + if (level) { + trace_usb_xhci_irq_msi(0); + msi_notify(&xhci->pci_dev, 0); +@@ -2859,32 +2863,20 @@ static int usb_xhci_initfn(struct PCIDevice *dev) + ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0); + assert(ret >= 0); + +- if (xhci->msi) { +- ret = msi_init(&xhci->pci_dev, 0x70, 1, true, false); +- assert(ret >= 0); ++ if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) { ++ msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false); + } + + return 0; + } + +-static void xhci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, +- int len) +-{ +- XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev); +- +- pci_default_write_config(dev, addr, val, len); +- if (xhci->msi) { +- msi_write_config(dev, addr, val, len); +- } +-} +- + static const VMStateDescription vmstate_xhci = { + .name = "xhci", + .unmigratable = 1, + }; + + static Property xhci_properties[] = { +- DEFINE_PROP_UINT32("msi", XHCIState, msi, 0), ++ DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), + DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), + DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), + DEFINE_PROP_END_OF_LIST(), +@@ -2904,7 +2896,6 @@ static void xhci_class_init(ObjectClass *klass, void *data) + k->class_id = PCI_CLASS_SERIAL_USB; + k->revision = 0x03; + k->is_express = 1; +- k->config_write = xhci_write_config; + } + + static TypeInfo xhci_info = { +-- +1.7.12 + diff --git a/0346-xhci-rework-interrupt-handling.patch b/0346-xhci-rework-interrupt-handling.patch new file mode 100644 index 0000000..b0ea1df --- /dev/null +++ b/0346-xhci-rework-interrupt-handling.patch @@ -0,0 +1,117 @@ +From da881a3422ae525f1d0a24f61117a47473261037 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 30 Aug 2012 13:05:10 +0200 +Subject: [PATCH 346/366] xhci: rework interrupt handling + +Split xhci_irq_update into a function which handles intx updates +(including lowering the irq line once the guests acks the interrupt) +and one which is used for raising an irq only. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 47 +++++++++++++++++++++++++++++++++-------------- + 1 file changed, 33 insertions(+), 14 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index e1d5d2a..5eae32e 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -612,24 +612,43 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) + return &xhci->ports[index]; + } + +-static void xhci_irq_update(XHCIState *xhci) ++static void xhci_intx_update(XHCIState *xhci) + { + int level = 0; + +- if (xhci->iman & IMAN_IP && xhci->iman & IMAN_IE && ++ if (msi_enabled(&xhci->pci_dev)) { ++ return; ++ } ++ ++ if (xhci->iman & IMAN_IP && ++ xhci->iman & IMAN_IE && + xhci->usbcmd & USBCMD_INTE) { + level = 1; + } + ++ trace_usb_xhci_irq_intx(level); ++ qemu_set_irq(xhci->irq, level); ++} ++ ++static void xhci_intr_raise(XHCIState *xhci) ++{ ++ if (!(xhci->iman & IMAN_IP) || ++ !(xhci->iman & IMAN_IE)) { ++ return; ++ } ++ ++ if (!(xhci->usbcmd & USBCMD_INTE)) { ++ return; ++ } ++ + if (msi_enabled(&xhci->pci_dev)) { +- if (level) { +- trace_usb_xhci_irq_msi(0); +- msi_notify(&xhci->pci_dev, 0); +- } +- } else { +- trace_usb_xhci_irq_intx(level); +- qemu_set_irq(xhci->irq, level); ++ trace_usb_xhci_irq_msi(0); ++ msi_notify(&xhci->pci_dev, 0); ++ return; + } ++ ++ trace_usb_xhci_irq_intx(1); ++ qemu_set_irq(xhci->irq, 1); + } + + static inline int xhci_running(XHCIState *xhci) +@@ -732,7 +751,7 @@ static void xhci_events_update(XHCIState *xhci) + xhci->erdp_low |= ERDP_EHB; + xhci->iman |= IMAN_IP; + xhci->usbsts |= USBSTS_EINT; +- xhci_irq_update(xhci); ++ xhci_intr_raise(xhci); + } + + if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) { +@@ -796,7 +815,7 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event) + xhci->iman |= IMAN_IP; + xhci->usbsts |= USBSTS_EINT; + +- xhci_irq_update(xhci); ++ xhci_intr_raise(xhci); + } + + static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring, +@@ -2481,13 +2500,13 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) + if (val & USBCMD_HCRST) { + xhci_reset(&xhci->pci_dev.qdev); + } +- xhci_irq_update(xhci); ++ xhci_intx_update(xhci); + break; + + case 0x04: /* USBSTS */ + /* these bits are write-1-to-clear */ + xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE)); +- xhci_irq_update(xhci); ++ xhci_intx_update(xhci); + break; + + case 0x14: /* DNCTRL */ +@@ -2572,7 +2591,7 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) + } + xhci->iman &= ~IMAN_IE; + xhci->iman |= val & IMAN_IE; +- xhci_irq_update(xhci); ++ xhci_intx_update(xhci); + break; + case 0x24: /* IMOD */ + xhci->imod = val; +-- +1.7.12 + diff --git a/0347-xhci-add-msix-support.patch b/0347-xhci-add-msix-support.patch new file mode 100644 index 0000000..5110aaa --- /dev/null +++ b/0347-xhci-add-msix-support.patch @@ -0,0 +1,156 @@ +From 03ab86ecf677d777864a3643ec8479037d3f41cd Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 30 Aug 2012 12:06:59 +0200 +Subject: [PATCH 347/366] xhci: add msix support + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- + trace-events | 3 +++ + 2 files changed, 49 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 5eae32e..3bac99a 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -23,6 +23,7 @@ + #include "hw/usb.h" + #include "hw/pci.h" + #include "hw/msi.h" ++#include "hw/msix.h" + #include "trace.h" + + //#define DEBUG_XHCI +@@ -59,6 +60,8 @@ + #define OFF_OPER LEN_CAP + #define OFF_RUNTIME 0x1000 + #define OFF_DOORBELL 0x2000 ++#define OFF_MSIX_TABLE 0x3000 ++#define OFF_MSIX_PBA 0x3800 + /* must be power of 2 */ + #define LEN_REGS 0x4000 + +@@ -411,6 +414,7 @@ struct XHCIState { + uint32_t erstba_high; + uint32_t erdp_low; + uint32_t erdp_high; ++ bool msix_used; + + int64_t mfindex_start; + QEMUTimer *mfwrap_timer; +@@ -437,6 +441,7 @@ typedef struct XHCIEvRingSeg { + + enum xhci_flags { + XHCI_FLAG_USE_MSI = 1, ++ XHCI_FLAG_USE_MSI_X, + }; + + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, +@@ -616,7 +621,8 @@ static void xhci_intx_update(XHCIState *xhci) + { + int level = 0; + +- if (msi_enabled(&xhci->pci_dev)) { ++ if (msix_enabled(&xhci->pci_dev) || ++ msi_enabled(&xhci->pci_dev)) { + return; + } + +@@ -630,6 +636,30 @@ static void xhci_intx_update(XHCIState *xhci) + qemu_set_irq(xhci->irq, level); + } + ++static void xhci_msix_update(XHCIState *xhci) ++{ ++ bool enabled; ++ ++ if (!msix_enabled(&xhci->pci_dev)) { ++ return; ++ } ++ ++ enabled = xhci->iman & IMAN_IE; ++ if (enabled == xhci->msix_used) { ++ return; ++ } ++ ++ if (enabled) { ++ trace_usb_xhci_irq_msix_use(0); ++ msix_vector_use(&xhci->pci_dev, 0); ++ xhci->msix_used = true; ++ } else { ++ trace_usb_xhci_irq_msix_unuse(0); ++ msix_vector_unuse(&xhci->pci_dev, 0); ++ xhci->msix_used = false; ++ } ++} ++ + static void xhci_intr_raise(XHCIState *xhci) + { + if (!(xhci->iman & IMAN_IP) || +@@ -641,6 +671,12 @@ static void xhci_intr_raise(XHCIState *xhci) + return; + } + ++ if (msix_enabled(&xhci->pci_dev)) { ++ trace_usb_xhci_irq_msix(0); ++ msix_notify(&xhci->pci_dev, 0); ++ return; ++ } ++ + if (msi_enabled(&xhci->pci_dev)) { + trace_usb_xhci_irq_msi(0); + msi_notify(&xhci->pci_dev, 0); +@@ -2284,6 +2320,7 @@ static void xhci_reset(DeviceState *dev) + xhci->erstba_high = 0; + xhci->erdp_low = 0; + xhci->erdp_high = 0; ++ xhci->msix_used = 0; + + xhci->er_ep_idx = 0; + xhci->er_pcs = 1; +@@ -2592,6 +2629,7 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) + xhci->iman &= ~IMAN_IE; + xhci->iman |= val & IMAN_IE; + xhci_intx_update(xhci); ++ xhci_msix_update(xhci); + break; + case 0x24: /* IMOD */ + xhci->imod = val; +@@ -2885,6 +2923,12 @@ static int usb_xhci_initfn(struct PCIDevice *dev) + if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) { + msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false); + } ++ if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) { ++ msix_init(&xhci->pci_dev, MAXINTRS, ++ &xhci->mem, 0, OFF_MSIX_TABLE, ++ &xhci->mem, 0, OFF_MSIX_PBA, ++ 0x90); ++ } + + return 0; + } +@@ -2896,6 +2940,7 @@ static const VMStateDescription vmstate_xhci = { + + static Property xhci_properties[] = { + DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), ++ DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), + DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), + DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), + DEFINE_PROP_END_OF_LIST(), +diff --git a/trace-events b/trace-events +index 5bc591a..8589ca4 100644 +--- a/trace-events ++++ b/trace-events +@@ -313,6 +313,9 @@ usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" + usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" + usb_xhci_irq_intx(uint32_t level) "level %d" + usb_xhci_irq_msi(uint32_t nr) "nr %d" ++usb_xhci_irq_msix(uint32_t nr) "nr %d" ++usb_xhci_irq_msix_use(uint32_t nr) "nr %d" ++usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d" + usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x" + usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" + usb_xhci_slot_enable(uint32_t slotid) "slotid %d" +-- +1.7.12 + diff --git a/0348-xhci-move-register-update-into-xhci_intr_raise.patch b/0348-xhci-move-register-update-into-xhci_intr_raise.patch new file mode 100644 index 0000000..60537da --- /dev/null +++ b/0348-xhci-move-register-update-into-xhci_intr_raise.patch @@ -0,0 +1,55 @@ +From c7ca31b2f54b945ba4babf8ad329c938462c75f5 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 30 Aug 2012 14:04:04 +0200 +Subject: [PATCH 348/366] xhci: move register update into xhci_intr_raise + +Now that we have a separate function to raise an IRQ we can move +some comon code into the function. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 3bac99a..e39fe04 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -662,8 +662,11 @@ static void xhci_msix_update(XHCIState *xhci) + + static void xhci_intr_raise(XHCIState *xhci) + { +- if (!(xhci->iman & IMAN_IP) || +- !(xhci->iman & IMAN_IE)) { ++ xhci->erdp_low |= ERDP_EHB; ++ xhci->iman |= IMAN_IP; ++ xhci->usbsts |= USBSTS_EINT; ++ ++ if (!(xhci->iman & IMAN_IE)) { + return; + } + +@@ -784,9 +787,6 @@ static void xhci_events_update(XHCIState *xhci) + } + + if (do_irq) { +- xhci->erdp_low |= ERDP_EHB; +- xhci->iman |= IMAN_IP; +- xhci->usbsts |= USBSTS_EINT; + xhci_intr_raise(xhci); + } + +@@ -847,10 +847,6 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event) + xhci_write_event(xhci, event); + } + +- xhci->erdp_low |= ERDP_EHB; +- xhci->iman |= IMAN_IP; +- xhci->usbsts |= USBSTS_EINT; +- + xhci_intr_raise(xhci); + } + +-- +1.7.12 + diff --git a/0349-xhci-add-XHCIInterrupter.patch b/0349-xhci-add-XHCIInterrupter.patch new file mode 100644 index 0000000..3ad7de3 --- /dev/null +++ b/0349-xhci-add-XHCIInterrupter.patch @@ -0,0 +1,642 @@ +From 84ce3d5155c984ad131ba7153b9c3646c88b8636 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 30 Aug 2012 15:49:03 +0200 +Subject: [PATCH 349/366] xhci: add XHCIInterrupter + +Move all state belonging to the (single) interrupter into a separate +struct. First step in adding support for multiple interrupters. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 307 ++++++++++++++++++++++++++++-------------------------- + trace-events | 2 +- + 2 files changed, 161 insertions(+), 148 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index e39fe04..ddc3825 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -378,6 +378,27 @@ typedef struct XHCIEvent { + uint8_t epid; + } XHCIEvent; + ++typedef struct XHCIInterrupter { ++ uint32_t iman; ++ uint32_t imod; ++ uint32_t erstsz; ++ uint32_t erstba_low; ++ uint32_t erstba_high; ++ uint32_t erdp_low; ++ uint32_t erdp_high; ++ ++ bool msix_used, er_pcs, er_full; ++ ++ dma_addr_t er_start; ++ uint32_t er_size; ++ unsigned int er_ep_idx; ++ ++ XHCIEvent ev_buffer[EV_QUEUE]; ++ unsigned int ev_buffer_put; ++ unsigned int ev_buffer_get; ++ ++} XHCIInterrupter; ++ + struct XHCIState { + PCIDevice pci_dev; + USBBus bus; +@@ -407,27 +428,9 @@ struct XHCIState { + uint32_t numports; + + /* Runtime Registers */ +- uint32_t iman; +- uint32_t imod; +- uint32_t erstsz; +- uint32_t erstba_low; +- uint32_t erstba_high; +- uint32_t erdp_low; +- uint32_t erdp_high; +- bool msix_used; +- + int64_t mfindex_start; + QEMUTimer *mfwrap_timer; +- +- dma_addr_t er_start; +- uint32_t er_size; +- bool er_pcs; +- unsigned int er_ep_idx; +- bool er_full; +- +- XHCIEvent ev_buffer[EV_QUEUE]; +- unsigned int ev_buffer_put; +- unsigned int ev_buffer_get; ++ XHCIInterrupter intr[MAXINTRS]; + + XHCIRing cmd_ring; + }; +@@ -446,8 +449,8 @@ enum xhci_flags { + + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid); +-static void xhci_event(XHCIState *xhci, XHCIEvent *event); +-static void xhci_write_event(XHCIState *xhci, XHCIEvent *event); ++static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); ++static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); + + static const char *TRBType_names[] = { + [TRB_RESERVED] = "TRB_RESERVED", +@@ -573,7 +576,7 @@ static void xhci_mfwrap_timer(void *opaque) + XHCIState *xhci = opaque; + XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS }; + +- xhci_event(xhci, &wrap); ++ xhci_event(xhci, &wrap, 0); + xhci_mfwrap_update(xhci); + } + +@@ -626,8 +629,8 @@ static void xhci_intx_update(XHCIState *xhci) + return; + } + +- if (xhci->iman & IMAN_IP && +- xhci->iman & IMAN_IE && ++ if (xhci->intr[0].iman & IMAN_IP && ++ xhci->intr[0].iman & IMAN_IE && + xhci->usbcmd & USBCMD_INTE) { + level = 1; + } +@@ -636,7 +639,7 @@ static void xhci_intx_update(XHCIState *xhci) + qemu_set_irq(xhci->irq, level); + } + +-static void xhci_msix_update(XHCIState *xhci) ++static void xhci_msix_update(XHCIState *xhci, int v) + { + bool enabled; + +@@ -644,29 +647,29 @@ static void xhci_msix_update(XHCIState *xhci) + return; + } + +- enabled = xhci->iman & IMAN_IE; +- if (enabled == xhci->msix_used) { ++ enabled = xhci->intr[v].iman & IMAN_IE; ++ if (enabled == xhci->intr[v].msix_used) { + return; + } + + if (enabled) { +- trace_usb_xhci_irq_msix_use(0); +- msix_vector_use(&xhci->pci_dev, 0); +- xhci->msix_used = true; ++ trace_usb_xhci_irq_msix_use(v); ++ msix_vector_use(&xhci->pci_dev, v); ++ xhci->intr[v].msix_used = true; + } else { +- trace_usb_xhci_irq_msix_unuse(0); +- msix_vector_unuse(&xhci->pci_dev, 0); +- xhci->msix_used = false; ++ trace_usb_xhci_irq_msix_unuse(v); ++ msix_vector_unuse(&xhci->pci_dev, v); ++ xhci->intr[v].msix_used = false; + } + } + +-static void xhci_intr_raise(XHCIState *xhci) ++static void xhci_intr_raise(XHCIState *xhci, int v) + { +- xhci->erdp_low |= ERDP_EHB; +- xhci->iman |= IMAN_IP; ++ xhci->intr[v].erdp_low |= ERDP_EHB; ++ xhci->intr[v].iman |= IMAN_IP; + xhci->usbsts |= USBSTS_EINT; + +- if (!(xhci->iman & IMAN_IE)) { ++ if (!(xhci->intr[v].iman & IMAN_IE)) { + return; + } + +@@ -675,24 +678,26 @@ static void xhci_intr_raise(XHCIState *xhci) + } + + if (msix_enabled(&xhci->pci_dev)) { +- trace_usb_xhci_irq_msix(0); +- msix_notify(&xhci->pci_dev, 0); ++ trace_usb_xhci_irq_msix(v); ++ msix_notify(&xhci->pci_dev, v); + return; + } + + if (msi_enabled(&xhci->pci_dev)) { +- trace_usb_xhci_irq_msi(0); +- msi_notify(&xhci->pci_dev, 0); ++ trace_usb_xhci_irq_msi(v); ++ msi_notify(&xhci->pci_dev, v); + return; + } + +- trace_usb_xhci_irq_intx(1); +- qemu_set_irq(xhci->irq, 1); ++ if (v == 0) { ++ trace_usb_xhci_irq_intx(1); ++ qemu_set_irq(xhci->irq, 1); ++ } + } + + static inline int xhci_running(XHCIState *xhci) + { +- return !(xhci->usbsts & USBSTS_HCH) && !xhci->er_full; ++ return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full; + } + + static void xhci_die(XHCIState *xhci) +@@ -701,8 +706,9 @@ static void xhci_die(XHCIState *xhci) + fprintf(stderr, "xhci: asserted controller error\n"); + } + +-static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) ++static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) + { ++ XHCIInterrupter *intr = &xhci->intr[v]; + XHCITRB ev_trb; + dma_addr_t addr; + +@@ -710,27 +716,28 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) + ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24)); + ev_trb.control = (event->slotid << 24) | (event->epid << 16) | + event->flags | (event->type << TRB_TYPE_SHIFT); +- if (xhci->er_pcs) { ++ if (intr->er_pcs) { + ev_trb.control |= TRB_C; + } + ev_trb.control = cpu_to_le32(ev_trb.control); + +- trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb), ++ trace_usb_xhci_queue_event(v, intr->er_ep_idx, trb_name(&ev_trb), + event_name(event), ev_trb.parameter, + ev_trb.status, ev_trb.control); + +- addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx; ++ addr = intr->er_start + TRB_SIZE*intr->er_ep_idx; + pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE); + +- xhci->er_ep_idx++; +- if (xhci->er_ep_idx >= xhci->er_size) { +- xhci->er_ep_idx = 0; +- xhci->er_pcs = !xhci->er_pcs; ++ intr->er_ep_idx++; ++ if (intr->er_ep_idx >= intr->er_size) { ++ intr->er_ep_idx = 0; ++ intr->er_pcs = !intr->er_pcs; + } + } + +-static void xhci_events_update(XHCIState *xhci) ++static void xhci_events_update(XHCIState *xhci, int v) + { ++ XHCIInterrupter *intr = &xhci->intr[v]; + dma_addr_t erdp; + unsigned int dp_idx; + bool do_irq = 0; +@@ -739,115 +746,116 @@ static void xhci_events_update(XHCIState *xhci) + return; + } + +- erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high); +- if (erdp < xhci->er_start || +- erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) { ++ erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); ++ if (erdp < intr->er_start || ++ erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { + fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); +- fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n", +- xhci->er_start, xhci->er_size); ++ fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", ++ v, intr->er_start, intr->er_size); + xhci_die(xhci); + return; + } +- dp_idx = (erdp - xhci->er_start) / TRB_SIZE; +- assert(dp_idx < xhci->er_size); ++ dp_idx = (erdp - intr->er_start) / TRB_SIZE; ++ assert(dp_idx < intr->er_size); + + /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus + * deadlocks when the ER is full. Hack it by holding off events until + * the driver decides to free at least half of the ring */ +- if (xhci->er_full) { +- int er_free = dp_idx - xhci->er_ep_idx; ++ if (intr->er_full) { ++ int er_free = dp_idx - intr->er_ep_idx; + if (er_free <= 0) { +- er_free += xhci->er_size; ++ er_free += intr->er_size; + } +- if (er_free < (xhci->er_size/2)) { ++ if (er_free < (intr->er_size/2)) { + DPRINTF("xhci_events_update(): event ring still " + "more than half full (hack)\n"); + return; + } + } + +- while (xhci->ev_buffer_put != xhci->ev_buffer_get) { +- assert(xhci->er_full); +- if (((xhci->er_ep_idx+1) % xhci->er_size) == dp_idx) { ++ while (intr->ev_buffer_put != intr->ev_buffer_get) { ++ assert(intr->er_full); ++ if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) { + DPRINTF("xhci_events_update(): event ring full again\n"); + #ifndef ER_FULL_HACK + XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; +- xhci_write_event(xhci, &full); ++ xhci_write_event(xhci, &full, v); + #endif + do_irq = 1; + break; + } +- XHCIEvent *event = &xhci->ev_buffer[xhci->ev_buffer_get]; +- xhci_write_event(xhci, event); +- xhci->ev_buffer_get++; ++ XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get]; ++ xhci_write_event(xhci, event, v); ++ intr->ev_buffer_get++; + do_irq = 1; +- if (xhci->ev_buffer_get == EV_QUEUE) { +- xhci->ev_buffer_get = 0; ++ if (intr->ev_buffer_get == EV_QUEUE) { ++ intr->ev_buffer_get = 0; + } + } + + if (do_irq) { +- xhci_intr_raise(xhci); ++ xhci_intr_raise(xhci, v); + } + +- if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) { ++ if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) { + DPRINTF("xhci_events_update(): event ring no longer full\n"); +- xhci->er_full = 0; ++ intr->er_full = 0; + } + return; + } + +-static void xhci_event(XHCIState *xhci, XHCIEvent *event) ++static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) + { ++ XHCIInterrupter *intr = &xhci->intr[v]; + dma_addr_t erdp; + unsigned int dp_idx; + +- if (xhci->er_full) { ++ if (intr->er_full) { + DPRINTF("xhci_event(): ER full, queueing\n"); +- if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) { ++ if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { + fprintf(stderr, "xhci: event queue full, dropping event!\n"); + return; + } +- xhci->ev_buffer[xhci->ev_buffer_put++] = *event; +- if (xhci->ev_buffer_put == EV_QUEUE) { +- xhci->ev_buffer_put = 0; ++ intr->ev_buffer[intr->ev_buffer_put++] = *event; ++ if (intr->ev_buffer_put == EV_QUEUE) { ++ intr->ev_buffer_put = 0; + } + return; + } + +- erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high); +- if (erdp < xhci->er_start || +- erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) { ++ erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); ++ if (erdp < intr->er_start || ++ erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { + fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); +- fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n", +- xhci->er_start, xhci->er_size); ++ fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", ++ v, intr->er_start, intr->er_size); + xhci_die(xhci); + return; + } + +- dp_idx = (erdp - xhci->er_start) / TRB_SIZE; +- assert(dp_idx < xhci->er_size); ++ dp_idx = (erdp - intr->er_start) / TRB_SIZE; ++ assert(dp_idx < intr->er_size); + +- if ((xhci->er_ep_idx+1) % xhci->er_size == dp_idx) { ++ if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) { + DPRINTF("xhci_event(): ER full, queueing\n"); + #ifndef ER_FULL_HACK + XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; + xhci_write_event(xhci, &full); + #endif +- xhci->er_full = 1; +- if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) { ++ intr->er_full = 1; ++ if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { + fprintf(stderr, "xhci: event queue full, dropping event!\n"); + return; + } +- xhci->ev_buffer[xhci->ev_buffer_put++] = *event; +- if (xhci->ev_buffer_put == EV_QUEUE) { +- xhci->ev_buffer_put = 0; ++ intr->ev_buffer[intr->ev_buffer_put++] = *event; ++ if (intr->ev_buffer_put == EV_QUEUE) { ++ intr->ev_buffer_put = 0; + } + } else { +- xhci_write_event(xhci, event); ++ xhci_write_event(xhci, event, v); + } + +- xhci_intr_raise(xhci); ++ xhci_intr_raise(xhci, v); + } + + static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring, +@@ -939,17 +947,18 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring) + } + } + +-static void xhci_er_reset(XHCIState *xhci) ++static void xhci_er_reset(XHCIState *xhci, int v) + { ++ XHCIInterrupter *intr = &xhci->intr[v]; + XHCIEvRingSeg seg; + + /* cache the (sole) event ring segment location */ +- if (xhci->erstsz != 1) { +- fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", xhci->erstsz); ++ if (intr->erstsz != 1) { ++ fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", intr->erstsz); + xhci_die(xhci); + return; + } +- dma_addr_t erstba = xhci_addr64(xhci->erstba_low, xhci->erstba_high); ++ dma_addr_t erstba = xhci_addr64(intr->erstba_low, intr->erstba_high); + pci_dma_read(&xhci->pci_dev, erstba, &seg, sizeof(seg)); + le32_to_cpus(&seg.addr_low); + le32_to_cpus(&seg.addr_high); +@@ -959,15 +968,15 @@ static void xhci_er_reset(XHCIState *xhci) + xhci_die(xhci); + return; + } +- xhci->er_start = xhci_addr64(seg.addr_low, seg.addr_high); +- xhci->er_size = seg.size; ++ intr->er_start = xhci_addr64(seg.addr_low, seg.addr_high); ++ intr->er_size = seg.size; + +- xhci->er_ep_idx = 0; +- xhci->er_pcs = 1; +- xhci->er_full = 0; ++ intr->er_ep_idx = 0; ++ intr->er_pcs = 1; ++ intr->er_full = 0; + +- DPRINTF("xhci: event ring:" DMA_ADDR_FMT " [%d]\n", +- xhci->er_start, xhci->er_size); ++ DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n", ++ v, intr->er_start, intr->er_size); + } + + static void xhci_run(XHCIState *xhci) +@@ -1368,7 +1377,7 @@ static void xhci_xfer_report(XHCITransfer *xfer) + DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length); + edtla = 0; + } +- xhci_event(xhci, &event); ++ xhci_event(xhci, &event, 0 /* FIXME */); + reported = 1; + if (xfer->status != CC_SUCCESS) { + return; +@@ -2246,7 +2255,7 @@ static void xhci_process_commands(XHCIState *xhci) + break; + } + event.slotid = slotid; +- xhci_event(xhci, &event); ++ xhci_event(xhci, &event, 0 /* FIXME */); + } + } + +@@ -2276,7 +2285,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) + port->portsc |= PORTSC_CSC; + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, + port->portnr << 24}; +- xhci_event(xhci, &ev); ++ xhci_event(xhci, &ev, 0 /* FIXME */); + DPRINTF("xhci: port change event for port %d\n", port->portnr); + } + } +@@ -2309,20 +2318,22 @@ static void xhci_reset(DeviceState *dev) + xhci_update_port(xhci, xhci->ports + i, 0); + } + +- xhci->iman = 0; +- xhci->imod = 0; +- xhci->erstsz = 0; +- xhci->erstba_low = 0; +- xhci->erstba_high = 0; +- xhci->erdp_low = 0; +- xhci->erdp_high = 0; +- xhci->msix_used = 0; ++ for (i = 0; i < MAXINTRS; i++) { ++ xhci->intr[i].iman = 0; ++ xhci->intr[i].imod = 0; ++ xhci->intr[i].erstsz = 0; ++ xhci->intr[i].erstba_low = 0; ++ xhci->intr[i].erstba_high = 0; ++ xhci->intr[i].erdp_low = 0; ++ xhci->intr[i].erdp_high = 0; ++ xhci->intr[i].msix_used = 0; + +- xhci->er_ep_idx = 0; +- xhci->er_pcs = 1; +- xhci->er_full = 0; +- xhci->ev_buffer_put = 0; +- xhci->ev_buffer_get = 0; ++ xhci->intr[i].er_ep_idx = 0; ++ xhci->intr[i].er_pcs = 1; ++ xhci->intr[i].er_full = 0; ++ xhci->intr[i].ev_buffer_put = 0; ++ xhci->intr[i].ev_buffer_get = 0; ++ } + + xhci->mfindex_start = qemu_get_clock_ns(vm_clock); + xhci_mfwrap_update(xhci); +@@ -2553,7 +2564,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) + if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) { + XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED}; + xhci->crcr_low &= ~CRCR_CRR; +- xhci_event(xhci, &event); ++ xhci_event(xhci, &event, 0 /* FIXME */); + DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low); + } else { + dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val); +@@ -2577,6 +2588,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) + + static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + { ++ XHCIInterrupter *intr = &xhci->intr[0]; + uint32_t ret; + + switch (reg) { +@@ -2584,25 +2596,25 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + ret = xhci_mfindex_get(xhci) & 0x3fff; + break; + case 0x20: /* IMAN */ +- ret = xhci->iman; ++ ret = intr->iman; + break; + case 0x24: /* IMOD */ +- ret = xhci->imod; ++ ret = intr->imod; + break; + case 0x28: /* ERSTSZ */ +- ret = xhci->erstsz; ++ ret = intr->erstsz; + break; + case 0x30: /* ERSTBA low */ +- ret = xhci->erstba_low; ++ ret = intr->erstba_low; + break; + case 0x34: /* ERSTBA high */ +- ret = xhci->erstba_high; ++ ret = intr->erstba_high; + break; + case 0x38: /* ERDP low */ +- ret = xhci->erdp_low; ++ ret = intr->erdp_low; + break; + case 0x3c: /* ERDP high */ +- ret = xhci->erdp_high; ++ ret = intr->erdp_high; + break; + default: + fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); +@@ -2615,42 +2627,43 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + + static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) + { ++ XHCIInterrupter *intr = &xhci->intr[0]; + trace_usb_xhci_runtime_write(reg, val); + + switch (reg) { + case 0x20: /* IMAN */ + if (val & IMAN_IP) { +- xhci->iman &= ~IMAN_IP; ++ intr->iman &= ~IMAN_IP; + } +- xhci->iman &= ~IMAN_IE; +- xhci->iman |= val & IMAN_IE; ++ intr->iman &= ~IMAN_IE; ++ intr->iman |= val & IMAN_IE; + xhci_intx_update(xhci); +- xhci_msix_update(xhci); ++ xhci_msix_update(xhci, 0); + break; + case 0x24: /* IMOD */ +- xhci->imod = val; ++ intr->imod = val; + break; + case 0x28: /* ERSTSZ */ +- xhci->erstsz = val & 0xffff; ++ intr->erstsz = val & 0xffff; + break; + case 0x30: /* ERSTBA low */ + /* XXX NEC driver bug: it doesn't align this to 64 bytes +- xhci->erstba_low = val & 0xffffffc0; */ +- xhci->erstba_low = val & 0xfffffff0; ++ intr->erstba_low = val & 0xffffffc0; */ ++ intr->erstba_low = val & 0xfffffff0; + break; + case 0x34: /* ERSTBA high */ +- xhci->erstba_high = val; +- xhci_er_reset(xhci); ++ intr->erstba_high = val; ++ xhci_er_reset(xhci, 0); + break; + case 0x38: /* ERDP low */ + if (val & ERDP_EHB) { +- xhci->erdp_low &= ~ERDP_EHB; ++ intr->erdp_low &= ~ERDP_EHB; + } +- xhci->erdp_low = (val & ~ERDP_EHB) | (xhci->erdp_low & ERDP_EHB); ++ intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB); + break; + case 0x3c: /* ERDP high */ +- xhci->erdp_high = val; +- xhci_events_update(xhci); ++ intr->erdp_high = val; ++ xhci_events_update(xhci, 0); + break; + default: + fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); +@@ -2780,7 +2793,7 @@ static void xhci_wakeup(USBPort *usbport) + return; + } + port->portsc |= PORTSC_PLC; +- xhci_event(xhci, &ev); ++ xhci_event(xhci, &ev, 0 /* FIXME */); + } + + static void xhci_complete(USBPort *port, USBPacket *packet) +diff --git a/trace-events b/trace-events +index 8589ca4..b25ae1c 100644 +--- a/trace-events ++++ b/trace-events +@@ -316,7 +316,7 @@ usb_xhci_irq_msi(uint32_t nr) "nr %d" + usb_xhci_irq_msix(uint32_t nr) "nr %d" + usb_xhci_irq_msix_use(uint32_t nr) "nr %d" + usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d" +-usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x" ++usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x" + usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" + usb_xhci_slot_enable(uint32_t slotid) "slotid %d" + usb_xhci_slot_disable(uint32_t slotid) "slotid %d" +-- +1.7.12 + diff --git a/0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch b/0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch new file mode 100644 index 0000000..305b321 --- /dev/null +++ b/0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch @@ -0,0 +1,159 @@ +From 8bbd489bb885f5799ebbc108022eee3b2d5a1682 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 30 Aug 2012 17:15:12 +0200 +Subject: [PATCH 350/366] xhci: prepare xhci_runtime_{read,write} for multiple + interrupters + +Prepare xhci runtime register access function for multiple interrupters. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 100 +++++++++++++++++++++++++++++++----------------------- + 1 file changed, 57 insertions(+), 43 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index ddc3825..68a19ab 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -2588,37 +2588,43 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) + + static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + { +- XHCIInterrupter *intr = &xhci->intr[0]; +- uint32_t ret; ++ uint32_t ret = 0; + +- switch (reg) { +- case 0x00: /* MFINDEX */ +- ret = xhci_mfindex_get(xhci) & 0x3fff; +- break; +- case 0x20: /* IMAN */ +- ret = intr->iman; +- break; +- case 0x24: /* IMOD */ +- ret = intr->imod; +- break; +- case 0x28: /* ERSTSZ */ +- ret = intr->erstsz; +- break; +- case 0x30: /* ERSTBA low */ +- ret = intr->erstba_low; +- break; +- case 0x34: /* ERSTBA high */ +- ret = intr->erstba_high; +- break; +- case 0x38: /* ERDP low */ +- ret = intr->erdp_low; +- break; +- case 0x3c: /* ERDP high */ +- ret = intr->erdp_high; +- break; +- default: +- fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); +- ret = 0; ++ if (reg < 0x20) { ++ switch (reg) { ++ case 0x00: /* MFINDEX */ ++ ret = xhci_mfindex_get(xhci) & 0x3fff; ++ break; ++ default: ++ fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); ++ break; ++ } ++ } else { ++ int v = (reg - 0x20) / 0x20; ++ XHCIInterrupter *intr = &xhci->intr[v]; ++ switch (reg & 0x1f) { ++ case 0x00: /* IMAN */ ++ ret = intr->iman; ++ break; ++ case 0x04: /* IMOD */ ++ ret = intr->imod; ++ break; ++ case 0x08: /* ERSTSZ */ ++ ret = intr->erstsz; ++ break; ++ case 0x10: /* ERSTBA low */ ++ ret = intr->erstba_low; ++ break; ++ case 0x14: /* ERSTBA high */ ++ ret = intr->erstba_high; ++ break; ++ case 0x18: /* ERDP low */ ++ ret = intr->erdp_low; ++ break; ++ case 0x1c: /* ERDP high */ ++ ret = intr->erdp_high; ++ break; ++ } + } + + trace_usb_xhci_runtime_read(reg, ret); +@@ -2627,43 +2633,51 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + + static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) + { +- XHCIInterrupter *intr = &xhci->intr[0]; ++ int v = (reg - 0x20) / 0x20; ++ XHCIInterrupter *intr = &xhci->intr[v]; + trace_usb_xhci_runtime_write(reg, val); + +- switch (reg) { +- case 0x20: /* IMAN */ ++ if (reg < 0x20) { ++ fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); ++ return; ++ } ++ ++ switch (reg & 0x1f) { ++ case 0x00: /* IMAN */ + if (val & IMAN_IP) { + intr->iman &= ~IMAN_IP; + } + intr->iman &= ~IMAN_IE; + intr->iman |= val & IMAN_IE; +- xhci_intx_update(xhci); +- xhci_msix_update(xhci, 0); ++ if (v == 0) { ++ xhci_intx_update(xhci); ++ } ++ xhci_msix_update(xhci, v); + break; +- case 0x24: /* IMOD */ ++ case 0x04: /* IMOD */ + intr->imod = val; + break; +- case 0x28: /* ERSTSZ */ ++ case 0x08: /* ERSTSZ */ + intr->erstsz = val & 0xffff; + break; +- case 0x30: /* ERSTBA low */ ++ case 0x10: /* ERSTBA low */ + /* XXX NEC driver bug: it doesn't align this to 64 bytes + intr->erstba_low = val & 0xffffffc0; */ + intr->erstba_low = val & 0xfffffff0; + break; +- case 0x34: /* ERSTBA high */ ++ case 0x14: /* ERSTBA high */ + intr->erstba_high = val; +- xhci_er_reset(xhci, 0); ++ xhci_er_reset(xhci, v); + break; +- case 0x38: /* ERDP low */ ++ case 0x18: /* ERDP low */ + if (val & ERDP_EHB) { + intr->erdp_low &= ~ERDP_EHB; + } + intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB); + break; +- case 0x3c: /* ERDP high */ ++ case 0x1c: /* ERDP high */ + intr->erdp_high = val; +- xhci_events_update(xhci, 0); ++ xhci_events_update(xhci, v); + break; + default: + fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); +-- +1.7.12 + diff --git a/0351-xhci-pick-target-interrupter.patch b/0351-xhci-pick-target-interrupter.patch new file mode 100644 index 0000000..bf95359 --- /dev/null +++ b/0351-xhci-pick-target-interrupter.patch @@ -0,0 +1,93 @@ +From 11ba203517cc28be513ac31c12762c4519e98ee5 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 31 Aug 2012 15:30:51 +0200 +Subject: [PATCH 351/366] xhci: pick target interrupter + +Pick the correct interrupter when queuing an event. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 68a19ab..d6ab0c6 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -264,6 +264,10 @@ typedef enum TRBCCode { + + #define TRB_LK_TC (1<<1) + ++#define TRB_INTR_SHIFT 22 ++#define TRB_INTR_MASK 0x3ff ++#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) ++ + #define EP_TYPE_MASK 0x7 + #define EP_TYPE_SHIFT 3 + +@@ -806,10 +810,16 @@ static void xhci_events_update(XHCIState *xhci, int v) + + static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) + { +- XHCIInterrupter *intr = &xhci->intr[v]; ++ XHCIInterrupter *intr; + dma_addr_t erdp; + unsigned int dp_idx; + ++ if (v >= MAXINTRS) { ++ DPRINTF("intr nr out of range (%d >= %d)\n", v, MAXINTRS); ++ return; ++ } ++ intr = &xhci->intr[v]; ++ + if (intr->er_full) { + DPRINTF("xhci_event(): ER full, queueing\n"); + if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { +@@ -1377,7 +1387,7 @@ static void xhci_xfer_report(XHCITransfer *xfer) + DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length); + edtla = 0; + } +- xhci_event(xhci, &event, 0 /* FIXME */); ++ xhci_event(xhci, &event, TRB_INTR(*trb)); + reported = 1; + if (xfer->status != CC_SUCCESS) { + return; +@@ -2255,7 +2265,7 @@ static void xhci_process_commands(XHCIState *xhci) + break; + } + event.slotid = slotid; +- xhci_event(xhci, &event, 0 /* FIXME */); ++ xhci_event(xhci, &event, 0); + } + } + +@@ -2285,7 +2295,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) + port->portsc |= PORTSC_CSC; + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, + port->portnr << 24}; +- xhci_event(xhci, &ev, 0 /* FIXME */); ++ xhci_event(xhci, &ev, 0); + DPRINTF("xhci: port change event for port %d\n", port->portnr); + } + } +@@ -2564,7 +2574,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) + if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) { + XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED}; + xhci->crcr_low &= ~CRCR_CRR; +- xhci_event(xhci, &event, 0 /* FIXME */); ++ xhci_event(xhci, &event, 0); + DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low); + } else { + dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val); +@@ -2807,7 +2817,7 @@ static void xhci_wakeup(USBPort *usbport) + return; + } + port->portsc |= PORTSC_PLC; +- xhci_event(xhci, &ev, 0 /* FIXME */); ++ xhci_event(xhci, &ev, 0); + } + + static void xhci_complete(USBPort *port, USBPacket *packet) +-- +1.7.12 + diff --git a/0352-xhci-support-multiple-interrupters.patch b/0352-xhci-support-multiple-interrupters.patch new file mode 100644 index 0000000..08b1d44 --- /dev/null +++ b/0352-xhci-support-multiple-interrupters.patch @@ -0,0 +1,40 @@ +From d6d045365af5cef5bc98ad48dcf4172cbe35c7c4 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 4 Sep 2012 12:56:55 +0200 +Subject: [PATCH 352/366] xhci: support multiple interrupters + +Everything is in place, flip the big switch now +and enable support for multiple interrupters. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index d6ab0c6..55e31ec 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -42,7 +42,7 @@ + + #define MAXPORTS (MAXPORTS_2+MAXPORTS_3) + #define MAXSLOTS MAXPORTS +-#define MAXINTRS 1 /* MAXPORTS */ ++#define MAXINTRS MAXPORTS + + #define TD_QUEUE 24 + +@@ -75,10 +75,6 @@ + # error Increase LEN_REGS + #endif + +-#if MAXINTRS > 1 +-# error TODO: only one interrupter supported +-#endif +- + /* bit definitions */ + #define USBCMD_RS (1<<0) + #define USBCMD_HCRST (1<<1) +-- +1.7.12 + diff --git a/0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch b/0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch new file mode 100644 index 0000000..04a2dbf --- /dev/null +++ b/0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch @@ -0,0 +1,281 @@ +From e5295db184d2b78a6a779aac019acbf58ed3da5e Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 4 Sep 2012 14:42:20 +0200 +Subject: [PATCH 353/366] xhci: kill xhci_mem_{read,write} dispatcher + functions + +... and register subregions instead, so we offload the dispatching +to the the memory subsystem which is designed to handle it. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 140 +++++++++++++++++++++++++++++------------------------- + 1 file changed, 75 insertions(+), 65 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 55e31ec..500892d 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -404,6 +404,10 @@ struct XHCIState { + USBBus bus; + qemu_irq irq; + MemoryRegion mem; ++ MemoryRegion mem_cap; ++ MemoryRegion mem_oper; ++ MemoryRegion mem_runtime; ++ MemoryRegion mem_doorbell; + const char *name; + unsigned int devaddr; + +@@ -2345,8 +2349,9 @@ static void xhci_reset(DeviceState *dev) + xhci_mfwrap_update(xhci); + } + +-static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) ++static uint64_t xhci_cap_read(void *ptr, target_phys_addr_t reg, unsigned size) + { ++ XHCIState *xhci = ptr; + uint32_t ret; + + switch (reg) { +@@ -2403,7 +2408,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) + ret = 0x00000000; /* reserved */ + break; + default: +- fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg); ++ fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg); + ret = 0; + } + +@@ -2484,8 +2489,9 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) + } + } + +-static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) ++static uint64_t xhci_oper_read(void *ptr, target_phys_addr_t reg, unsigned size) + { ++ XHCIState *xhci = ptr; + uint32_t ret; + + if (reg >= 0x400) { +@@ -2521,7 +2527,7 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) + ret = xhci->config; + break; + default: +- fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg); ++ fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg); + ret = 0; + } + +@@ -2529,8 +2535,11 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) + return ret; + } + +-static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) ++static void xhci_oper_write(void *ptr, target_phys_addr_t reg, ++ uint64_t val, unsigned size) + { ++ XHCIState *xhci = ptr; ++ + if (reg >= 0x400) { + xhci_port_write(xhci, reg - 0x400, val); + return; +@@ -2588,12 +2597,14 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) + xhci->config = val & 0xff; + break; + default: +- fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); ++ fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg); + } + } + +-static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) ++static uint64_t xhci_runtime_read(void *ptr, target_phys_addr_t reg, ++ unsigned size) + { ++ XHCIState *xhci = ptr; + uint32_t ret = 0; + + if (reg < 0x20) { +@@ -2602,7 +2613,8 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + ret = xhci_mfindex_get(xhci) & 0x3fff; + break; + default: +- fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); ++ fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", ++ (int)reg); + break; + } + } else { +@@ -2637,14 +2649,16 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) + return ret; + } + +-static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) ++static void xhci_runtime_write(void *ptr, target_phys_addr_t reg, ++ uint64_t val, unsigned size) + { ++ XHCIState *xhci = ptr; + int v = (reg - 0x20) / 0x20; + XHCIInterrupter *intr = &xhci->intr[v]; + trace_usb_xhci_runtime_write(reg, val); + + if (reg < 0x20) { +- fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); ++ fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg); + return; + } + +@@ -2686,19 +2700,24 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) + xhci_events_update(xhci, v); + break; + default: +- fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); ++ fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", ++ (int)reg); + } + } + +-static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg) ++static uint64_t xhci_doorbell_read(void *ptr, target_phys_addr_t reg, ++ unsigned size) + { + /* doorbells always read as 0 */ + trace_usb_xhci_doorbell_read(reg, 0); + return 0; + } + +-static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val) ++static void xhci_doorbell_write(void *ptr, target_phys_addr_t reg, ++ uint64_t val, unsigned size) + { ++ XHCIState *xhci = ptr; ++ + trace_usb_xhci_doorbell_write(reg, val); + + if (!xhci_running(xhci)) { +@@ -2712,69 +2731,47 @@ static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val) + if (val == 0) { + xhci_process_commands(xhci); + } else { +- fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", val); ++ fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", ++ (uint32_t)val); + } + } else { + if (reg > MAXSLOTS) { +- fprintf(stderr, "xhci: bad doorbell %d\n", reg); ++ fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg); + } else if (val > 31) { +- fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", reg, val); ++ fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", ++ (int)reg, (uint32_t)val); + } else { + xhci_kick_ep(xhci, reg, val); + } + } + } + +-static uint64_t xhci_mem_read(void *ptr, target_phys_addr_t addr, +- unsigned size) +-{ +- XHCIState *xhci = ptr; +- +- /* Only aligned reads are allowed on xHCI */ +- if (addr & 3) { +- fprintf(stderr, "xhci_mem_read: Mis-aligned read\n"); +- return 0; +- } +- +- if (addr < LEN_CAP) { +- return xhci_cap_read(xhci, addr); +- } else if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) { +- return xhci_oper_read(xhci, addr - OFF_OPER); +- } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) { +- return xhci_runtime_read(xhci, addr - OFF_RUNTIME); +- } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) { +- return xhci_doorbell_read(xhci, addr - OFF_DOORBELL); +- } else { +- fprintf(stderr, "xhci_mem_read: Bad offset %x\n", (int)addr); +- return 0; +- } +-} +- +-static void xhci_mem_write(void *ptr, target_phys_addr_t addr, +- uint64_t val, unsigned size) +-{ +- XHCIState *xhci = ptr; ++static const MemoryRegionOps xhci_cap_ops = { ++ .read = xhci_cap_read, ++ .valid.min_access_size = 4, ++ .valid.max_access_size = 4, ++ .endianness = DEVICE_LITTLE_ENDIAN, ++}; + +- /* Only aligned writes are allowed on xHCI */ +- if (addr & 3) { +- fprintf(stderr, "xhci_mem_write: Mis-aligned write\n"); +- return; +- } ++static const MemoryRegionOps xhci_oper_ops = { ++ .read = xhci_oper_read, ++ .write = xhci_oper_write, ++ .valid.min_access_size = 4, ++ .valid.max_access_size = 4, ++ .endianness = DEVICE_LITTLE_ENDIAN, ++}; + +- if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) { +- xhci_oper_write(xhci, addr - OFF_OPER, val); +- } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) { +- xhci_runtime_write(xhci, addr - OFF_RUNTIME, val); +- } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) { +- xhci_doorbell_write(xhci, addr - OFF_DOORBELL, val); +- } else { +- fprintf(stderr, "xhci_mem_write: Bad offset %x\n", (int)addr); +- } +-} ++static const MemoryRegionOps xhci_runtime_ops = { ++ .read = xhci_runtime_read, ++ .write = xhci_runtime_write, ++ .valid.min_access_size = 4, ++ .valid.max_access_size = 4, ++ .endianness = DEVICE_LITTLE_ENDIAN, ++}; + +-static const MemoryRegionOps xhci_mem_ops = { +- .read = xhci_mem_read, +- .write = xhci_mem_write, ++static const MemoryRegionOps xhci_doorbell_ops = { ++ .read = xhci_doorbell_read, ++ .write = xhci_doorbell_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +@@ -2940,8 +2937,21 @@ static int usb_xhci_initfn(struct PCIDevice *dev) + + xhci->irq = xhci->pci_dev.irq[0]; + +- memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci, +- "xhci", LEN_REGS); ++ memory_region_init(&xhci->mem, "xhci", LEN_REGS); ++ memory_region_init_io(&xhci->mem_cap, &xhci_cap_ops, xhci, ++ "capabilities", LEN_CAP); ++ memory_region_init_io(&xhci->mem_oper, &xhci_oper_ops, xhci, ++ "operational", 0x400 + 0x10 * xhci->numports); ++ memory_region_init_io(&xhci->mem_runtime, &xhci_runtime_ops, xhci, ++ "runtime", LEN_RUNTIME); ++ memory_region_init_io(&xhci->mem_doorbell, &xhci_doorbell_ops, xhci, ++ "doorbell", LEN_DOORBELL); ++ ++ memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap); ++ memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper); ++ memory_region_add_subregion(&xhci->mem, OFF_RUNTIME, &xhci->mem_runtime); ++ memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell); ++ + pci_register_bar(&xhci->pci_dev, 0, + PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, + &xhci->mem); +-- +1.7.12 + diff --git a/0354-xhci-allow-bytewise-capability-register-reads.patch b/0354-xhci-allow-bytewise-capability-register-reads.patch new file mode 100644 index 0000000..12b4338 --- /dev/null +++ b/0354-xhci-allow-bytewise-capability-register-reads.patch @@ -0,0 +1,32 @@ +From 25968a7c7947b7e215e351b159a5f4ebf4609aab Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Tue, 4 Sep 2012 14:48:03 +0200 +Subject: [PATCH 354/366] xhci: allow bytewise capability register reads + +Some guests need this according to +Alejandro Martinez Ruiz + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 500892d..2918e64 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -2748,8 +2748,10 @@ static void xhci_doorbell_write(void *ptr, target_phys_addr_t reg, + + static const MemoryRegionOps xhci_cap_ops = { + .read = xhci_cap_read, +- .valid.min_access_size = 4, ++ .valid.min_access_size = 1, + .valid.max_access_size = 4, ++ .impl.min_access_size = 4, ++ .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, + }; + +-- +1.7.12 + diff --git a/0355-ehci-switch-to-new-style-memory-ops.patch b/0355-ehci-switch-to-new-style-memory-ops.patch new file mode 100644 index 0000000..78e6b4f --- /dev/null +++ b/0355-ehci-switch-to-new-style-memory-ops.patch @@ -0,0 +1,367 @@ +From 538ee859ae415782e5be3b4a07e7db655cf70aa2 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 6 Sep 2012 11:24:51 +0200 +Subject: [PATCH 355/366] ehci: switch to new-style memory ops + +Also register different memory regions for capabilities, +operational registers and port status registers. Create +separate tracepoints for operational regs and port status +regs. Ditch a bunch of sanity checks because the memory +core will do this for us now. + +Offloading the byte, word and dword access handling to the +memory core also has the side effect of fixing ehci register +access on bigendian hosts. + +Cc: David Gibson +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 173 ++++++++++++++++++++++++++---------------------------- + trace-events | 9 ++- + 2 files changed, 90 insertions(+), 92 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 2f3e9c0..f5ba8e1 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -389,6 +389,9 @@ struct EHCIState { + USBBus bus; + qemu_irq irq; + MemoryRegion mem; ++ MemoryRegion mem_caps; ++ MemoryRegion mem_opreg; ++ MemoryRegion mem_ports; + int companion_count; + + /* properties */ +@@ -398,10 +401,10 @@ struct EHCIState { + * EHCI spec version 1.0 Section 2.3 + * Host Controller Operational Registers + */ ++ uint8_t caps[OPREGBASE]; + union { +- uint8_t mmio[MMIO_SIZE]; ++ uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)]; + struct { +- uint8_t cap[OPREGBASE]; + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; +@@ -411,9 +414,9 @@ struct EHCIState { + uint32_t asynclistaddr; + uint32_t notused[9]; + uint32_t configflag; +- uint32_t portsc[NB_PORTS]; + }; + }; ++ uint32_t portsc[NB_PORTS]; + + /* + * Internal states, shadow registers, etc +@@ -471,22 +474,12 @@ static const char *ehci_state_names[] = { + }; + + static const char *ehci_mmio_names[] = { +- [CAPLENGTH] = "CAPLENGTH", +- [HCIVERSION] = "HCIVERSION", +- [HCSPARAMS] = "HCSPARAMS", +- [HCCPARAMS] = "HCCPARAMS", + [USBCMD] = "USBCMD", + [USBSTS] = "USBSTS", + [USBINTR] = "USBINTR", + [FRINDEX] = "FRINDEX", + [PERIODICLISTBASE] = "P-LIST BASE", + [ASYNCLISTADDR] = "A-LIST ADDR", +- [PORTSC_BEGIN] = "PORTSC #0", +- [PORTSC_BEGIN + 4] = "PORTSC #1", +- [PORTSC_BEGIN + 8] = "PORTSC #2", +- [PORTSC_BEGIN + 12] = "PORTSC #3", +- [PORTSC_BEGIN + 16] = "PORTSC #4", +- [PORTSC_BEGIN + 20] = "PORTSC #5", + [CONFIGFLAG] = "CONFIGFLAG", + }; + +@@ -509,7 +502,8 @@ static const char *state2str(uint32_t state) + + static const char *addr2str(target_phys_addr_t addr) + { +- return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); ++ return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), ++ addr + OPREGBASE); + } + + static void ehci_trace_usbsts(uint32_t mask, int state) +@@ -1018,7 +1012,7 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], + } + + s->companion_count++; +- s->mmio[0x05] = (s->companion_count << 4) | portcount; ++ s->caps[0x05] = (s->companion_count << 4) | portcount; + + return 0; + } +@@ -1063,7 +1057,8 @@ static void ehci_reset(void *opaque) + } + } + +- memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); ++ memset(&s->opreg, 0x00, sizeof(s->opreg)); ++ memset(&s->portsc, 0x00, sizeof(s->portsc)); + + s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; + s->usbsts = USBSTS_HALT; +@@ -1090,50 +1085,35 @@ static void ehci_reset(void *opaque) + qemu_bh_cancel(s->async_bh); + } + +-static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) ++static uint64_t ehci_caps_read(void *ptr, target_phys_addr_t addr, ++ unsigned size) + { + EHCIState *s = ptr; +- uint32_t val; +- +- val = s->mmio[addr]; +- +- return val; ++ return s->caps[addr]; + } + +-static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr) ++static uint64_t ehci_opreg_read(void *ptr, target_phys_addr_t addr, ++ unsigned size) + { + EHCIState *s = ptr; + uint32_t val; + +- val = s->mmio[addr] | (s->mmio[addr+1] << 8); +- ++ val = s->opreg[addr >> 2]; ++ trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val); + return val; + } + +-static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr) ++static uint64_t ehci_port_read(void *ptr, target_phys_addr_t addr, ++ unsigned size) + { + EHCIState *s = ptr; + uint32_t val; + +- val = s->mmio[addr] | (s->mmio[addr+1] << 8) | +- (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24); +- +- trace_usb_ehci_mmio_readl(addr, addr2str(addr), val); ++ val = s->portsc[addr >> 2]; ++ trace_usb_ehci_portsc_read(addr + PORTSC_BEGIN, addr >> 2, val); + return val; + } + +-static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val) +-{ +- fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n"); +- exit(1); +-} +- +-static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) +-{ +- fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n"); +- exit(1); +-} +- + static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) + { + USBDevice *dev = s->ports[port].dev; +@@ -1162,11 +1142,17 @@ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) + } + } + +-static void handle_port_status_write(EHCIState *s, int port, uint32_t val) ++static void ehci_port_write(void *ptr, target_phys_addr_t addr, ++ uint64_t val, unsigned size) + { ++ EHCIState *s = ptr; ++ int port = addr >> 2; + uint32_t *portsc = &s->portsc[port]; ++ uint32_t old = *portsc; + USBDevice *dev = s->ports[port].dev; + ++ trace_usb_ehci_portsc_write(addr + PORTSC_BEGIN, addr >> 2, val); ++ + /* Clear rwc bits */ + *portsc &= ~(val & PORTSC_RWC_MASK); + /* The guest may clear, but not set the PED bit */ +@@ -1198,39 +1184,20 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) + + *portsc &= ~PORTSC_RO_MASK; + *portsc |= val; ++ trace_usb_ehci_portsc_change(addr + PORTSC_BEGIN, addr >> 2, *portsc, old); + } + +-static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) ++static void ehci_opreg_write(void *ptr, target_phys_addr_t addr, ++ uint64_t val, unsigned size) + { + EHCIState *s = ptr; +- uint32_t *mmio = (uint32_t *)(&s->mmio[addr]); ++ uint32_t *mmio = s->opreg + (addr >> 2); + uint32_t old = *mmio; + int i; + +- trace_usb_ehci_mmio_writel(addr, addr2str(addr), val); +- +- /* Only aligned reads are allowed on OHCI */ +- if (addr & 3) { +- fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x" +- TARGET_FMT_plx "\n", addr); +- return; +- } +- +- if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) { +- handle_port_status_write(s, (addr-PORTSC)/4, val); +- trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old); +- return; +- } +- +- if (addr < OPREGBASE) { +- fprintf(stderr, "usb-ehci: write attempt to read-only register" +- TARGET_FMT_plx "\n", addr); +- return; +- } +- ++ trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val); + +- /* Do any register specific pre-write processing here. */ +- switch(addr) { ++ switch (addr + OPREGBASE) { + case USBCMD: + if (val & USBCMD_HCRESET) { + ehci_reset(s); +@@ -1241,7 +1208,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) + /* not supporting dynamic frame list size at the moment */ + if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { + fprintf(stderr, "attempt to set frame list size -- value %d\n", +- val & USBCMD_FLS); ++ (int)val & USBCMD_FLS); + val &= ~USBCMD_FLS; + } + +@@ -1308,7 +1275,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) + } + + *mmio = val; +- trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old); ++ trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old); + } + + +@@ -2520,11 +2487,28 @@ static void ehci_async_bh(void *opaque) + ehci_advance_async_state(ehci); + } + +-static const MemoryRegionOps ehci_mem_ops = { +- .old_mmio = { +- .read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl }, +- .write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel }, +- }, ++static const MemoryRegionOps ehci_mmio_caps_ops = { ++ .read = ehci_caps_read, ++ .valid.min_access_size = 1, ++ .valid.max_access_size = 4, ++ .impl.min_access_size = 1, ++ .impl.max_access_size = 1, ++ .endianness = DEVICE_LITTLE_ENDIAN, ++}; ++ ++static const MemoryRegionOps ehci_mmio_opreg_ops = { ++ .read = ehci_opreg_read, ++ .write = ehci_opreg_write, ++ .valid.min_access_size = 4, ++ .valid.max_access_size = 4, ++ .endianness = DEVICE_LITTLE_ENDIAN, ++}; ++ ++static const MemoryRegionOps ehci_mmio_port_ops = { ++ .read = ehci_port_read, ++ .write = ehci_port_write, ++ .valid.min_access_size = 4, ++ .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, + }; + +@@ -2681,19 +2665,19 @@ static int usb_ehci_initfn(PCIDevice *dev) + pci_conf[0x6e] = 0x00; + pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS + +- // 2.2 host controller interface version +- s->mmio[0x00] = (uint8_t) OPREGBASE; +- s->mmio[0x01] = 0x00; +- s->mmio[0x02] = 0x00; +- s->mmio[0x03] = 0x01; // HC version +- s->mmio[0x04] = NB_PORTS; // Number of downstream ports +- s->mmio[0x05] = 0x00; // No companion ports at present +- s->mmio[0x06] = 0x00; +- s->mmio[0x07] = 0x00; +- s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable +- s->mmio[0x09] = 0x68; // EECP +- s->mmio[0x0a] = 0x00; +- s->mmio[0x0b] = 0x00; ++ /* 2.2 host controller interface version */ ++ s->caps[0x00] = (uint8_t) OPREGBASE; ++ s->caps[0x01] = 0x00; ++ s->caps[0x02] = 0x00; ++ s->caps[0x03] = 0x01; /* HC version */ ++ s->caps[0x04] = NB_PORTS; /* Number of downstream ports */ ++ s->caps[0x05] = 0x00; /* No companion ports at present */ ++ s->caps[0x06] = 0x00; ++ s->caps[0x07] = 0x00; ++ s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */ ++ s->caps[0x09] = 0x68; /* EECP */ ++ s->caps[0x0a] = 0x00; ++ s->caps[0x0b] = 0x00; + + s->irq = s->dev.irq[3]; + +@@ -2712,7 +2696,18 @@ static int usb_ehci_initfn(PCIDevice *dev) + + qemu_register_reset(ehci_reset, s); + +- memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE); ++ memory_region_init(&s->mem, "ehci", MMIO_SIZE); ++ memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s, ++ "capabilities", OPREGBASE); ++ memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s, ++ "operational", PORTSC_BEGIN - OPREGBASE); ++ memory_region_init_io(&s->mem_ports, &ehci_mmio_port_ops, s, ++ "ports", PORTSC_END - PORTSC_BEGIN); ++ ++ memory_region_add_subregion(&s->mem, 0, &s->mem_caps); ++ memory_region_add_subregion(&s->mem, OPREGBASE, &s->mem_opreg); ++ memory_region_add_subregion(&s->mem, PORTSC_BEGIN, &s->mem_ports); ++ + pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); + + return 0; +diff --git a/trace-events b/trace-events +index b25ae1c..a58b0b7 100644 +--- a/trace-events ++++ b/trace-events +@@ -243,9 +243,12 @@ usb_port_release(int bus, const char *port) "bus %d, port %s" + + # hw/usb/hcd-ehci.c + usb_ehci_reset(void) "=== RESET ===" +-usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" +-usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x" +-usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)" ++usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" ++usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x" ++usb_ehci_opreg_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)" ++usb_ehci_portsc_read(uint32_t addr, uint32_t port, uint32_t val) "rd mmio %04x [port %d] = %x" ++usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio %04x [port %d] = %x" ++usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio %04x [port %d] = %x (old: %x)" + usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" + usb_ehci_state(const char *schedule, const char *state) "%s schedule %s" + usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x" +-- +1.7.12 + diff --git a/0356-xhci-drop-unused-wlength.patch b/0356-xhci-drop-unused-wlength.patch new file mode 100644 index 0000000..7bc3008 --- /dev/null +++ b/0356-xhci-drop-unused-wlength.patch @@ -0,0 +1,33 @@ +From 0b32fd99ab948db3b4cac32562272095a2594f4c Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 6 Sep 2012 11:55:06 +0200 +Subject: [PATCH 356/366] xhci: drop unused wlength + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-xhci.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c +index 2918e64..e0ca690 100644 +--- a/hw/usb/hcd-xhci.c ++++ b/hw/usb/hcd-xhci.c +@@ -1505,7 +1505,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + { + XHCITRB *trb_setup, *trb_status; + uint8_t bmRequestType; +- uint16_t wLength; + int ret; + + trb_setup = &xfer->trbs[0]; +@@ -1540,7 +1539,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) + } + + bmRequestType = trb_setup->parameter; +- wLength = trb_setup->parameter >> 48; + + xfer->in_xfer = bmRequestType & USB_DIR_IN; + xfer->iso_xfer = false; +-- +1.7.12 + diff --git a/0357-usb-host-allow-emulated-non-async-control-requests-w.patch b/0357-usb-host-allow-emulated-non-async-control-requests-w.patch new file mode 100644 index 0000000..752582f --- /dev/null +++ b/0357-usb-host-allow-emulated-non-async-control-requests-w.patch @@ -0,0 +1,37 @@ +From 487e24442148aa659a53f69db394642a7d93c3c6 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 6 Sep 2012 12:03:41 +0200 +Subject: [PATCH 357/366] usb-host: allow emulated (non-async) control + requests without USBPacket + +xhci needs this for USB_REQ_SET_ADDRESS due to the way +usb addressing is handled by the xhci hardware. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb/host-linux.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c +index 8df9207..44f1a64 100644 +--- a/hw/usb/host-linux.c ++++ b/hw/usb/host-linux.c +@@ -1045,7 +1045,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, + + /* Note request is (bRequestType << 8) | bRequest */ + trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index); +- assert(p->result == 0); + + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: +@@ -1074,6 +1073,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, + } + + /* The rest are asynchronous */ ++ assert(p && p->result == 0); + + if (length > sizeof(dev->data_buf)) { + fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", +-- +1.7.12 + diff --git a/0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch b/0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch new file mode 100644 index 0000000..f40d17b --- /dev/null +++ b/0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch @@ -0,0 +1,101 @@ +From 833eeda8129b2cf4955a34600b60c01e00652526 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 5 Sep 2012 12:13:41 +0200 +Subject: [PATCH 358/366] ehci: Don't set seen to 0 when removing unseen + queue-heads + +When removing unseen queue-heads from the async queue list, we should not +set the seen flag to 0, as this may cause them to be removed by +ehci_queues_rip_unused() during the next call to ehci_advance_async_state() +if the timer is late or running at a low frequency. + +Note: +1) This *may* have caused the instant unlink / relinks described in commit + 9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0 + +2) Rather then putting more if-s inside ehci_queues_rip_unused, this patch + instead introduces a new ehci_queues_rip_unseen function. + +3) This patch also makes it save to call ehci_queues_rip_unseen() multiple + times, which gets used in the folluw up patch titled: + "ehci: Walk async schedule before and after migration" + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index f5ba8e1..6f48132 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -847,10 +847,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, + return NULL; + } + +-static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) ++static void ehci_queues_rip_unused(EHCIState *ehci, int async) + { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; +- const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL; ++ const char *warn = async ? "guest unlinked busy QH" : NULL; + uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; + EHCIQueue *q, *tmp; + +@@ -860,13 +860,25 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) + q->ts = ehci->last_run_ns; + continue; + } +- if (!flush && ehci->last_run_ns < q->ts + maxage) { ++ if (ehci->last_run_ns < q->ts + maxage) { + continue; + } + ehci_free_queue(q, warn); + } + } + ++static void ehci_queues_rip_unseen(EHCIState *ehci, int async) ++{ ++ EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; ++ EHCIQueue *q, *tmp; ++ ++ QTAILQ_FOREACH_SAFE(q, head, next, tmp) { ++ if (!q->seen) { ++ ehci_free_queue(q, NULL); ++ } ++ } ++} ++ + static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) + { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; +@@ -1699,7 +1711,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) + ehci_set_usbsts(ehci, USBSTS_REC); + } + +- ehci_queues_rip_unused(ehci, async, 0); ++ ehci_queues_rip_unused(ehci, async); + + /* Find the head of the list (4.9.1.1) */ + for(i = 0; i < MAX_QH; i++) { +@@ -2331,7 +2343,7 @@ static void ehci_advance_async_state(EHCIState *ehci) + */ + if (ehci->usbcmd & USBCMD_IAAD) { + /* Remove all unseen qhs from the async qhs queue */ +- ehci_queues_rip_unused(ehci, async, 1); ++ ehci_queues_rip_unseen(ehci, async); + trace_usb_ehci_doorbell_ack(); + ehci->usbcmd &= ~USBCMD_IAAD; + ehci_raise_irq(ehci, USBSTS_IAA); +@@ -2384,7 +2396,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) + ehci_set_fetch_addr(ehci, async,entry); + ehci_set_state(ehci, async, EST_FETCHENTRY); + ehci_advance_state(ehci, async); +- ehci_queues_rip_unused(ehci, async, 0); ++ ehci_queues_rip_unused(ehci, async); + break; + + default: +-- +1.7.12 + diff --git a/0359-ehci-Walk-async-schedule-before-and-after-migration.patch b/0359-ehci-Walk-async-schedule-before-and-after-migration.patch new file mode 100644 index 0000000..3c2bf54 --- /dev/null +++ b/0359-ehci-Walk-async-schedule-before-and-after-migration.patch @@ -0,0 +1,66 @@ +From fec70ddafe1632f40608ef6917760a7f946f278a Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 5 Sep 2012 12:07:10 +0200 +Subject: [PATCH 359/366] ehci: Walk async schedule before and after migration + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 6f48132..30d2b56 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -34,6 +34,7 @@ + #include "monitor.h" + #include "trace.h" + #include "dma.h" ++#include "sysemu.h" + + #define EHCI_DEBUG 0 + +@@ -2558,6 +2559,32 @@ static int usb_ehci_post_load(void *opaque, int version_id) + return 0; + } + ++static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) ++{ ++ EHCIState *ehci = opaque; ++ ++ /* ++ * We don't migrate the EHCIQueue-s, instead we rebuild them for the ++ * schedule in guest memory. We must do the rebuilt ASAP, so that ++ * USB-devices which have async handled packages have a packet in the ++ * ep queue to match the completion with. ++ */ ++ if (state == RUN_STATE_RUNNING) { ++ ehci_advance_async_state(ehci); ++ } ++ ++ /* ++ * The schedule rebuilt from guest memory could cause the migration dest ++ * to miss a QH unlink, and fail to cancel packets, since the unlinked QH ++ * will never have existed on the destination. Therefor we must flush the ++ * async schedule on savevm to catch any not yet noticed unlinks. ++ */ ++ if (state == RUN_STATE_SAVE_VM) { ++ ehci_advance_async_state(ehci); ++ ehci_queues_rip_unseen(ehci, 1); ++ } ++} ++ + static const VMStateDescription vmstate_ehci = { + .name = "ehci", + .version_id = 2, +@@ -2707,6 +2734,7 @@ static int usb_ehci_initfn(PCIDevice *dev) + usb_packet_init(&s->ipacket); + + qemu_register_reset(ehci_reset, s); ++ qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); + + memory_region_init(&s->mem, "ehci", MMIO_SIZE); + memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s, +-- +1.7.12 + diff --git a/0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch b/0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch new file mode 100644 index 0000000..fb0fe6b --- /dev/null +++ b/0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch @@ -0,0 +1,49 @@ +From d2901e4798cb106d5b265aa4e3ae05c06bf2bd1c Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 6 Sep 2012 17:10:48 +0200 +Subject: [PATCH 360/366] ehci: Don't process too much frames in 1 timer tick + +The Linux ehci isoc scheduling code fills the entire schedule ahead of +time minus 80 frames. If we make a large jump in where we are in the +schedule, ie 40 frames, then the scheduler all of a sudden will only have +40 frames left to work in, causing it to fail packet submissions +with error -27 (-EFBIG). + +Note at first I had MAX_FR_PER_TICK set to 8, which works well with newer +Linux guest kernels, but not with older ones (such as the RHEL-6 kernel). + +Signed-off-by: Hans de Goede +--- + hw/usb/hcd-ehci.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 30d2b56..5398544 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -140,6 +140,7 @@ + #define NB_PORTS 6 // Number of downstream ports + #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction + #define MAX_QH 100 // Max allowable queue heads in a chain ++#define MAX_FR_PER_TICK 4 /* Max frames to process in one timer tick */ + + /* Internal periodic / asynchronous schedule state machine states + */ +@@ -2460,6 +2461,14 @@ static void ehci_frame_timer(void *opaque) + DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames); + } + ++ /* ++ * Processing too much frames at once causes the Linux EHCI isoc ++ * scheduling code to fail packet re-submissions with -EFBIG. ++ */ ++ if (frames > MAX_FR_PER_TICK) { ++ frames = MAX_FR_PER_TICK; ++ } ++ + for (i = 0; i < frames; i++) { + ehci_update_frindex(ehci, 1); + ehci_advance_periodic_state(ehci); +-- +1.7.12 + diff --git a/0361-usb-Migrate-over-device-speed-and-speedmask.patch b/0361-usb-Migrate-over-device-speed-and-speedmask.patch new file mode 100644 index 0000000..d775b9c --- /dev/null +++ b/0361-usb-Migrate-over-device-speed-and-speedmask.patch @@ -0,0 +1,43 @@ +From 6ba840c192897029895930a504527d4350b88d26 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 6 Sep 2012 15:34:19 +0200 +Subject: [PATCH 361/366] usb: Migrate over device speed and speedmask + +Signed-off-by: Hans de Goede +--- + hw/usb.h | 4 ++-- + hw/usb/bus.c | 2 ++ + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/hw/usb.h b/hw/usb.h +index 48c8926..918af99 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -204,9 +204,9 @@ struct USBDevice { + uint32_t flags; + + /* Actual connected speed */ +- int speed; ++ int32_t speed; + /* Supported speeds, not in info because it may be variable (hostdevs) */ +- int speedmask; ++ int32_t speedmask; + uint8_t addr; + char product_desc[32]; + int auto_attach; +diff --git a/hw/usb/bus.c b/hw/usb/bus.c +index b649360..223c1df 100644 +--- a/hw/usb/bus.c ++++ b/hw/usb/bus.c +@@ -55,6 +55,8 @@ const VMStateDescription vmstate_usb_device = { + .minimum_version_id = 1, + .post_load = usb_device_post_load, + .fields = (VMStateField []) { ++ VMSTATE_INT32(speed, USBDevice), ++ VMSTATE_INT32(speedmask, USBDevice), + VMSTATE_UINT8(addr, USBDevice), + VMSTATE_INT32(state, USBDevice), + VMSTATE_INT32(remote_wakeup, USBDevice), +-- +1.7.12 + diff --git a/0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch b/0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch new file mode 100644 index 0000000..ee0c0f9 --- /dev/null +++ b/0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch @@ -0,0 +1,184 @@ +From 4d7d1b57fa5d3a950818e5dee459aefa4dc6ae27 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 4 Sep 2012 14:18:34 +0200 +Subject: [PATCH 362/366] usb-redir: Change cancelled packet code into a + generic packet-id queue + +Signed-off-by: Hans de Goede +--- + hw/usb/redirect.c | 102 +++++++++++++++++++++++++++++++++++++----------------- + 1 file changed, 71 insertions(+), 31 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 9cbcddb..08776d9 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -43,7 +43,6 @@ + #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) + #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) + +-typedef struct Cancelled Cancelled; + typedef struct USBRedirDevice USBRedirDevice; + + /* Struct to hold buffered packets (iso or int input packets) */ +@@ -69,6 +68,18 @@ struct endp_data { + int bufpq_target_size; + }; + ++struct PacketIdQueueEntry { ++ uint64_t id; ++ QTAILQ_ENTRY(PacketIdQueueEntry)next; ++}; ++ ++struct PacketIdQueue { ++ USBRedirDevice *dev; ++ const char *name; ++ QTAILQ_HEAD(, PacketIdQueueEntry) head; ++ int size; ++}; ++ + struct USBRedirDevice { + USBDevice dev; + /* Properties */ +@@ -86,7 +97,7 @@ struct USBRedirDevice { + int64_t next_attach_time; + struct usbredirparser *parser; + struct endp_data endpoint[MAX_ENDPOINTS]; +- QTAILQ_HEAD(, Cancelled) cancelled; ++ struct PacketIdQueue cancelled; + /* Data for device filtering */ + struct usb_redir_device_connect_header device_info; + struct usb_redir_interface_info_header interface_info; +@@ -94,11 +105,6 @@ struct USBRedirDevice { + int filter_rules_count; + }; + +-struct Cancelled { +- uint64_t id; +- QTAILQ_ENTRY(Cancelled)next; +-}; +- + static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); + static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect); +@@ -249,37 +255,75 @@ static int usbredir_write(void *priv, uint8_t *data, int count) + * Cancelled and buffered packets helpers + */ + +-static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) ++static void packet_id_queue_init(struct PacketIdQueue *q, ++ USBRedirDevice *dev, const char *name) + { +- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); +- Cancelled *c; ++ q->dev = dev; ++ q->name = name; ++ QTAILQ_INIT(&q->head); ++ q->size = 0; ++} ++ ++static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id) ++{ ++ USBRedirDevice *dev = q->dev; ++ struct PacketIdQueueEntry *e; ++ ++ DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name); ++ ++ e = g_malloc0(sizeof(struct PacketIdQueueEntry)); ++ e->id = id; ++ QTAILQ_INSERT_TAIL(&q->head, e, next); ++ q->size++; ++} ++ ++static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id) ++{ ++ USBRedirDevice *dev = q->dev; ++ struct PacketIdQueueEntry *e; ++ ++ QTAILQ_FOREACH(e, &q->head, next) { ++ if (e->id == id) { ++ DPRINTF("removing packet id %"PRIu64" from %s queue\n", ++ id, q->name); ++ QTAILQ_REMOVE(&q->head, e, next); ++ q->size--; ++ g_free(e); ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static void packet_id_queue_empty(struct PacketIdQueue *q) ++{ ++ USBRedirDevice *dev = q->dev; ++ struct PacketIdQueueEntry *e, *next_e; + +- DPRINTF("cancel packet id %"PRIu64"\n", p->id); ++ DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name); + +- c = g_malloc0(sizeof(Cancelled)); +- c->id = p->id; +- QTAILQ_INSERT_TAIL(&dev->cancelled, c, next); ++ QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) { ++ QTAILQ_REMOVE(&q->head, e, next); ++ g_free(e); ++ } ++ q->size = 0; ++} + ++static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) ++{ ++ USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); ++ ++ packet_id_queue_add(&dev->cancelled, p->id); + usbredirparser_send_cancel_data_packet(dev->parser, p->id); + usbredirparser_do_write(dev->parser); + } + + static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) + { +- Cancelled *c; +- + if (!dev->dev.attached) { + return 1; /* Treat everything as cancelled after a disconnect */ + } +- +- QTAILQ_FOREACH(c, &dev->cancelled, next) { +- if (c->id == id) { +- QTAILQ_REMOVE(&dev->cancelled, c, next); +- g_free(c); +- return 1; +- } +- } +- return 0; ++ return packet_id_queue_remove(&dev->cancelled, id); + } + + static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, +@@ -942,7 +986,7 @@ static int usbredir_initfn(USBDevice *udev) + dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + +- QTAILQ_INIT(&dev->cancelled); ++ packet_id_queue_init(&dev->cancelled, dev, "cancelled"); + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } +@@ -960,13 +1004,9 @@ static int usbredir_initfn(USBDevice *udev) + + static void usbredir_cleanup_device_queues(USBRedirDevice *dev) + { +- Cancelled *c, *next_c; + int i; + +- QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) { +- QTAILQ_REMOVE(&dev->cancelled, c, next); +- g_free(c); +- } ++ packet_id_queue_empty(&dev->cancelled); + for (i = 0; i < MAX_ENDPOINTS; i++) { + usbredir_free_bufpq(dev, I2EP(i)); + } +-- +1.7.12 + diff --git a/0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch b/0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch new file mode 100644 index 0000000..9fea679 --- /dev/null +++ b/0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch @@ -0,0 +1,119 @@ +From 4e0fcd7ef28b0ee06fd6f9d736dbdccefcf0b2bf Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 4 Sep 2012 17:03:54 +0200 +Subject: [PATCH 363/366] usb-redir: Add an already_in_flight packet-id queue + +After a live migration, the usb-hcd will re-queue all packets by +walking over the schedule in the guest memory again, but requests which +were encountered on the migration source before will already be in flight, +so these should *not* be re-send to the usbredir-host. + +This patch adds an already in flight packet ud queue, which will be filled by +the source before migration and then moved over to the migration dest, any +async handled packets are then checked against this queue to avoid sending +the same packet to the usbredir-host twice. + +Signed-off-by: Hans de Goede +--- + hw/usb/redirect.c | 43 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 08776d9..1c8edd3 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -98,6 +98,7 @@ struct USBRedirDevice { + struct usbredirparser *parser; + struct endp_data endpoint[MAX_ENDPOINTS]; + struct PacketIdQueue cancelled; ++ struct PacketIdQueue already_in_flight; + /* Data for device filtering */ + struct usb_redir_device_connect_header device_info; + struct usb_redir_interface_info_header interface_info; +@@ -326,6 +327,34 @@ static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) + return packet_id_queue_remove(&dev->cancelled, id); + } + ++static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, ++ struct USBEndpoint *ep) ++{ ++ static USBPacket *p; ++ ++ QTAILQ_FOREACH(p, &ep->queue, queue) { ++ packet_id_queue_add(&dev->already_in_flight, p->id); ++ } ++} ++ ++static void usbredir_fill_already_in_flight(USBRedirDevice *dev) ++{ ++ int ep; ++ struct USBDevice *udev = &dev->dev; ++ ++ usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl); ++ ++ for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { ++ usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]); ++ usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]); ++ } ++} ++ ++static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id) ++{ ++ return packet_id_queue_remove(&dev->already_in_flight, id); ++} ++ + static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, + uint8_t ep, uint64_t id) + { +@@ -541,6 +570,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, + + DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id); + ++ if (usbredir_already_in_flight(dev, p->id)) { ++ return USB_RET_ASYNC; ++ } ++ + bulk_packet.endpoint = ep; + bulk_packet.length = p->iov.size; + bulk_packet.stream_id = 0; +@@ -621,6 +654,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep, + p->iov.size, p->id); + ++ if (usbredir_already_in_flight(dev, p->id)) { ++ return USB_RET_ASYNC; ++ } ++ + interrupt_packet.endpoint = ep; + interrupt_packet.length = p->iov.size; + +@@ -763,6 +800,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + struct usb_redir_control_packet_header control_packet; + ++ if (usbredir_already_in_flight(dev, p->id)) { ++ return USB_RET_ASYNC; ++ } ++ + /* Special cases for certain standard device requests */ + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: +@@ -987,6 +1028,7 @@ static int usbredir_initfn(USBDevice *udev) + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + + packet_id_queue_init(&dev->cancelled, dev, "cancelled"); ++ packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight"); + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } +@@ -1007,6 +1049,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev) + int i; + + packet_id_queue_empty(&dev->cancelled); ++ packet_id_queue_empty(&dev->already_in_flight); + for (i = 0; i < MAX_ENDPOINTS; i++) { + usbredir_free_bufpq(dev, I2EP(i)); + } +-- +1.7.12 + diff --git a/0364-usb-redir-Store-max_packet_size-in-endp_data.patch b/0364-usb-redir-Store-max_packet_size-in-endp_data.patch new file mode 100644 index 0000000..6976b2c --- /dev/null +++ b/0364-usb-redir-Store-max_packet_size-in-endp_data.patch @@ -0,0 +1,38 @@ +From 372d6c55bbe5fb092ff9e96c72ff53bfd10fdede Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 6 Sep 2012 20:52:36 +0200 +Subject: [PATCH 364/366] usb-redir: Store max_packet_size in endp_data + +So that we've a place to migrate it to / from to allow restoring it after +migration. + +Signed-off-by: Hans de Goede +--- + hw/usb/redirect.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 1c8edd3..d8568ae 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -57,6 +57,7 @@ struct endp_data { + uint8_t type; + uint8_t interval; + uint8_t interface; /* bInterfaceNumber this ep belongs to */ ++ uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */ + uint8_t iso_started; + uint8_t iso_error; /* For reporting iso errors to the HC */ + uint8_t interrupt_started; +@@ -1305,7 +1306,8 @@ static void usbredir_ep_info(void *priv, + usb_ep->ifnum = dev->endpoint[i].interface; + if (usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size)) { +- usb_ep->max_packet_size = ep_info->max_packet_size[i]; ++ dev->endpoint[i].max_packet_size = ++ usb_ep->max_packet_size = ep_info->max_packet_size[i]; + } + if (ep_info->type[i] == usb_redir_type_bulk) { + usb_ep->pipeline = true; +-- +1.7.12 + diff --git a/0365-usb-redir-Add-support-for-migration.patch b/0365-usb-redir-Add-support-for-migration.patch new file mode 100644 index 0000000..97b8ab2 --- /dev/null +++ b/0365-usb-redir-Add-support-for-migration.patch @@ -0,0 +1,411 @@ +From e6a683c844845c53bcf14f9fb3ea175331eaca0c Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 5 Sep 2012 09:21:44 +0200 +Subject: [PATCH 365/366] usb-redir: Add support for migration + +Signed-off-by: Hans de Goede +--- + hw/usb/redirect.c | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 328 insertions(+), 3 deletions(-) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index d8568ae..8dbb722 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -65,8 +65,8 @@ struct endp_data { + uint8_t bufpq_prefilled; + uint8_t bufpq_dropping_packets; + QTAILQ_HEAD(, buf_packet) bufpq; +- int bufpq_size; +- int bufpq_target_size; ++ int32_t bufpq_size; ++ int32_t bufpq_target_size; + }; + + struct PacketIdQueueEntry { +@@ -241,6 +241,11 @@ static int usbredir_write(void *priv, uint8_t *data, int count) + return 0; + } + ++ /* Don't send new data to the chardev until our state is fully synced */ ++ if (!runstate_check(RUN_STATE_RUNNING)) { ++ return 0; ++ } ++ + r = qemu_chr_fe_write(dev->cs, data, count); + + if (r < 0) { +@@ -868,6 +873,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) + { + uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; + char version[32]; ++ int flags = 0; + + /* Make sure any pending closes are handled (no-op if none pending) */ + usbredir_chardev_close_bh(dev); +@@ -903,7 +909,12 @@ static void usbredir_chardev_open(USBRedirDevice *dev) + usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); + usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); + usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); +- usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); ++ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ flags |= usbredirparser_fl_no_hello; ++ } ++ usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, ++ flags); + usbredirparser_do_write(dev->parser); + } + +@@ -949,6 +960,11 @@ static int usbredir_chardev_can_read(void *opaque) + return 0; + } + ++ /* Don't read new data from the chardev until our state is fully synced */ ++ if (!runstate_check(RUN_STATE_RUNNING)) { ++ return 0; ++ } ++ + /* usbredir_parser_do_read will consume *all* data we give it */ + return 1024 * 1024; + } +@@ -1004,6 +1020,15 @@ static const QemuChrHandlers usbredir_chr_handlers = { + * init + destroy + */ + ++static void usbredir_vm_state_change(void *priv, int running, RunState state) ++{ ++ USBRedirDevice *dev = priv; ++ ++ if (state == RUN_STATE_RUNNING && dev->parser != NULL) { ++ usbredirparser_do_write(dev->parser); /* Flush any pending writes */ ++ } ++} ++ + static int usbredir_initfn(USBDevice *udev) + { + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); +@@ -1041,6 +1066,7 @@ static int usbredir_initfn(USBDevice *udev) + qemu_chr_fe_open(dev->cs); + qemu_chr_add_handlers(dev->cs, &usbredir_chr_handlers, dev); + ++ qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); + add_boot_device_path(dev->bootindex, &udev->qdev, NULL); + return 0; + } +@@ -1530,6 +1556,304 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id, + } + } + ++/* ++ * Migration code ++ */ ++ ++static void usbredir_pre_save(void *priv) ++{ ++ USBRedirDevice *dev = priv; ++ ++ usbredir_fill_already_in_flight(dev); ++} ++ ++static int usbredir_post_load(void *priv, int version_id) ++{ ++ USBRedirDevice *dev = priv; ++ struct USBEndpoint *usb_ep; ++ int i; ++ ++ for (i = 0; i < MAX_ENDPOINTS; i++) { ++ usb_ep = usb_ep_get(&dev->dev, ++ (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, ++ i & 0x0f); ++ usb_ep->type = dev->endpoint[i].type; ++ usb_ep->ifnum = dev->endpoint[i].interface; ++ usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; ++ if (dev->endpoint[i].type == usb_redir_type_bulk) { ++ usb_ep->pipeline = true; ++ } ++ } ++ return 0; ++} ++ ++/* For usbredirparser migration */ ++static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused) ++{ ++ USBRedirDevice *dev = priv; ++ uint8_t *data; ++ int len; ++ ++ if (dev->parser == NULL) { ++ qemu_put_be32(f, 0); ++ return; ++ } ++ ++ usbredirparser_serialize(dev->parser, &data, &len); ++ qemu_oom_check(data); ++ ++ qemu_put_be32(f, len); ++ qemu_put_buffer(f, data, len); ++ ++ free(data); ++} ++ ++static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused) ++{ ++ USBRedirDevice *dev = priv; ++ uint8_t *data; ++ int len, ret; ++ ++ len = qemu_get_be32(f); ++ if (len == 0) { ++ return 0; ++ } ++ ++ /* ++ * Our chardev should be open already at this point, otherwise ++ * the usbredir channel will be broken (ie spice without seamless) ++ */ ++ if (dev->parser == NULL) { ++ ERROR("get_parser called with closed chardev, failing migration\n"); ++ return -1; ++ } ++ ++ data = g_malloc(len); ++ qemu_get_buffer(f, data, len); ++ ++ ret = usbredirparser_unserialize(dev->parser, data, len); ++ ++ g_free(data); ++ ++ return ret; ++} ++ ++static const VMStateInfo usbredir_parser_vmstate_info = { ++ .name = "usb-redir-parser", ++ .put = usbredir_put_parser, ++ .get = usbredir_get_parser, ++}; ++ ++ ++/* For buffered packets (iso/irq) queue migration */ ++static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused) ++{ ++ struct endp_data *endp = priv; ++ struct buf_packet *bufp; ++ int remain = endp->bufpq_size; ++ ++ qemu_put_be32(f, endp->bufpq_size); ++ QTAILQ_FOREACH(bufp, &endp->bufpq, next) { ++ qemu_put_be32(f, bufp->len); ++ qemu_put_be32(f, bufp->status); ++ qemu_put_buffer(f, bufp->data, bufp->len); ++ remain--; ++ } ++ assert(remain == 0); ++} ++ ++static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) ++{ ++ struct endp_data *endp = priv; ++ struct buf_packet *bufp; ++ int i; ++ ++ endp->bufpq_size = qemu_get_be32(f); ++ for (i = 0; i < endp->bufpq_size; i++) { ++ bufp = g_malloc(sizeof(struct buf_packet)); ++ bufp->len = qemu_get_be32(f); ++ bufp->status = qemu_get_be32(f); ++ bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */ ++ qemu_get_buffer(f, bufp->data, bufp->len); ++ QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next); ++ } ++ return 0; ++} ++ ++static const VMStateInfo usbredir_ep_bufpq_vmstate_info = { ++ .name = "usb-redir-bufpq", ++ .put = usbredir_put_bufpq, ++ .get = usbredir_get_bufpq, ++}; ++ ++ ++/* For endp_data migration */ ++static const VMStateDescription usbredir_ep_vmstate = { ++ .name = "usb-redir-ep", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField []) { ++ VMSTATE_UINT8(type, struct endp_data), ++ VMSTATE_UINT8(interval, struct endp_data), ++ VMSTATE_UINT8(interface, struct endp_data), ++ VMSTATE_UINT16(max_packet_size, struct endp_data), ++ VMSTATE_UINT8(iso_started, struct endp_data), ++ VMSTATE_UINT8(iso_error, struct endp_data), ++ VMSTATE_UINT8(interrupt_started, struct endp_data), ++ VMSTATE_UINT8(interrupt_error, struct endp_data), ++ VMSTATE_UINT8(bufpq_prefilled, struct endp_data), ++ VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data), ++ { ++ .name = "bufpq", ++ .version_id = 0, ++ .field_exists = NULL, ++ .size = 0, ++ .info = &usbredir_ep_bufpq_vmstate_info, ++ .flags = VMS_SINGLE, ++ .offset = 0, ++ }, ++ VMSTATE_INT32(bufpq_target_size, struct endp_data), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++ ++/* For PacketIdQueue migration */ ++static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused) ++{ ++ struct PacketIdQueue *q = priv; ++ USBRedirDevice *dev = q->dev; ++ struct PacketIdQueueEntry *e; ++ int remain = q->size; ++ ++ DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size); ++ qemu_put_be32(f, q->size); ++ QTAILQ_FOREACH(e, &q->head, next) { ++ qemu_put_be64(f, e->id); ++ remain--; ++ } ++ assert(remain == 0); ++} ++ ++static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused) ++{ ++ struct PacketIdQueue *q = priv; ++ USBRedirDevice *dev = q->dev; ++ int i, size; ++ uint64_t id; ++ ++ size = qemu_get_be32(f); ++ DPRINTF("get_packet_id_q %s size %d\n", q->name, size); ++ for (i = 0; i < size; i++) { ++ id = qemu_get_be64(f); ++ packet_id_queue_add(q, id); ++ } ++ assert(q->size == size); ++ return 0; ++} ++ ++static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = { ++ .name = "usb-redir-packet-id-q", ++ .put = usbredir_put_packet_id_q, ++ .get = usbredir_get_packet_id_q, ++}; ++ ++static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = { ++ .name = "usb-redir-packet-id-queue", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField []) { ++ { ++ .name = "queue", ++ .version_id = 0, ++ .field_exists = NULL, ++ .size = 0, ++ .info = &usbredir_ep_packet_id_q_vmstate_info, ++ .flags = VMS_SINGLE, ++ .offset = 0, ++ }, ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++ ++/* For usb_redir_device_connect_header migration */ ++static const VMStateDescription usbredir_device_info_vmstate = { ++ .name = "usb-redir-device-info", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField []) { ++ VMSTATE_UINT8(speed, struct usb_redir_device_connect_header), ++ VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header), ++ VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header), ++ VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header), ++ VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header), ++ VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header), ++ VMSTATE_UINT16(device_version_bcd, ++ struct usb_redir_device_connect_header), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++ ++/* For usb_redir_interface_info_header migration */ ++static const VMStateDescription usbredir_interface_info_vmstate = { ++ .name = "usb-redir-interface-info", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField []) { ++ VMSTATE_UINT32(interface_count, ++ struct usb_redir_interface_info_header), ++ VMSTATE_UINT8_ARRAY(interface, ++ struct usb_redir_interface_info_header, 32), ++ VMSTATE_UINT8_ARRAY(interface_class, ++ struct usb_redir_interface_info_header, 32), ++ VMSTATE_UINT8_ARRAY(interface_subclass, ++ struct usb_redir_interface_info_header, 32), ++ VMSTATE_UINT8_ARRAY(interface_protocol, ++ struct usb_redir_interface_info_header, 32), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++ ++/* And finally the USBRedirDevice vmstate itself */ ++static const VMStateDescription usbredir_vmstate = { ++ .name = "usb-redir", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .pre_save = usbredir_pre_save, ++ .post_load = usbredir_post_load, ++ .fields = (VMStateField []) { ++ VMSTATE_USB_DEVICE(dev, USBRedirDevice), ++ VMSTATE_TIMER(attach_timer, USBRedirDevice), ++ { ++ .name = "parser", ++ .version_id = 0, ++ .field_exists = NULL, ++ .size = 0, ++ .info = &usbredir_parser_vmstate_info, ++ .flags = VMS_SINGLE, ++ .offset = 0, ++ }, ++ VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1, ++ usbredir_ep_vmstate, struct endp_data), ++ VMSTATE_STRUCT(cancelled, USBRedirDevice, 1, ++ usbredir_ep_packet_id_queue_vmstate, ++ struct PacketIdQueue), ++ VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1, ++ usbredir_ep_packet_id_queue_vmstate, ++ struct PacketIdQueue), ++ VMSTATE_STRUCT(device_info, USBRedirDevice, 1, ++ usbredir_device_info_vmstate, ++ struct usb_redir_device_connect_header), ++ VMSTATE_STRUCT(interface_info, USBRedirDevice, 1, ++ usbredir_interface_info_vmstate, ++ struct usb_redir_interface_info_header), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ + static Property usbredir_properties[] = { + DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), + DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), +@@ -1550,6 +1874,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) + uc->handle_reset = usbredir_handle_reset; + uc->handle_data = usbredir_handle_data; + uc->handle_control = usbredir_handle_control; ++ dc->vmsd = &usbredir_vmstate; + dc->props = usbredir_properties; + } + +-- +1.7.12 + diff --git a/0366-usb-redir-Add-chardev-open-close-debug-logging.patch b/0366-usb-redir-Add-chardev-open-close-debug-logging.patch new file mode 100644 index 0000000..42719c9 --- /dev/null +++ b/0366-usb-redir-Add-chardev-open-close-debug-logging.patch @@ -0,0 +1,54 @@ +From 93691f50a5200651f22e698cc29be0b1a020f5c5 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Wed, 5 Sep 2012 15:56:57 +0200 +Subject: [PATCH 366/366] usb-redir: Add chardev open / close debug logging + +Signed-off-by: Hans de Goede +--- + hw/usb/redirect.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c +index 8dbb722..95a2167 100644 +--- a/hw/usb/redirect.c ++++ b/hw/usb/redirect.c +@@ -864,6 +864,7 @@ static void usbredir_chardev_close_bh(void *opaque) + usbredir_device_disconnect(dev); + + if (dev->parser) { ++ DPRINTF("destroying usbredirparser\n"); + usbredirparser_destroy(dev->parser); + dev->parser = NULL; + } +@@ -879,6 +880,8 @@ static void usbredir_chardev_open(USBRedirDevice *dev) + usbredir_chardev_close_bh(dev); + qemu_bh_cancel(dev->chardev_close_bh); + ++ DPRINTF("creating usbredirparser\n"); ++ + strcpy(version, "qemu usb-redir guest "); + pstrcat(version, sizeof(version), qemu_get_version()); + +@@ -990,9 +993,11 @@ static void usbredir_chardev_event(void *opaque, int event) + + switch (event) { + case CHR_EVENT_OPENED: ++ DPRINTF("chardev open\n"); + usbredir_chardev_open(dev); + break; + case CHR_EVENT_CLOSED: ++ DPRINTF("chardev close\n"); + qemu_bh_schedule(dev->chardev_close_bh); + break; + } +@@ -1255,6 +1260,7 @@ static void usbredir_device_disconnect(void *priv) + qemu_del_timer(dev->attach_timer); + + if (dev->dev.attached) { ++ DPRINTF("detaching device\n"); + usb_device_detach(&dev->dev); + /* + * Delay next usb device attach to give the guest a chance to see +-- +1.7.12 + diff --git a/qemu.spec b/qemu.spec index de64f2a..452f280 100644 --- a/qemu.spec +++ b/qemu.spec @@ -34,12 +34,10 @@ %bcond_without fdt # enabled %endif -%global rcversion rc1 - Summary: QEMU is a FAST! processor emulator Name: qemu Version: 1.2.0 -Release: 0.5.%{rcversion}%{?dist} +Release: 1%{?dist} # Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD @@ -55,7 +53,10 @@ ExclusiveArch: x86_64 %define _smp_mflags %{nil} %endif -Source0: http://downloads.sourceforge.net/sourceforge/kvm/qemu-kvm-%{version}-%{rcversion}.tar.gz +# This is generated from the git qemu-kvm-1.2.0 tag, replace with proper +# upstream tarbal once available +Source0: qemu-kvm-%{version}.tar.gz +#Source0: http://downloads.sourceforge.net/sourceforge/kvm/qemu-kvm-%{version}.tar.gz Source1: qemu.binfmt @@ -98,6 +99,96 @@ Patch111: 0111-usb-redir-Add-flow-control-support.patch Patch112: 0112-virtio-serial-bus-replay-guest_open-on-migration.patch Patch113: 0113-char-Disable-write-callback-if-throttled-chardev-is-.patch +# Spice features from upstream master: seamless migration & dynamic monitors +Patch201: 0201-spice-abort-on-invalid-streaming-cmdline-params.patch +Patch202: 0202-spice-notify-spice-server-on-vm-start-stop.patch +Patch203: 0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch +Patch204: 0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch +Patch205: 0205-spice-add-migrated-flag-to-spice-info.patch +Patch206: 0206-spice-adding-seamless-migration-option-to-the-comman.patch +Patch207: 0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch +Patch208: 0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch +Patch209: 0209-qxl-disallow-unknown-revisions.patch +Patch210: 0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch +Patch211: 0211-configure-print-spice-protocol-and-spice-server-vers.patch +Patch212: 0212-spice-make-number-of-surfaces-runtime-configurable.patch +Patch213: 0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch +Patch214: 0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch +Patch215: 0215-qxl-dont-update-invalid-area.patch + +# Ugh, ton of USB bugfixes / preparation patches for usb-redir +# live-migration which did not make 1.2.0 :| +# All are in upstream master so can be dropped next qemu release +Patch0301: 0301-usb-controllers-do-not-need-to-check-for-babble-them.patch +Patch0302: 0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch +Patch0303: 0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch +Patch0304: 0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch +Patch0305: 0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch +Patch0306: 0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch +Patch0307: 0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch +Patch0308: 0308-ehci-Properly-cleanup-packets-on-cancel.patch +Patch0309: 0309-ehci-Properly-report-completed-but-not-yet-processed.patch +Patch0310: 0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch +Patch0311: 0311-ehci-trace-guest-bugs.patch +Patch0312: 0312-ehci-add-doorbell-trace-events.patch +Patch0313: 0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch +Patch0314: 0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch +Patch0315: 0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch +Patch0316: 0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch +Patch0317: 0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch +Patch0318: 0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch +Patch0319: 0319-usb-redir-Get-rid-of-async-struct-get-member.patch +Patch0320: 0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch +Patch0321: 0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch +Patch0322: 0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch +Patch0323: 0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch +Patch0324: 0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch +Patch0325: 0325-usb-redir-Set-ep-max_packet_size-if-available.patch +Patch0326: 0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch +Patch0327: 0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch +Patch0328: 0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch +Patch0329: 0329-Better-name-usb-braille-device.patch +Patch0330: 0330-usb-audio-fix-usb-version.patch +Patch0331: 0331-xhci-rip-out-background-transfer-code.patch +Patch0332: 0332-xhci-drop-buffering.patch +Patch0333: 0333-xhci-move-device-lookup-into-xhci_setup_packet.patch +Patch0334: 0334-xhci-implement-mfindex.patch +Patch0335: 0335-xhci-iso-xfer-support.patch +Patch0336: 0336-xhci-trace-cc-codes-in-cleartext.patch +Patch0337: 0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch +Patch0338: 0338-xhci-fix-runtime-write-tracepoint.patch +Patch0339: 0339-xhci-update-register-layout.patch +Patch0340: 0340-xhci-update-port-handling.patch +Patch0341: 0341-usb3-superspeed-descriptors.patch +Patch0342: 0342-usb3-superspeed-endpoint-companion.patch +Patch0343: 0343-usb3-bos-decriptor.patch +Patch0344: 0344-usb-storage-usb3-support.patch +Patch0345: 0345-xhci-fix-cleanup-msi.patch +Patch0346: 0346-xhci-rework-interrupt-handling.patch +Patch0347: 0347-xhci-add-msix-support.patch +Patch0348: 0348-xhci-move-register-update-into-xhci_intr_raise.patch +Patch0349: 0349-xhci-add-XHCIInterrupter.patch +Patch0350: 0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch +Patch0351: 0351-xhci-pick-target-interrupter.patch +Patch0352: 0352-xhci-support-multiple-interrupters.patch +Patch0353: 0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch +Patch0354: 0354-xhci-allow-bytewise-capability-register-reads.patch +Patch0355: 0355-ehci-switch-to-new-style-memory-ops.patch +Patch0356: 0356-xhci-drop-unused-wlength.patch +Patch0357: 0357-usb-host-allow-emulated-non-async-control-requests-w.patch + +# And the last few ehci fixes + the actual usb-redir live migration code +# Not yet upstream but should get there real soon +Patch0358: 0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch +Patch0359: 0359-ehci-Walk-async-schedule-before-and-after-migration.patch +Patch0360: 0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch +Patch0361: 0361-usb-Migrate-over-device-speed-and-speedmask.patch +Patch0362: 0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch +Patch0363: 0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch +Patch0364: 0364-usb-redir-Store-max_packet_size-in-endp_data.patch +Patch0365: 0365-usb-redir-Add-support-for-migration.patch +Patch0366: 0366-usb-redir-Add-chardev-open-close-debug-logging.patch + BuildRequires: SDL-devel BuildRequires: zlib-devel BuildRequires: which @@ -111,11 +202,11 @@ BuildRequires: pciutils-devel BuildRequires: pulseaudio-libs-devel BuildRequires: ncurses-devel BuildRequires: libattr-devel -BuildRequires: usbredir-devel >= 0.4.1 +BuildRequires: usbredir-devel >= 0.5 BuildRequires: texinfo %ifarch %{ix86} x86_64 -BuildRequires: spice-protocol >= 0.8.1 -BuildRequires: spice-server-devel >= 0.9.0 +BuildRequires: spice-protocol >= 0.12.1 +BuildRequires: spice-server-devel >= 0.11.3 BuildRequires: libseccomp-devel >= 1.0.0 %endif # For network block driver @@ -375,7 +466,7 @@ such as kvm_stat. %endif %prep -%setup -q -n qemu-kvm-%{version}-%{rcversion} +%setup -q -n qemu-kvm-%{version} %patch1 -p1 %patch2 -p1 @@ -394,6 +485,90 @@ such as kvm_stat. %patch112 -p1 %patch113 -p1 +%patch201 -p1 +%patch202 -p1 +%patch203 -p1 +%patch204 -p1 +%patch205 -p1 +%patch206 -p1 +%patch207 -p1 +%patch208 -p1 +%patch209 -p1 +%patch210 -p1 +%patch211 -p1 +%patch212 -p1 +%patch213 -p1 +%patch214 -p1 +%patch215 -p1 + +%patch301 -p1 +%patch302 -p1 +%patch303 -p1 +%patch304 -p1 +%patch305 -p1 +%patch306 -p1 +%patch307 -p1 +%patch308 -p1 +%patch309 -p1 +%patch310 -p1 +%patch311 -p1 +%patch312 -p1 +%patch313 -p1 +%patch314 -p1 +%patch315 -p1 +%patch316 -p1 +%patch317 -p1 +%patch318 -p1 +%patch319 -p1 +%patch320 -p1 +%patch321 -p1 +%patch322 -p1 +%patch323 -p1 +%patch324 -p1 +%patch325 -p1 +%patch326 -p1 +%patch327 -p1 +%patch328 -p1 +%patch329 -p1 +%patch330 -p1 +%patch331 -p1 +%patch332 -p1 +%patch333 -p1 +%patch334 -p1 +%patch335 -p1 +%patch336 -p1 +%patch337 -p1 +%patch338 -p1 +%patch339 -p1 +%patch340 -p1 +%patch341 -p1 +%patch342 -p1 +%patch343 -p1 +%patch344 -p1 +%patch345 -p1 +%patch346 -p1 +%patch347 -p1 +%patch348 -p1 +%patch349 -p1 +%patch350 -p1 +%patch351 -p1 +%patch352 -p1 +%patch353 -p1 +%patch354 -p1 +%patch355 -p1 +%patch356 -p1 +%patch357 -p1 +%patch358 -p1 +%patch359 -p1 +%patch360 -p1 +%patch361 -p1 +%patch362 -p1 +%patch363 -p1 +%patch364 -p1 +%patch365 -p1 +%patch366 -p1 + + %build buildarch="i386-softmmu x86_64-softmmu arm-softmmu cris-softmmu \ m68k-softmmu mips-softmmu mipsel-softmmu mips64-softmmu \ @@ -871,6 +1046,12 @@ fi %{_mandir}/man1/qemu-img.1* %changelog +* Fri Sep 9 2012 Hans de Goede - 2:1.2.0-1 +- New upstream release 1.2.0 final +- Add support for Spice seamless migration +- Add support for Spice dynamic monitors +- Add support for usb-redir live migration + * Tue Sep 04 2012 Adam Jackson 1.2.0-0.5.rc1 - Flip Requires: ceph >= foo to Conflicts: ceph < foo, so we pull in only the libraries which we need and not the rest of ceph which we don't. diff --git a/sources b/sources index 00beae2..a841146 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -1ec2342c322102b4028b4ca3fbdde737 qemu-kvm-1.2.0-rc1.tar.gz +2d8f71a31d73d669e9744b65b248a497 qemu-kvm-1.2.0.tar.gz