summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS18
-rw-r--r--accel/kvm/kvm-all.c7
-rw-r--r--audio/coreaudio.c428
-rwxr-xr-xconfigure60
-rw-r--r--default-configs/devices/m68k-softmmu.mak1
-rw-r--r--docs/devel/fuzzing.rst56
-rw-r--r--docs/specs/virt-ctlr.txt26
-rw-r--r--docs/system/deprecated.rst8
-rw-r--r--hw/9pfs/9p-synth.c12
-rw-r--r--hw/char/Kconfig3
-rw-r--r--hw/char/goldfish_tty.c285
-rw-r--r--hw/char/meson.build2
-rw-r--r--hw/char/trace-events10
-rw-r--r--hw/intc/Kconfig6
-rw-r--r--hw/intc/goldfish_pic.c219
-rw-r--r--hw/intc/m68k_irqc.c119
-rw-r--r--hw/intc/meson.build2
-rw-r--r--hw/intc/trace-events8
-rw-r--r--hw/m68k/Kconfig9
-rw-r--r--hw/m68k/meson.build1
-rw-r--r--hw/m68k/virt.c313
-rw-r--r--hw/mem/meson.build1
-rw-r--r--hw/mem/sparse-mem.c151
-rw-r--r--hw/misc/Kconfig3
-rw-r--r--hw/misc/mac_via.c194
-rw-r--r--hw/misc/meson.build3
-rw-r--r--hw/misc/trace-events11
-rw-r--r--hw/misc/virt_ctrl.c151
-rw-r--r--hw/ppc/spapr_iommu.c5
-rw-r--r--hw/s390x/s390-pci-vfio.c9
-rw-r--r--hw/timer/i8254.c2
-rw-r--r--hw/vfio/common.c103
-rw-r--r--hw/vfio/migration.c63
-rw-r--r--hw/vfio/pci-quirks.c14
-rw-r--r--hw/vfio/pci.c24
-rw-r--r--hw/vfio/pci.h2
-rw-r--r--hw/vfio/trace-events2
-rw-r--r--include/hw/char/goldfish_tty.h35
-rw-r--r--include/hw/intc/goldfish_pic.h33
-rw-r--r--include/hw/intc/m68k_irqc.h41
-rw-r--r--include/hw/mem/sparse-mem.h19
-rw-r--r--include/hw/misc/mac_via.h8
-rw-r--r--include/hw/misc/virt_ctrl.h22
-rw-r--r--include/qapi/qmp/dispatch.h4
-rw-r--r--include/qemu/timer.h6
-rw-r--r--include/standard-headers/asm-m68k/bootinfo-virt.h18
-rw-r--r--include/sysemu/kvm.h4
-rw-r--r--migration/migration.c6
-rw-r--r--migration/multifd.c21
-rw-r--r--migration/ram.c6
-rw-r--r--monitor/monitor.c8
-rw-r--r--monitor/qmp.c51
-rw-r--r--pc-bios/openbios-ppcbin696912 -> 696912 bytes
-rw-r--r--pc-bios/openbios-sparc32bin382048 -> 382048 bytes
-rw-r--r--pc-bios/openbios-sparc64bin1593408 -> 1593408 bytes
-rw-r--r--qapi/qmp-dispatch.c6
-rw-r--r--qapi/qmp-registry.c10
-rw-r--r--qemu-options.hx18
-rw-r--r--qga/channel-win32.c7
-rw-r--r--qga/commands-posix.c47
-rw-r--r--qga/commands-win32.c26
-rw-r--r--qga/commands.c4
-rw-r--r--qga/main.c63
m---------roms/openbios0
-rwxr-xr-xscripts/oss-fuzz/output_reproducer.py160
-rw-r--r--scsi/utils.c4
-rw-r--r--softmmu/memory.c1
-rw-r--r--softmmu/physmem.c38
-rw-r--r--target/s390x/cpu.h5
-rw-r--r--target/s390x/cpu_models.c4
-rw-r--r--target/s390x/excp_helper.c3
-rw-r--r--target/s390x/helper.h2
-rw-r--r--target/s390x/insn-data.def2
-rw-r--r--target/s390x/kvm.c43
-rw-r--r--target/s390x/mem_helper.c160
-rw-r--r--target/s390x/translate.c7
-rw-r--r--tests/acceptance/avocado_qemu/__init__.py23
-rw-r--r--tests/acceptance/multiprocess.py95
-rw-r--r--tests/migration/guestperf/engine.py7
-rw-r--r--tests/qtest/fuzz-megasas-test.c49
-rw-r--r--tests/qtest/fuzz-test.c76
-rw-r--r--tests/qtest/fuzz-virtio-scsi-test.c75
-rw-r--r--tests/qtest/fuzz/generic_fuzz.c29
-rw-r--r--tests/qtest/fuzz/generic_fuzz_configs.h8
-rw-r--r--tests/qtest/meson.build5
-rw-r--r--tools/virtiofsd/fuse_virtio.c6
-rw-r--r--tools/virtiofsd/passthrough_ll.c52
-rw-r--r--ui/cocoa.m46
-rw-r--r--ui/console-gl.c19
-rw-r--r--ui/console.c14
-rw-r--r--ui/spice-core.c32
-rw-r--r--ui/trace-events16
-rw-r--r--ui/vnc-jobs.c44
-rw-r--r--ui/vnc.c71
-rw-r--r--ui/vnc.h1
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;
diff --git a/configure b/configure
index f7d022a5db..847bc4d095 100755
--- a/configure
+++ b/configure
@@ -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
index def6d4cf63..e40e1d7025 100644
--- a/pc-bios/openbios-ppc
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index 67b8b83d98..0c0aa094ec 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index dfa1f10bbc..a3e458517a 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
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 */
diff --git a/ui/vnc.c b/ui/vnc.c
index 310abc9378..9c004a11f4 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -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,
diff --git a/ui/vnc.h b/ui/vnc.h
index 116463d5f0..d4f3e15558 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -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;