From df6322a8973b5e69bdc8931ff79d3bfe3901cab5 Mon Sep 17 00:00:00 2001 From: Cal Peake Date: Thu, 11 Aug 2022 18:01:38 -0400 Subject: ui/console: Get tab completion working again in the SDL monitor vc Define a QEMU special key constant for the tab key and add an entry for it in the qcode_to_keysym table. This allows tab completion to work again in the SDL monitor virtual console, which has been broken ever since the migration from SDL1 to SDL2. Signed-off-by: Cal Peake Message-Id: <7054816e-99c-7e2-6737-7cf98cc56e2@absolutedigital.net> Signed-off-by: Gerd Hoffmann --- include/ui/console.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/ui/console.h b/include/ui/console.h index c0520c694c..e400ee9fa7 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -70,6 +70,7 @@ void hmp_mouse_set(Monitor *mon, const QDict *qdict); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx constants) */ #define QEMU_KEY_ESC1(c) ((c) | 0xe100) +#define QEMU_KEY_TAB 0x0009 #define QEMU_KEY_BACKSPACE 0x007f #define QEMU_KEY_UP QEMU_KEY_ESC1('A') #define QEMU_KEY_DOWN QEMU_KEY_ESC1('B') -- cgit v1.2.3-55-g7522 From bab6a301c58286229ca8fbc36728d1469f243260 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 19 Aug 2022 22:27:54 +0900 Subject: ui/cocoa: Run qemu_init in the main thread This work is based on: https://patchew.org/QEMU/20220317125534.38706-1-philippe.mathieu.daude@gmail.com/ Simplify the initialization dance by running qemu_init() in the main thread before the Cocoa event loop starts. The secondary thread only runs only qemu_main_loop() and qemu_cleanup(). This fixes a case where addRemovableDevicesMenuItems() calls qmp_query_block() while expecting the main thread to still hold the BQL. Overriding the code after calling qemu_init() is done by dynamically replacing a function pointer variable, qemu_main when initializing ui/cocoa, which unifies the static implementation of main() for builds with ui/cocoa and ones without ui/cocoa. Signed-off-by: Akihiko Odaki Message-Id: <20220819132756.74641-2-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- docs/devel/fuzzing.rst | 4 +- include/qemu-main.h | 3 +- include/sysemu/sysemu.h | 2 +- softmmu/main.c | 10 ++-- softmmu/vl.c | 2 +- tests/qtest/fuzz/fuzz.c | 2 +- ui/cocoa.m | 144 +++++++++++++++++------------------------------- 7 files changed, 62 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/docs/devel/fuzzing.rst b/docs/devel/fuzzing.rst index 784ecb99e6..715330c856 100644 --- a/docs/devel/fuzzing.rst +++ b/docs/devel/fuzzing.rst @@ -287,8 +287,8 @@ select the fuzz target. Then, the qtest client is initialized. If the target requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized. Then the QGraph is walked and the QEMU cmd_line is determined and saved. -After this, the ``vl.c:qemu_main`` is called to set up the guest. There are -target-specific hooks that can be called before and after qemu_main, for +After this, the ``vl.c:main`` is called to set up the guest. There are +target-specific hooks that can be called before and after main, for additional setup(e.g. PCI setup, or VM snapshotting). ``LLVMFuzzerTestOneInput``: Uses qtest/qos functions to act based on the fuzz diff --git a/include/qemu-main.h b/include/qemu-main.h index 6a3e90d0ad..940960a7db 100644 --- a/include/qemu-main.h +++ b/include/qemu-main.h @@ -5,6 +5,7 @@ #ifndef QEMU_MAIN_H #define QEMU_MAIN_H -int qemu_main(int argc, char **argv, char **envp); +int qemu_default_main(void); +extern int (*qemu_main)(void); #endif /* QEMU_MAIN_H */ diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 31aa45160b..6a7a31e64d 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -102,7 +102,7 @@ void qemu_boot_set(const char *boot_order, Error **errp); bool defaults_enabled(void); -void qemu_init(int argc, char **argv, char **envp); +void qemu_init(int argc, char **argv); int qemu_main_loop(void); void qemu_cleanup(void); diff --git a/softmmu/main.c b/softmmu/main.c index 1b675a8c03..694388bd7f 100644 --- a/softmmu/main.c +++ b/softmmu/main.c @@ -30,20 +30,20 @@ #include #endif -int qemu_main(int argc, char **argv, char **envp) +int qemu_default_main(void) { int status; - qemu_init(argc, argv, envp); status = qemu_main_loop(); qemu_cleanup(); return status; } -#ifndef CONFIG_COCOA +int (*qemu_main)(void) = qemu_default_main; + int main(int argc, char **argv) { - return qemu_main(argc, argv, NULL); + qemu_init(argc, argv); + return qemu_main(); } -#endif diff --git a/softmmu/vl.c b/softmmu/vl.c index e62b9cc35d..9abadcc150 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2605,7 +2605,7 @@ void qmp_x_exit_preconfig(Error **errp) } } -void qemu_init(int argc, char **argv, char **envp) +void qemu_init(int argc, char **argv) { QemuOpts *opts; QemuOpts *icount_opts = NULL, *accel_opts = NULL; diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index 2b3bc1fb9d..eb7520544b 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -218,7 +218,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) g_free(pretty_cmd_line); } - qemu_init(result.we_wordc, result.we_wordv, NULL); + qemu_init(result.we_wordc, result.we_wordv); /* re-enable the rcu atfork, which was previously disabled in qemu_init */ rcu_enable_atfork(); diff --git a/ui/cocoa.m b/ui/cocoa.m index 5a8bd5dd84..660d3e0935 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -100,13 +100,9 @@ static int cursor_hide = 1; static int left_command_key_enabled = 1; static bool swap_opt_cmd; -static int gArgc; -static char **gArgv; static bool stretch_video; static NSTextField *pauseLabel; -static QemuSemaphore display_init_sem; -static QemuSemaphore app_started_sem; static bool allow_events; static NSInteger cbchangecount = -1; @@ -597,7 +593,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* * Don't try to tell QEMU about UI information in the application * startup phase -- we haven't yet registered dcl with the QEMU UI - * layer, and also trying to take the iothread lock would deadlock. + * layer. * When cocoa_display_init() does register the dcl, the UI layer * will call cocoa_switch(), which will call updateUIInfo, so * we don't lose any information here. @@ -790,16 +786,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven - (bool) handleEvent:(NSEvent *)event { - if(!allow_events) { - /* - * Just let OSX have all events that arrive before - * applicationDidFinishLaunching. - * This avoids a deadlock on the iothread lock, which cocoa_display_init() - * will not drop until after the app_started_sem is posted. (In theory - * there should not be any such events, but OSX Catalina now emits some.) - */ - return false; - } return bool_with_iothread_lock(^{ return [self handleEventLocked:event]; }); @@ -1287,8 +1273,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); allow_events = true; - /* Tell cocoa_display_init to proceed */ - qemu_sem_post(&app_started_sem); } - (void)applicationWillTerminate:(NSNotification *)aNotification @@ -1919,92 +1903,45 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info, /* * The startup process for the OSX/Cocoa UI is complicated, because * OSX insists that the UI runs on the initial main thread, and so we - * need to start a second thread which runs the vl.c qemu_main(): - * - * Initial thread: 2nd thread: + * need to start a second thread which runs the qemu_default_main(): * in main(): - * create qemu-main thread - * wait on display_init semaphore - * call qemu_main() - * ... - * in cocoa_display_init(): - * post the display_init semaphore - * wait on app_started semaphore - * create application, menus, etc - * enter OSX run loop - * in applicationDidFinishLaunching: - * post app_started semaphore - * tell main thread to fullscreen if needed - * [...] - * run qemu main-loop - * - * We do this in two stages so that we don't do the creation of the - * GUI application menus and so on for command line options like --help - * where we want to just print text to stdout and exit immediately. + * in cocoa_display_init(): + * assign cocoa_main to qemu_main + * create application, menus, etc + * in cocoa_main(): + * create qemu-main thread + * enter OSX run loop */ static void *call_qemu_main(void *opaque) { int status; - COCOA_DEBUG("Second thread: calling qemu_main()\n"); - status = qemu_main(gArgc, gArgv, *_NSGetEnviron()); - COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n"); + COCOA_DEBUG("Second thread: calling qemu_default_main()\n"); + qemu_mutex_lock_iothread(); + status = qemu_default_main(); + qemu_mutex_unlock_iothread(); + COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n"); [cbowner release]; exit(status); } -int main (int argc, char **argv) { +static int cocoa_main() +{ QemuThread thread; - COCOA_DEBUG("Entered main()\n"); - gArgc = argc; - gArgv = argv; - - qemu_sem_init(&display_init_sem, 0); - qemu_sem_init(&app_started_sem, 0); + COCOA_DEBUG("Entered %s()\n", __func__); + qemu_mutex_unlock_iothread(); qemu_thread_create(&thread, "qemu_main", call_qemu_main, NULL, QEMU_THREAD_DETACHED); - COCOA_DEBUG("Main thread: waiting for display_init_sem\n"); - qemu_sem_wait(&display_init_sem); - COCOA_DEBUG("Main thread: initializing app\n"); - - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - // Pull this console process up to being a fully-fledged graphical - // app with a menubar and Dock icon - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - - [QemuApplication sharedApplication]; - - create_initial_menus(); - - /* - * Create the menu entries which depend on QEMU state (for consoles - * and removeable devices). These make calls back into QEMU functions, - * which is OK because at this point we know that the second thread - * holds the iothread lock and is synchronously waiting for us to - * finish. - */ - add_console_menu_entries(); - addRemovableDevicesMenuItems(); - - // Create an Application controller - QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; - [NSApp setDelegate:appController]; - // Start the main event loop COCOA_DEBUG("Main thread: entering OSX run loop\n"); [NSApp run]; - COCOA_DEBUG("Main thread: left OSX run loop, exiting\n"); + COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n"); - [appController release]; - [pool release]; - - return 0; + abort(); } @@ -2083,25 +2020,42 @@ static void cocoa_refresh(DisplayChangeListener *dcl) static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); - /* Tell main thread to go ahead and create the app and enter the run loop */ - qemu_sem_post(&display_init_sem); - qemu_sem_wait(&app_started_sem); - COCOA_DEBUG("cocoa_display_init: app start completed\n"); + qemu_main = cocoa_main; + + // Pull this console process up to being a fully-fledged graphical + // app with a menubar and Dock icon + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + [QemuApplication sharedApplication]; + + create_initial_menus(); + + /* + * Create the menu entries which depend on QEMU state (for consoles + * and removeable devices). These make calls back into QEMU functions, + * which is OK because at this point we know that the second thread + * holds the iothread lock and is synchronously waiting for us to + * finish. + */ + add_console_menu_entries(); + addRemovableDevicesMenuItems(); + + // Create an Application controller + QemuCocoaAppController *controller = [[QemuCocoaAppController alloc] init]; + [NSApp setDelegate:controller]; - QemuCocoaAppController *controller = (QemuCocoaAppController *)[[NSApplication sharedApplication] delegate]; /* if fullscreen mode is to be used */ if (opts->has_full_screen && opts->full_screen) { - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp activateIgnoringOtherApps: YES]; - [controller toggleFullScreen: nil]; - }); + [NSApp activateIgnoringOtherApps: YES]; + [controller toggleFullScreen: nil]; } if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) { - dispatch_async(dispatch_get_main_queue(), ^{ - [controller setFullGrab: nil]; - }); + [controller setFullGrab: nil]; } if (opts->has_show_cursor && opts->show_cursor) { @@ -2121,6 +2075,8 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) qemu_event_init(&cbevent, false); cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init]; qemu_clipboard_peer_register(&cbpeer); + + [pool release]; } static QemuDisplay qemu_display_cocoa = { -- cgit v1.2.3-55-g7522 From 2dc7f90a835a4cd2362ace5e2388687afd296b85 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 19 Aug 2022 22:27:55 +0900 Subject: Revert "main-loop: Disable block backend global state assertion on Cocoa" This reverts commit 47281859f66bdab1974fb122cab2cbb4a1c9af7f. Signed-off-by: Akihiko Odaki Reviewed-by: Emanuele Giuseppe Esposito Reviewed-by: Peter Maydell Reviewed-by: Paolo Bonzini Message-Id: <20220819132756.74641-3-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann --- include/qemu/main-loop.h | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'include') diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index c50d1b7e3a..aac707d073 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -284,23 +284,10 @@ bool qemu_in_main_thread(void); * Please refer to include/block/block-global-state.h for more * information about GS API. */ -#ifdef CONFIG_COCOA -/* - * When using the Cocoa UI, addRemovableDevicesMenuItems() is called from - * a thread different from the QEMU main thread and can not take the BQL, - * triggering this assertions in the block layer (commit 0439c5a462). - * As the Cocoa fix is not trivial, disable this assertion for the v7.0.0 - * release (when using Cocoa); we will restore it immediately after the - * release. - * This issue is tracked as https://gitlab.com/qemu-project/qemu/-/issues/926 - */ -#define GLOBAL_STATE_CODE() -#else #define GLOBAL_STATE_CODE() \ do { \ assert(qemu_in_main_thread()); \ } while (0) -#endif /* CONFIG_COCOA */ /* * Mark and check that the function is part of the I/O API. -- cgit v1.2.3-55-g7522 From 12b69878fc7b4b92b1bbd3959f2c3d4c717881fb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Aug 2022 08:38:27 +0200 Subject: usb/msd: add usb_msd_fatal_error() and fix guest-triggerable assert Add handler for fatal errors. Moves device into error state where it stops responding until the guest resets it. Guest can send illegal requests where scsi command and usb packet transfer directions are inconsistent. Use the new usb_msd_fatal_error() function instead of assert() in that case. Reported-by: Qiang Liu Signed-off-by: Gerd Hoffmann Tested-by: Qiang Liu Message-Id: <20220830063827.813053-3-kraxel@redhat.com> --- hw/usb/dev-storage.c | 30 +++++++++++++++++++++++++++++- hw/usb/trace-events | 1 + include/hw/usb/msd.h | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 140ef2aeaa..e3bcffb3e0 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -191,6 +191,23 @@ static void usb_msd_packet_complete(MSDState *s) usb_packet_complete(&s->dev, p); } +static void usb_msd_fatal_error(MSDState *s) +{ + trace_usb_msd_fatal_error(); + + if (s->packet) { + s->packet->status = USB_RET_STALL; + usb_msd_packet_complete(s); + } + + /* + * Guest messed up up device state with illegal requests. Go + * ignore any requests until the guests resets the device (and + * brings it into a known state that way). + */ + s->needs_reset = true; +} + static void usb_msd_copy_data(MSDState *s, USBPacket *p) { uint32_t len; @@ -227,7 +244,11 @@ void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); + if ((s->mode == USB_MSDM_DATAOUT) != (req->cmd.mode == SCSI_XFER_TO_DEV)) { + usb_msd_fatal_error(s); + return; + } + s->scsi_len = len; s->scsi_off = 0; if (p) { @@ -317,6 +338,8 @@ void usb_msd_handle_reset(USBDevice *dev) memset(&s->csw, 0, sizeof(s->csw)); s->mode = USB_MSDM_CBW; + + s->needs_reset = false; } static void usb_msd_handle_control(USBDevice *dev, USBPacket *p, @@ -382,6 +405,11 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) SCSIDevice *scsi_dev; uint32_t len; + if (s->needs_reset) { + p->status = USB_RET_STALL; + return; + } + switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 914ca71668..b65269892c 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -263,6 +263,7 @@ usb_msd_packet_complete(void) "" usb_msd_cmd_submit(unsigned lun, unsigned tag, unsigned flags, unsigned len, unsigned data_len) "lun %u, tag 0x%x, flags 0x%08x, len %d, data-len %d" usb_msd_cmd_complete(unsigned status, unsigned tag) "status %d, tag 0x%x" usb_msd_cmd_cancel(unsigned tag) "tag 0x%x" +usb_msd_fatal_error(void) "" # dev-uas.c usb_uas_reset(int addr) "dev %d" diff --git a/include/hw/usb/msd.h b/include/hw/usb/msd.h index 54e9f38bda..f9fd862b52 100644 --- a/include/hw/usb/msd.h +++ b/include/hw/usb/msd.h @@ -40,6 +40,7 @@ struct MSDState { bool removable; bool commandlog; SCSIDevice *scsi_dev; + bool needs_reset; }; typedef struct MSDState MSDState; -- cgit v1.2.3-55-g7522