diff options
95 files changed, 3059 insertions, 832 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index b6ab3d25a7..25fc49d1dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1130,6 +1130,19 @@ F: include/hw/nubus/* F: include/hw/display/macfb.h F: include/hw/block/swim.h +virt +M: Laurent Vivier <laurent@vivier.eu> +S: Maintained +F: hw/m68k/virt.c +F: hw/char/goldfish_tty.c +F: hw/intc/goldfish_pic.c +F: hw/intc/m68k_irqc.c +F: hw/misc/virt_ctrl.c +F: include/hw/char/goldfish_tty.h +F: include/hw/intc/goldfish_pic.h +F: include/hw/intc/m68k_irqc.h +F: include/hw/misc/virt_ctrl.h + MicroBlaze Machines ------------------- petalogix_s3adsp1800 @@ -1760,6 +1773,7 @@ S: Supported F: include/hw/scsi/* F: hw/scsi/* F: tests/qtest/virtio-scsi-test.c +F: tests/qtest/fuzz-virtio-scsi-test.c T: git https://github.com/bonzini/qemu.git scsi-next SSI @@ -1809,6 +1823,7 @@ M: Alex Williamson <alex.williamson@redhat.com> S: Supported F: hw/vfio/* F: include/hw/vfio/ +F: docs/igd-assign.txt vfio-ccw M: Cornelia Huck <cohuck@redhat.com> @@ -1970,6 +1985,7 @@ S: Supported F: hw/scsi/megasas.c F: hw/scsi/mfi.h F: tests/qtest/megasas-test.c +F: tests/qtest/fuzz-megasas-test.c Network packet abstractions M: Dmitry Fleytman <dmitry.fleytman@gmail.com> @@ -2633,7 +2649,9 @@ R: Stefan Hajnoczi <stefanha@redhat.com> R: Thomas Huth <thuth@redhat.com> S: Maintained F: tests/qtest/fuzz/ +F: tests/qtest/fuzz-*test.c F: scripts/oss-fuzz/ +F: hw/mem/sparse-mem.c F: docs/devel/fuzzing.rst Register API diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 37b0a1861e..b6d9f92f15 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -673,10 +673,6 @@ out: #define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size << KVM_CLEAR_LOG_SHIFT) #define KVM_CLEAR_LOG_MASK (-KVM_CLEAR_LOG_ALIGN) -/* - * As the granule of kvm dirty log is qemu_real_host_page_size, - * @start and @size are expected and restricted to align to it. - */ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start, uint64_t size) { @@ -686,9 +682,6 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start, unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size; int ret; - /* Make sure start and size are qemu_real_host_page_size aligned */ - assert(QEMU_IS_ALIGNED(start | size, psize)); - /* * We need to extend either the start or the size or both to * satisfy the KVM interface requirement. Firstly, do the start diff --git a/audio/coreaudio.c b/audio/coreaudio.c index b7c02e0e51..578ec9b8b2 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -32,33 +32,30 @@ #define AUDIO_CAP "coreaudio" #include "audio_int.h" -#ifndef MAC_OS_X_VERSION_10_6 -#define MAC_OS_X_VERSION_10_6 1060 -#endif - typedef struct coreaudioVoiceOut { HWVoiceOut hw; pthread_mutex_t mutex; AudioDeviceID outputDeviceID; + int frameSizeSetting; + uint32_t bufferCount; UInt32 audioDevicePropertyBufferFrameSize; AudioStreamBasicDescription outputStreamBasicDescription; AudioDeviceIOProcID ioprocid; + bool enabled; } coreaudioVoiceOut; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -/* The APIs used here only become available from 10.6 */ +static const AudioObjectPropertyAddress voice_addr = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster +}; static OSStatus coreaudio_get_voice(AudioDeviceID *id) { UInt32 size = sizeof(*id); - AudioObjectPropertyAddress addr = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; return AudioObjectGetPropertyData(kAudioObjectSystemObject, - &addr, + &voice_addr, 0, NULL, &size, @@ -169,102 +166,6 @@ static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) &size, result); } -#else -/* Legacy versions of functions using deprecated APIs */ - -static OSStatus coreaudio_get_voice(AudioDeviceID *id) -{ - UInt32 size = sizeof(*id); - - return AudioHardwareGetProperty( - kAudioHardwarePropertyDefaultOutputDevice, - &size, - id); -} - -static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, - AudioValueRange *framerange) -{ - UInt32 size = sizeof(*framerange); - - return AudioDeviceGetProperty( - id, - 0, - 0, - kAudioDevicePropertyBufferFrameSizeRange, - &size, - framerange); -} - -static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) -{ - UInt32 size = sizeof(*framesize); - - return AudioDeviceGetProperty( - id, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - &size, - framesize); -} - -static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) -{ - UInt32 size = sizeof(*framesize); - - return AudioDeviceSetProperty( - id, - NULL, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - size, - framesize); -} - -static OSStatus coreaudio_get_streamformat(AudioDeviceID id, - AudioStreamBasicDescription *d) -{ - UInt32 size = sizeof(*d); - - return AudioDeviceGetProperty( - id, - 0, - false, - kAudioDevicePropertyStreamFormat, - &size, - d); -} - -static OSStatus coreaudio_set_streamformat(AudioDeviceID id, - AudioStreamBasicDescription *d) -{ - UInt32 size = sizeof(*d); - - return AudioDeviceSetProperty( - id, - 0, - 0, - 0, - kAudioDevicePropertyStreamFormat, - size, - d); -} - -static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) -{ - UInt32 size = sizeof(*result); - - return AudioDeviceGetProperty( - id, - 0, - 0, - kAudioDevicePropertyDeviceIsRunning, - &size, - result); -} -#endif static void coreaudio_logstatus (OSStatus status) { @@ -356,17 +257,8 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( coreaudio_logstatus (status); } -static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) -{ - OSStatus status; - UInt32 result = 0; - status = coreaudio_get_isrunning(outputDeviceID, &result); - if (status != kAudioHardwareNoError) { - coreaudio_logerr(status, - "Could not determine whether Device is playing\n"); - } - return result; -} +#define coreaudio_playback_logerr(status, ...) \ + coreaudio_logerr2(status, "playback", __VA_ARGS__) static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) { @@ -439,6 +331,11 @@ static OSStatus audioDeviceIOProc( return 0; } + if (inDevice != core->outputDeviceID) { + coreaudio_unlock (core, "audioDeviceIOProc(old device)"); + return 0; + } + frameCount = core->audioDevicePropertyBufferFrameSize; pending_frames = hw->pending_emul / hw->info.bytes_per_frame; @@ -471,175 +368,320 @@ static OSStatus audioDeviceIOProc( return 0; } -static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, - void *drv_opaque) +static OSStatus init_out_device(coreaudioVoiceOut *core) { OSStatus status; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - int err; - const char *typ = "playback"; AudioValueRange frameRange; - Audiodev *dev = drv_opaque; - AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; - int frames; - struct audsettings obt_as; - - /* create mutex */ - err = pthread_mutex_init(&core->mutex, NULL); - if (err) { - dolog("Could not create mutex\nReason: %s\n", strerror (err)); - return -1; - } - - obt_as = *as; - as = &obt_as; - as->fmt = AUDIO_FORMAT_F32; - audio_pcm_init_info (&hw->info, as); status = coreaudio_get_voice(&core->outputDeviceID); if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get default output Device\n"); - return -1; + coreaudio_playback_logerr (status, + "Could not get default output Device\n"); + return status; } if (core->outputDeviceID == kAudioDeviceUnknown) { - dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); - return -1; + dolog ("Could not initialize playback - Unknown Audiodevice\n"); + return status; } /* get minimum and maximum buffer frame sizes */ status = coreaudio_get_framesizerange(core->outputDeviceID, &frameRange); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get device buffer frame range\n"); - return -1; + coreaudio_playback_logerr (status, + "Could not get device buffer frame range\n"); + return status; } - frames = audio_buffer_frames( - qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); - if (frameRange.mMinimum > frames) { + if (frameRange.mMinimum > core->frameSizeSetting) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); - } else if (frameRange.mMaximum < frames) { + } else if (frameRange.mMaximum < core->frameSizeSetting) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); } else { - core->audioDevicePropertyBufferFrameSize = frames; + core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting; } /* set Buffer Frame Size */ status = coreaudio_set_framesize(core->outputDeviceID, &core->audioDevicePropertyBufferFrameSize); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not set device buffer frame size %" PRIu32 "\n", - (uint32_t)core->audioDevicePropertyBufferFrameSize); - return -1; + coreaudio_playback_logerr (status, + "Could not set device buffer frame size %" PRIu32 "\n", + (uint32_t)core->audioDevicePropertyBufferFrameSize); + return status; } /* get Buffer Frame Size */ status = coreaudio_get_framesize(core->outputDeviceID, &core->audioDevicePropertyBufferFrameSize); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get device buffer frame size\n"); - return -1; + coreaudio_playback_logerr (status, + "Could not get device buffer frame size\n"); + return status; } - hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * - core->audioDevicePropertyBufferFrameSize; + core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize; /* get StreamFormat */ status = coreaudio_get_streamformat(core->outputDeviceID, &core->outputStreamBasicDescription); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get Device Stream properties\n"); + coreaudio_playback_logerr (status, + "Could not get Device Stream properties\n"); core->outputDeviceID = kAudioDeviceUnknown; - return -1; + return status; } /* set Samplerate */ - core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; - status = coreaudio_set_streamformat(core->outputDeviceID, &core->outputStreamBasicDescription); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", - as->freq); + coreaudio_playback_logerr (status, + "Could not set samplerate %lf\n", + core->outputStreamBasicDescription.mSampleRate); core->outputDeviceID = kAudioDeviceUnknown; - return -1; + return status; } /* set Callback */ core->ioprocid = NULL; status = AudioDeviceCreateIOProcID(core->outputDeviceID, audioDeviceIOProc, - hw, + &core->hw, &core->ioprocid); + if (status == kAudioHardwareBadDeviceError) { + return 0; + } if (status != kAudioHardwareNoError || core->ioprocid == NULL) { - coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); + coreaudio_playback_logerr (status, "Could not set IOProc\n"); core->outputDeviceID = kAudioDeviceUnknown; - return -1; + return status; } return 0; } -static void coreaudio_fini_out (HWVoiceOut *hw) +static void fini_out_device(coreaudioVoiceOut *core) { OSStatus status; - int err; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + UInt32 isrunning; /* stop playback */ - if (isPlaying(core->outputDeviceID)) { - status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); + status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); + if (status != kAudioHardwareBadObjectError) { if (status != kAudioHardwareNoError) { - coreaudio_logerr(status, "Could not stop playback\n"); + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + + if (isrunning) { + status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not stop playback\n"); + } } } /* remove callback */ status = AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareNoError) { + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { coreaudio_logerr(status, "Could not remove IOProc\n"); } core->outputDeviceID = kAudioDeviceUnknown; - - /* destroy mutex */ - err = pthread_mutex_destroy(&core->mutex); - if (err) { - dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); - } } -static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) +static void update_device_playback_state(coreaudioVoiceOut *core) { OSStatus status; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + UInt32 isrunning; + + status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); + if (status != kAudioHardwareNoError) { + if (status != kAudioHardwareBadObjectError) { + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + + return; + } - if (enable) { + if (core->enabled) { /* start playback */ - if (!isPlaying(core->outputDeviceID)) { + if (!isrunning) { status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareNoError) { + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { coreaudio_logerr (status, "Could not resume playback\n"); } } } else { /* stop playback */ - if (isPlaying(core->outputDeviceID)) { + if (isrunning) { status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareNoError) { + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { coreaudio_logerr(status, "Could not pause playback\n"); } } } } +static OSStatus handle_voice_change( + AudioObjectID in_object_id, + UInt32 in_number_addresses, + const AudioObjectPropertyAddress *in_addresses, + void *in_client_data) +{ + OSStatus status; + coreaudioVoiceOut *core = in_client_data; + + if (coreaudio_lock(core, __func__)) { + abort(); + } + + if (core->outputDeviceID) { + fini_out_device(core); + } + + status = init_out_device(core); + if (!status) { + update_device_playback_state(core); + } + + coreaudio_unlock (core, __func__); + return status; +} + +static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) +{ + OSStatus status; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + int err; + Audiodev *dev = drv_opaque; + AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; + struct audsettings obt_as; + + /* create mutex */ + err = pthread_mutex_init(&core->mutex, NULL); + if (err) { + dolog("Could not create mutex\nReason: %s\n", strerror (err)); + goto mutex_error; + } + + if (coreaudio_lock(core, __func__)) { + goto lock_error; + } + + obt_as = *as; + as = &obt_as; + as->fmt = AUDIO_FORMAT_F32; + audio_pcm_init_info (&hw->info, as); + + core->frameSizeSetting = audio_buffer_frames( + qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); + + core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4; + core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; + + status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, + &voice_addr, handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not listen to voice property change\n"); + goto listener_error; + } + + if (init_out_device(core)) { + goto device_error; + } + + coreaudio_unlock(core, __func__); + return 0; + +device_error: + status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, + &voice_addr, + handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr(status, + "Could not remove voice property change listener\n"); + } + +listener_error: + coreaudio_unlock(core, __func__); + +lock_error: + err = pthread_mutex_destroy(&core->mutex); + if (err) { + dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); + } + +mutex_error: + return -1; +} + +static void coreaudio_fini_out (HWVoiceOut *hw) +{ + OSStatus status; + int err; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + if (coreaudio_lock(core, __func__)) { + abort(); + } + + status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, + &voice_addr, + handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not remove voice property change listener\n"); + } + + fini_out_device(core); + + coreaudio_unlock(core, __func__); + + /* destroy mutex */ + err = pthread_mutex_destroy(&core->mutex); + if (err) { + dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); + } +} + +static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) +{ + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + if (coreaudio_lock(core, __func__)) { + abort(); + } + + core->enabled = enable; + update_device_playback_state(core); + + coreaudio_unlock(core, __func__); +} + static void *coreaudio_audio_init(Audiodev *dev) { return dev; @@ -465,6 +465,7 @@ fuse_lseek="auto" multiprocess="auto" malloc_trim="auto" +gio="$default_feature" # parse CC options second for opt do @@ -1560,6 +1561,10 @@ for opt do ;; --disable-multiprocess) multiprocess="disabled" ;; + --enable-gio) gio=yes + ;; + --disable-gio) gio=no + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1913,6 +1918,7 @@ disabled with --disable-FEATURE, default is enabled if available fuse FUSE block device export fuse-lseek SEEK_HOLE/SEEK_DATA support for FUSE exports multiprocess Out of process device emulation support + gio libgio support NOTE: The object files are built at the place where configure is launched EOF @@ -3319,17 +3325,19 @@ if test "$static" = yes && test "$mingw32" = yes; then glib_cflags="-DGLIB_STATIC_COMPILATION $glib_cflags" fi -if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then - gio_cflags=$($pkg_config --cflags gio-2.0) - gio_libs=$($pkg_config --libs gio-2.0) - gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0) - if [ ! -x "$gdbus_codegen" ]; then - gdbus_codegen= - fi - # Check that the libraries actually work -- Ubuntu 18.04 ships - # with pkg-config --static --libs data for gio-2.0 that is missing - # -lblkid and will give a link error. - cat > $TMPC <<EOF +if ! test "$gio" = "no"; then + pass=no + if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then + gio_cflags=$($pkg_config --cflags gio-2.0) + gio_libs=$($pkg_config --libs gio-2.0) + gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0) + if [ ! -x "$gdbus_codegen" ]; then + gdbus_codegen= + fi + # Check that the libraries actually work -- Ubuntu 18.04 ships + # with pkg-config --static --libs data for gio-2.0 that is missing + # -lblkid and will give a link error. + cat > $TMPC <<EOF #include <gio/gio.h> int main(void) { @@ -3337,18 +3345,28 @@ int main(void) return 0; } EOF - if compile_prog "$gio_cflags" "$gio_libs" ; then - gio=yes - else - gio=no + if compile_prog "$gio_cflags" "$gio_libs" ; then + pass=yes + else + pass=no + fi + + if test "$pass" = "yes" && + $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then + gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)" + gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)" + fi fi -else - gio=no -fi -if $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then - gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)" - gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)" + if test "$pass" = "no"; then + if test "$gio" = "yes"; then + feature_not_found "gio" "Install libgio >= 2.0" + else + gio=no + fi + else + gio=yes + fi fi # Sanity check that the current size_t matches the diff --git a/default-configs/devices/m68k-softmmu.mak b/default-configs/devices/m68k-softmmu.mak index 6629fd2aa3..7f8619e427 100644 --- a/default-configs/devices/m68k-softmmu.mak +++ b/default-configs/devices/m68k-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_AN5206=y CONFIG_MCF5208=y CONFIG_NEXTCUBE=y CONFIG_Q800=y +CONFIG_M68K_VIRT=y diff --git a/docs/devel/fuzzing.rst b/docs/devel/fuzzing.rst index 97797c4f8c..2749bb9bed 100644 --- a/docs/devel/fuzzing.rst +++ b/docs/devel/fuzzing.rst @@ -210,6 +210,62 @@ Build details: - The script responsible for building the fuzzers can be found in the QEMU source tree at ``scripts/oss-fuzz/build.sh`` +Building Crash Reproducers +----------------------------------------- +When we find a crash, we should try to create an independent reproducer, that +can be used on a non-fuzzer build of QEMU. This filters out any potential +false-positives, and improves the debugging experience for developers. +Here are the steps for building a reproducer for a crash found by the +generic-fuzz target. + +- Ensure the crash reproduces:: + + qemu-fuzz-i386 --fuzz-target... ./crash-... + +- Gather the QTest output for the crash:: + + QEMU_FUZZ_TIMEOUT=0 QTEST_LOG=1 FUZZ_SERIALIZE_QTEST=1 \ + qemu-fuzz-i386 --fuzz-target... ./crash-... &> /tmp/trace + +- Reorder and clean-up the resulting trace:: + + scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py /tmp/trace > /tmp/reproducer + +- Get the arguments needed to start qemu, and provide a path to qemu:: + + less /tmp/trace # The args should be logged at the top of this file + export QEMU_ARGS="-machine ..." + export QEMU_PATH="path/to/qemu-system" + +- Ensure the crash reproduces in qemu-system:: + + $QEMU_PATH $QEMU_ARGS -qtest stdio < /tmp/reproducer + +- From the crash output, obtain some string that identifies the crash. This + can be a line in the stack-trace, for example:: + + export CRASH_TOKEN="hw/usb/hcd-xhci.c:1865" + +- Minimize the reproducer:: + + scripts/oss-fuzz/minimize_qtest_trace.py -M1 -M2 \ + /tmp/reproducer /tmp/reproducer-minimized + +- Confirm that the minimized reproducer still crashes:: + + $QEMU_PATH $QEMU_ARGS -qtest stdio < /tmp/reproducer-minimized + +- Create a one-liner reproducer that can be sent over email:: + + ./scripts/oss-fuzz/output_reproducer.py -bash /tmp/reproducer-minimized + +- Output the C source code for a test case that will reproduce the bug:: + + ./scripts/oss-fuzz/output_reproducer.py -owner "John Smith <john@smith.com>"\ + -name "test_function_name" /tmp/reproducer-minimized + +- Report the bug and send a patch with the C reproducer upstream + Implementation Details / Fuzzer Lifecycle ----------------------------------------- diff --git a/docs/specs/virt-ctlr.txt b/docs/specs/virt-ctlr.txt new file mode 100644 index 0000000000..24d38084f7 --- /dev/null +++ b/docs/specs/virt-ctlr.txt @@ -0,0 +1,26 @@ +Virtual System Controller +========================= + +This device is a simple interface defined for the pure virtual machine with no +hardware reference implementation to allow the guest kernel to send command +to the host hypervisor. + +The specification can evolve, the current state is defined as below. + +This is a MMIO mapped device using 256 bytes. + +Two 32bit registers are defined: + +1- the features register (read-only, address 0x00) + + This register allows the device to report features supported by the + controller. + The only feature supported for the moment is power control (0x01). + +2- the command register (write-only, address 0x04) + + This register allows the kernel to send the commands to the hypervisor. + The implemented commands are part of the power control feature and + are reset (1), halt (2) and panic (3). + A basic command, no-op (0), is always present and can be used to test the + register access. This command has no effect. diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 62a64a8c41..1db30f8f27 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -165,6 +165,14 @@ Input parameters that take a size value should only use a size suffix the value is hexadecimal. That is, '0x20M' is deprecated, and should be written either as '32M' or as '0x2000000'. +``-spice password=string`` (since 6.0) +'''''''''''''''''''''''''''''''''''''' + +This option is insecure because the SPICE password remains visible in +the process listing. This is replaced by the new ``password-secret`` +option which lets the password be securely provided on the command +line using a ``secret`` object instance. + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 7eb210ffa8..473ef914b0 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -79,11 +79,11 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, if (!parent) { parent = &synth_root; } - qemu_mutex_lock(&synth_mutex); + QEMU_LOCK_GUARD(&synth_mutex); QLIST_FOREACH(tmp, &parent->child, sibling) { if (!strcmp(tmp->name, name)) { ret = EEXIST; - goto err_out; + return ret; } } /* Add the name */ @@ -94,8 +94,6 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, node->attr, node->attr->inode); *result = node; ret = 0; -err_out: - qemu_mutex_unlock(&synth_mutex); return ret; } @@ -116,11 +114,11 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, parent = &synth_root; } - qemu_mutex_lock(&synth_mutex); + QEMU_LOCK_GUARD(&synth_mutex); QLIST_FOREACH(tmp, &parent->child, sibling) { if (!strcmp(tmp->name, name)) { ret = EEXIST; - goto err_out; + return ret; } } /* Add file type and remove write bits */ @@ -136,8 +134,6 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, pstrcpy(node->name, sizeof(node->name), name); QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); ret = 0; -err_out: - qemu_mutex_unlock(&synth_mutex); return ret; } diff --git a/hw/char/Kconfig b/hw/char/Kconfig index f6f4fffd1b..4cf36ac637 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -64,3 +64,6 @@ config MCHP_PFSOC_MMUART config SIFIVE_UART bool + +config GOLDFISH_TTY + bool diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c new file mode 100644 index 0000000000..8365a18761 --- /dev/null +++ b/hw/char/goldfish_tty.c @@ -0,0 +1,285 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * Goldfish TTY + * + * (c) 2020 Laurent Vivier <laurent@vivier.eu> + * + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/qdev-properties-system.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "chardev/char-fe.h" +#include "qemu/log.h" +#include "trace.h" +#include "exec/address-spaces.h" +#include "hw/char/goldfish_tty.h" + +#define GOLDFISH_TTY_VERSION 1 + +/* registers */ + +enum { + REG_PUT_CHAR = 0x00, + REG_BYTES_READY = 0x04, + REG_CMD = 0x08, + REG_DATA_PTR = 0x10, + REG_DATA_LEN = 0x14, + REG_DATA_PTR_HIGH = 0x18, + REG_VERSION = 0x20, +}; + +/* commands */ + +enum { + CMD_INT_DISABLE = 0x00, + CMD_INT_ENABLE = 0x01, + CMD_WRITE_BUFFER = 0x02, + CMD_READ_BUFFER = 0x03, +}; + +static uint64_t goldfish_tty_read(void *opaque, hwaddr addr, + unsigned size) +{ + GoldfishTTYState *s = opaque; + uint64_t value = 0; + + switch (addr) { + case REG_BYTES_READY: + value = fifo8_num_used(&s->rx_fifo); + break; + case REG_VERSION: + value = GOLDFISH_TTY_VERSION; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n", + __func__, addr); + break; + } + + trace_goldfish_tty_read(s, addr, size, value); + + return value; +} + +static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) +{ + uint32_t to_copy; + uint8_t *buf; + uint8_t data_out[GOLFISH_TTY_BUFFER_SIZE]; + int len; + uint64_t ptr; + + switch (cmd) { + case CMD_INT_DISABLE: + if (s->int_enabled) { + if (!fifo8_is_empty(&s->rx_fifo)) { + qemu_set_irq(s->irq, 0); + } + s->int_enabled = false; + } + break; + case CMD_INT_ENABLE: + if (!s->int_enabled) { + if (!fifo8_is_empty(&s->rx_fifo)) { + qemu_set_irq(s->irq, 1); + } + s->int_enabled = true; + } + break; + case CMD_WRITE_BUFFER: + len = s->data_len; + ptr = s->data_ptr; + while (len) { + to_copy = MIN(GOLFISH_TTY_BUFFER_SIZE, len); + + address_space_rw(&address_space_memory, ptr, + MEMTXATTRS_UNSPECIFIED, data_out, to_copy, 0); + qemu_chr_fe_write_all(&s->chr, data_out, to_copy); + + len -= to_copy; + ptr += to_copy; + } + break; + case CMD_READ_BUFFER: + len = s->data_len; + ptr = s->data_ptr; + while (len && !fifo8_is_empty(&s->rx_fifo)) { + buf = (uint8_t *)fifo8_pop_buf(&s->rx_fifo, len, &to_copy); + address_space_rw(&address_space_memory, ptr, + MEMTXATTRS_UNSPECIFIED, buf, to_copy, 1); + + len -= to_copy; + ptr += to_copy; + } + if (s->int_enabled && fifo8_is_empty(&s->rx_fifo)) { + qemu_set_irq(s->irq, 0); + } + break; + } +} + +static void goldfish_tty_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + GoldfishTTYState *s = opaque; + unsigned char c; + + trace_goldfish_tty_write(s, addr, size, value); + + switch (addr) { + case REG_PUT_CHAR: + c = value; + qemu_chr_fe_write_all(&s->chr, &c, sizeof(c)); + break; + case REG_CMD: + goldfish_tty_cmd(s, value); + break; + case REG_DATA_PTR: + s->data_ptr = value; + break; + case REG_DATA_PTR_HIGH: + s->data_ptr = deposit64(s->data_ptr, 32, 32, value); + break; + case REG_DATA_LEN: + s->data_len = value; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n", + __func__, addr); + break; + } +} + +static const MemoryRegionOps goldfish_tty_ops = { + .read = goldfish_tty_read, + .write = goldfish_tty_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.max_access_size = 4, + .impl.max_access_size = 4, + .impl.min_access_size = 4, +}; + +static int goldfish_tty_can_receive(void *opaque) +{ + GoldfishTTYState *s = opaque; + int available = fifo8_num_free(&s->rx_fifo); + + trace_goldfish_tty_can_receive(s, available); + + return available; +} + +static void goldfish_tty_receive(void *opaque, const uint8_t *buffer, int size) +{ + GoldfishTTYState *s = opaque; + + trace_goldfish_tty_receive(s, size); + + g_assert(size <= fifo8_num_free(&s->rx_fifo)); + + fifo8_push_all(&s->rx_fifo, buffer, size); + + if (s->int_enabled && !fifo8_is_empty(&s->rx_fifo)) { + qemu_set_irq(s->irq, 1); + } +} + +static void goldfish_tty_reset(DeviceState *dev) +{ + GoldfishTTYState *s = GOLDFISH_TTY(dev); + + trace_goldfish_tty_reset(s); + + fifo8_reset(&s->rx_fifo); + s->int_enabled = false; + s->data_ptr = 0; + s->data_len = 0; +} + +static void goldfish_tty_realize(DeviceState *dev, Error **errp) +{ + GoldfishTTYState *s = GOLDFISH_TTY(dev); + + trace_goldfish_tty_realize(s); + + fifo8_create(&s->rx_fifo, GOLFISH_TTY_BUFFER_SIZE); + memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_tty_ops, s, + "goldfish_tty", 0x24); + + if (qemu_chr_fe_backend_connected(&s->chr)) { + qemu_chr_fe_set_handlers(&s->chr, goldfish_tty_can_receive, + goldfish_tty_receive, NULL, NULL, + s, NULL, true); + } +} + +static void goldfish_tty_unrealize(DeviceState *dev) +{ + GoldfishTTYState *s = GOLDFISH_TTY(dev); + + trace_goldfish_tty_unrealize(s); + + fifo8_destroy(&s->rx_fifo); +} + +static const VMStateDescription vmstate_goldfish_tty = { + .name = "goldfish_tty", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(data_len, GoldfishTTYState), + VMSTATE_UINT64(data_ptr, GoldfishTTYState), + VMSTATE_BOOL(int_enabled, GoldfishTTYState), + VMSTATE_FIFO8(rx_fifo, GoldfishTTYState), + VMSTATE_END_OF_LIST() + } +}; + +static Property goldfish_tty_properties[] = { + DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void goldfish_tty_instance_init(Object *obj) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + GoldfishTTYState *s = GOLDFISH_TTY(obj); + + trace_goldfish_tty_instance_init(s); + + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); +} + +static void goldfish_tty_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, goldfish_tty_properties); + dc->reset = goldfish_tty_reset; + dc->realize = goldfish_tty_realize; + dc->unrealize = goldfish_tty_unrealize; + dc->vmsd = &vmstate_goldfish_tty; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo goldfish_tty_info = { + .name = TYPE_GOLDFISH_TTY, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = goldfish_tty_class_init, + .instance_init = goldfish_tty_instance_init, + .instance_size = sizeof(GoldfishTTYState), +}; + +static void goldfish_tty_register_types(void) +{ + type_register_static(&goldfish_tty_info); +} + +type_init(goldfish_tty_register_types) diff --git a/hw/char/meson.build b/hw/char/meson.build index 7ba38dbd96..da5bb8b762 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -39,3 +39,5 @@ specific_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c')) specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c')) + +specific_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c')) diff --git a/hw/char/trace-events b/hw/char/trace-events index 81026f6612..76d52938ea 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -20,6 +20,16 @@ virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, i virtio_console_chr_read(unsigned int port, int size) "port %u, size %d" virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" +# goldfish_tty.c +goldfish_tty_read(void *dev, unsigned int addr, unsigned int size, uint64_t value) "tty: %p reg: 0x%02x size: %d value: 0x%"PRIx64 +goldfish_tty_write(void *dev, unsigned int addr, unsigned int size, uint64_t value) "tty: %p reg: 0x%02x size: %d value: 0x%"PRIx64 +goldfish_tty_can_receive(void *dev, unsigned int available) "tty: %p available: %u" +goldfish_tty_receive(void *dev, unsigned int size) "tty: %p size: %u" +goldfish_tty_reset(void *dev) "tty: %p" +goldfish_tty_realize(void *dev) "tty: %p" +goldfish_tty_unrealize(void *dev) "tty: %p" +goldfish_tty_instance_init(void *dev) "tty: %p" + # grlib_apbuart.c grlib_apbuart_event(int event) "event:%d" grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x" diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 66bf0b90b4..f4694088a4 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -67,3 +67,9 @@ config SIFIVE_CLINT config SIFIVE_PLIC bool + +config GOLDFISH_PIC + bool + +config M68K_IRQC + bool diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c new file mode 100644 index 0000000000..e3b43a69f1 --- /dev/null +++ b/hw/intc/goldfish_pic.c @@ -0,0 +1,219 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * Goldfish PIC + * + * (c) 2020 Laurent Vivier <laurent@vivier.eu> + * + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "monitor/monitor.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/intc/intc.h" +#include "hw/intc/goldfish_pic.h" + +/* registers */ + +enum { + REG_STATUS = 0x00, + REG_IRQ_PENDING = 0x04, + REG_IRQ_DISABLE_ALL = 0x08, + REG_DISABLE = 0x0c, + REG_ENABLE = 0x10, +}; + +static bool goldfish_pic_get_statistics(InterruptStatsProvider *obj, + uint64_t **irq_counts, + unsigned int *nb_irqs) +{ + GoldfishPICState *s = GOLDFISH_PIC(obj); + + *irq_counts = s->stats_irq_count; + *nb_irqs = ARRAY_SIZE(s->stats_irq_count); + return true; +} + +static void goldfish_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) +{ + GoldfishPICState *s = GOLDFISH_PIC(obj); + monitor_printf(mon, "goldfish-pic.%d: pending=0x%08x enabled=0x%08x\n", + s->idx, s->pending, s->enabled); +} + +static void goldfish_pic_update(GoldfishPICState *s) +{ + if (s->pending & s->enabled) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static void goldfish_irq_request(void *opaque, int irq, int level) +{ + GoldfishPICState *s = opaque; + + trace_goldfish_irq_request(s, s->idx, irq, level); + + if (level) { + s->pending |= 1 << irq; + s->stats_irq_count[irq]++; + } else { + s->pending &= ~(1 << irq); + } + goldfish_pic_update(s); +} + +static uint64_t goldfish_pic_read(void *opaque, hwaddr addr, + unsigned size) +{ + GoldfishPICState *s = opaque; + uint64_t value = 0; + + switch (addr) { + case REG_STATUS: + /* The number of pending interrupts (0 to 32) */ + value = ctpop32(s->pending & s->enabled); + break; + case REG_IRQ_PENDING: + /* The pending interrupt mask */ + value = s->pending & s->enabled; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n", + __func__, addr); + break; + } + + trace_goldfish_pic_read(s, s->idx, addr, size, value); + + return value; +} + +static void goldfish_pic_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + GoldfishPICState *s = opaque; + + trace_goldfish_pic_write(s, s->idx, addr, size, value); + + switch (addr) { + case REG_IRQ_DISABLE_ALL: + s->enabled = 0; + s->pending = 0; + break; + case REG_DISABLE: + s->enabled &= ~value; + break; + case REG_ENABLE: + s->enabled |= value; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n", + __func__, addr); + break; + } + goldfish_pic_update(s); +} + +static const MemoryRegionOps goldfish_pic_ops = { + .read = goldfish_pic_read, + .write = goldfish_pic_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.max_access_size = 4, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static void goldfish_pic_reset(DeviceState *dev) +{ + GoldfishPICState *s = GOLDFISH_PIC(dev); + int i; + + trace_goldfish_pic_reset(s, s->idx); + s->pending = 0; + s->enabled = 0; + + for (i = 0; i < ARRAY_SIZE(s->stats_irq_count); i++) { + s->stats_irq_count[i] = 0; + } +} + +static void goldfish_pic_realize(DeviceState *dev, Error **errp) +{ + GoldfishPICState *s = GOLDFISH_PIC(dev); + + trace_goldfish_pic_realize(s, s->idx); + + memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_pic_ops, s, + "goldfish_pic", 0x24); +} + +static const VMStateDescription vmstate_goldfish_pic = { + .name = "goldfish_pic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(pending, GoldfishPICState), + VMSTATE_UINT32(enabled, GoldfishPICState), + VMSTATE_END_OF_LIST() + } +}; + +static void goldfish_pic_instance_init(Object *obj) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + GoldfishPICState *s = GOLDFISH_PIC(obj); + + trace_goldfish_pic_instance_init(s); + + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + + qdev_init_gpio_in(DEVICE(obj), goldfish_irq_request, GOLDFISH_PIC_IRQ_NB); +} + +static Property goldfish_pic_properties[] = { + DEFINE_PROP_UINT8("index", GoldfishPICState, idx, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void goldfish_pic_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); + + dc->reset = goldfish_pic_reset; + dc->realize = goldfish_pic_realize; + dc->vmsd = &vmstate_goldfish_pic; + ic->get_statistics = goldfish_pic_get_statistics; + ic->print_info = goldfish_pic_print_info; + device_class_set_props(dc, goldfish_pic_properties); +} + +static const TypeInfo goldfish_pic_info = { + .name = TYPE_GOLDFISH_PIC, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = goldfish_pic_class_init, + .instance_init = goldfish_pic_instance_init, + .instance_size = sizeof(GoldfishPICState), + .interfaces = (InterfaceInfo[]) { + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, +}; + +static void goldfish_pic_register_types(void) +{ + type_register_static(&goldfish_pic_info); +} + +type_init(goldfish_pic_register_types) diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c new file mode 100644 index 0000000000..2133d2a698 --- /dev/null +++ b/hw/intc/m68k_irqc.c @@ -0,0 +1,119 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * QEMU Motorola 680x0 IRQ Controller + * + * (c) 2020 Laurent Vivier <laurent@vivier.eu> + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "migration/vmstate.h" +#include "monitor/monitor.h" +#include "hw/nmi.h" +#include "hw/intc/intc.h" +#include "hw/intc/m68k_irqc.h" + + +static bool m68k_irqc_get_statistics(InterruptStatsProvider *obj, + uint64_t **irq_counts, unsigned int *nb_irqs) +{ + M68KIRQCState *s = M68K_IRQC(obj); + + *irq_counts = s->stats_irq_count; + *nb_irqs = ARRAY_SIZE(s->stats_irq_count); + return true; +} + +static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon) +{ + M68KIRQCState *s = M68K_IRQC(obj); + monitor_printf(mon, "m68k-irqc: ipr=0x%x\n", s->ipr); +} + +static void m68k_set_irq(void *opaque, int irq, int level) +{ + M68KIRQCState *s = opaque; + M68kCPU *cpu = M68K_CPU(first_cpu); + int i; + + if (level) { + s->ipr |= 1 << irq; + s->stats_irq_count[irq]++; + } else { + s->ipr &= ~(1 << irq); + } + + for (i = M68K_IRQC_LEVEL_7; i >= M68K_IRQC_LEVEL_1; i--) { + if ((s->ipr >> i) & 1) { + m68k_set_irq_level(cpu, i + 1, i + M68K_IRQC_AUTOVECTOR_BASE); + return; + } + } + m68k_set_irq_level(cpu, 0, 0); +} + +static void m68k_irqc_reset(DeviceState *d) +{ + M68KIRQCState *s = M68K_IRQC(d); + int i; + + s->ipr = 0; + for (i = 0; i < ARRAY_SIZE(s->stats_irq_count); i++) { + s->stats_irq_count[i] = 0; + } +} + +static void m68k_irqc_instance_init(Object *obj) +{ + qdev_init_gpio_in(DEVICE(obj), m68k_set_irq, M68K_IRQC_LEVEL_NUM); +} + +static void m68k_nmi(NMIState *n, int cpu_index, Error **errp) +{ + m68k_set_irq(n, M68K_IRQC_LEVEL_7, 1); +} + +static const VMStateDescription vmstate_m68k_irqc = { + .name = "m68k-irqc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(ipr, M68KIRQCState), + VMSTATE_END_OF_LIST() + } +}; + +static void m68k_irqc_class_init(ObjectClass *oc, void *data) + { + DeviceClass *dc = DEVICE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); + + nc->nmi_monitor_handler = m68k_nmi; + dc->reset = m68k_irqc_reset; + dc->vmsd = &vmstate_m68k_irqc; + ic->get_statistics = m68k_irqc_get_statistics; + ic->print_info = m68k_irqc_print_info; +} + +static const TypeInfo m68k_irqc_type_info = { + .name = TYPE_M68K_IRQC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(M68KIRQCState), + .instance_init = m68k_irqc_instance_init, + .class_init = m68k_irqc_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, +}; + +static void q800_irq_register_types(void) +{ + type_register_static(&m68k_irqc_type_info); +} + +type_init(q800_irq_register_types); diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 8df3656419..1c299039f6 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -57,3 +57,5 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) +specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) +specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 45ddaf48df..c9ab17234b 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -239,3 +239,11 @@ xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x # pnv_xive.c pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64 + +# goldfish_pic.c +goldfish_irq_request(void *dev, int idx, int irq, int level) "pic: %p goldfish-irq.%d irq: %d level: %d" +goldfish_pic_read(void *dev, int idx, unsigned int addr, unsigned int size, uint64_t value) "pic: %p goldfish-irq.%d reg: 0x%02x size: %d value: 0x%"PRIx64 +goldfish_pic_write(void *dev, int idx, unsigned int addr, unsigned int size, uint64_t value) "pic: %p goldfish-irq.%d reg: 0x%02x size: %d value: 0x%"PRIx64 +goldfish_pic_reset(void *dev, int idx) "pic: %p goldfish-irq.%d" +goldfish_pic_realize(void *dev, int idx) "pic: %p goldfish-irq.%d" +goldfish_pic_instance_init(void *dev) "pic: %p goldfish-irq" diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig index 60d7bcfb8f..f839f8a030 100644 --- a/hw/m68k/Kconfig +++ b/hw/m68k/Kconfig @@ -23,3 +23,12 @@ config Q800 select ESP select DP8393X select OR_IRQ + +config M68K_VIRT + bool + select M68K_IRQC + select VIRT_CTRL + select GOLDFISH_PIC + select GOLDFISH_TTY + select GOLDFISH_RTC + select VIRTIO_MMIO diff --git a/hw/m68k/meson.build b/hw/m68k/meson.build index ca0044c652..31248641d3 100644 --- a/hw/m68k/meson.build +++ b/hw/m68k/meson.build @@ -3,5 +3,6 @@ m68k_ss.add(when: 'CONFIG_AN5206', if_true: files('an5206.c', 'mcf5206.c')) m68k_ss.add(when: 'CONFIG_MCF5208', if_true: files('mcf5208.c', 'mcf_intc.c')) m68k_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-kbd.c', 'next-cube.c')) m68k_ss.add(when: 'CONFIG_Q800', if_true: files('q800.c')) +m68k_ss.add(when: 'CONFIG_M68K_VIRT', if_true: files('virt.c')) hw_arch += {'m68k': m68k_ss} diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c new file mode 100644 index 0000000000..e9a5d4c69b --- /dev/null +++ b/hw/m68k/virt.c @@ -0,0 +1,313 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * QEMU Vitual M68K Machine + * + * (c) 2020 Laurent Vivier <laurent@vivier.eu> + * + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu-common.h" +#include "sysemu/sysemu.h" +#include "cpu.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "elf.h" +#include "hw/loader.h" +#include "ui/console.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" +#include "standard-headers/asm-m68k/bootinfo.h" +#include "standard-headers/asm-m68k/bootinfo-virt.h" +#include "bootinfo.h" +#include "net/net.h" +#include "qapi/error.h" +#include "sysemu/qtest.h" +#include "sysemu/runstate.h" +#include "sysemu/reset.h" + +#include "hw/intc/m68k_irqc.h" +#include "hw/misc/virt_ctrl.h" +#include "hw/char/goldfish_tty.h" +#include "hw/rtc/goldfish_rtc.h" +#include "hw/intc/goldfish_pic.h" +#include "hw/virtio/virtio-mmio.h" +#include "hw/virtio/virtio-blk.h" + +/* + * 6 goldfish-pic for CPU IRQ #1 to IRQ #6 + * CPU IRQ #1 -> PIC #1 + * IRQ #1 to IRQ #31 -> unused + * IRQ #32 -> goldfish-tty + * CPU IRQ #2 -> PIC #2 + * IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32 + * CPU IRQ #3 -> PIC #3 + * IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64 + * CPU IRQ #4 -> PIC #4 + * IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96 + * CPU IRQ #5 -> PIC #5 + * IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128 + * CPU IRQ #6 -> PIC #6 + * IRQ #1 -> goldfish-rtc + * IRQ #2 to IRQ #32 -> unused + * CPU IRQ #7 -> NMI + */ + +#define PIC_IRQ_BASE(num) (8 + (num - 1) * 32) +#define PIC_IRQ(num, irq) (PIC_IRQ_BASE(num) + irq - 1) +#define PIC_GPIO(pic_irq) (qdev_get_gpio_in(pic_dev[(pic_irq - 8) / 32], \ + (pic_irq - 8) % 32)) + +#define VIRT_GF_PIC_MMIO_BASE 0xff000000 /* MMIO: 0xff000000 - 0xff005fff */ +#define VIRT_GF_PIC_IRQ_BASE 1 /* IRQ: #1 -> #6 */ +#define VIRT_GF_PIC_NB 6 + +/* 2 goldfish-rtc (and timer) */ +#define VIRT_GF_RTC_MMIO_BASE 0xff006000 /* MMIO: 0xff006000 - 0xff007fff */ +#define VIRT_GF_RTC_IRQ_BASE PIC_IRQ(6, 1) /* PIC: #6, IRQ: #1 */ +#define VIRT_GF_RTC_NB 2 + +/* 1 goldfish-tty */ +#define VIRT_GF_TTY_MMIO_BASE 0xff008000 /* MMIO: 0xff008000 - 0xff008fff */ +#define VIRT_GF_TTY_IRQ_BASE PIC_IRQ(1, 32) /* PIC: #1, IRQ: #32 */ + +/* 1 virt-ctrl */ +#define VIRT_CTRL_MMIO_BASE 0xff009000 /* MMIO: 0xff009000 - 0xff009fff */ +#define VIRT_CTRL_IRQ_BASE PIC_IRQ(1, 1) /* PIC: #1, IRQ: #1 */ + +/* + * virtio-mmio size is 0x200 bytes + * we use 4 goldfish-pic to attach them, + * we can attach 32 virtio devices / goldfish-pic + * -> we can manage 32 * 4 = 128 virtio devices + */ +#define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */ +#define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */ + +static void main_cpu_reset(void *opaque) +{ + M68kCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + cpu_reset(cs); + cpu->env.aregs[7] = ldl_phys(cs->as, 0); + cpu->env.pc = ldl_phys(cs->as, 4); +} + +static void virt_init(MachineState *machine) +{ + M68kCPU *cpu = NULL; + int32_t kernel_size; + uint64_t elf_entry; + ram_addr_t initrd_base; + int32_t initrd_size; + ram_addr_t ram_size = machine->ram_size; + const char *kernel_filename = machine->kernel_filename; + const char *initrd_filename = machine->initrd_filename; + const char *kernel_cmdline = machine->kernel_cmdline; + hwaddr parameters_base; + DeviceState *dev; + DeviceState *irqc_dev; + DeviceState *pic_dev[VIRT_GF_PIC_NB]; + SysBusDevice *sysbus; + hwaddr io_base; + int i; + + if (ram_size > 3399672 * KiB) { + /* + * The physical memory can be up to 4 GiB - 16 MiB, but linux + * kernel crashes after this limit (~ 3.2 GiB) + */ + error_report("Too much memory for this machine: %" PRId64 " KiB, " + "maximum 3399672 KiB", ram_size / KiB); + exit(1); + } + + /* init CPUs */ + cpu = M68K_CPU(cpu_create(machine->cpu_type)); + qemu_register_reset(main_cpu_reset, cpu); + + /* RAM */ + memory_region_add_subregion(get_system_memory(), 0, machine->ram); + + /* IRQ Controller */ + + irqc_dev = qdev_new(TYPE_M68K_IRQC); + sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal); + + /* + * 6 goldfish-pic + * + * map: 0xff000000 - 0xff006fff = 28 KiB + * IRQ: #1 (lower priority) -> #6 (higher priority) + * + */ + io_base = VIRT_GF_PIC_MMIO_BASE; + for (i = 0; i < VIRT_GF_PIC_NB; i++) { + pic_dev[i] = qdev_new(TYPE_GOLDFISH_PIC); + sysbus = SYS_BUS_DEVICE(pic_dev[i]); + qdev_prop_set_uint8(pic_dev[i], "index", i); + sysbus_realize_and_unref(sysbus, &error_fatal); + + sysbus_mmio_map(sysbus, 0, io_base); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(irqc_dev, i)); + + io_base += 0x1000; + } + + /* goldfish-rtc */ + io_base = VIRT_GF_RTC_MMIO_BASE; + for (i = 0; i < VIRT_GF_RTC_NB; i++) { + dev = qdev_new(TYPE_GOLDFISH_RTC); + sysbus = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_mmio_map(sysbus, 0, io_base); + sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_RTC_IRQ_BASE + i)); + + io_base += 0x1000; + } + + /* goldfish-tty */ + dev = qdev_new(TYPE_GOLDFISH_TTY); + sysbus = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_mmio_map(sysbus, 0, VIRT_GF_TTY_MMIO_BASE); + sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE)); + + /* virt controller */ + dev = qdev_new(TYPE_VIRT_CTRL); + sysbus = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE); + sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE)); + + /* virtio-mmio */ + io_base = VIRT_VIRTIO_MMIO_BASE; + for (i = 0; i < 128; i++) { + dev = qdev_new(TYPE_VIRTIO_MMIO); + qdev_prop_set_bit(dev, "force-legacy", false); + sysbus = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbus, &error_fatal); + sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_VIRTIO_IRQ_BASE + i)); + sysbus_mmio_map(sysbus, 0, io_base); + io_base += 0x200; + } + + if (kernel_filename) { + CPUState *cs = CPU(cpu); + uint64_t high; + + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &elf_entry, NULL, &high, NULL, 1, + EM_68K, 0, 0); + if (kernel_size < 0) { + error_report("could not load kernel '%s'", kernel_filename); + exit(1); + } + stl_phys(cs->as, 4, elf_entry); /* reset initial PC */ + parameters_base = (high + 1) & ~1; + + BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT); + BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040); + BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040); + BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040); + BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size); + + BOOTINFO1(cs->as, parameters_base, BI_VIRT_QEMU_VERSION, + ((QEMU_VERSION_MAJOR << 24) | (QEMU_VERSION_MINOR << 16) | + (QEMU_VERSION_MICRO << 8))); + BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_PIC_BASE, + VIRT_GF_PIC_MMIO_BASE, VIRT_GF_PIC_IRQ_BASE); + BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_RTC_BASE, + VIRT_GF_RTC_MMIO_BASE, VIRT_GF_RTC_IRQ_BASE); + BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_TTY_BASE, + VIRT_GF_TTY_MMIO_BASE, VIRT_GF_TTY_IRQ_BASE); + BOOTINFO2(cs->as, parameters_base, BI_VIRT_CTRL_BASE, + VIRT_CTRL_MMIO_BASE, VIRT_CTRL_IRQ_BASE); + BOOTINFO2(cs->as, parameters_base, BI_VIRT_VIRTIO_BASE, + VIRT_VIRTIO_MMIO_BASE, VIRT_VIRTIO_IRQ_BASE); + + if (kernel_cmdline) { + BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE, + kernel_cmdline); + } + + /* load initrd */ + if (initrd_filename) { + initrd_size = get_image_size(initrd_filename); + if (initrd_size < 0) { + error_report("could not load initial ram disk '%s'", + initrd_filename); + exit(1); + } + + initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; + load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base, + initrd_size); + } else { + initrd_base = 0; + initrd_size = 0; + } + BOOTINFO0(cs->as, parameters_base, BI_LAST); + } +} + +static void virt_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "QEMU M68K Virtual Machine"; + mc->init = virt_init; + mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); + mc->max_cpus = 1; + mc->no_floppy = 1; + mc->no_parallel = 1; + mc->default_ram_id = "m68k_virt.ram"; +} + +static const TypeInfo virt_machine_info = { + .name = MACHINE_TYPE_NAME("virt"), + .parent = TYPE_MACHINE, + .abstract = true, + .class_init = virt_machine_class_init, +}; + +static void virt_machine_register_types(void) +{ + type_register_static(&virt_machine_info); +} + +type_init(virt_machine_register_types) + +#define DEFINE_VIRT_MACHINE(major, minor, latest) \ + static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ + void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + virt_machine_##major##_##minor##_options(mc); \ + mc->desc = "QEMU " # major "." # minor " M68K Virtual Machine"; \ + if (latest) { \ + mc->alias = "virt"; \ + } \ + } \ + static const TypeInfo machvirt_##major##_##minor##_info = { \ + .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ + .parent = MACHINE_TYPE_NAME("virt"), \ + .class_init = virt_##major##_##minor##_class_init, \ + }; \ + static void machvirt_machine_##major##_##minor##_init(void) \ + { \ + type_register_static(&machvirt_##major##_##minor##_info); \ + } \ + type_init(machvirt_machine_##major##_##minor##_init); + +static void virt_machine_6_0_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE(6, 0, true) diff --git a/hw/mem/meson.build b/hw/mem/meson.build index 0d22f2b572..ef79e04678 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -1,5 +1,6 @@ mem_ss = ss.source_set() mem_ss.add(files('memory-device.c')) +mem_ss.add(when: 'CONFIG_FUZZ', if_true: files('sparse-mem.c')) mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c')) mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c')) mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c')) diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c new file mode 100644 index 0000000000..a13ac74dd9 --- /dev/null +++ b/hw/mem/sparse-mem.c @@ -0,0 +1,151 @@ +/* + * A sparse memory device. Useful for fuzzing + * + * Copyright Red Hat Inc., 2021 + * + * Authors: + * Alexander Bulekov <alxndr@bu.edu> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qapi/error.h" +#include "qemu/units.h" +#include "sysemu/qtest.h" +#include "hw/mem/sparse-mem.h" + +#define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM) +#define SPARSE_BLOCK_SIZE 0x1000 + +typedef struct SparseMemState { + SysBusDevice parent_obj; + MemoryRegion mmio; + uint64_t baseaddr; + uint64_t length; + uint64_t size_used; + uint64_t maxsize; + GHashTable *mapped; +} SparseMemState; + +typedef struct sparse_mem_block { + uint8_t data[SPARSE_BLOCK_SIZE]; +} sparse_mem_block; + +static uint64_t sparse_mem_read(void *opaque, hwaddr addr, unsigned int size) +{ + SparseMemState *s = opaque; + uint64_t ret = 0; + size_t pfn = addr / SPARSE_BLOCK_SIZE; + size_t offset = addr % SPARSE_BLOCK_SIZE; + sparse_mem_block *block; + + block = g_hash_table_lookup(s->mapped, (void *)pfn); + if (block) { + assert(offset + size <= sizeof(block->data)); + memcpy(&ret, block->data + offset, size); + } + return ret; +} + +static void sparse_mem_write(void *opaque, hwaddr addr, uint64_t v, + unsigned int size) +{ + SparseMemState *s = opaque; + size_t pfn = addr / SPARSE_BLOCK_SIZE; + size_t offset = addr % SPARSE_BLOCK_SIZE; + sparse_mem_block *block; + + if (!g_hash_table_lookup(s->mapped, (void *)pfn) && + s->size_used + SPARSE_BLOCK_SIZE < s->maxsize && v) { + g_hash_table_insert(s->mapped, (void *)pfn, + g_new0(sparse_mem_block, 1)); + s->size_used += sizeof(block->data); + } + block = g_hash_table_lookup(s->mapped, (void *)pfn); + if (!block) { + return; + } + + assert(offset + size <= sizeof(block->data)); + + memcpy(block->data + offset, &v, size); + +} + +static const MemoryRegionOps sparse_mem_ops = { + .read = sparse_mem_read, + .write = sparse_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + .unaligned = false, + }, +}; + +static Property sparse_mem_properties[] = { + /* The base address of the memory */ + DEFINE_PROP_UINT64("baseaddr", SparseMemState, baseaddr, 0x0), + /* The length of the sparse memory region */ + DEFINE_PROP_UINT64("length", SparseMemState, length, UINT64_MAX), + /* Max amount of actual memory that can be used to back the sparse memory */ + DEFINE_PROP_UINT64("maxsize", SparseMemState, maxsize, 10 * MiB), + DEFINE_PROP_END_OF_LIST(), +}; + +MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length) +{ + DeviceState *dev; + + dev = qdev_new(TYPE_SPARSE_MEM); + qdev_prop_set_uint64(dev, "baseaddr", addr); + qdev_prop_set_uint64(dev, "length", length); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, addr, -10000); + return &SPARSE_MEM(dev)->mmio; +} + +static void sparse_mem_realize(DeviceState *dev, Error **errp) +{ + SparseMemState *s = SPARSE_MEM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + if (!qtest_enabled()) { + error_setg(errp, "sparse_mem device should only be used " + "for testing with QTest"); + return; + } + + assert(s->baseaddr + s->length > s->baseaddr); + + s->mapped = g_hash_table_new(NULL, NULL); + memory_region_init_io(&s->mmio, OBJECT(s), &sparse_mem_ops, s, + "sparse-mem", s->length); + sysbus_init_mmio(sbd, &s->mmio); +} + +static void sparse_mem_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, sparse_mem_properties); + + dc->desc = "Sparse Memory Device"; + dc->realize = sparse_mem_realize; +} + +static const TypeInfo sparse_mem_types[] = { + { + .name = TYPE_SPARSE_MEM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SparseMemState), + .class_init = sparse_mem_class_init, + }, +}; +DEFINE_TYPES(sparse_mem_types); diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 5426b9b1a1..c71ed25820 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -183,4 +183,7 @@ config SIFIVE_U_OTP config SIFIVE_U_PRCI bool +config VIRT_CTRL + bool + source macio/Kconfig diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index ca2f939dd5..ff0156db76 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -279,6 +279,12 @@ #define VIA_TIMER_FREQ (783360) #define VIA_ADB_POLL_FREQ 50 /* XXX: not real */ +/* + * Guide to the Macintosh Family Hardware ch. 12 "Displays" p. 401 gives the + * precise 60Hz interrupt frequency as ~60.15Hz with a period of 16625.8 us + */ +#define VIA_60HZ_TIMER_PERIOD_NS 16625800 + /* VIA returns time offset from Jan 1, 1904, not 1970 */ #define RTC_OFFSET 2082844800 @@ -297,44 +303,32 @@ enum { REG_EMPTY = 0xff, }; -static void via1_VBL_update(MOS6522Q800VIA1State *v1s) +static void via1_sixty_hz_update(MOS6522Q800VIA1State *v1s) { - MOS6522State *s = MOS6522(v1s); - /* 60 Hz irq */ - v1s->next_VBL = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 16630) / - 16630 * 16630; - - if (s->ier & VIA1_IRQ_VBLANK) { - timer_mod(v1s->VBL_timer, v1s->next_VBL); - } else { - timer_del(v1s->VBL_timer); - } + v1s->next_sixty_hz = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + VIA_60HZ_TIMER_PERIOD_NS) / + VIA_60HZ_TIMER_PERIOD_NS * VIA_60HZ_TIMER_PERIOD_NS; + timer_mod(v1s->sixty_hz_timer, v1s->next_sixty_hz); } static void via1_one_second_update(MOS6522Q800VIA1State *v1s) { - MOS6522State *s = MOS6522(v1s); - v1s->next_second = (qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000) / 1000 * 1000; - if (s->ier & VIA1_IRQ_ONE_SECOND) { - timer_mod(v1s->one_second_timer, v1s->next_second); - } else { - timer_del(v1s->one_second_timer); - } + timer_mod(v1s->one_second_timer, v1s->next_second); } -static void via1_VBL(void *opaque) +static void via1_sixty_hz(void *opaque) { MOS6522Q800VIA1State *v1s = opaque; MOS6522State *s = MOS6522(v1s); MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); - s->ifr |= VIA1_IRQ_VBLANK; + s->ifr |= VIA1_IRQ_60HZ; mdc->update_irq(s); - via1_VBL_update(v1s); + via1_sixty_hz_update(v1s); } static void via1_one_second(void *opaque) @@ -609,7 +603,6 @@ static void adb_via_poll(void *opaque) uint8_t obuf[9]; uint8_t *data = &s->sr; int olen; - uint16_t pending; /* * Setting vADBInt below indicates that an autopoll reply has been @@ -618,36 +611,36 @@ static void adb_via_poll(void *opaque) */ adb_autopoll_block(adb_bus); - m->adb_data_in_index = 0; - m->adb_data_out_index = 0; - olen = adb_poll(adb_bus, obuf, adb_bus->autopoll_mask); - - if (olen > 0) { - /* Autopoll response */ - *data = obuf[0]; - olen--; - memcpy(m->adb_data_in, &obuf[1], olen); - m->adb_data_in_size = olen; + if (m->adb_data_in_size > 0 && m->adb_data_in_index == 0) { + /* + * For older Linux kernels that switch to IDLE mode after sending the + * ADB command, detect if there is an existing response and return that + * as a a "fake" autopoll reply or bus timeout accordingly + */ + *data = m->adb_data_out[0]; + olen = m->adb_data_in_size; s->b &= ~VIA1B_vADBInt; qemu_irq_raise(m->adb_data_ready); - } else if (olen < 0) { - /* Bus timeout (device does not exist) */ - *data = 0xff; - s->b |= VIA1B_vADBInt; - adb_autopoll_unblock(adb_bus); } else { - pending = adb_bus->pending & ~(1 << (m->adb_autopoll_cmd >> 4)); + /* + * Otherwise poll as normal + */ + m->adb_data_in_index = 0; + m->adb_data_out_index = 0; + olen = adb_poll(adb_bus, obuf, adb_bus->autopoll_mask); + + if (olen > 0) { + /* Autopoll response */ + *data = obuf[0]; + olen--; + memcpy(m->adb_data_in, &obuf[1], olen); + m->adb_data_in_size = olen; - if (pending) { - /* - * Bus timeout (device exists but another device has data). Block - * autopoll so the OS can read out the first EVEN and first ODD - * byte to determine bus timeout and SRQ status - */ - *data = m->adb_autopoll_cmd; s->b &= ~VIA1B_vADBInt; - + qemu_irq_raise(m->adb_data_ready); + } else { + *data = m->adb_autopoll_cmd; obuf[0] = 0xff; obuf[1] = 0xff; olen = 2; @@ -655,12 +648,8 @@ static void adb_via_poll(void *opaque) memcpy(m->adb_data_in, obuf, olen); m->adb_data_in_size = olen; + s->b &= ~VIA1B_vADBInt; qemu_irq_raise(m->adb_data_ready); - } else { - /* Bus timeout (device exists but no other device has data) */ - *data = 0; - s->b |= VIA1B_vADBInt; - adb_autopoll_unblock(adb_bus); } } @@ -783,27 +772,8 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) return; case ADB_STATE_IDLE: - /* - * Since adb_request() will have already consumed the data from the - * device, we must detect this extra state change and re-inject the - * reponse as either a "fake" autopoll reply or bus timeout - * accordingly - */ - if (s->adb_data_in_index == 0) { - if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { - *data = 0xff; - ms->b |= VIA1B_vADBInt; - qemu_irq_raise(s->adb_data_ready); - } else if (s->adb_data_in_size > 0) { - adb_bus->status = ADB_STATUS_POLLREPLY; - *data = s->adb_autopoll_cmd; - ms->b &= ~VIA1B_vADBInt; - qemu_irq_raise(s->adb_data_ready); - } - } else { - ms->b |= VIA1B_vADBInt; - adb_autopoll_unblock(adb_bus); - } + ms->b |= VIA1B_vADBInt; + adb_autopoll_unblock(adb_bus); trace_via1_adb_receive("IDLE", *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", adb_bus->status, @@ -816,33 +786,37 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) switch (s->adb_data_in_index) { case 0: /* First EVEN byte: vADBInt indicates bus timeout */ - trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", - *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", - adb_bus->status, s->adb_data_in_index, - s->adb_data_in_size); - - *data = s->adb_data_in[s->adb_data_in_index++]; + *data = s->adb_data_in[s->adb_data_in_index]; if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { ms->b &= ~VIA1B_vADBInt; } else { ms->b |= VIA1B_vADBInt; } - break; - case 1: - /* First ODD byte: vADBInt indicates SRQ */ trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", adb_bus->status, s->adb_data_in_index, s->adb_data_in_size); - *data = s->adb_data_in[s->adb_data_in_index++]; + s->adb_data_in_index++; + break; + + case 1: + /* First ODD byte: vADBInt indicates SRQ */ + *data = s->adb_data_in[s->adb_data_in_index]; pending = adb_bus->pending & ~(1 << (s->adb_autopoll_cmd >> 4)); if (pending) { ms->b &= ~VIA1B_vADBInt; } else { ms->b |= VIA1B_vADBInt; } + + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + s->adb_data_in_index++; break; default: @@ -852,14 +826,9 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) * end of the poll reply, so provide these extra bytes below to * keep it happy */ - trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", - *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", - adb_bus->status, s->adb_data_in_index, - s->adb_data_in_size); - if (s->adb_data_in_index < s->adb_data_in_size) { /* Next data byte */ - *data = s->adb_data_in[s->adb_data_in_index++]; + *data = s->adb_data_in[s->adb_data_in_index]; ms->b |= VIA1B_vADBInt; } else if (s->adb_data_in_index == s->adb_data_in_size) { if (adb_bus->status & ADB_STATUS_BUSTIMEOUT) { @@ -869,7 +838,6 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) /* Return 0x0 after reply */ *data = 0; } - s->adb_data_in_index++; ms->b &= ~VIA1B_vADBInt; } else { /* Bus timeout (no more data) */ @@ -878,6 +846,15 @@ static void adb_via_receive(MacVIAState *s, int state, uint8_t *data) adb_bus->status = 0; adb_autopoll_unblock(adb_bus); } + + trace_via1_adb_receive(state == ADB_STATE_EVEN ? "EVEN" : " ODD", + *data, (ms->b & VIA1B_vADBInt) ? "+" : "-", + adb_bus->status, s->adb_data_in_index, + s->adb_data_in_size); + + if (s->adb_data_in_index <= s->adb_data_in_size) { + s->adb_data_in_index++; + } break; } @@ -910,21 +887,6 @@ static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size) { MOS6522Q800VIA1State *s = MOS6522_Q800_VIA1(opaque); MOS6522State *ms = MOS6522(s); - int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); - - /* - * If IRQs are disabled, timers are disabled, but we need to update - * VIA1_IRQ_VBLANK and VIA1_IRQ_ONE_SECOND bits in the IFR - */ - - if (now >= s->next_VBL) { - ms->ifr |= VIA1_IRQ_VBLANK; - via1_VBL_update(s); - } - if (now >= s->next_second) { - ms->ifr |= VIA1_IRQ_ONE_SECOND; - via1_one_second_update(s); - } addr = (addr >> 9) & 0xf; return mos6522_read(ms, addr, size); @@ -948,9 +910,6 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val, v1s->last_b = ms->b; break; } - - via1_one_second_update(v1s); - via1_VBL_update(v1s); } static const MemoryRegionOps mos6522_q800_via1_ops = { @@ -959,7 +918,7 @@ static const MemoryRegionOps mos6522_q800_via1_ops = { .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 1, + .max_access_size = 4, }, }; @@ -988,23 +947,17 @@ static const MemoryRegionOps mos6522_q800_via2_ops = { .endianness = DEVICE_BIG_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 1, + .max_access_size = 4, }, }; static void mac_via_reset(DeviceState *dev) { MacVIAState *m = MAC_VIA(dev); - MOS6522Q800VIA1State *v1s = &m->mos6522_via1; ADBBusState *adb_bus = &m->adb_bus; adb_set_autopoll_enabled(adb_bus, true); - timer_del(v1s->VBL_timer); - v1s->next_VBL = 0; - timer_del(v1s->one_second_timer); - v1s->next_second = 0; - m->cmd = REG_EMPTY; m->alt = REG_EMPTY; } @@ -1043,8 +996,11 @@ static void mac_via_realize(DeviceState *dev, Error **errp) m->mos6522_via1.one_second_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, via1_one_second, &m->mos6522_via1); - m->mos6522_via1.VBL_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, via1_VBL, - &m->mos6522_via1); + via1_one_second_update(&m->mos6522_via1); + m->mos6522_via1.sixty_hz_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + via1_sixty_hz, + &m->mos6522_via1); + via1_sixty_hz_update(&m->mos6522_via1); qemu_get_timedate(&tm, 0); m->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; @@ -1133,8 +1089,8 @@ static const VMStateDescription vmstate_mac_via = { VMSTATE_BUFFER(mos6522_via1.PRAM, MacVIAState), VMSTATE_TIMER_PTR(mos6522_via1.one_second_timer, MacVIAState), VMSTATE_INT64(mos6522_via1.next_second, MacVIAState), - VMSTATE_TIMER_PTR(mos6522_via1.VBL_timer, MacVIAState), - VMSTATE_INT64(mos6522_via1.next_VBL, MacVIAState), + VMSTATE_TIMER_PTR(mos6522_via1.sixty_hz_timer, MacVIAState), + VMSTATE_INT64(mos6522_via1.next_sixty_hz, MacVIAState), VMSTATE_STRUCT(mos6522_via2.parent_obj, MacVIAState, 0, vmstate_mos6522, MOS6522State), /* RTC */ diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 7a2b0d031a..21034dc60a 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -24,6 +24,9 @@ softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) # Mac devices softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) +# virt devices +softmmu_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) + # RISC-V devices softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b87d0b4c90..d0a89eb059 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -233,8 +233,8 @@ via1_rtc_cmd_test_write(int value) "value=0x%02x" via1_rtc_cmd_wprotect_write(int value) "value=0x%02x" via1_rtc_cmd_pram_read(int addr, int value) "addr=%u value=0x%02x" via1_rtc_cmd_pram_write(int addr, int value) "addr=%u value=0x%02x" -via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" -via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=%d value=0x%02x" +via1_rtc_cmd_pram_sect_read(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=0x%x value=0x%02x" +via1_rtc_cmd_pram_sect_write(int sector, int offset, int addr, int value) "sector=%u offset=%u addr=0x%x value=0x%02x" via1_adb_send(const char *state, uint8_t data, const char *vadbint) "state %s data=0x%02x vADBInt=%s" via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d" @@ -255,3 +255,10 @@ pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, u bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 bcm2835_cprman_write_invalid_magic(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 + +# virt_ctrl.c +virt_ctrl_read(void *dev, unsigned int addr, unsigned int size, uint64_t value) "ctrl: %p reg: 0x%02x size: %d value: 0x%"PRIx64 +virt_ctrl_write(void *dev, unsigned int addr, unsigned int size, uint64_t value) "ctrl: %p reg: 0x%02x size: %d value: 0x%"PRIx64 +virt_ctrl_reset(void *dev) "ctrl: %p" +virt_ctrl_realize(void *dev) "ctrl: %p" +virt_ctrl_instance_init(void *dev) "ctrl: %p" diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c new file mode 100644 index 0000000000..2ea01bd7a1 --- /dev/null +++ b/hw/misc/virt_ctrl.c @@ -0,0 +1,151 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * Virt system Controller + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "trace.h" +#include "sysemu/runstate.h" +#include "hw/misc/virt_ctrl.h" + +enum { + REG_FEATURES = 0x00, + REG_CMD = 0x04, +}; + +#define FEAT_POWER_CTRL 0x00000001 + +enum { + CMD_NOOP, + CMD_RESET, + CMD_HALT, + CMD_PANIC, +}; + +static uint64_t virt_ctrl_read(void *opaque, hwaddr addr, unsigned size) +{ + VirtCtrlState *s = opaque; + uint64_t value = 0; + + switch (addr) { + case REG_FEATURES: + value = FEAT_POWER_CTRL; + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n", + __func__, addr); + break; + } + + trace_virt_ctrl_write(s, addr, size, value); + + return value; +} + +static void virt_ctrl_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + VirtCtrlState *s = opaque; + + trace_virt_ctrl_write(s, addr, size, value); + + switch (addr) { + case REG_CMD: + switch (value) { + case CMD_NOOP: + break; + case CMD_RESET: + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + break; + case CMD_HALT: + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + break; + case CMD_PANIC: + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_PANIC); + break; + } + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n", + __func__, addr); + break; + } +} + +static const MemoryRegionOps virt_ctrl_ops = { + .read = virt_ctrl_read, + .write = virt_ctrl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.max_access_size = 4, + .impl.max_access_size = 4, +}; + +static void virt_ctrl_reset(DeviceState *dev) +{ + VirtCtrlState *s = VIRT_CTRL(dev); + + trace_virt_ctrl_reset(s); +} + +static void virt_ctrl_realize(DeviceState *dev, Error **errp) +{ + VirtCtrlState *s = VIRT_CTRL(dev); + + trace_virt_ctrl_instance_init(s); + + memory_region_init_io(&s->iomem, OBJECT(s), &virt_ctrl_ops, s, + "virt-ctrl", 0x100); +} + +static const VMStateDescription vmstate_virt_ctrl = { + .name = "virt-ctrl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(irq_enabled, VirtCtrlState), + VMSTATE_END_OF_LIST() + } +}; + +static void virt_ctrl_instance_init(Object *obj) +{ + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + VirtCtrlState *s = VIRT_CTRL(obj); + + trace_virt_ctrl_instance_init(s); + + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); +} + +static void virt_ctrl_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->reset = virt_ctrl_reset; + dc->realize = virt_ctrl_realize; + dc->vmsd = &vmstate_virt_ctrl; +} + +static const TypeInfo virt_ctrl_info = { + .name = TYPE_VIRT_CTRL, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = virt_ctrl_class_init, + .instance_init = virt_ctrl_instance_init, + .instance_size = sizeof(VirtCtrlState), +}; + +static void virt_ctrl_register_types(void) +{ + type_register_static(&virt_ctrl_info); +} + +type_init(virt_ctrl_register_types) diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 30352df00e..24537ffcbd 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -212,6 +212,11 @@ static int spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu, { struct SpaprTceTable *tbl = container_of(iommu, SpaprTceTable, iommu); + if (new & IOMMU_NOTIFIER_DEVIOTLB_UNMAP) { + error_setg(errp, "spart_tce does not support dev-iotlb yet"); + return -EINVAL; + } + if (old == IOMMU_NOTIFIER_NONE && new != IOMMU_NOTIFIER_NONE) { spapr_tce_set_need_vfio(tbl, true); } else if (old != IOMMU_NOTIFIER_NONE && new == IOMMU_NOTIFIER_NONE) { diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index ead4f222d5..2a153fa8c9 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -29,14 +29,11 @@ */ bool s390_pci_update_dma_avail(int fd, unsigned int *avail) { - g_autofree struct vfio_iommu_type1_info *info; - uint32_t argsz; + uint32_t argsz = sizeof(struct vfio_iommu_type1_info); + g_autofree struct vfio_iommu_type1_info *info = g_malloc0(argsz); assert(avail); - argsz = sizeof(struct vfio_iommu_type1_info); - info = g_malloc0(argsz); - /* * If the specified argsz is not large enough to contain all capabilities * it will be updated upon return from the ioctl. Retry until we have @@ -230,7 +227,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, */ void s390_pci_get_clp_info(S390PCIBusDevice *pbdev) { - g_autofree struct vfio_device_info *info; + g_autofree struct vfio_device_info *info = NULL; VFIOPCIDevice *vfio_pci; uint32_t argsz; int fd; diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index c01ee2c72a..c8388ea432 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -324,7 +324,7 @@ static void pit_post_load(PITCommonState *s) { PITChannelState *sc = &s->channels[0]; - if (sc->next_transition_time != -1) { + if (sc->next_transition_time != -1 && !sc->irq_disabled) { timer_mod(sc->irq_timer, sc->next_transition_time); } else { timer_del(sc->irq_timer); diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 6ff1daa763..ae5654fcdb 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -311,7 +311,7 @@ bool vfio_mig_active(void) return true; } -static bool vfio_devices_all_saving(VFIOContainer *container) +static bool vfio_devices_all_dirty_tracking(VFIOContainer *container) { VFIOGroup *group; VFIODevice *vbasedev; @@ -329,13 +329,8 @@ static bool vfio_devices_all_saving(VFIOContainer *container) return false; } - if (migration->device_state & VFIO_DEVICE_STATE_SAVING) { - if ((vbasedev->pre_copy_dirty_page_tracking == ON_OFF_AUTO_OFF) - && (migration->device_state & VFIO_DEVICE_STATE_RUNNING)) { - return false; - } - continue; - } else { + if ((vbasedev->pre_copy_dirty_page_tracking == ON_OFF_AUTO_OFF) + && (migration->device_state & VFIO_DEVICE_STATE_RUNNING)) { return false; } } @@ -378,7 +373,7 @@ static int vfio_dma_unmap_bitmap(VFIOContainer *container, { struct vfio_iommu_type1_dma_unmap *unmap; struct vfio_bitmap *bitmap; - uint64_t pages = TARGET_PAGE_ALIGN(size) >> TARGET_PAGE_BITS; + uint64_t pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size; int ret; unmap = g_malloc0(sizeof(*unmap) + sizeof(*bitmap)); @@ -390,12 +385,12 @@ static int vfio_dma_unmap_bitmap(VFIOContainer *container, bitmap = (struct vfio_bitmap *)&unmap->data; /* - * cpu_physical_memory_set_dirty_lebitmap() expects pages in bitmap of - * TARGET_PAGE_SIZE to mark those dirty. Hence set bitmap_pgsize to - * TARGET_PAGE_SIZE. + * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * qemu_real_host_page_size to mark those dirty. Hence set bitmap_pgsize + * to qemu_real_host_page_size. */ - bitmap->pgsize = TARGET_PAGE_SIZE; + bitmap->pgsize = qemu_real_host_page_size; bitmap->size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / BITS_PER_BYTE; @@ -674,16 +669,17 @@ static void vfio_listener_region_add(MemoryListener *listener, return; } - if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { + if (unlikely((section->offset_within_address_space & + ~qemu_real_host_page_mask) != + (section->offset_within_region & ~qemu_real_host_page_mask))) { error_report("%s received unaligned region", __func__); return; } - iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); if (int128_ge(int128_make64(iova), llend)) { return; @@ -787,7 +783,7 @@ static void vfio_listener_region_add(MemoryListener *listener, iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, MEMTXATTRS_UNSPECIFIED); iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, - IOMMU_NOTIFIER_ALL, + IOMMU_NOTIFIER_IOTLB_EVENTS, section->offset_within_region, int128_get64(llend), iommu_idx); @@ -892,8 +888,9 @@ static void vfio_listener_region_del(MemoryListener *listener, return; } - if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { + if (unlikely((section->offset_within_address_space & + ~qemu_real_host_page_mask) != + (section->offset_within_region & ~qemu_real_host_page_mask))) { error_report("%s received unaligned region", __func__); return; } @@ -921,10 +918,10 @@ static void vfio_listener_region_del(MemoryListener *listener, */ } - iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); llend = int128_make64(section->offset_within_address_space); llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); + llend = int128_and(llend, int128_exts64(qemu_real_host_page_mask)); if (int128_ge(int128_make64(iova), llend)) { return; @@ -987,6 +984,40 @@ static void vfio_listener_region_del(MemoryListener *listener, } } +static void vfio_set_dirty_page_tracking(VFIOContainer *container, bool start) +{ + int ret; + struct vfio_iommu_type1_dirty_bitmap dirty = { + .argsz = sizeof(dirty), + }; + + if (start) { + dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START; + } else { + dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP; + } + + ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty); + if (ret) { + error_report("Failed to set dirty tracking flag 0x%x errno: %d", + dirty.flags, errno); + } +} + +static void vfio_listener_log_global_start(MemoryListener *listener) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, listener); + + vfio_set_dirty_page_tracking(container, true); +} + +static void vfio_listener_log_global_stop(MemoryListener *listener) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, listener); + + vfio_set_dirty_page_tracking(container, false); +} + static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, uint64_t size, ram_addr_t ram_addr) { @@ -1004,13 +1035,13 @@ static int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova, range->size = size; /* - * cpu_physical_memory_set_dirty_lebitmap() expects pages in bitmap of - * TARGET_PAGE_SIZE to mark those dirty. Hence set bitmap's pgsize to - * TARGET_PAGE_SIZE. + * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * qemu_real_host_page_size to mark those dirty. Hence set bitmap's pgsize + * to qemu_real_host_page_size. */ - range->bitmap.pgsize = TARGET_PAGE_SIZE; + range->bitmap.pgsize = qemu_real_host_page_size; - pages = TARGET_PAGE_ALIGN(range->size) >> TARGET_PAGE_BITS; + pages = REAL_HOST_PAGE_ALIGN(range->size) / qemu_real_host_page_size; range->bitmap.size = ROUND_UP(pages, sizeof(__u64) * BITS_PER_BYTE) / BITS_PER_BYTE; range->bitmap.data = g_try_malloc0(range->bitmap.size); @@ -1114,11 +1145,11 @@ static int vfio_sync_dirty_bitmap(VFIOContainer *container, section->offset_within_region; return vfio_get_dirty_bitmap(container, - TARGET_PAGE_ALIGN(section->offset_within_address_space), - int128_get64(section->size), ram_addr); + REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), + int128_get64(section->size), ram_addr); } -static void vfio_listerner_log_sync(MemoryListener *listener, +static void vfio_listener_log_sync(MemoryListener *listener, MemoryRegionSection *section) { VFIOContainer *container = container_of(listener, VFIOContainer, listener); @@ -1128,7 +1159,7 @@ static void vfio_listerner_log_sync(MemoryListener *listener, return; } - if (vfio_devices_all_saving(container)) { + if (vfio_devices_all_dirty_tracking(container)) { vfio_sync_dirty_bitmap(container, section); } } @@ -1136,7 +1167,9 @@ static void vfio_listerner_log_sync(MemoryListener *listener, static const MemoryListener vfio_memory_listener = { .region_add = vfio_listener_region_add, .region_del = vfio_listener_region_del, - .log_sync = vfio_listerner_log_sync, + .log_global_start = vfio_listener_log_global_start, + .log_global_stop = vfio_listener_log_global_stop, + .log_sync = vfio_listener_log_sync, }; static void vfio_listener_release(VFIOContainer *container) @@ -1655,10 +1688,10 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, header); /* - * cpu_physical_memory_set_dirty_lebitmap() expects pages in bitmap of - * TARGET_PAGE_SIZE to mark those dirty. + * cpu_physical_memory_set_dirty_lebitmap() supports pages in bitmap of + * qemu_real_host_page_size to mark those dirty. */ - if (cap_mig->pgsize_bitmap & TARGET_PAGE_SIZE) { + if (cap_mig->pgsize_bitmap & qemu_real_host_page_size) { container->dirty_pages_supported = true; container->max_dirty_bitmap_size = cap_mig->max_dirty_bitmap_size; container->dirty_pgsizes = cap_mig->pgsize_bitmap; diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 134bdccc4f..384576cfc0 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -395,40 +395,10 @@ static int vfio_load_device_config_state(QEMUFile *f, void *opaque) return qemu_file_get_error(f); } -static int vfio_set_dirty_page_tracking(VFIODevice *vbasedev, bool start) -{ - int ret; - VFIOMigration *migration = vbasedev->migration; - VFIOContainer *container = vbasedev->group->container; - struct vfio_iommu_type1_dirty_bitmap dirty = { - .argsz = sizeof(dirty), - }; - - if (start) { - if (migration->device_state & VFIO_DEVICE_STATE_SAVING) { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START; - } else { - return -EINVAL; - } - } else { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP; - } - - ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty); - if (ret) { - error_report("Failed to set dirty tracking flag 0x%x errno: %d", - dirty.flags, errno); - return -errno; - } - return ret; -} - static void vfio_migration_cleanup(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; - vfio_set_dirty_page_tracking(vbasedev, false); - if (migration->region.mmaps) { vfio_region_unmap(&migration->region); } @@ -469,11 +439,6 @@ static int vfio_save_setup(QEMUFile *f, void *opaque) return ret; } - ret = vfio_set_dirty_page_tracking(vbasedev, true); - if (ret) { - return ret; - } - qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); ret = qemu_file_get_error(f); @@ -575,11 +540,6 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) return ret; } - ret = vfio_save_device_config_state(f, opaque); - if (ret) { - return ret; - } - ret = vfio_update_pending(vbasedev); if (ret) { return ret; @@ -620,6 +580,19 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque) return ret; } +static void vfio_save_state(QEMUFile *f, void *opaque) +{ + VFIODevice *vbasedev = opaque; + int ret; + + ret = vfio_save_device_config_state(f, opaque); + if (ret) { + error_report("%s: Failed to save device config space", + vbasedev->name); + qemu_file_set_error(f, ret); + } +} + static int vfio_load_setup(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; @@ -670,11 +643,7 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id) switch (data) { case VFIO_MIG_FLAG_DEV_CONFIG_STATE: { - ret = vfio_load_device_config_state(f, opaque); - if (ret) { - return ret; - } - break; + return vfio_load_device_config_state(f, opaque); } case VFIO_MIG_FLAG_DEV_SETUP_STATE: { @@ -720,6 +689,7 @@ static SaveVMHandlers savevm_vfio_handlers = { .save_live_pending = vfio_save_pending, .save_live_iterate = vfio_save_iterate, .save_live_complete_precopy = vfio_save_complete_precopy, + .save_state = vfio_save_state, .load_setup = vfio_load_setup, .load_cleanup = vfio_load_cleanup, .load_state = vfio_load_state, @@ -857,7 +827,8 @@ static int vfio_migration_init(VFIODevice *vbasedev, register_savevm_live(id, VMSTATE_INSTANCE_ID_ANY, 1, &savevm_vfio_handlers, vbasedev); - migration->vm_state = qemu_add_vm_change_state_handler(vfio_vmstate_change, + migration->vm_state = qdev_add_vm_change_state_handler(vbasedev->dev, + vfio_vmstate_change, vbasedev); migration->migration_state.notify = vfio_migration_state_notifier; add_migration_state_change_notifier(&migration->migration_state); diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index c5c4c61d01..b90cf3d37c 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -44,19 +44,19 @@ static const struct { uint32_t vendor; uint32_t device; -} romblacklist[] = { +} rom_denylist[] = { { 0x14e4, 0x168e }, /* Broadcom BCM 57810 */ }; -bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev) +bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev) { int i; - for (i = 0 ; i < ARRAY_SIZE(romblacklist); i++) { - if (vfio_pci_is(vdev, romblacklist[i].vendor, romblacklist[i].device)) { - trace_vfio_quirk_rom_blacklisted(vdev->vbasedev.name, - romblacklist[i].vendor, - romblacklist[i].device); + for (i = 0 ; i < ARRAY_SIZE(rom_denylist); i++) { + if (vfio_pci_is(vdev, rom_denylist[i].vendor, rom_denylist[i].device)) { + trace_vfio_quirk_rom_in_denylist(vdev->vbasedev.name, + rom_denylist[i].vendor, + rom_denylist[i].device); return true; } } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index f74be78209..5c65aa0a98 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -569,6 +569,9 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) static void vfio_msix_enable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = &vdev->pdev; + unsigned int nr, max_vec = 0; + vfio_disable_interrupts(vdev); vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries); @@ -587,11 +590,22 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) * triggering to userspace, then immediately release the vector, leaving * the physical device with no vectors enabled, but MSI-X enabled, just * like the guest view. + * If there are already unmasked vectors (in migration resume phase and + * some guest startups) which will be enabled soon, we can allocate all + * of them here to avoid inefficiently disabling and enabling vectors + * repeatedly later. */ - vfio_msix_vector_do_use(&vdev->pdev, 0, NULL, NULL); - vfio_msix_vector_release(&vdev->pdev, 0); + if (!pdev->msix_function_masked) { + for (nr = 0; nr < msix_nr_vectors_allocated(pdev); nr++) { + if (!msix_is_masked(pdev, nr)) { + max_vec = nr; + } + } + } + vfio_msix_vector_do_use(pdev, max_vec, NULL, NULL); + vfio_msix_vector_release(pdev, max_vec); - if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + if (msix_set_vector_notifiers(pdev, vfio_msix_vector_use, vfio_msix_vector_release, NULL)) { error_report("vfio: msix_set_vector_notifiers failed"); } @@ -900,7 +914,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { /* Since pci handles romfile, just print a message and return */ - if (vfio_blacklist_opt_rom(vdev) && vdev->pdev.romfile) { + if (vfio_opt_rom_in_denylist(vdev) && vdev->pdev.romfile) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); @@ -927,7 +941,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) return; } - if (vfio_blacklist_opt_rom(vdev)) { + if (vfio_opt_rom_in_denylist(vdev)) { if (dev->opts && qemu_opt_get(dev->opts, "rombar")) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 1574ef983f..64777516d1 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -197,7 +197,7 @@ void vfio_pci_write_config(PCIDevice *pdev, uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); -bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev); +bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev); void vfio_vga_quirk_setup(VFIOPCIDevice *vdev); void vfio_vga_quirk_exit(VFIOPCIDevice *vdev); void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev); diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index c0e75f24b7..079f53acf2 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -49,7 +49,7 @@ vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s 0x%04x" vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s 0x%04x" # pci-quirks.c -vfio_quirk_rom_blacklisted(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x" +vfio_quirk_rom_in_denylist(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x" vfio_quirk_generic_window_address_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 vfio_quirk_generic_window_data_read(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 vfio_quirk_generic_window_data_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 diff --git a/include/hw/char/goldfish_tty.h b/include/hw/char/goldfish_tty.h new file mode 100644 index 0000000000..b9dd67362a --- /dev/null +++ b/include/hw/char/goldfish_tty.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * Goldfish TTY + * + * (c) 2020 Laurent Vivier <laurent@vivier.eu> + * + */ + +#ifndef HW_CHAR_GOLDFISH_TTY_H +#define HW_CHAR_GOLDFISH_TTY_H + +#include "qemu/fifo8.h" +#include "chardev/char-fe.h" + +#define TYPE_GOLDFISH_TTY "goldfish_tty" +OBJECT_DECLARE_SIMPLE_TYPE(GoldfishTTYState, GOLDFISH_TTY) + +#define GOLFISH_TTY_BUFFER_SIZE 128 + +struct GoldfishTTYState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + CharBackend chr; + + uint32_t data_len; + uint64_t data_ptr; + bool int_enabled; + + Fifo8 rx_fifo; +}; + +#endif diff --git a/include/hw/intc/goldfish_pic.h b/include/hw/intc/goldfish_pic.h new file mode 100644 index 0000000000..ad13ab37fc --- /dev/null +++ b/include/hw/intc/goldfish_pic.h @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * Goldfish PIC + * + * (c) 2020 Laurent Vivier <laurent@vivier.eu> + * + */ + +#ifndef HW_INTC_GOLDFISH_PIC_H +#define HW_INTC_GOLDFISH_PIC_H + +#define TYPE_GOLDFISH_PIC "goldfish_pic" +OBJECT_DECLARE_SIMPLE_TYPE(GoldfishPICState, GOLDFISH_PIC) + +#define GOLDFISH_PIC_IRQ_NB 32 + +struct GoldfishPICState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + uint32_t pending; + uint32_t enabled; + + /* statistics */ + uint64_t stats_irq_count[32]; + /* for tracing */ + uint8_t idx; +}; + +#endif diff --git a/include/hw/intc/m68k_irqc.h b/include/hw/intc/m68k_irqc.h new file mode 100644 index 0000000000..dbcfcfc2e0 --- /dev/null +++ b/include/hw/intc/m68k_irqc.h @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * QEMU Motorola 680x0 IRQ Controller + * + * (c) 2020 Laurent Vivier <laurent@vivier.eu> + * + */ + +#ifndef M68K_IRQC_H +#define M68K_IRQC_H + +#include "hw/sysbus.h" + +#define TYPE_M68K_IRQC "m68k-irq-controller" +#define M68K_IRQC(obj) OBJECT_CHECK(M68KIRQCState, (obj), \ + TYPE_M68K_IRQC) + +#define M68K_IRQC_AUTOVECTOR_BASE 25 + +enum { + M68K_IRQC_LEVEL_1 = 0, + M68K_IRQC_LEVEL_2, + M68K_IRQC_LEVEL_3, + M68K_IRQC_LEVEL_4, + M68K_IRQC_LEVEL_5, + M68K_IRQC_LEVEL_6, + M68K_IRQC_LEVEL_7, +}; +#define M68K_IRQC_LEVEL_NUM (M68K_IRQC_LEVEL_7 - M68K_IRQC_LEVEL_1 + 1) + +typedef struct M68KIRQCState { + SysBusDevice parent_obj; + + uint8_t ipr; + + /* statistics */ + uint64_t stats_irq_count[M68K_IRQC_LEVEL_NUM]; +} M68KIRQCState; + +#endif diff --git a/include/hw/mem/sparse-mem.h b/include/hw/mem/sparse-mem.h new file mode 100644 index 0000000000..f9863b154b --- /dev/null +++ b/include/hw/mem/sparse-mem.h @@ -0,0 +1,19 @@ +/* + * A sparse memory device. Useful for fuzzing + * + * Copyright Red Hat Inc., 2021 + * + * Authors: + * Alexander Bulekov <alxndr@bu.edu> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef SPARSE_MEM_H +#define SPARSE_MEM_H +#define TYPE_SPARSE_MEM "sparse-mem" + +MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length); + +#endif diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index a59f0bd422..3058b30685 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -17,7 +17,7 @@ /* VIA 1 */ #define VIA1_IRQ_ONE_SECOND_BIT 0 -#define VIA1_IRQ_VBLANK_BIT 1 +#define VIA1_IRQ_60HZ_BIT 1 #define VIA1_IRQ_ADB_READY_BIT 2 #define VIA1_IRQ_ADB_DATA_BIT 3 #define VIA1_IRQ_ADB_CLOCK_BIT 4 @@ -25,7 +25,7 @@ #define VIA1_IRQ_NB 8 #define VIA1_IRQ_ONE_SECOND (1 << VIA1_IRQ_ONE_SECOND_BIT) -#define VIA1_IRQ_VBLANK (1 << VIA1_IRQ_VBLANK_BIT) +#define VIA1_IRQ_60HZ (1 << VIA1_IRQ_60HZ_BIT) #define VIA1_IRQ_ADB_READY (1 << VIA1_IRQ_ADB_READY_BIT) #define VIA1_IRQ_ADB_DATA (1 << VIA1_IRQ_ADB_DATA_BIT) #define VIA1_IRQ_ADB_CLOCK (1 << VIA1_IRQ_ADB_CLOCK_BIT) @@ -45,8 +45,8 @@ struct MOS6522Q800VIA1State { /* external timers */ QEMUTimer *one_second_timer; int64_t next_second; - QEMUTimer *VBL_timer; - int64_t next_VBL; + QEMUTimer *sixty_hz_timer; + int64_t next_sixty_hz; }; diff --git a/include/hw/misc/virt_ctrl.h b/include/hw/misc/virt_ctrl.h new file mode 100644 index 0000000000..edfadc4695 --- /dev/null +++ b/include/hw/misc/virt_ctrl.h @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifer: GPL-2.0-or-later + * + * Virt system Controller + */ + +#ifndef VIRT_CTRL_H +#define VIRT_CTRL_H + +#define TYPE_VIRT_CTRL "virt-ctrl" +OBJECT_DECLARE_SIMPLE_TYPE(VirtCtrlState, VIRT_CTRL) + +struct VirtCtrlState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + uint32_t irq_enabled; +}; + +#endif diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 1486cac3ef..135dfdef71 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -36,6 +36,7 @@ typedef struct QmpCommand QmpCommandOptions options; QTAILQ_ENTRY(QmpCommand) node; bool enabled; + const char *disable_reason; } QmpCommand; typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; @@ -44,7 +45,8 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name); -void qmp_disable_command(QmpCommandList *cmds, const char *name); +void qmp_disable_command(QmpCommandList *cmds, const char *name, + const char *err_msg); void qmp_enable_command(QmpCommandList *cmds, const char *name); bool qmp_command_is_enabled(const QmpCommand *cmd); diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 5e76e3f8c2..301fa47b42 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -629,8 +629,10 @@ void timer_del(QEMUTimer *ts); */ static inline void timer_free(QEMUTimer *ts) { - timer_del(ts); - g_free(ts); + if (ts) { + timer_del(ts); + g_free(ts); + } } /** diff --git a/include/standard-headers/asm-m68k/bootinfo-virt.h b/include/standard-headers/asm-m68k/bootinfo-virt.h new file mode 100644 index 0000000000..81be1e0924 --- /dev/null +++ b/include/standard-headers/asm-m68k/bootinfo-virt.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* +** asm/bootinfo-virt.h -- Virtual-m68k-specific boot information definitions +*/ + +#ifndef _UAPI_ASM_M68K_BOOTINFO_VIRT_H +#define _UAPI_ASM_M68K_BOOTINFO_VIRT_H + +#define BI_VIRT_QEMU_VERSION 0x8000 +#define BI_VIRT_GF_PIC_BASE 0x8001 +#define BI_VIRT_GF_RTC_BASE 0x8002 +#define BI_VIRT_GF_TTY_BASE 0x8003 +#define BI_VIRT_VIRTIO_BASE 0x8004 +#define BI_VIRT_CTRL_BASE 0x8005 + +#define VIRT_BOOTI_VERSION MK_BI_VERSION(2, 0) + +#endif /* _UAPI_ASM_M68K_BOOTINFO_MAC_H */ diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 687c598be9..a1ab1ee12d 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -249,10 +249,6 @@ int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_trap); int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); -/* interface with exec.c */ - -void phys_mem_set_alloc(void *(*alloc)(size_t, uint64_t *align, bool shared)); - /* internal API */ int kvm_ioctl(KVMState *s, int type, ...); diff --git a/migration/migration.c b/migration/migration.c index a5ddf43559..36768391b6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -323,7 +323,7 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, int ret = 0; trace_migrate_send_rp_message((int)message_type, len); - qemu_mutex_lock(&mis->rp_mutex); + QEMU_LOCK_GUARD(&mis->rp_mutex); /* * It's possible that the file handle got lost due to network @@ -331,7 +331,7 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, */ if (!mis->to_src_file) { ret = -EIO; - goto error; + return ret; } qemu_put_be16(mis->to_src_file, (unsigned int)message_type); @@ -342,8 +342,6 @@ static int migrate_send_rp_message(MigrationIncomingState *mis, /* It's possible that qemu file got error during sending */ ret = qemu_file_get_error(mis->to_src_file); -error: - qemu_mutex_unlock(&mis->rp_mutex); return ret; } diff --git a/migration/multifd.c b/migration/multifd.c index 1a1e589064..03527c564c 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -739,7 +739,16 @@ static void multifd_tls_outgoing_handshake(QIOTask *task, } else { trace_multifd_tls_outgoing_handshake_complete(ioc); } - multifd_channel_connect(p, ioc, err); + + if (!multifd_channel_connect(p, ioc, err)) { + /* + * Error happen, mark multifd_send_thread status as 'quit' although it + * is not created, and then tell who pay attention to me. + */ + p->quit = true; + qemu_sem_post(&multifd_send_state->channels_ready); + qemu_sem_post(&p->sem_sync); + } } static void *multifd_tls_handshake_thread(void *opaque) @@ -798,9 +807,9 @@ static bool multifd_channel_connect(MultiFDSendParams *p, * function after the TLS handshake, * so we mustn't call multifd_send_thread until then */ - return false; - } else { return true; + } else { + return false; } } else { /* update for tls qio channel */ @@ -808,10 +817,10 @@ static bool multifd_channel_connect(MultiFDSendParams *p, qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, QEMU_THREAD_JOINABLE); } - return false; + return true; } - return true; + return false; } static void multifd_new_send_channel_cleanup(MultiFDSendParams *p, @@ -844,7 +853,7 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) p->c = QIO_CHANNEL(sioc); qio_channel_set_delay(p->c, false); p->running = true; - if (multifd_channel_connect(p, sioc, local_err)) { + if (!multifd_channel_connect(p, sioc, local_err)) { goto cleanup; } return; diff --git a/migration/ram.c b/migration/ram.c index 72143da0ac..52537f14ac 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -819,7 +819,7 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, { bool ret; - qemu_mutex_lock(&rs->bitmap_mutex); + QEMU_LOCK_GUARD(&rs->bitmap_mutex); /* * Clear dirty bitmap if needed. This _must_ be called before we @@ -852,7 +852,6 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, if (ret) { rs->migration_dirty_pages--; } - qemu_mutex_unlock(&rs->bitmap_mutex); return ret; } @@ -3275,7 +3274,7 @@ static void decompress_data_with_multi_threads(QEMUFile *f, int idx, thread_count; thread_count = migrate_decompress_threads(); - qemu_mutex_lock(&decomp_done_lock); + QEMU_LOCK_GUARD(&decomp_done_lock); while (true) { for (idx = 0; idx < thread_count; idx++) { if (decomp_param[idx].done) { @@ -3295,7 +3294,6 @@ static void decompress_data_with_multi_threads(QEMUFile *f, qemu_cond_wait(&decomp_done_cond, &decomp_done_lock); } } - qemu_mutex_unlock(&decomp_done_lock); } /* diff --git a/monitor/monitor.c b/monitor/monitor.c index e94f532cf5..640496e562 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -349,7 +349,7 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) evconf = &monitor_qapi_event_conf[event]; trace_monitor_protocol_event_queue(event, qdict, evconf->rate); - qemu_mutex_lock(&monitor_lock); + QEMU_LOCK_GUARD(&monitor_lock); if (!evconf->rate) { /* Unthrottled event */ @@ -391,8 +391,6 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) timer_mod_ns(evstate->timer, now + evconf->rate); } } - - qemu_mutex_unlock(&monitor_lock); } void qapi_event_emit(QAPIEvent event, QDict *qdict) @@ -447,7 +445,7 @@ static void monitor_qapi_event_handler(void *opaque) MonitorQAPIEventConf *evconf = &monitor_qapi_event_conf[evstate->event]; trace_monitor_protocol_event_handler(evstate->event, evstate->qdict); - qemu_mutex_lock(&monitor_lock); + QEMU_LOCK_GUARD(&monitor_lock); if (evstate->qdict) { int64_t now = qemu_clock_get_ns(monitor_get_event_clock()); @@ -462,8 +460,6 @@ static void monitor_qapi_event_handler(void *opaque) timer_free(evstate->timer); g_free(evstate); } - - qemu_mutex_unlock(&monitor_lock); } static unsigned int qapi_event_throttle_hash(const void *key) diff --git a/monitor/qmp.c b/monitor/qmp.c index 2326bd7f9b..2b0308f933 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -76,7 +76,7 @@ static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon) static void monitor_qmp_cleanup_queue_and_resume(MonitorQMP *mon) { - qemu_mutex_lock(&mon->qmp_queue_lock); + QEMU_LOCK_GUARD(&mon->qmp_queue_lock); /* * Same condition as in monitor_qmp_dispatcher_co(), but before @@ -103,7 +103,6 @@ static void monitor_qmp_cleanup_queue_and_resume(MonitorQMP *mon) monitor_resume(&mon->common); } - qemu_mutex_unlock(&mon->qmp_queue_lock); } void qmp_send_response(MonitorQMP *mon, const QDict *rsp) @@ -179,7 +178,7 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) Monitor *mon; MonitorQMP *qmp_mon; - qemu_mutex_lock(&monitor_lock); + QEMU_LOCK_GUARD(&monitor_lock); QTAILQ_FOREACH(mon, &mon_list, entry) { if (!monitor_is_qmp(mon)) { @@ -205,8 +204,6 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) QTAILQ_INSERT_TAIL(&mon_list, mon, entry); } - qemu_mutex_unlock(&monitor_lock); - return req_obj; } @@ -376,30 +373,30 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) req_obj->err = err; /* Protect qmp_requests and fetching its length. */ - qemu_mutex_lock(&mon->qmp_queue_lock); + WITH_QEMU_LOCK_GUARD(&mon->qmp_queue_lock) { - /* - * Suspend the monitor when we can't queue more requests after - * this one. Dequeuing in monitor_qmp_dispatcher_co() or - * monitor_qmp_cleanup_queue_and_resume() will resume it. - * Note that when OOB is disabled, we queue at most one command, - * for backward compatibility. - */ - if (!qmp_oob_enabled(mon) || - mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) { - monitor_suspend(&mon->common); - } + /* + * Suspend the monitor when we can't queue more requests after + * this one. Dequeuing in monitor_qmp_dispatcher_co() or + * monitor_qmp_cleanup_queue_and_resume() will resume it. + * Note that when OOB is disabled, we queue at most one command, + * for backward compatibility. + */ + if (!qmp_oob_enabled(mon) || + mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) { + monitor_suspend(&mon->common); + } - /* - * Put the request to the end of queue so that requests will be - * handled in time order. Ownership for req_obj, req, - * etc. will be delivered to the handler side. - */ - trace_monitor_qmp_in_band_enqueue(req_obj, mon, - mon->qmp_requests->length); - assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX); - g_queue_push_tail(mon->qmp_requests, req_obj); - qemu_mutex_unlock(&mon->qmp_queue_lock); + /* + * Put the request to the end of queue so that requests will be + * handled in time order. Ownership for req_obj, req, + * etc. will be delivered to the handler side. + */ + trace_monitor_qmp_in_band_enqueue(req_obj, mon, + mon->qmp_requests->length); + assert(mon->qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX); + g_queue_push_tail(mon->qmp_requests, req_obj); + } /* Kick the dispatcher routine */ if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc Binary files differindex def6d4cf63..e40e1d7025 100644 --- a/pc-bios/openbios-ppc +++ b/pc-bios/openbios-ppc diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 Binary files differindex 67b8b83d98..0c0aa094ec 100644 --- a/pc-bios/openbios-sparc32 +++ b/pc-bios/openbios-sparc32 diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 Binary files differindex dfa1f10bbc..a3e458517a 100644 --- a/pc-bios/openbios-sparc64 +++ b/pc-bios/openbios-sparc64 diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 0a2b20a4e4..5e597c76f7 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -157,8 +157,10 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, } if (!cmd->enabled) { error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, - "The command %s has been disabled for this instance", - command); + "Command %s has been disabled%s%s", + command, + cmd->disable_reason ? ": " : "", + cmd->disable_reason ?: ""); goto out; } if (oob && !(cmd->options & QCO_ALLOW_OOB)) { diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 58c65b5052..f78c064aae 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -43,26 +43,28 @@ const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name) } static void qmp_toggle_command(QmpCommandList *cmds, const char *name, - bool enabled) + bool enabled, const char *disable_reason) { QmpCommand *cmd; QTAILQ_FOREACH(cmd, cmds, node) { if (strcmp(cmd->name, name) == 0) { cmd->enabled = enabled; + cmd->disable_reason = disable_reason; return; } } } -void qmp_disable_command(QmpCommandList *cmds, const char *name) +void qmp_disable_command(QmpCommandList *cmds, const char *name, + const char *disable_reason) { - qmp_toggle_command(cmds, name, false); + qmp_toggle_command(cmds, name, false, disable_reason); } void qmp_enable_command(QmpCommandList *cmds, const char *name) { - qmp_toggle_command(cmds, name, true); + qmp_toggle_command(cmds, name, true, NULL); } bool qmp_command_is_enabled(const QmpCommand *cmd) diff --git a/qemu-options.hx b/qemu-options.hx index fe83ea09b2..9128dec510 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1922,7 +1922,8 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,tls-ciphers=<list>]\n" " [,tls-channel=[main|display|cursor|inputs|record|playback]]\n" " [,plaintext-channel=[main|display|cursor|inputs|record|playback]]\n" - " [,sasl=on|off][,password=<secret>][,disable-ticketing=on|off]\n" + " [,sasl=on|off][,disable-ticketing=on|off]\n" + " [,password=<string>][,password-secret=<secret-id>]\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" @@ -1947,9 +1948,17 @@ SRST ``ipv4=on|off``; \ ``ipv6=on|off``; \ ``unix=on|off`` Force using the specified IP version. - ``password=<secret>`` + ``password=<string>`` Set the password you need to authenticate. + This option is deprecated and insecure because it leaves the + password visible in the process listing. Use ``password-secret`` + instead. + + ``password-secret=<secret-id>`` + Set the ID of the ``secret`` object containing the password + you need to authenticate. + ``sasl=on|off`` Require that the client use SASL to authenticate with the spice. The exact choice of authentication method used is controlled @@ -2188,6 +2197,11 @@ SRST time to allow <protocol> password to expire immediately or never expire. + ``password-secret=<secret-id>`` + Require that password based authentication is used for client + connections, using the password provided by the ``secret`` + object identified by ``secret-id``. + ``tls-creds=ID`` Provides the ID of a set of TLS credentials to use to secure the VNC server. They will apply to both the normal VNC server socket diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 4f04868a76..779007e39b 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -292,9 +292,9 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, return false; } - if (method == GA_CHANNEL_ISA_SERIAL){ + if (method == GA_CHANNEL_ISA_SERIAL) { snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path); - }else { + } else { g_strlcpy(newpath, path, sizeof(newpath)); } @@ -307,7 +307,8 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, return false; } - if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) { + if (method == GA_CHANNEL_ISA_SERIAL + && !SetCommTimeouts(c->handle, &comTimeOut)) { g_autofree gchar *emsg = g_win32_error_message(GetLastError()); g_critical("error setting timeout for com port: %s", emsg); CloseHandle(c->handle); diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 3f18df1bb6..4299ebd96f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -110,7 +110,7 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) reopen_fd_to_null(2); execle("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", - "hypervisor initiated shutdown", (char*)NULL, environ); + "hypervisor initiated shutdown", (char *)NULL, environ); _exit(EXIT_FAILURE); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); @@ -479,7 +479,7 @@ GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh, gfh->state = RW_STATE_NEW; } - buf = g_malloc0(count+1); + buf = g_malloc0(count + 1); read_count = fread(buf, 1, count, fh); if (ferror(fh)) { error_setg_errno(errp, errno, "failed to read file"); @@ -2370,24 +2370,6 @@ error: return NULL; } -#define SYSCONF_EXACT(name, errp) sysconf_exact((name), #name, (errp)) - -static long sysconf_exact(int name, const char *name_str, Error **errp) -{ - long ret; - - errno = 0; - ret = sysconf(name); - if (ret == -1) { - if (errno == 0) { - error_setg(errp, "sysconf(%s): value indefinite", name_str); - } else { - error_setg_errno(errp, errno, "sysconf(%s)", name_str); - } - } - return ret; -} - /* Transfer online/offline status between @vcpu and the guest system. * * On input either @errp or *@errp must be NULL. @@ -2458,30 +2440,33 @@ static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) { - int64_t current; GuestLogicalProcessorList *head, **tail; - long sc_max; + const char *cpu_dir = "/sys/devices/system/cpu"; + const gchar *line; + g_autoptr(GDir) cpu_gdir = NULL; Error *local_err = NULL; - current = 0; head = NULL; tail = &head; - sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err); + cpu_gdir = g_dir_open(cpu_dir, 0, NULL); - while (local_err == NULL && current < sc_max) { - GuestLogicalProcessor *vcpu; - int64_t id = current++; - char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", - id); + if (cpu_gdir == NULL) { + error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir); + return NULL; + } - if (g_file_test(path, G_FILE_TEST_EXISTS)) { + while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) { + GuestLogicalProcessor *vcpu; + int64_t id; + if (sscanf(line, "cpu%" PRId64, &id)) { + g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/" + "cpu%" PRId64 "/", id); vcpu = g_malloc0(sizeof *vcpu); vcpu->logical_id = id; vcpu->has_can_offline = true; /* lolspeak ftw */ transfer_vcpu(vcpu, true, path, &local_err); QAPI_LIST_APPEND(tail, vcpu); } - g_free(path); } if (local_err == NULL) { diff --git a/qga/commands-win32.c b/qga/commands-win32.c index a00e6cb165..27baf17d6c 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -110,15 +110,15 @@ static OpenFlags guest_file_open_modes[] = { {"w", GENERIC_WRITE, CREATE_ALWAYS}, {"wb", GENERIC_WRITE, CREATE_ALWAYS}, {"a", FILE_GENERIC_APPEND, OPEN_ALWAYS }, - {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, - {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, - {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING}, - {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, - {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, - {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS}, - {"a+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }, - {"ab+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }, - {"a+b", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS } + {"r+", GENERIC_WRITE | GENERIC_READ, OPEN_EXISTING}, + {"rb+", GENERIC_WRITE | GENERIC_READ, OPEN_EXISTING}, + {"r+b", GENERIC_WRITE | GENERIC_READ, OPEN_EXISTING}, + {"w+", GENERIC_WRITE | GENERIC_READ, CREATE_ALWAYS}, + {"wb+", GENERIC_WRITE | GENERIC_READ, CREATE_ALWAYS}, + {"w+b", GENERIC_WRITE | GENERIC_READ, CREATE_ALWAYS}, + {"a+", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS }, + {"ab+", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS }, + {"a+b", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS } }; #define debug_error(msg) do { \ @@ -280,7 +280,7 @@ static void acquire_privilege(const char *name, Error **errp) Error *local_err = NULL; if (OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { error_setg(&local_err, QERR_QGA_COMMAND_FAILED, @@ -1116,7 +1116,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) len = strlen(mnt_point); mnt_point[len] = '\\'; - mnt_point[len+1] = 0; + mnt_point[len + 1] = 0; if (!GetVolumeInformationByHandleW(hLocalDiskHandle, vol_info, sizeof(vol_info), NULL, NULL, NULL, @@ -1323,7 +1323,7 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) DWORD char_count = 0; char *path, *out; GError *gerr = NULL; - gchar * argv[4]; + gchar *argv[4]; GetVolumePathNamesForVolumeNameW(guid, NULL, 0, &char_count); @@ -2174,7 +2174,7 @@ static ga_win_10_0_server_t const WIN_10_0_SERVER_VERSION_MATRIX[3] = { static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) { - typedef NTSTATUS(WINAPI * rtl_get_version_t)( + typedef NTSTATUS(WINAPI *rtl_get_version_t)( RTL_OSVERSIONINFOEXW *os_version_info_ex); info->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); diff --git a/qga/commands.c b/qga/commands.c index e866fc7081..a6491d2cf8 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -22,9 +22,9 @@ #include "commands-common.h" /* Maximum captured guest-exec out_data/err_data - 16MB */ -#define GUEST_EXEC_MAX_OUTPUT (16*1024*1024) +#define GUEST_EXEC_MAX_OUTPUT (16 * 1024 * 1024) /* Allocation and I/O buffer for reading guest-exec out_data/err_data - 4KB */ -#define GUEST_EXEC_IO_SIZE (4*1024) +#define GUEST_EXEC_IO_SIZE (4 * 1024) /* * Maximum file size to read - 48MB * diff --git a/qga/main.c b/qga/main.c index e7f8f3b161..15fd3a4149 100644 --- a/qga/main.c +++ b/qga/main.c @@ -279,20 +279,20 @@ QEMU_HELP_BOTTOM "\n" static const char *ga_log_level_str(GLogLevelFlags level) { switch (level & G_LOG_LEVEL_MASK) { - case G_LOG_LEVEL_ERROR: - return "error"; - case G_LOG_LEVEL_CRITICAL: - return "critical"; - case G_LOG_LEVEL_WARNING: - return "warning"; - case G_LOG_LEVEL_MESSAGE: - return "message"; - case G_LOG_LEVEL_INFO: - return "info"; - case G_LOG_LEVEL_DEBUG: - return "debug"; - default: - return "user"; + case G_LOG_LEVEL_ERROR: + return "error"; + case G_LOG_LEVEL_CRITICAL: + return "critical"; + case G_LOG_LEVEL_WARNING: + return "warning"; + case G_LOG_LEVEL_MESSAGE: + return "message"; + case G_LOG_LEVEL_INFO: + return "info"; + case G_LOG_LEVEL_DEBUG: + return "debug"; + default: + return "user"; } } @@ -375,7 +375,7 @@ static void ga_disable_non_whitelisted(const QmpCommand *cmd, void *opaque) } if (!whitelisted) { g_debug("disabling command: %s", name); - qmp_disable_command(&ga_commands, name); + qmp_disable_command(&ga_commands, name, "the agent is in frozen state"); } } @@ -586,7 +586,7 @@ end: static gboolean channel_event_cb(GIOCondition condition, gpointer data) { GAState *s = data; - gchar buf[QGA_READ_COUNT_DEFAULT+1]; + gchar buf[QGA_READ_COUNT_DEFAULT + 1]; gsize count; GIOStatus status = ga_channel_read(s->channel, buf, QGA_READ_COUNT_DEFAULT, &count); switch (status) { @@ -610,7 +610,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) * host-side chardev. sleep a bit to mitigate this */ if (s->virtio) { - usleep(100*1000); + usleep(100 * 1000); } return true; default: @@ -686,21 +686,20 @@ DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, DWORD ret = NO_ERROR; GAService *service = &ga_state->service; - switch (ctrl) - { - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - quit_handler(SIGTERM); - SetEvent(ga_state->wakeup_event); - service->status.dwCurrentState = SERVICE_STOP_PENDING; - SetServiceStatus(service->status_handle, &service->status); - break; - case SERVICE_CONTROL_DEVICEEVENT: - handle_serial_device_events(type, data); - break; + switch (ctrl) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + quit_handler(SIGTERM); + SetEvent(ga_state->wakeup_event); + service->status.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(service->status_handle, &service->status); + break; + case SERVICE_CONTROL_DEVICEEVENT: + handle_serial_device_events(type, data); + break; - default: - ret = ERROR_CALL_NOT_IMPLEMENTED; + default: + ret = ERROR_CALL_NOT_IMPLEMENTED; } return ret; } @@ -1329,7 +1328,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) s->blacklist = config->blacklist; do { g_debug("disabling command: %s", (char *)l->data); - qmp_disable_command(&ga_commands, l->data); + qmp_disable_command(&ga_commands, l->data, NULL); l = g_list_next(l); } while (l); } diff --git a/roms/openbios b/roms/openbios -Subproject 7f28286f5cb1ca682e3ba0a8706d8884f12bc49 +Subproject 4a0041107b8ef77e0e8337bfcb5f8078887261a diff --git a/scripts/oss-fuzz/output_reproducer.py b/scripts/oss-fuzz/output_reproducer.py new file mode 100755 index 0000000000..3608b0600e --- /dev/null +++ b/scripts/oss-fuzz/output_reproducer.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Convert plain qtest traces to C or Bash reproducers + +Use this to help build bug-reports or create in-tree reproducers for bugs. +Note: This will not format C code for you. Pipe the output through +clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, ColumnLimit: 90}" +or similar +""" + +import sys +import os +import argparse +import textwrap +from datetime import date + +__author__ = "Alexander Bulekov <alxndr@bu.edu>" +__copyright__ = "Copyright (C) 2021, Red Hat, Inc." +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Alexander Bulekov" +__email__ = "alxndr@bu.edu" + + +def c_header(owner): + return """/* + * Autogenerated Fuzzer Test Case + * + * Copyright (c) {date} {owner} + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqos/libqtest.h" + + """.format(date=date.today().year, owner=owner) + +def c_comment(s): + """ Return a multi-line C comment. Assume the text is already wrapped """ + return "/*\n * " + "\n * ".join(s.splitlines()) + "\n*/" + +def print_c_function(s): + print("/* ") + for l in s.splitlines(): + print(" * {}".format(l)) + +def bash_reproducer(path, args, trace): + result = '\\\n'.join(textwrap.wrap("cat << EOF | {} {}".format(path, args), + 72, break_on_hyphens=False, + drop_whitespace=False)) + for l in trace.splitlines(): + result += "\n" + '\\\n'.join(textwrap.wrap(l,72,drop_whitespace=False)) + result += "\nEOF" + return result + +def c_reproducer(name, args, trace): + result = [] + result.append("""static void {}(void)\n{{""".format(name)) + + # libqtest will add its own qtest args, so get rid of them + args = args.replace("-accel qtest","") + args = args.replace(",accel=qtest","") + args = args.replace("-machine accel=qtest","") + args = args.replace("-qtest stdio","") + result.append("""QTestState *s = qtest_init("{}");""".format(args)) + for l in trace.splitlines(): + param = l.split() + cmd = param[0] + if cmd == "write": + buf = param[3][2:] #Get the 0x... buffer and trim the "0x" + assert len(buf)%2 == 0 + bufbytes = [buf[i:i+2] for i in range(0, len(buf), 2)] + bufstring = '\\x'+'\\x'.join(bufbytes) + addr = param[1] + size = param[2] + result.append("""qtest_bufwrite(s, {}, "{}", {});""".format( + addr, bufstring, size)) + elif cmd.startswith("in") or cmd.startswith("read"): + result.append("qtest_{}(s, {});".format( + cmd, param[1])) + elif cmd.startswith("out") or cmd.startswith("write"): + result.append("qtest_{}(s, {}, {});".format( + cmd, param[1], param[2])) + elif cmd == "clock_step": + if len(param) ==1: + result.append("qtest_clock_step_next(s);") + else: + result.append("qtest_clock_step(s, {});".format(param[1])) + result.append("qtest_quit(s);\n}") + return "\n".join(result) + +def c_main(name, arch): + return """int main(int argc, char **argv) +{{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "{arch}") == 0) {{ + qtest_add_func("fuzz/{name}",{name}); + }} + + return g_test_run(); +}}""".format(name=name, arch=arch) + +def main(): + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group() + group.add_argument("-bash", help="Only output a copy-pastable bash command", + action="store_true") + group.add_argument("-c", help="Only output a c function", + action="store_true") + parser.add_argument('-owner', help="If generating complete C source code, \ + this specifies the Copyright owner", + nargs='?', default="<name of author>") + parser.add_argument("-no_comment", help="Don't include a bash reproducer \ + as a comment in the C reproducers", + action="store_true") + parser.add_argument('-name', help="The name of the c function", + nargs='?', default="test_fuzz") + parser.add_argument('input_trace', help="input QTest command sequence \ + (stdin by default)", + nargs='?', type=argparse.FileType('r'), + default=sys.stdin) + args = parser.parse_args() + + qemu_path = os.getenv("QEMU_PATH") + qemu_args = os.getenv("QEMU_ARGS") + if not qemu_args or not qemu_path: + print("Please set QEMU_PATH and QEMU_ARGS environment variables") + sys.exit(1) + + bash_args = qemu_args + if " -qtest stdio" not in qemu_args: + bash_args += " -qtest stdio" + + arch = qemu_path.split("-")[-1] + trace = args.input_trace.read().strip() + + if args.bash : + print(bash_reproducer(qemu_path, bash_args, trace)) + else: + output = "" + if not args.c: + output += c_header(args.owner) + "\n" + if not args.no_comment: + output += c_comment(bash_reproducer(qemu_path, bash_args, trace)) + output += c_reproducer(args.name, qemu_args, trace) + if not args.c: + output += c_main(args.name, arch) + print(output) + + +if __name__ == '__main__': + main() diff --git a/scsi/utils.c b/scsi/utils.c index 873e05aeaf..357b036671 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -589,7 +589,7 @@ int scsi_sense_from_errno(int errno_value, SCSISense *sense) return TASK_SET_FULL; #ifdef CONFIG_LINUX /* These errno mapping are specific to Linux. For more information: - * - scsi_decide_disposition in drivers/scsi/scsi_error.c + * - scsi_check_sense and scsi_decide_disposition in drivers/scsi/scsi_error.c * - scsi_result_to_blk_status in drivers/scsi/scsi_lib.c * - blk_errors[] in block/blk-core.c */ @@ -599,7 +599,7 @@ int scsi_sense_from_errno(int errno_value, SCSISense *sense) *sense = SENSE_CODE(READ_ERROR); return CHECK_CONDITION; case EREMOTEIO: - *sense = SENSE_CODE(LUN_COMM_FAILURE); + *sense = SENSE_CODE(TARGET_FAILURE); return CHECK_CONDITION; #endif case ENOMEDIUM: diff --git a/softmmu/memory.c b/softmmu/memory.c index 9db47b7db6..c4730ec47a 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -1440,7 +1440,6 @@ MemTxResult memory_region_dispatch_read(MemoryRegion *mr, unsigned size = memop_size(op); MemTxResult r; - fuzz_dma_read_cb(addr, size, mr); if (!memory_region_access_valid(mr, addr, size, false, attrs)) { *pval = unassigned_mem_read(mr, addr, size); return MEMTX_DECODE_ERROR; diff --git a/softmmu/physmem.c b/softmmu/physmem.c index 7e8b0fab89..85034d9c11 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -1144,19 +1144,6 @@ static int subpage_register(subpage_t *mmio, uint32_t start, uint32_t end, uint16_t section); static subpage_t *subpage_init(FlatView *fv, hwaddr base); -static void *(*phys_mem_alloc)(size_t size, uint64_t *align, bool shared) = - qemu_anon_ram_alloc; - -/* - * Set a custom physical guest memory alloator. - * Accelerators with unusual needs may need this. Hopefully, we can - * get rid of it eventually. - */ -void phys_mem_set_alloc(void *(*alloc)(size_t, uint64_t *align, bool shared)) -{ - phys_mem_alloc = alloc; -} - static uint16_t phys_section_add(PhysPageMap *map, MemoryRegionSection *section) { @@ -1962,8 +1949,9 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared) return; } } else { - new_block->host = phys_mem_alloc(new_block->max_length, - &new_block->mr->align, shared); + new_block->host = qemu_anon_ram_alloc(new_block->max_length, + &new_block->mr->align, + shared); if (!new_block->host) { error_setg_errno(errp, errno, "cannot set up guest memory '%s'", @@ -2047,17 +2035,6 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, return NULL; } - if (phys_mem_alloc != qemu_anon_ram_alloc) { - /* - * file_ram_alloc() needs to allocate just like - * phys_mem_alloc, but we haven't bothered to provide - * a hook there. - */ - error_setg(errp, - "-mem-path not supported with this accelerator"); - return NULL; - } - size = HOST_PAGE_ALIGN(size); file_size = get_file_size(fd); if (file_size > 0 && file_size < size) { @@ -2247,13 +2224,6 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) area = mmap(vaddr, length, PROT_READ | PROT_WRITE, flags, block->fd, offset); } else { - /* - * Remap needs to match alloc. Accelerators that - * set phys_mem_alloc never remap. If they did, - * we'd need a remap hook here. - */ - assert(phys_mem_alloc == qemu_anon_ram_alloc); - flags |= MAP_PRIVATE | MAP_ANONYMOUS; area = mmap(vaddr, length, PROT_READ | PROT_WRITE, flags, -1, 0); @@ -2831,6 +2801,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, bool release_lock = false; uint8_t *buf = ptr; + fuzz_dma_read_cb(addr, len, mr); for (;;) { if (!memory_access_is_direct(mr, false)) { /* I/O case */ @@ -2841,7 +2812,6 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, stn_he_p(buf, l, val); } else { /* RAM case */ - fuzz_dma_read_cb(addr, len, mr); ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); memcpy(buf, ram_ptr, l); } diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 60d434d5ed..468b4430f3 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -114,6 +114,11 @@ struct CPUS390XState { uint64_t diag318_info; +#if !defined(CONFIG_USER_ONLY) + uint64_t tlb_fill_tec; /* translation exception code during tlb_fill */ + int tlb_fill_exc; /* exception number seen during tlb_fill */ +#endif + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index dd474c5e9a..050dcf2d42 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -86,8 +86,8 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"), CPUDEF_INIT(0x3906, 14, 2, 47, 0x08000000U, "z14.2", "IBM z14 GA2"), CPUDEF_INIT(0x3907, 14, 1, 47, 0x08000000U, "z14ZR1", "IBM z14 Model ZR1 GA1"), - CPUDEF_INIT(0x8561, 15, 1, 47, 0x08000000U, "gen15a", "IBM z15 GA1"), - CPUDEF_INIT(0x8562, 15, 1, 47, 0x08000000U, "gen15b", "IBM 8562 GA1"), + CPUDEF_INIT(0x8561, 15, 1, 47, 0x08000000U, "gen15a", "IBM z15 T01 GA1"), + CPUDEF_INIT(0x8562, 15, 1, 47, 0x08000000U, "gen15b", "IBM z15 T02 GA1"), }; #define QEMU_MAX_CPU_TYPE 0x2964 diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index ce16af394b..c48cd6b46f 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -164,6 +164,9 @@ bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size, tec = 0; /* unused */ } + env->tlb_fill_exc = excp; + env->tlb_fill_tec = tec; + if (!excp) { qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 55bd1551e6..d4e4f3388f 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -18,7 +18,7 @@ DEF_HELPER_3(srstu, void, env, i32, i32) DEF_HELPER_4(clst, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(mvn, TCG_CALL_NO_WG, void, env, i32, i64, i64) DEF_HELPER_FLAGS_4(mvo, TCG_CALL_NO_WG, void, env, i32, i64, i64) -DEF_HELPER_FLAGS_4(mvpg, TCG_CALL_NO_WG, i32, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(mvpg, TCG_CALL_NO_WG, i32, env, i64, i32, i32) DEF_HELPER_FLAGS_4(mvz, TCG_CALL_NO_WG, void, env, i32, i64, i64) DEF_HELPER_3(mvst, i32, env, i32, i32) DEF_HELPER_4(ex, void, env, i32, i64, i64) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index e5b6efabf3..0bb1886a2e 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -641,7 +641,7 @@ /* MOVE NUMERICS */ C(0xd100, MVN, SS_a, Z, la1, a2, 0, 0, mvn, 0) /* MOVE PAGE */ - C(0xb254, MVPG, RRE, Z, r1_o, r2_o, 0, 0, mvpg, 0) + C(0xb254, MVPG, RRE, Z, 0, 0, 0, 0, mvpg, 0) /* MOVE STRING */ C(0xb255, MVST, RRE, Z, 0, 0, 0, 0, mvst, 0) /* MOVE WITH OPTIONAL SPECIFICATION */ diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index 73f816a722..4fb3bbfef5 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -161,8 +161,6 @@ static int cap_protected; static int active_cmma; -static void *legacy_s390_alloc(size_t size, uint64_t *align, bool shared); - static int kvm_s390_query_mem_limit(uint64_t *memory_limit) { struct kvm_device_attr attr = { @@ -349,6 +347,11 @@ int kvm_arch_init(MachineState *ms, KVMState *s) "please use kernel 3.15 or newer"); return -1; } + if (!kvm_check_extension(s, KVM_CAP_S390_COW)) { + error_report("KVM is missing capability KVM_CAP_S390_COW - " + "unsupported environment"); + return -1; + } cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS); cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF); @@ -357,11 +360,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_vcpu_resets = kvm_check_extension(s, KVM_CAP_S390_VCPU_RESETS); cap_protected = kvm_check_extension(s, KVM_CAP_S390_PROTECTED); - if (!kvm_check_extension(s, KVM_CAP_S390_GMAP) - || !kvm_check_extension(s, KVM_CAP_S390_COW)) { - phys_mem_set_alloc(legacy_s390_alloc); - } - kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0); kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0); @@ -889,37 +887,6 @@ int kvm_s390_mem_op_pv(S390CPU *cpu, uint64_t offset, void *hostbuf, return ret; } -/* - * Legacy layout for s390: - * Older S390 KVM requires the topmost vma of the RAM to be - * smaller than an system defined value, which is at least 256GB. - * Larger systems have larger values. We put the guest between - * the end of data segment (system break) and this value. We - * use 32GB as a base to have enough room for the system break - * to grow. We also have to use MAP parameters that avoid - * read-only mapping of guest pages. - */ -static void *legacy_s390_alloc(size_t size, uint64_t *align, bool shared) -{ - static void *mem; - - if (mem) { - /* we only support one allocation, which is enough for initial ram */ - return NULL; - } - - mem = mmap((void *) 0x800000000ULL, size, - PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - if (mem == MAP_FAILED) { - mem = NULL; - } - if (mem && align) { - *align = QEMU_VMALLOC_ALIGN; - } - return mem; -} - static uint8_t const *sw_bp_inst; static uint8_t sw_bp_ilen; diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index 25cfede806..12e84a4285 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -130,28 +130,103 @@ typedef struct S390Access { int mmu_idx; } S390Access; -static S390Access access_prepare(CPUS390XState *env, vaddr vaddr, int size, - MMUAccessType access_type, int mmu_idx, - uintptr_t ra) +/* + * With nonfault=1, return the PGM_ exception that would have been injected + * into the guest; return 0 if no exception was detected. + * + * For !CONFIG_USER_ONLY, the TEC is stored stored to env->tlb_fill_tec. + * For CONFIG_USER_ONLY, the faulting address is stored to env->__excp_addr. + */ +static int s390_probe_access(CPUArchState *env, target_ulong addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, uintptr_t ra) { - S390Access access = { - .vaddr1 = vaddr, - .size1 = MIN(size, -(vaddr | TARGET_PAGE_MASK)), - .mmu_idx = mmu_idx, - }; + int flags; + +#if defined(CONFIG_USER_ONLY) + flags = page_get_flags(addr); + if (!(flags & (access_type == MMU_DATA_LOAD ? PAGE_READ : PAGE_WRITE))) { + env->__excp_addr = addr; + flags = (flags & PAGE_VALID) ? PGM_PROTECTION : PGM_ADDRESSING; + if (nonfault) { + return flags; + } + tcg_s390_program_interrupt(env, flags, ra); + } + *phost = g2h(env_cpu(env), addr); +#else + /* + * For !CONFIG_USER_ONLY, we cannot rely on TLB_INVALID_MASK or haddr==NULL + * to detect if there was an exception during tlb_fill(). + */ + env->tlb_fill_exc = 0; + flags = probe_access_flags(env, addr, access_type, mmu_idx, nonfault, phost, + ra); + if (env->tlb_fill_exc) { + return env->tlb_fill_exc; + } + + if (unlikely(flags & TLB_WATCHPOINT)) { + /* S390 does not presently use transaction attributes. */ + cpu_check_watchpoint(env_cpu(env), addr, size, + MEMTXATTRS_UNSPECIFIED, + (access_type == MMU_DATA_STORE + ? BP_MEM_WRITE : BP_MEM_READ), ra); + } +#endif + return 0; +} - g_assert(size > 0 && size <= 4096); - access.haddr1 = probe_access(env, access.vaddr1, access.size1, access_type, - mmu_idx, ra); +static int access_prepare_nf(S390Access *access, CPUS390XState *env, + bool nonfault, vaddr vaddr1, int size, + MMUAccessType access_type, + int mmu_idx, uintptr_t ra) +{ + void *haddr1, *haddr2 = NULL; + int size1, size2, exc; + vaddr vaddr2 = 0; - if (unlikely(access.size1 != size)) { - /* The access crosses page boundaries. */ - access.vaddr2 = wrap_address(env, vaddr + access.size1); - access.size2 = size - access.size1; - access.haddr2 = probe_access(env, access.vaddr2, access.size2, - access_type, mmu_idx, ra); + assert(size > 0 && size <= 4096); + + size1 = MIN(size, -(vaddr1 | TARGET_PAGE_MASK)), + size2 = size - size1; + + exc = s390_probe_access(env, vaddr1, size1, access_type, mmu_idx, nonfault, + &haddr1, ra); + if (exc) { + return exc; } - return access; + if (unlikely(size2)) { + /* The access crosses page boundaries. */ + vaddr2 = wrap_address(env, vaddr1 + size1); + exc = s390_probe_access(env, vaddr2, size2, access_type, mmu_idx, + nonfault, &haddr2, ra); + if (exc) { + return exc; + } + } + + *access = (S390Access) { + .vaddr1 = vaddr1, + .vaddr2 = vaddr2, + .haddr1 = haddr1, + .haddr2 = haddr2, + .size1 = size1, + .size2 = size2, + .mmu_idx = mmu_idx + }; + return 0; +} + +static S390Access access_prepare(CPUS390XState *env, vaddr vaddr, int size, + MMUAccessType access_type, int mmu_idx, + uintptr_t ra) +{ + S390Access ret; + int exc = access_prepare_nf(&ret, env, false, vaddr, size, + access_type, mmu_idx, ra); + assert(!exc); + return ret; } /* Helper to handle memset on a single page. */ @@ -840,33 +915,58 @@ uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2) } /* move page */ -uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2) +uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint32_t r1, uint32_t r2) { + const uint64_t src = get_address(env, r2) & TARGET_PAGE_MASK; + const uint64_t dst = get_address(env, r1) & TARGET_PAGE_MASK; const int mmu_idx = cpu_mmu_index(env, false); const bool f = extract64(r0, 11, 1); const bool s = extract64(r0, 10, 1); + const bool cco = extract64(r0, 8, 1); uintptr_t ra = GETPC(); S390Access srca, desta; + int exc; if ((f && s) || extract64(r0, 12, 4)) { tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC()); } - r1 = wrap_address(env, r1 & TARGET_PAGE_MASK); - r2 = wrap_address(env, r2 & TARGET_PAGE_MASK); - /* - * TODO: - * - Access key handling - * - CC-option with surpression of page-translation exceptions - * - Store r1/r2 register identifiers at real location 162 + * We always manually handle exceptions such that we can properly store + * r1/r2 to the lowcore on page-translation exceptions. + * + * TODO: Access key handling */ - srca = access_prepare(env, r2, TARGET_PAGE_SIZE, MMU_DATA_LOAD, mmu_idx, - ra); - desta = access_prepare(env, r1, TARGET_PAGE_SIZE, MMU_DATA_STORE, mmu_idx, - ra); + exc = access_prepare_nf(&srca, env, true, src, TARGET_PAGE_SIZE, + MMU_DATA_LOAD, mmu_idx, ra); + if (exc) { + if (cco) { + return 2; + } + goto inject_exc; + } + exc = access_prepare_nf(&desta, env, true, dst, TARGET_PAGE_SIZE, + MMU_DATA_STORE, mmu_idx, ra); + if (exc) { + if (cco && exc != PGM_PROTECTION) { + return 1; + } + goto inject_exc; + } access_memmove(env, &desta, &srca, ra); return 0; /* data moved */ +inject_exc: +#if !defined(CONFIG_USER_ONLY) + if (exc != PGM_ADDRESSING) { + stq_phys(env_cpu(env)->as, env->psa + offsetof(LowCore, trans_exc_code), + env->tlb_fill_tec); + } + if (exc == PGM_PAGE_TRANS) { + stb_phys(env_cpu(env)->as, env->psa + offsetof(LowCore, op_access_id), + r1 << 4 | r2); + } +#endif + tcg_s390_program_interrupt(env, exc, ra); } /* string copy */ diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 61dd0341e4..4f953ddfba 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -3513,7 +3513,12 @@ static DisasJumpType op_mvo(DisasContext *s, DisasOps *o) static DisasJumpType op_mvpg(DisasContext *s, DisasOps *o) { - gen_helper_mvpg(cc_op, cpu_env, regs[0], o->in1, o->in2); + TCGv_i32 t1 = tcg_const_i32(get_field(s, r1)); + TCGv_i32 t2 = tcg_const_i32(get_field(s, r2)); + + gen_helper_mvpg(cc_op, cpu_env, regs[0], t1, t2); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); set_cc_static(s); return DISAS_NEXT; } diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index df167b142c..83b1741ec8 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -93,11 +93,12 @@ def _console_interaction(test, success_message, failure_message, if not msg: continue console_logger.debug(msg) - if success_message in msg: + if success_message is None or success_message in msg: break if failure_message and failure_message in msg: console.close() - fail = 'Failure message found in console: %s' % failure_message + fail = 'Failure message found in console: "%s". Expected: "%s"' % \ + (failure_message, success_message) test.fail(fail) def interrupt_interactive_console_until_pattern(test, success_message, @@ -139,6 +140,18 @@ def wait_for_console_pattern(test, success_message, failure_message=None, """ _console_interaction(test, success_message, failure_message, None, vm=vm) +def exec_command(test, command): + """ + Send a command to a console (appending CRLF characters), while logging + the content. + + :param test: an Avocado test containing a VM. + :type test: :class:`avocado_qemu.Test` + :param command: the command to send + :type command: str + """ + _console_interaction(test, None, None, command + '\r') + def exec_command_and_wait_for_pattern(test, command, success_message, failure_message=None): """ @@ -304,8 +317,10 @@ class LinuxTest(Test): try: cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') self.phone_home_port = network.find_free_port() - with open(ssh_pubkey) as pubkey: - pubkey_content = pubkey.read() + pubkey_content = None + if ssh_pubkey: + with open(ssh_pubkey) as pubkey: + pubkey_content = pubkey.read() cloudinit.iso(cloudinit_iso, self.name, username='root', password='password', diff --git a/tests/acceptance/multiprocess.py b/tests/acceptance/multiprocess.py new file mode 100644 index 0000000000..96627f022a --- /dev/null +++ b/tests/acceptance/multiprocess.py @@ -0,0 +1,95 @@ +# Test for multiprocess qemu +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +import os +import socket + +from avocado_qemu import Test +from avocado_qemu import wait_for_console_pattern +from avocado_qemu import exec_command +from avocado_qemu import exec_command_and_wait_for_pattern + +class Multiprocess(Test): + """ + :avocado: tags=multiprocess + """ + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + def do_test(self, kernel_url, initrd_url, kernel_command_line, + machine_type): + """Main test method""" + self.require_accelerator('kvm') + + # Create socketpair to connect proxy and remote processes + proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX, + socket.SOCK_STREAM) + os.set_inheritable(proxy_sock.fileno(), True) + os.set_inheritable(remote_sock.fileno(), True) + + kernel_path = self.fetch_asset(kernel_url) + initrd_path = self.fetch_asset(initrd_url) + + # Create remote process + remote_vm = self.get_vm() + remote_vm.add_args('-machine', 'x-remote') + remote_vm.add_args('-nodefaults') + remote_vm.add_args('-device', 'lsi53c895a,id=lsi1') + remote_vm.add_args('-object', 'x-remote-object,id=robj1,' + 'devid=lsi1,fd='+str(remote_sock.fileno())) + remote_vm.launch() + + # Create proxy process + self.vm.set_console() + self.vm.add_args('-machine', machine_type) + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-cpu', 'host') + self.vm.add_args('-object', + 'memory-backend-memfd,id=sysmem-file,size=2G') + self.vm.add_args('--numa', 'node,memdev=sysmem-file') + self.vm.add_args('-m', '2048') + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_command_line) + self.vm.add_args('-device', + 'x-pci-proxy-dev,' + 'id=lsi1,fd='+str(proxy_sock.fileno())) + self.vm.launch() + wait_for_console_pattern(self, 'as init process', + 'Kernel panic - not syncing') + exec_command(self, 'mount -t sysfs sysfs /sys') + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/pci/devices/*/uevent', + 'PCI_ID=1000:0012') + + def test_multiprocess_x86_64(self): + """ + :avocado: tags=arch:x86_64 + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/31/Everything/x86_64/os/images' + '/pxeboot/vmlinuz') + initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/31/Everything/x86_64/os/images' + '/pxeboot/initrd.img') + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 rdinit=/bin/bash') + machine_type = 'pc' + self.do_test(kernel_url, initrd_url, kernel_command_line, machine_type) + + def test_multiprocess_aarch64(self): + """ + :avocado: tags=arch:aarch64 + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/31/Everything/aarch64/os/images' + '/pxeboot/vmlinuz') + initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' + '/linux/releases/31/Everything/aarch64/os/images' + '/pxeboot/initrd.img') + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'rdinit=/bin/bash console=ttyAMA0') + machine_type = 'virt,gic-version=3' + self.do_test(kernel_url, initrd_url, kernel_command_line, machine_type) diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index 83bfc3b6bb..5189cf9677 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -407,6 +407,13 @@ class Engine(object): vcpu_timings = ret[2] if uri[0:5] == "unix:": os.remove(uri[5:]) + + if os.path.exists(srcmonaddr): + os.remove(srcmonaddr) + + if self._dst_host == "localhost" and os.path.exists(dstmonaddr): + os.remove(dstmonaddr) + if self._verbose: print("Finished migration") diff --git a/tests/qtest/fuzz-megasas-test.c b/tests/qtest/fuzz-megasas-test.c new file mode 100644 index 0000000000..940a76bf25 --- /dev/null +++ b/tests/qtest/fuzz-megasas-test.c @@ -0,0 +1,49 @@ +/* + * QTest fuzzer-generated testcase for megasas device + * + * Copyright (c) 2020 Li Qiang <liq3ea@gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqos/libqtest.h" + +/* + * This used to trigger the assert in scsi_dma_complete + * https://bugs.launchpad.net/qemu/+bug/1878263 + */ +static void test_lp1878263_megasas_zero_iov_cnt(void) +{ + QTestState *s; + + s = qtest_init("-nographic -monitor none -serial none " + "-M q35 -device megasas -device scsi-cd,drive=null0 " + "-blockdev driver=null-co,read-zeroes=on,node-name=null0"); + qtest_outl(s, 0xcf8, 0x80001818); + qtest_outl(s, 0xcfc, 0xc101); + qtest_outl(s, 0xcf8, 0x8000181c); + qtest_outl(s, 0xcf8, 0x80001804); + qtest_outw(s, 0xcfc, 0x7); + qtest_outl(s, 0xcf8, 0x8000186a); + qtest_writeb(s, 0x14, 0xfe); + qtest_writeb(s, 0x0, 0x02); + qtest_outb(s, 0xc1c0, 0x17); + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("fuzz/test_lp1878263_megasas_zero_iov_cnt", + test_lp1878263_megasas_zero_iov_cnt); + } + + return g_test_run(); +} diff --git a/tests/qtest/fuzz-test.c b/tests/qtest/fuzz-test.c index 6f161c93be..00149abec7 100644 --- a/tests/qtest/fuzz-test.c +++ b/tests/qtest/fuzz-test.c @@ -11,29 +11,6 @@ #include "libqos/libqtest.h" -/* - * This used to trigger the assert in scsi_dma_complete - * https://bugs.launchpad.net/qemu/+bug/1878263 - */ -static void test_lp1878263_megasas_zero_iov_cnt(void) -{ - QTestState *s; - - s = qtest_init("-nographic -monitor none -serial none " - "-M q35 -device megasas -device scsi-cd,drive=null0 " - "-blockdev driver=null-co,read-zeroes=on,node-name=null0"); - qtest_outl(s, 0xcf8, 0x80001818); - qtest_outl(s, 0xcfc, 0xc101); - qtest_outl(s, 0xcf8, 0x8000181c); - qtest_outl(s, 0xcf8, 0x80001804); - qtest_outw(s, 0xcfc, 0x7); - qtest_outl(s, 0xcf8, 0x8000186a); - qtest_writeb(s, 0x14, 0xfe); - qtest_writeb(s, 0x0, 0x02); - qtest_outb(s, 0xc1c0, 0x17); - qtest_quit(s); -} - static void test_lp1878642_pci_bus_get_irq_level_assert(void) { QTestState *s; @@ -47,55 +24,6 @@ static void test_lp1878642_pci_bus_get_irq_level_assert(void) qtest_quit(s); } -/* - * Here a MemoryRegionCache pointed to an MMIO region but had a - * larger size than the underlying region. - */ -static void test_mmio_oob_from_memory_region_cache(void) -{ - QTestState *s; - - s = qtest_init("-M pc-q35-5.2 -display none -m 512M " - "-device virtio-scsi,num_queues=8,addr=03.0 "); - - qtest_outl(s, 0xcf8, 0x80001811); - qtest_outb(s, 0xcfc, 0x6e); - qtest_outl(s, 0xcf8, 0x80001824); - qtest_outl(s, 0xcf8, 0x80001813); - qtest_outl(s, 0xcfc, 0xa080000); - qtest_outl(s, 0xcf8, 0x80001802); - qtest_outl(s, 0xcfc, 0x5a175a63); - qtest_outb(s, 0x6e08, 0x9e); - qtest_writeb(s, 0x9f003, 0xff); - qtest_writeb(s, 0x9f004, 0x01); - qtest_writeb(s, 0x9e012, 0x0e); - qtest_writeb(s, 0x9e01b, 0x0e); - qtest_writeb(s, 0x9f006, 0x01); - qtest_writeb(s, 0x9f008, 0x01); - qtest_writeb(s, 0x9f00a, 0x01); - qtest_writeb(s, 0x9f00c, 0x01); - qtest_writeb(s, 0x9f00e, 0x01); - qtest_writeb(s, 0x9f010, 0x01); - qtest_writeb(s, 0x9f012, 0x01); - qtest_writeb(s, 0x9f014, 0x01); - qtest_writeb(s, 0x9f016, 0x01); - qtest_writeb(s, 0x9f018, 0x01); - qtest_writeb(s, 0x9f01a, 0x01); - qtest_writeb(s, 0x9f01c, 0x01); - qtest_writeb(s, 0x9f01e, 0x01); - qtest_writeb(s, 0x9f020, 0x01); - qtest_writeb(s, 0x9f022, 0x01); - qtest_writeb(s, 0x9f024, 0x01); - qtest_writeb(s, 0x9f026, 0x01); - qtest_writeb(s, 0x9f028, 0x01); - qtest_writeb(s, 0x9f02a, 0x01); - qtest_writeb(s, 0x9f02c, 0x01); - qtest_writeb(s, 0x9f02e, 0x01); - qtest_writeb(s, 0x9f030, 0x01); - qtest_outb(s, 0x6e10, 0x00); - qtest_quit(s); -} - int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -103,12 +31,8 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("fuzz/test_lp1878263_megasas_zero_iov_cnt", - test_lp1878263_megasas_zero_iov_cnt); qtest_add_func("fuzz/test_lp1878642_pci_bus_get_irq_level_assert", test_lp1878642_pci_bus_get_irq_level_assert); - qtest_add_func("fuzz/test_mmio_oob_from_memory_region_cache", - test_mmio_oob_from_memory_region_cache); } return g_test_run(); diff --git a/tests/qtest/fuzz-virtio-scsi-test.c b/tests/qtest/fuzz-virtio-scsi-test.c new file mode 100644 index 0000000000..aaf6d10e18 --- /dev/null +++ b/tests/qtest/fuzz-virtio-scsi-test.c @@ -0,0 +1,75 @@ +/* + * QTest fuzzer-generated testcase for virtio-scsi device + * + * Copyright (c) 2020 Li Qiang <liq3ea@gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqos/libqtest.h" + +/* + * Here a MemoryRegionCache pointed to an MMIO region but had a + * larger size than the underlying region. + */ +static void test_mmio_oob_from_memory_region_cache(void) +{ + QTestState *s; + + s = qtest_init("-M pc-q35-5.2 -display none -m 512M " + "-device virtio-scsi,num_queues=8,addr=03.0 "); + + qtest_outl(s, 0xcf8, 0x80001811); + qtest_outb(s, 0xcfc, 0x6e); + qtest_outl(s, 0xcf8, 0x80001824); + qtest_outl(s, 0xcf8, 0x80001813); + qtest_outl(s, 0xcfc, 0xa080000); + qtest_outl(s, 0xcf8, 0x80001802); + qtest_outl(s, 0xcfc, 0x5a175a63); + qtest_outb(s, 0x6e08, 0x9e); + qtest_writeb(s, 0x9f003, 0xff); + qtest_writeb(s, 0x9f004, 0x01); + qtest_writeb(s, 0x9e012, 0x0e); + qtest_writeb(s, 0x9e01b, 0x0e); + qtest_writeb(s, 0x9f006, 0x01); + qtest_writeb(s, 0x9f008, 0x01); + qtest_writeb(s, 0x9f00a, 0x01); + qtest_writeb(s, 0x9f00c, 0x01); + qtest_writeb(s, 0x9f00e, 0x01); + qtest_writeb(s, 0x9f010, 0x01); + qtest_writeb(s, 0x9f012, 0x01); + qtest_writeb(s, 0x9f014, 0x01); + qtest_writeb(s, 0x9f016, 0x01); + qtest_writeb(s, 0x9f018, 0x01); + qtest_writeb(s, 0x9f01a, 0x01); + qtest_writeb(s, 0x9f01c, 0x01); + qtest_writeb(s, 0x9f01e, 0x01); + qtest_writeb(s, 0x9f020, 0x01); + qtest_writeb(s, 0x9f022, 0x01); + qtest_writeb(s, 0x9f024, 0x01); + qtest_writeb(s, 0x9f026, 0x01); + qtest_writeb(s, 0x9f028, 0x01); + qtest_writeb(s, 0x9f02a, 0x01); + qtest_writeb(s, 0x9f02c, 0x01); + qtest_writeb(s, 0x9f02e, 0x01); + qtest_writeb(s, 0x9f030, 0x01); + qtest_outb(s, 0x6e10, 0x00); + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("fuzz/test_mmio_oob_from_memory_region_cache", + test_mmio_oob_from_memory_region_cache); + } + + return g_test_run(); +} diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index ee8c17a04c..b5fe27aae1 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -28,6 +28,7 @@ #include "hw/pci/pci.h" #include "hw/boards.h" #include "generic_fuzz_configs.h" +#include "hw/mem/sparse-mem.h" /* * SEPARATOR is used to separate "operations" in the fuzz input @@ -64,6 +65,8 @@ static useconds_t timeout = DEFAULT_TIMEOUT_US; static bool qtest_log_enabled; +MemoryRegion *sparse_mem_mr; + /* * A pattern used to populate a DMA region or perform a memwrite. This is * useful for e.g. populating tables of unique addresses. @@ -191,8 +194,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr) */ if (dma_patterns->len == 0 || len == 0 - || mr != current_machine->ram - || addr > current_machine->ram_size) { + || (mr != current_machine->ram && mr != sparse_mem_mr)) { return; } @@ -238,7 +240,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr) MEMTXATTRS_UNSPECIFIED); if (!(memory_region_is_ram(mr1) || - memory_region_is_romd(mr1))) { + memory_region_is_romd(mr1)) && mr1 != sparse_mem_mr) { l = memory_access_size(mr1, l, addr1); } else { /* ROM/RAM case */ @@ -583,6 +585,21 @@ static void handle_timeout(int sig) fprintf(stderr, "[Timeout]\n"); fflush(stderr); } + + /* + * If there is a crash, libfuzzer/ASAN forks a child to run an + * "llvm-symbolizer" process for printing out a pretty stacktrace. It + * communicates with this child using a pipe. If we timeout+Exit, while + * libfuzzer is still communicating with the llvm-symbolizer child, we will + * be left with an orphan llvm-symbolizer process. Sometimes, this appears + * to lead to a deadlock in the forkserver. Use waitpid to check if there + * are any waitable children. If so, exit out of the signal-handler, and + * let libfuzzer finish communicating with the child, and exit, on its own. + */ + if (waitpid(-1, NULL, WNOHANG) == 0) { + return; + } + _Exit(0); } @@ -799,6 +816,12 @@ static void generic_pre_fuzz(QTestState *s) } qts_global = s; + /* + * Create a special device that we can use to back DMA buffers at very + * high memory addresses + */ + sparse_mem_mr = sparse_mem_init(0, UINT64_MAX); + dma_regions = g_array_new(false, false, sizeof(address_range)); dma_patterns = g_array_new(false, false, sizeof(pattern)); diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h index 5d599765c4..8b8c7ac553 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -177,7 +177,7 @@ const generic_fuzz_config predefined_configs[] = { .name = "i82550", .args = "-machine q35 -nodefaults " "-device i82550,netdev=net0 -netdev user,id=net0", - .objects = "eepro*" + .objects = "i8255*" },{ .name = "sdhci-v3", .args = "-nodefaults -device sdhci-pci,sd-spec-version=3 " @@ -209,6 +209,12 @@ const generic_fuzz_config predefined_configs[] = { "-blockdev driver=null-co,read-zeroes=on,node-name=null0", .objects = "megasas*", },{ + .name = "am53c974", + .args = "-device am53c974,id=scsi -device scsi-hd,drive=disk0 " + "-drive id=disk0,if=none,file=null-co://,format=raw " + "-nodefaults", + .objects = "*esp* *scsi* *am53c974*", + },{ .name = "ac97", .args = "-machine q35 -nodefaults " "-device ac97,audiodev=snd0 -audiodev none,id=snd0 -nodefaults", diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 66ee9fbf45..6e871077c1 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -17,7 +17,10 @@ slow_qtests = { 'test-hmp' : 120, } -qtests_generic = [ +qtests_generic = \ + (config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \ + [ 'cdrom-test', 'device-introspect-test', 'machine-none-test', diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 523ee64fb7..3e13997406 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -792,7 +792,13 @@ static void fv_queue_set_started(VuDev *dev, int qidx, bool started) assert(0); } } else { + /* + * Temporarily drop write-lock taken in virtio_loop() so that + * the queue thread doesn't block in virtio_send_msg(). + */ + vu_dispatch_unlock(vud); fv_queue_cleanup_thread(vud, qidx); + vu_dispatch_wrlock(vud); } } diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index fc7e1b1e8e..b144320e48 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -37,6 +37,8 @@ #include "qemu/osdep.h" #include "qemu/timer.h" +#include "qemu-version.h" +#include "qemu-common.h" #include "fuse_virtio.h" #include "fuse_log.h" #include "fuse_lowlevel.h" @@ -221,22 +223,27 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st, static int xattr_map_client(const struct lo_data *lo, const char *client_name, char **out_name); -static int is_dot_or_dotdot(const char *name) +static bool is_dot_or_dotdot(const char *name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } /* Is `path` a single path component that is not "." or ".."? */ -static int is_safe_path_component(const char *path) +static bool is_safe_path_component(const char *path) { if (strchr(path, '/')) { - return 0; + return false; } return !is_dot_or_dotdot(path); } +static bool is_empty(const char *name) +{ + return name[0] == '\0'; +} + static struct lo_data *lo_data(fuse_req_t req) { return (struct lo_data *)fuse_req_userdata(req); @@ -1083,6 +1090,11 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, name); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + /* * Don't use is_safe_path_component(), allow "." and ".." for NFS export * support. @@ -1174,6 +1186,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, struct fuse_entry_param e; struct lo_cred old = {}; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1246,6 +1263,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, char procname[64]; int saverr; + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1308,8 +1330,7 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, return NULL; } - res = do_statx(lo, dir->fd, name, &attr, - AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, &mnt_id); + res = do_statx(lo, dir->fd, name, &attr, AT_SYMLINK_NOFOLLOW, &mnt_id); lo_inode_put(lo, &dir); if (res == -1) { return NULL; @@ -1324,6 +1345,11 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -1353,6 +1379,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, struct lo_inode *newinode = NULL; struct lo_data *lo = lo_data(req); + if (is_empty(name) || is_empty(newname)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { fuse_reply_err(req, EINVAL); return; @@ -1406,6 +1437,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) struct lo_inode *inode; struct lo_data *lo = lo_data(req); + if (is_empty(name)) { + fuse_reply_err(req, ENOENT); + return; + } + if (!is_safe_path_component(name)) { fuse_reply_err(req, EINVAL); return; @@ -3666,6 +3702,11 @@ static void fuse_lo_data_cleanup(struct lo_data *lo) free(lo->source); } +static void qemu_version(void) +{ + printf("virtiofsd version " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n"); +} + int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); @@ -3737,6 +3778,7 @@ int main(int argc, char *argv[]) ret = 0; goto err_out1; } else if (opts.show_version) { + qemu_version(); fuse_lowlevel_version(); ret = 0; goto err_out1; diff --git a/ui/cocoa.m b/ui/cocoa.m index a7848ae0a3..37e1fb52eb 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -690,7 +690,43 @@ QemuCocoaView *cocoaView; NSPoint p = [self screenLocationOfEvent:event]; NSUInteger modifiers = [event modifierFlags]; - // emulate caps lock keydown and keyup + /* + * Check -[NSEvent modifierFlags] here. + * + * There is a NSEventType for an event notifying the change of + * -[NSEvent modifierFlags], NSEventTypeFlagsChanged but these operations + * are performed for any events because a modifier state may change while + * the application is inactive (i.e. no events fire) and we don't want to + * wait for another modifier state change to detect such a change. + * + * NSEventModifierFlagCapsLock requires a special treatment. The other flags + * are handled in similar manners. + * + * NSEventModifierFlagCapsLock + * --------------------------- + * + * If CapsLock state is changed, "up" and "down" events will be fired in + * sequence, effectively updates CapsLock state on the guest. + * + * The other flags + * --------------- + * + * If a flag is not set, fire "up" events for all keys which correspond to + * the flag. Note that "down" events are not fired here because the flags + * checked here do not tell what exact keys are down. + * + * If one of the keys corresponding to a flag is down, we rely on + * -[NSEvent keyCode] of an event whose -[NSEvent type] is + * NSEventTypeFlagsChanged to know the exact key which is down, which has + * the following two downsides: + * - It does not work when the application is inactive as described above. + * - It malfactions *after* the modifier state is changed while the + * application is inactive. It is because -[NSEvent keyCode] does not tell + * if the key is up or down, and requires to infer the current state from + * the previous state. It is still possible to fix such a malfanction by + * completely leaving your hands from the keyboard, which hopefully makes + * this implementation usable enough. + */ if (!!(modifiers & NSEventModifierFlagCapsLock) != qkbd_state_modifier_get(kbd, QKBD_MOD_CAPSLOCK)) { qkbd_state_key_event(kbd, Q_KEY_CODE_CAPS_LOCK, true); @@ -1115,7 +1151,13 @@ QemuCocoaView *cocoaView; COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); - exit(0); + + /* + * Sleep here, because returning will cause OSX to kill us + * immediately; the QEMU main loop will handle the shutdown + * request and terminate the process. + */ + [NSThread sleepForTimeInterval:INFINITY]; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication diff --git a/ui/console-gl.c b/ui/console-gl.c index 0a6478161f..7c9894a51d 100644 --- a/ui/console-gl.c +++ b/ui/console-gl.c @@ -73,11 +73,20 @@ void surface_gl_create_texture(QemuGLShader *gls, glBindTexture(GL_TEXTURE_2D, surface->texture); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, surface_stride(surface) / surface_bytes_per_pixel(surface)); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, - surface_width(surface), - surface_height(surface), - 0, surface->glformat, surface->gltype, - surface_data(surface)); + if (epoxy_is_desktop_gl()) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + surface_width(surface), + surface_height(surface), + 0, surface->glformat, surface->gltype, + surface_data(surface)); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, surface->glformat, + surface_width(surface), + surface_height(surface), + 0, surface->glformat, surface->gltype, + surface_data(surface)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); + } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); diff --git a/ui/console.c b/ui/console.c index c2fdf975b6..2de5f4105b 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1386,26 +1386,18 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, return s; } -static void qemu_alloc_display(DisplaySurface *surface, int width, int height) +DisplaySurface *qemu_create_displaysurface(int width, int height) { - qemu_pixman_image_unref(surface->image); - surface->image = NULL; + DisplaySurface *surface = g_new0(DisplaySurface, 1); + trace_displaysurface_create(surface, width, height); surface->format = PIXMAN_x8r8g8b8; surface->image = pixman_image_create_bits(surface->format, width, height, NULL, width * 4); assert(surface->image != NULL); - surface->flags = QEMU_ALLOCATED_FLAG; -} -DisplaySurface *qemu_create_displaysurface(int width, int height) -{ - DisplaySurface *surface = g_new0(DisplaySurface, 1); - - trace_displaysurface_create(surface, width, height); - qemu_alloc_display(surface, width, height); return surface; } diff --git a/ui/spice-core.c b/ui/spice-core.c index cadec766fe..272d19b0c1 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -34,6 +34,7 @@ #include "qapi/qapi-events-ui.h" #include "qemu/notify.h" #include "qemu/option.h" +#include "crypto/secret_common.h" #include "migration/misc.h" #include "hw/pci/pci_bus.h" #include "ui/spice-display.h" @@ -416,6 +417,9 @@ static QemuOptsList qemu_spice_opts = { .name = "password", .type = QEMU_OPT_STRING, },{ + .name = "password-secret", + .type = QEMU_OPT_STRING, + },{ .name = "disable-ticketing", .type = QEMU_OPT_BOOL, },{ @@ -636,7 +640,9 @@ void qemu_spice_display_init_done(void) static void qemu_spice_init(void) { QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); - const char *password, *str, *x509_dir, *addr, + char *password = NULL; + const char *passwordSecret; + const char *str, *x509_dir, *addr, *x509_key_password = NULL, *x509_dh_file = NULL, *tls_ciphers = NULL; @@ -663,7 +669,28 @@ static void qemu_spice_init(void) error_report("spice tls-port is out of range"); exit(1); } - password = qemu_opt_get(opts, "password"); + passwordSecret = qemu_opt_get(opts, "password-secret"); + if (passwordSecret) { + Error *local_err = NULL; + if (qemu_opt_get(opts, "password")) { + error_report("'password' option is mutually exclusive with " + "'password-secret'"); + exit(1); + } + password = qcrypto_secret_lookup_as_utf8(passwordSecret, + &local_err); + if (!password) { + error_report_err(local_err); + exit(1); + } + } else { + str = qemu_opt_get(opts, "password"); + if (str) { + warn_report("'password' option is deprecated and insecure, " + "use 'password-secret' instead"); + password = g_strdup(str); + } + } if (tls_port) { x509_dir = qemu_opt_get(opts, "x509-dir"); @@ -809,6 +836,7 @@ static void qemu_spice_init(void) g_free(x509_key_file); g_free(x509_cert_file); g_free(x509_cacert_file); + g_free(password); #ifdef HAVE_SPICE_GL if (qemu_opt_get_bool(opts, "gl", 0)) { diff --git a/ui/trace-events b/ui/trace-events index 0ffcdb4408..5d1da6f236 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -37,6 +37,15 @@ vnc_key_event_ext(bool down, int sym, int keycode, const char *name) "down %d, s vnc_key_event_map(bool down, int sym, int keycode, const char *name) "down %d, sym 0x%x -> keycode 0x%x [%s]" vnc_key_sync_numlock(bool on) "%d" vnc_key_sync_capslock(bool on) "%d" +vnc_msg_server_audio_begin(void *state, void *ioc) "VNC server msg audio begin state=%p ioc=%p" +vnc_msg_server_audio_end(void *state, void *ioc) "VNC server msg audio end state=%p ioc=%p" +vnc_msg_server_audio_data(void *state, void *ioc, const void *buf, size_t len) "VNC server msg audio data state=%p ioc=%p buf=%p len=%zd" +vnc_msg_server_desktop_resize(void *state, void *ioc, int width, int height) "VNC server msg ext resize state=%p ioc=%p size=%dx%d" +vnc_msg_server_ext_desktop_resize(void *state, void *ioc, int width, int height, int reason) "VNC server msg ext resize state=%p ioc=%p size=%dx%d reason=%d" +vnc_msg_client_audio_enable(void *state, void *ioc) "VNC client msg audio enable state=%p ioc=%p" +vnc_msg_client_audio_disable(void *state, void *ioc) "VNC client msg audio disable state=%p ioc=%p" +vnc_msg_client_audio_format(void *state, void *ioc, int fmt, int channels, int freq) "VNC client msg audio format state=%p ioc=%p fmt=%d channels=%d freq=%d" +vnc_msg_client_set_desktop_size(void *state, void *ioc, int width, int height, int screens) "VNC client msg set desktop size state=%p ioc=%p size=%dx%d screens=%d" vnc_client_eof(void *state, void *ioc) "VNC client EOF state=%p ioc=%p" vnc_client_io_error(void *state, void *ioc, const char *msg) "VNC client I/O error state=%p ioc=%p errmsg=%s" vnc_client_connect(void *state, void *ioc) "VNC client connect state=%p ioc=%p" @@ -50,6 +59,13 @@ vnc_client_throttle_audio(void *state, void *ioc, size_t offset) "VNC client thr vnc_client_unthrottle_forced(void *state, void *ioc) "VNC client unthrottle forced offset state=%p ioc=%p" vnc_client_unthrottle_incremental(void *state, void *ioc, size_t offset) "VNC client unthrottle incremental state=%p ioc=%p offset=%zu" vnc_client_output_limit(void *state, void *ioc, size_t offset, size_t threshold) "VNC client output limit state=%p ioc=%p offset=%zu threshold=%zu" +vnc_server_dpy_pageflip(void *dpy, int w, int h, int fmt) "VNC server dpy pageflip dpy=%p size=%dx%d fmt=%d" +vnc_server_dpy_recreate(void *dpy, int w, int h, int fmt) "VNC server dpy recreate dpy=%p size=%dx%d fmt=%d" +vnc_job_add_rect(void *state, void *job, int x, int y, int w, int h) "VNC add rect state=%p job=%p offset=%d,%d size=%dx%d" +vnc_job_discard_rect(void *state, void *job, int x, int y, int w, int h) "VNC job discard rect state=%p job=%p offset=%d,%d size=%dx%d" +vnc_job_clamp_rect(void *state, void *job, int x, int y, int w, int h) "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d" +vnc_job_clamped_rect(void *state, void *job, int x, int y, int w, int h) "VNC job clamp rect state=%p job=%p offset=%d,%d size=%dx%d" +vnc_job_nrects(void *state, void *job, int nrects) "VNC job state=%p job=%p nrects=%d" vnc_auth_init(void *display, int websock, int auth, int subauth) "VNC auth init state=%p websock=%d auth=%d subauth=%d" vnc_auth_start(void *state, int method) "VNC client auth start state=%p method=%d" vnc_auth_pass(void *state, int method) "VNC client auth passed state=%p method=%d" diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index dbbfbefe56..4562bf8928 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -32,6 +32,7 @@ #include "qemu/sockets.h" #include "qemu/main-loop.h" #include "block/aio.h" +#include "trace.h" /* * Locking: @@ -94,6 +95,8 @@ int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h) { VncRectEntry *entry = g_new0(VncRectEntry, 1); + trace_vnc_job_add_rect(job->vs, job, x, y, w, h); + entry->rect.x = x; entry->rect.y = y; entry->rect.w = w; @@ -190,6 +193,8 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->zlib = orig->zlib; local->hextile = orig->hextile; local->zrle = orig->zrle; + local->client_width = orig->client_width; + local->client_height = orig->client_height; } static void vnc_async_encoding_end(VncState *orig, VncState *local) @@ -202,6 +207,34 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local) orig->lossy_rect = local->lossy_rect; } +static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect) +{ + trace_vnc_job_clamp_rect(vs, job, rect->x, rect->y, rect->w, rect->h); + + if (rect->x >= vs->client_width) { + goto discard; + } + rect->w = MIN(vs->client_width - rect->x, rect->w); + if (rect->w == 0) { + goto discard; + } + + if (rect->y >= vs->client_height) { + goto discard; + } + rect->h = MIN(vs->client_height - rect->y, rect->h); + if (rect->h == 0) { + goto discard; + } + + trace_vnc_job_clamped_rect(vs, job, rect->x, rect->y, rect->w, rect->h); + return true; + + discard: + trace_vnc_job_discard_rect(vs, job, rect->x, rect->y, rect->w, rect->h); + return false; +} + static int vnc_worker_thread_loop(VncJobQueue *queue) { VncJob *job; @@ -260,14 +293,17 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) goto disconnected; } - n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y, - entry->rect.w, entry->rect.h); + if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) { + n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y, + entry->rect.w, entry->rect.h); - if (n >= 0) { - n_rectangles += n; + if (n >= 0) { + n_rectangles += n; + } } g_free(entry); } + trace_vnc_job_nrects(&vs, job, n_rectangles); vnc_unlock_display(job->vs->vd); /* Put n_rectangles at the beginning of the message */ @@ -48,6 +48,7 @@ #include "crypto/tlscredsanon.h" #include "crypto/tlscredsx509.h" #include "crypto/random.h" +#include "crypto/secret_common.h" #include "qom/object_interfaces.h" #include "qemu/cutils.h" #include "qemu/help_option.h" @@ -607,6 +608,11 @@ static int vnc_width(VncDisplay *vd) VNC_DIRTY_PIXELS_PER_BIT)); } +static int vnc_true_width(VncDisplay *vd) +{ + return MIN(VNC_MAX_WIDTH, surface_width(vd->ds)); +} + static int vnc_height(VncDisplay *vd) { return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds)); @@ -658,6 +664,9 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, static void vnc_desktop_resize_ext(VncState *vs, int reject_reason) { + trace_vnc_msg_server_ext_desktop_resize( + vs, vs->ioc, vs->client_width, vs->client_height, reject_reason); + vnc_lock_output(vs); vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); vnc_write_u8(vs, 0); @@ -687,16 +696,16 @@ static void vnc_desktop_resize(VncState *vs) !vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT))) { return; } - if (vs->client_width == pixman_image_get_width(vs->vd->server) && + if (vs->client_width == vs->vd->true_width && vs->client_height == pixman_image_get_height(vs->vd->server)) { return; } - assert(pixman_image_get_width(vs->vd->server) < 65536 && - pixman_image_get_width(vs->vd->server) >= 0); + assert(vs->vd->true_width < 65536 && + vs->vd->true_width >= 0); assert(pixman_image_get_height(vs->vd->server) < 65536 && pixman_image_get_height(vs->vd->server) >= 0); - vs->client_width = pixman_image_get_width(vs->vd->server); + vs->client_width = vs->vd->true_width; vs->client_height = pixman_image_get_height(vs->vd->server); if (vnc_has_feature(vs, VNC_FEATURE_RESIZE_EXT)) { @@ -704,6 +713,9 @@ static void vnc_desktop_resize(VncState *vs) return; } + trace_vnc_msg_server_desktop_resize( + vs, vs->ioc, vs->client_width, vs->client_height); + vnc_lock_output(vs); vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); vnc_write_u8(vs, 0); @@ -767,6 +779,7 @@ static void vnc_update_server_surface(VncDisplay *vd) width = vnc_width(vd); height = vnc_height(vd); + vd->true_width = vnc_true_width(vd); vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, width, height, NULL, 0); @@ -802,13 +815,22 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, vd->guest.fb = pixman_image_ref(surface->image); vd->guest.format = surface->format; + if (pageflip) { + trace_vnc_server_dpy_pageflip(vd, + surface_width(surface), + surface_height(surface), + surface_format(surface)); vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, surface_width(surface), surface_height(surface)); return; } + trace_vnc_server_dpy_recreate(vd, + surface_width(surface), + surface_height(surface), + surface_format(surface)); /* server surface */ vnc_update_server_surface(vd); @@ -1181,6 +1203,7 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd) assert(vs->magic == VNC_MAGIC); switch (cmd) { case AUD_CNOTIFY_DISABLE: + trace_vnc_msg_server_audio_end(vs, vs->ioc); vnc_lock_output(vs); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); @@ -1190,6 +1213,7 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd) break; case AUD_CNOTIFY_ENABLE: + trace_vnc_msg_server_audio_begin(vs, vs->ioc); vnc_lock_output(vs); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); @@ -1209,6 +1233,7 @@ static void audio_capture(void *opaque, const void *buf, int size) VncState *vs = opaque; assert(vs->magic == VNC_MAGIC); + trace_vnc_msg_server_audio_data(vs, vs->ioc, buf, size); vnc_lock_output(vs); if (vs->output.offset < vs->throttle_output_offset) { vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); @@ -2309,8 +2334,8 @@ static void vnc_colordepth(VncState *vs) vnc_write_u8(vs, 0); vnc_write_u16(vs, 1); /* number of rects */ vnc_framebuffer_update(vs, 0, 0, - pixman_image_get_width(vs->vd->server), - pixman_image_get_height(vs->vd->server), + vs->client_width, + vs->client_height, VNC_ENCODING_WMVi); pixel_format_message(vs); vnc_unlock_output(vs); @@ -2453,9 +2478,11 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) switch (read_u16 (data, 2)) { case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE: + trace_vnc_msg_client_audio_enable(vs, vs->ioc); audio_add(vs); break; case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE: + trace_vnc_msg_client_audio_disable(vs, vs->ioc); audio_del(vs); break; case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT: @@ -2491,6 +2518,8 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) break; } vs->as.freq = freq; + trace_vnc_msg_client_audio_format( + vs, vs->ioc, vs->as.fmt, vs->as.nchannels, vs->as.freq); break; default: VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4)); @@ -2509,6 +2538,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) { size_t size; uint8_t screens; + int w, h; if (len < 8) { return 8; @@ -2519,12 +2549,15 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len < size) { return size; } + w = read_u16(data, 2); + h = read_u16(data, 4); + trace_vnc_msg_client_set_desktop_size(vs, vs->ioc, w, h, screens); if (dpy_ui_info_supported(vs->vd->dcl.con)) { QemuUIInfo info; memset(&info, 0, sizeof(info)); - info.width = read_u16(data, 2); - info.height = read_u16(data, 4); + info.width = w; + info.height = h; dpy_set_ui_info(vs->vd->dcl.con, &info); vnc_desktop_resize_ext(vs, 4 /* Request forwarded */); } else { @@ -3460,6 +3493,9 @@ static QemuOptsList qemu_vnc_opts = { .name = "password", .type = QEMU_OPT_BOOL, },{ + .name = "password-secret", + .type = QEMU_OPT_STRING, + },{ .name = "reverse", .type = QEMU_OPT_BOOL, },{ @@ -3931,6 +3967,7 @@ void vnc_display_open(const char *id, Error **errp) int lock_key_sync = 1; int key_delay_ms; const char *audiodev; + const char *passwordSecret; if (!vd) { error_setg(errp, "VNC display not active"); @@ -3948,7 +3985,23 @@ void vnc_display_open(const char *id, Error **errp) goto fail; } - password = qemu_opt_get_bool(opts, "password", false); + + passwordSecret = qemu_opt_get(opts, "password-secret"); + if (passwordSecret) { + if (qemu_opt_get(opts, "password")) { + error_setg(errp, + "'password' flag is redundant with 'password-secret'"); + goto fail; + } + vd->password = qcrypto_secret_lookup_as_utf8(passwordSecret, + errp); + if (!vd->password) { + goto fail; + } + password = true; + } else { + password = qemu_opt_get_bool(opts, "password", false); + } if (password) { if (fips_get_state()) { error_setg(errp, @@ -164,6 +164,7 @@ struct VncDisplay struct VncSurface guest; /* guest visible surface (aka ds->surface) */ pixman_image_t *server; /* vnc server surface */ + int true_width; /* server surface width before rounding up */ const char *id; QTAILQ_ENTRY(VncDisplay) next; |