Merge remote-tracking branch 'upstream/master' into main

This commit is contained in:
Andrea Fioraldi 2022-10-17 17:48:40 +02:00
commit 97211a7b7c
87 changed files with 3720 additions and 1104 deletions

View File

@ -77,86 +77,12 @@
do { } while (0) do { } while (0)
#endif #endif
#define KVM_MSI_HASHTAB_SIZE 256
struct KVMParkedVcpu { struct KVMParkedVcpu {
unsigned long vcpu_id; unsigned long vcpu_id;
int kvm_fd; int kvm_fd;
QLIST_ENTRY(KVMParkedVcpu) node; QLIST_ENTRY(KVMParkedVcpu) node;
}; };
enum KVMDirtyRingReaperState {
KVM_DIRTY_RING_REAPER_NONE = 0,
/* The reaper is sleeping */
KVM_DIRTY_RING_REAPER_WAIT,
/* The reaper is reaping for dirty pages */
KVM_DIRTY_RING_REAPER_REAPING,
};
/*
* KVM reaper instance, responsible for collecting the KVM dirty bits
* via the dirty ring.
*/
struct KVMDirtyRingReaper {
/* The reaper thread */
QemuThread reaper_thr;
volatile uint64_t reaper_iteration; /* iteration number of reaper thr */
volatile enum KVMDirtyRingReaperState reaper_state; /* reap thr state */
};
struct KVMState
{
AccelState parent_obj;
int nr_slots;
int fd;
int vmfd;
int coalesced_mmio;
int coalesced_pio;
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
bool coalesced_flush_in_progress;
int vcpu_events;
int robust_singlestep;
int debugregs;
#ifdef KVM_CAP_SET_GUEST_DEBUG
QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
#endif
int max_nested_state_len;
int many_ioeventfds;
int intx_set_mask;
int kvm_shadow_mem;
bool kernel_irqchip_allowed;
bool kernel_irqchip_required;
OnOffAuto kernel_irqchip_split;
bool sync_mmu;
uint64_t manual_dirty_log_protect;
/* The man page (and posix) say ioctl numbers are signed int, but
* they're not. Linux, glibc and *BSD all treat ioctl numbers as
* unsigned, and treating them as signed here can break things */
unsigned irq_set_ioctl;
unsigned int sigmask_len;
GHashTable *gsimap;
#ifdef KVM_CAP_IRQ_ROUTING
struct kvm_irq_routing *irq_routes;
int nr_allocated_irq_routes;
unsigned long *used_gsi_bitmap;
unsigned int gsi_count;
QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
#endif
KVMMemoryListener memory_listener;
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
/* For "info mtree -f" to tell if an MR is registered in KVM */
int nr_as;
struct KVMAs {
KVMMemoryListener *ml;
AddressSpace *as;
} *as;
uint64_t kvm_dirty_ring_bytes; /* Size of the per-vcpu dirty ring */
uint32_t kvm_dirty_ring_size; /* Number of dirty GFNs per ring */
struct KVMDirtyRingReaper reaper;
};
KVMState *kvm_state; KVMState *kvm_state;
bool kvm_kernel_irqchip; bool kvm_kernel_irqchip;
bool kvm_split_irqchip; bool kvm_split_irqchip;
@ -3692,6 +3618,8 @@ static void kvm_accel_instance_init(Object *obj)
s->kernel_irqchip_split = ON_OFF_AUTO_AUTO; s->kernel_irqchip_split = ON_OFF_AUTO_AUTO;
/* KVM dirty ring is by default off */ /* KVM dirty ring is by default off */
s->kvm_dirty_ring_size = 0; s->kvm_dirty_ring_size = 0;
s->notify_vmexit = NOTIFY_VMEXIT_OPTION_RUN;
s->notify_window = 0;
} }
/** /**
@ -3731,6 +3659,8 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data)
NULL, NULL); NULL, NULL);
object_class_property_set_description(oc, "dirty-ring-size", object_class_property_set_description(oc, "dirty-ring-size",
"Size of KVM dirty page ring buffer (default: 0, i.e. use bitmap)"); "Size of KVM dirty page ring buffer (default: 0, i.e. use bitmap)");
kvm_arch_accel_class_init(oc);
} }
static const TypeInfo kvm_accel_type = { static const TypeInfo kvm_accel_type = {

View File

@ -602,6 +602,42 @@ static int alsa_open(bool in, struct alsa_params_req *req,
return -1; return -1;
} }
static size_t alsa_buffer_get_free(HWVoiceOut *hw)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *)hw;
snd_pcm_sframes_t avail;
size_t alsa_free, generic_free, generic_in_use;
avail = snd_pcm_avail_update(alsa->handle);
if (avail < 0) {
if (avail == -EPIPE) {
if (!alsa_recover(alsa->handle)) {
avail = snd_pcm_avail_update(alsa->handle);
}
}
if (avail < 0) {
alsa_logerr(avail,
"Could not obtain number of available frames\n");
avail = 0;
}
}
alsa_free = avail * hw->info.bytes_per_frame;
generic_free = audio_generic_buffer_get_free(hw);
generic_in_use = hw->samples * hw->info.bytes_per_frame - generic_free;
if (generic_in_use) {
/*
* This code can only be reached in the unlikely case that
* snd_pcm_avail_update() returned a larger number of frames
* than snd_pcm_writei() could write. Make sure that all
* remaining bytes in the generic buffer can be written.
*/
alsa_free = alsa_free > generic_in_use ? alsa_free - generic_in_use : 0;
}
return alsa_free;
}
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len) static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
{ {
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
@ -916,7 +952,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out, .init_out = alsa_init_out,
.fini_out = alsa_fini_out, .fini_out = alsa_fini_out,
.write = alsa_write, .write = alsa_write,
.buffer_get_free = audio_generic_buffer_get_free, .buffer_get_free = alsa_buffer_get_free,
.run_buffer_out = audio_generic_run_buffer_out, .run_buffer_out = audio_generic_run_buffer_out,
.enable_out = alsa_enable_out, .enable_out = alsa_enable_out,

View File

@ -986,6 +986,18 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
} }
} }
/**
* audio_frontend_frames_in() - returns the number of frames the resampling
* code generates from frames_in frames
*
* @sw: audio recording frontend
* @frames_in: number of frames
*/
static size_t audio_frontend_frames_in(SWVoiceIn *sw, size_t frames_in)
{
return (int64_t)frames_in * sw->ratio >> 32;
}
static size_t audio_get_avail (SWVoiceIn *sw) static size_t audio_get_avail (SWVoiceIn *sw)
{ {
size_t live; size_t live;
@ -1002,17 +1014,24 @@ static size_t audio_get_avail (SWVoiceIn *sw)
} }
ldebug ( ldebug (
"%s: get_avail live %zu ret %" PRId64 "\n", "%s: get_avail live %zu frontend frames %zu\n",
SW_NAME (sw), SW_NAME (sw),
live, (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame live, audio_frontend_frames_in(sw, live)
); );
return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame; return live;
} }
static size_t audio_sw_bytes_free(SWVoiceOut *sw, size_t free) /**
* audio_frontend_frames_out() - returns the number of frames needed to
* get frames_out frames after resampling
*
* @sw: audio playback frontend
* @frames_out: number of frames
*/
static size_t audio_frontend_frames_out(SWVoiceOut *sw, size_t frames_out)
{ {
return (((int64_t)free << 32) / sw->ratio) * sw->info.bytes_per_frame; return ((int64_t)frames_out << 32) / sw->ratio;
} }
static size_t audio_get_free(SWVoiceOut *sw) static size_t audio_get_free(SWVoiceOut *sw)
@ -1034,8 +1053,8 @@ static size_t audio_get_free(SWVoiceOut *sw)
dead = sw->hw->mix_buf->size - live; dead = sw->hw->mix_buf->size - live;
#ifdef DEBUG_OUT #ifdef DEBUG_OUT
dolog("%s: get_free live %zu dead %zu sw_bytes %zu\n", dolog("%s: get_free live %zu dead %zu frontend frames %zu\n",
SW_NAME(sw), live, dead, audio_sw_bytes_free(sw, dead)); SW_NAME(sw), live, dead, audio_frontend_frames_out(sw, dead));
#endif #endif
return dead; return dead;
@ -1121,8 +1140,12 @@ static void audio_run_out (AudioState *s)
HWVoiceOut *hw = NULL; HWVoiceOut *hw = NULL;
SWVoiceOut *sw; SWVoiceOut *sw;
if (!audio_get_pdo_out(s->dev)->mixing_engine) {
while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
size_t played, live, prev_rpos;
size_t hw_free = audio_pcm_hw_get_free(hw);
int nb_live;
if (!audio_get_pdo_out(s->dev)->mixing_engine) {
/* there is exactly 1 sw for each hw with no mixeng */ /* there is exactly 1 sw for each hw with no mixeng */
sw = hw->sw_head.lh_first; sw = hw->sw_head.lh_first;
@ -1135,16 +1158,16 @@ static void audio_run_out (AudioState *s)
} }
if (sw->active) { if (sw->active) {
sw->callback.fn(sw->callback.opaque, INT_MAX); sw->callback.fn(sw->callback.opaque,
} hw_free * sw->info.bytes_per_frame);
}
return;
} }
while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { if (hw->pcm_ops->run_buffer_out) {
size_t played, live, prev_rpos; hw->pcm_ops->run_buffer_out(hw);
size_t hw_free = audio_pcm_hw_get_free(hw); }
int nb_live;
continue;
}
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) { if (sw->active) {
@ -1152,13 +1175,14 @@ static void audio_run_out (AudioState *s)
size_t free; size_t free;
if (hw_free > sw->total_hw_samples_mixed) { if (hw_free > sw->total_hw_samples_mixed) {
free = audio_sw_bytes_free(sw, free = audio_frontend_frames_out(sw,
MIN(sw_free, hw_free - sw->total_hw_samples_mixed)); MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
} else { } else {
free = 0; free = 0;
} }
if (free > 0) { if (free > 0) {
sw->callback.fn(sw->callback.opaque, free); sw->callback.fn(sw->callback.opaque,
free * sw->info.bytes_per_frame);
} }
} }
} }
@ -1297,11 +1321,13 @@ static void audio_run_in (AudioState *s)
sw->total_hw_samples_acquired -= min; sw->total_hw_samples_acquired -= min;
if (sw->active) { if (sw->active) {
size_t sw_avail = audio_get_avail(sw);
size_t avail; size_t avail;
avail = audio_get_avail (sw); avail = audio_frontend_frames_in(sw, sw_avail);
if (avail > 0) { if (avail > 0) {
sw->callback.fn (sw->callback.opaque, avail); sw->callback.fn(sw->callback.opaque,
avail * sw->info.bytes_per_frame);
} }
} }
} }
@ -1501,10 +1527,6 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
} }
} }
if (hw->pcm_ops->run_buffer_out) {
hw->pcm_ops->run_buffer_out(hw);
}
return total; return total;
} }
@ -1750,13 +1772,13 @@ static AudioState *audio_init(Audiodev *dev, const char *name)
s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices; s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices;
s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices; s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices;
if (s->nb_hw_voices_out <= 0) { if (s->nb_hw_voices_out < 1) {
dolog ("Bogus number of playback voices %d, setting to 1\n", dolog ("Bogus number of playback voices %d, setting to 1\n",
s->nb_hw_voices_out); s->nb_hw_voices_out);
s->nb_hw_voices_out = 1; s->nb_hw_voices_out = 1;
} }
if (s->nb_hw_voices_in <= 0) { if (s->nb_hw_voices_in < 0) {
dolog ("Bogus number of capture voices %d, setting to 0\n", dolog ("Bogus number of capture voices %d, setting to 0\n",
s->nb_hw_voices_in); s->nb_hw_voices_in);
s->nb_hw_voices_in = 0; s->nb_hw_voices_in = 0;
@ -2251,26 +2273,39 @@ void audio_rate_start(RateCtl *rate)
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
} }
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate, size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)
size_t bytes_avail)
{ {
int64_t now; int64_t now;
int64_t ticks; int64_t ticks;
int64_t bytes; int64_t bytes;
int64_t samples; int64_t frames;
size_t ret;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - rate->start_ticks; ticks = now - rate->start_ticks;
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND); bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
samples = (bytes - rate->bytes_sent) / info->bytes_per_frame; frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;
if (samples < 0 || samples > 65536) { if (frames < 0 || frames > 65536) {
AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)\n", samples); AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames);
audio_rate_start(rate); audio_rate_start(rate);
samples = 0; frames = 0;
} }
ret = MIN(samples * info->bytes_per_frame, bytes_avail); return frames * info->bytes_per_frame;
rate->bytes_sent += ret; }
return ret;
void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)
{
rate->bytes_sent += bytes_used;
}
size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
size_t bytes_avail)
{
size_t bytes;
bytes = audio_rate_peek_bytes(rate, info);
bytes = MIN(bytes, bytes_avail);
audio_rate_add_bytes(rate, bytes);
return bytes;
} }

View File

@ -263,7 +263,9 @@ typedef struct RateCtl {
} RateCtl; } RateCtl;
void audio_rate_start(RateCtl *rate); void audio_rate_start(RateCtl *rate);
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate, size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info);
void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used);
size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
size_t bytes_avail); size_t bytes_avail);
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len) static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)

View File

@ -110,7 +110,11 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
return 0; return 0;
} }
#ifdef DAC
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio; samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
#else
samples = (int64_t)sw->HWBUF->size * sw->ratio >> 32;
#endif
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample)); sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) { if (!sw->buf) {

View File

@ -82,7 +82,7 @@ static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
} }
*size = MIN(vo->buf_size - vo->buf_pos, *size); *size = MIN(vo->buf_size - vo->buf_pos, *size);
*size = audio_rate_get_bytes(&hw->info, &vo->rate, *size); *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
return vo->buf + vo->buf_pos; return vo->buf + vo->buf_pos;
@ -343,7 +343,7 @@ dbus_read(HWVoiceIn *hw, void *buf, size_t size)
trace_dbus_audio_read(size); trace_dbus_audio_read(size);
/* size = audio_rate_get_bytes(&hw->info, &vo->rate, size); */ /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
g_hash_table_iter_init(&iter, da->in_listeners); g_hash_table_iter_init(&iter, da->in_listeners);
while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {

View File

@ -44,7 +44,7 @@ typedef struct NoVoiceIn {
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len) static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
{ {
NoVoiceOut *no = (NoVoiceOut *) hw; NoVoiceOut *no = (NoVoiceOut *) hw;
return audio_rate_get_bytes(&hw->info, &no->rate, len); return audio_rate_get_bytes(&no->rate, &hw->info, len);
} }
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
@ -89,7 +89,7 @@ static void no_fini_in (HWVoiceIn *hw)
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size) static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
{ {
NoVoiceIn *no = (NoVoiceIn *) hw; NoVoiceIn *no = (NoVoiceIn *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size); int64_t bytes = audio_rate_get_bytes(&no->rate, &hw->info, size);
audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame); audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame);
return bytes; return bytes;

View File

@ -72,11 +72,6 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
ilast = *ibuf++; ilast = *ibuf++;
rate->ipos++; rate->ipos++;
/* if ipos overflow, there is a infinite loop */
if (rate->ipos == 0xffffffff) {
rate->ipos = 1;
rate->opos = rate->opos & 0xffffffff;
}
/* See if we finished the input buffer yet */ /* See if we finished the input buffer yet */
if (ibuf >= iend) { if (ibuf >= iend) {
goto the_end; goto the_end;
@ -85,6 +80,12 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
icur = *ibuf; icur = *ibuf;
/* wrap ipos and opos around long before they overflow */
if (rate->ipos >= 0x10001) {
rate->ipos = 1;
rate->opos &= 0xffffffff;
}
/* interpolate */ /* interpolate */
#ifdef FLOAT_MIXENG #ifdef FLOAT_MIXENG
#ifdef RECIPROCAL #ifdef RECIPROCAL

View File

@ -120,6 +120,13 @@ static void line_out_fini (HWVoiceOut *hw)
spice_server_remove_interface (&out->sin.base); spice_server_remove_interface (&out->sin.base);
} }
static size_t line_out_get_free(HWVoiceOut *hw)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
return audio_rate_peek_bytes(&out->rate, &hw->info);
}
static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size) static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
{ {
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
@ -133,8 +140,6 @@ static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
*size = MIN((out->fsize - out->fpos) << 2, *size); *size = MIN((out->fsize - out->fpos) << 2, *size);
} }
*size = audio_rate_get_bytes(&hw->info, &out->rate, *size);
return out->frame + out->fpos; return out->frame + out->fpos;
} }
@ -142,6 +147,8 @@ static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
{ {
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
audio_rate_add_bytes(&out->rate, size);
if (buf) { if (buf) {
assert(buf == out->frame + out->fpos && out->fpos <= out->fsize); assert(buf == out->frame + out->fpos && out->fpos <= out->fsize);
out->fpos += size >> 2; out->fpos += size >> 2;
@ -232,10 +239,13 @@ static void line_in_fini (HWVoiceIn *hw)
static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len) static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
{ {
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2; uint64_t to_read = audio_rate_get_bytes(&in->rate, &hw->info, len) >> 2;
size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read); size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
/* XXX: do we need this? */ /*
* If the client didn't send new frames, it most likely disconnected.
* Generate silence in this case to avoid a stalled audio stream.
*/
if (ready == 0) { if (ready == 0) {
memset(buf, 0, to_read << 2); memset(buf, 0, to_read << 2);
ready = to_read; ready = to_read;
@ -282,6 +292,7 @@ static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init, .init_out = line_out_init,
.fini_out = line_out_fini, .fini_out = line_out_fini,
.write = audio_generic_write, .write = audio_generic_write,
.buffer_get_free = line_out_get_free,
.get_buffer_out = line_out_get_buffer, .get_buffer_out = line_out_get_buffer,
.put_buffer_out = line_out_put_buffer, .put_buffer_out = line_out_put_buffer,
.enable_out = line_out_enable, .enable_out = line_out_enable,

View File

@ -42,7 +42,7 @@ typedef struct WAVVoiceOut {
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len) static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
{ {
WAVVoiceOut *wav = (WAVVoiceOut *) hw; WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len); int64_t bytes = audio_rate_get_bytes(&wav->rate, &hw->info, len);
assert(bytes % hw->info.bytes_per_frame == 0); assert(bytes % hw->info.bytes_per_frame == 0);
if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) { if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {

File diff suppressed because it is too large Load Diff

View File

@ -113,13 +113,22 @@
# Virtualization, as specified in the AMD64 Architecture # Virtualization, as specified in the AMD64 Architecture
# Programmer's Manual. QEMU command line options related to # Programmer's Manual. QEMU command line options related to
# this feature are documented in # this feature are documented in
# "docs/amd-memory-encryption.txt". # "docs/system/i386/amd-memory-encryption.rst".
# #
# @amd-sev-es: The firmware supports running under AMD Secure Encrypted # @amd-sev-es: The firmware supports running under AMD Secure Encrypted
# Virtualization - Encrypted State, as specified in the AMD64 # Virtualization - Encrypted State, as specified in the AMD64
# Architecture Programmer's Manual. QEMU command line options # Architecture Programmer's Manual. QEMU command line options
# related to this feature are documented in # related to this feature are documented in
# "docs/amd-memory-encryption.txt". # "docs/system/i386/amd-memory-encryption.rst".
#
# @amd-sev-snp: The firmware supports running under AMD Secure Encrypted
# Virtualization - Secure Nested Paging, as specified in the
# AMD64 Architecture Programmer's Manual. QEMU command line
# options related to this feature are documented in
# "docs/system/i386/amd-memory-encryption.rst".
#
# @intel-tdx: The firmware supports running under Intel Trust Domain
# Extensions (TDX).
# #
# @enrolled-keys: The variable store (NVRAM) template associated with # @enrolled-keys: The variable store (NVRAM) template associated with
# the firmware binary has the UEFI Secure Boot # the firmware binary has the UEFI Secure Boot
@ -185,9 +194,11 @@
# Since: 3.0 # Since: 3.0
## ##
{ 'enum' : 'FirmwareFeature', { 'enum' : 'FirmwareFeature',
'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'amd-sev-es', 'enrolled-keys', 'data' : [ 'acpi-s3', 'acpi-s4',
'requires-smm', 'secure-boot', 'verbose-dynamic', 'amd-sev', 'amd-sev-es', 'amd-sev-snp',
'verbose-static' ] } 'intel-tdx',
'enrolled-keys', 'requires-smm', 'secure-boot',
'verbose-dynamic', 'verbose-static' ] }
## ##
# @FirmwareFlashFile: # @FirmwareFlashFile:

View File

@ -22,16 +22,14 @@ maintained as part of the virtio specification.
1af4:1004 SCSI host bus adapter device (legacy) 1af4:1004 SCSI host bus adapter device (legacy)
1af4:1005 entropy generator device (legacy) 1af4:1005 entropy generator device (legacy)
1af4:1009 9p filesystem device (legacy) 1af4:1009 9p filesystem device (legacy)
1af4:1012 vsock device (bug compatibility)
1af4:1041 network device (modern) 1af4:1040 Start of ID range for modern virtio devices. The PCI device
1af4:1042 block device (modern) to ID is calculated from the virtio device ID by adding the
1af4:1043 console device (modern) 1af4:10ef 0x1040 offset. The virtio IDs are defined in the virtio
1af4:1044 entropy generator device (modern) specification. The Linux kernel has a header file with
1af4:1045 balloon device (modern) defines for all virtio IDs (linux/virtio_ids.h), qemu has a
1af4:1048 SCSI host bus adapter device (modern) copy in include/standard-headers/.
1af4:1049 9p filesystem device (modern)
1af4:1050 virtio gpu device (modern)
1af4:1052 virtio input device (modern)
1af4:10f0 Available for experimental usage without registration. Must get 1af4:10f0 Available for experimental usage without registration. Must get
to official ID when the code leaves the test lab (i.e. when seeking to official ID when the code leaves the test lab (i.e. when seeking

View File

@ -822,55 +822,6 @@ static void do_cpu_reset(void *opaque)
} }
} }
/**
* load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified
* by key.
* @fw_cfg: The firmware config instance to store the data in.
* @size_key: The firmware config key to store the size of the loaded
* data under, with fw_cfg_add_i32().
* @data_key: The firmware config key to store the loaded data under,
* with fw_cfg_add_bytes().
* @image_name: The name of the image file to load. If it is NULL, the
* function returns without doing anything.
* @try_decompress: Whether the image should be decompressed (gunzipped) before
* adding it to fw_cfg. If decompression fails, the image is
* loaded as-is.
*
* In case of failure, the function prints an error message to stderr and the
* process exits with status 1.
*/
static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
error_report("failed to load \"%s\"", image_name);
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static int do_arm_linux_init(Object *obj, void *opaque) static int do_arm_linux_init(Object *obj, void *opaque)
{ {
if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) { if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) {

View File

@ -834,7 +834,7 @@ static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
word alignment, so we keep them for the next line */ word alignment, so we keep them for the next line */
/* XXX: keep alignment to speed up transfer */ /* XXX: keep alignment to speed up transfer */
end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
copy_count = s->cirrus_srcptr_end - end_ptr; copy_count = MIN(s->cirrus_srcptr_end - end_ptr, CIRRUS_BLTBUFSIZE);
memmove(s->cirrus_bltbuf, end_ptr, copy_count); memmove(s->cirrus_bltbuf, end_ptr, copy_count);
s->cirrus_srcptr = s->cirrus_bltbuf + copy_count; s->cirrus_srcptr = s->cirrus_bltbuf + copy_count;
s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;

View File

@ -180,8 +180,18 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) { if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) {
uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
plic->source_priority[irq] = value & 7; if (((plic->num_priorities + 1) & plic->num_priorities) == 0) {
/*
* if "num_priorities + 1" is power-of-2, make each register bit of
* interrupt priority WARL (Write-Any-Read-Legal). Just filter
* out the access to unsupported priority bits.
*/
plic->source_priority[irq] = value % (plic->num_priorities + 1);
sifive_plic_update(plic); sifive_plic_update(plic);
} else if (value <= plic->num_priorities) {
plic->source_priority[irq] = value;
sifive_plic_update(plic);
}
} else if (addr_between(addr, plic->pending_base, } else if (addr_between(addr, plic->pending_base,
plic->num_sources >> 3)) { plic->num_sources >> 3)) {
qemu_log_mask(LOG_GUEST_ERROR, qemu_log_mask(LOG_GUEST_ERROR,
@ -205,7 +215,16 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
uint32_t contextid = (addr & (plic->context_stride - 1)); uint32_t contextid = (addr & (plic->context_stride - 1));
if (contextid == 0) { if (contextid == 0) {
if (value <= plic->num_priorities) { if (((plic->num_priorities + 1) & plic->num_priorities) == 0) {
/*
* if "num_priorities + 1" is power-of-2, each register bit of
* interrupt priority is WARL (Write-Any-Read-Legal). Just
* filter out the access to unsupported priority bits.
*/
plic->target_priority[addrid] = value %
(plic->num_priorities + 1);
sifive_plic_update(plic);
} else if (value <= plic->num_priorities) {
plic->target_priority[addrid] = value; plic->target_priority[addrid] = value;
sifive_plic_update(plic); sifive_plic_update(plic);
} }

View File

@ -598,39 +598,6 @@ static void reset_load_elf(void *opaque)
} }
} }
/* Load an image file into an fw_cfg entry identified by key. */
static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
error_report("failed to load \"%s\"", image_name);
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static void fw_cfg_add_kernel_info(FWCfgState *fw_cfg) static void fw_cfg_add_kernel_info(FWCfgState *fw_cfg)
{ {
/* /*

View File

@ -41,6 +41,7 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "hw/acpi/aml-build.h" #include "hw/acpi/aml-build.h"
#include "hw/pci/pci_bus.h" #include "hw/pci/pci_bus.h"
#include "hw/loader.h"
#define FW_CFG_FILE_SLOTS_DFLT 0x20 #define FW_CFG_FILE_SLOTS_DFLT 0x20
@ -1221,6 +1222,37 @@ FWCfgState *fw_cfg_find(void)
return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL)); return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL));
} }
void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
error_report("failed to load \"%s\"", image_name);
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static void fw_cfg_class_init(ObjectClass *klass, void *data) static void fw_cfg_class_init(ObjectClass *klass, void *data)
{ {

View File

@ -111,8 +111,8 @@ char *riscv_find_firmware(const char *firmware_filename)
if (filename == NULL) { if (filename == NULL) {
if (!qtest_enabled()) { if (!qtest_enabled()) {
/* /*
* We only ship plain binary bios images in the QEMU source. * We only ship OpenSBI binary bios images in the QEMU source.
* With Spike machine that uses ELF images as the default bios, * For machines that use images other than the default bios,
* running QEMU test will complain hence let's suppress the error * running QEMU test will complain hence let's suppress the error
* report for QEMU testing. * report for QEMU testing.
*/ */
@ -338,3 +338,32 @@ void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr)
riscv_cpu->env.fdt_addr = fdt_addr; riscv_cpu->env.fdt_addr = fdt_addr;
} }
} }
void riscv_setup_firmware_boot(MachineState *machine)
{
if (machine->kernel_filename) {
FWCfgState *fw_cfg;
fw_cfg = fw_cfg_find();
assert(fw_cfg);
/*
* Expose the kernel, the command line, and the initrd in fw_cfg.
* We don't process them here at all, it's all left to the
* firmware.
*/
load_image_to_fw_cfg(fw_cfg,
FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
machine->kernel_filename,
true);
load_image_to_fw_cfg(fw_cfg,
FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
machine->initrd_filename, false);
if (machine->kernel_cmdline) {
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
strlen(machine->kernel_cmdline) + 1);
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
machine->kernel_cmdline);
}
}
}

View File

@ -1267,7 +1267,30 @@ static void virt_machine_done(Notifier *notifier, void *data)
RISCV64_BIOS_BIN, start_addr, NULL); RISCV64_BIOS_BIN, start_addr, NULL);
} }
if (machine->kernel_filename) { /*
* Init fw_cfg. Must be done before riscv_load_fdt, otherwise the device
* tree cannot be altered and we get FDT_ERR_NOSPACE.
*/
s->fw_cfg = create_fw_cfg(machine);
rom_set_fw(s->fw_cfg);
if (drive_get(IF_PFLASH, 0, 1)) {
/*
* S-mode FW like EDK2 will be kept in second plash (unit 1).
* When both kernel, initrd and pflash options are provided in the
* command line, the kernel and initrd will be copied to the fw_cfg
* table and opensbi will jump to the flash address which is the
* entry point of S-mode FW. It is the job of the S-mode FW to load
* the kernel and initrd using fw_cfg table.
*
* If only pflash is given but not -kernel, then it is the job of
* of the S-mode firmware to locate and load the kernel.
* In either case, the next_addr for opensbi will be the flash address.
*/
riscv_setup_firmware_boot(machine);
kernel_entry = virt_memmap[VIRT_FLASH].base +
virt_memmap[VIRT_FLASH].size / 2;
} else if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0],
firmware_end_addr); firmware_end_addr);
@ -1300,13 +1323,6 @@ static void virt_machine_done(Notifier *notifier, void *data)
start_addr = virt_memmap[VIRT_FLASH].base; start_addr = virt_memmap[VIRT_FLASH].base;
} }
/*
* Init fw_cfg. Must be done before riscv_load_fdt, otherwise the device
* tree cannot be altered and we get FDT_ERR_NOSPACE.
*/
s->fw_cfg = create_fw_cfg(machine);
rom_set_fw(s->fw_cfg);
/* Compute the fdt load address in dram */ /* Compute the fdt load address in dram */
fdt_load_addr = riscv_load_fdt(memmap[VIRT_DRAM].base, fdt_load_addr = riscv_load_fdt(memmap[VIRT_DRAM].base,
machine->ram_size, machine->fdt); machine->ram_size, machine->fdt);

View File

@ -2544,6 +2544,7 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
AioContext *ctx; AioContext *ctx;
int ret; int ret;
uint32_t blocksize = 2048;
if (!dev->conf.blk) { if (!dev->conf.blk) {
/* Anonymous BlockBackend for an empty drive. As we put it into /* Anonymous BlockBackend for an empty drive. As we put it into
@ -2553,9 +2554,13 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp)
assert(ret == 0); assert(ret == 0);
} }
if (dev->conf.physical_block_size != 0) {
blocksize = dev->conf.physical_block_size;
}
ctx = blk_get_aio_context(dev->conf.blk); ctx = blk_get_aio_context(dev->conf.blk);
aio_context_acquire(ctx); aio_context_acquire(ctx);
s->qdev.blocksize = 2048; s->qdev.blocksize = blocksize;
s->qdev.type = TYPE_ROM; s->qdev.type = TYPE_ROM;
s->features |= 1 << SCSI_DISK_F_REMOVABLE; s->features |= 1 << SCSI_DISK_F_REMOVABLE;
if (!s->product) { if (!s->product) {

View File

@ -108,18 +108,22 @@ static inline uint8_t div4_round_up(uint8_t dividend)
static void ibex_spi_rxfifo_reset(IbexSPIHostState *s) static void ibex_spi_rxfifo_reset(IbexSPIHostState *s)
{ {
uint32_t data = s->regs[IBEX_SPI_HOST_STATUS];
/* Empty the RX FIFO and assert RXEMPTY */ /* Empty the RX FIFO and assert RXEMPTY */
fifo8_reset(&s->rx_fifo); fifo8_reset(&s->rx_fifo);
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK; data = FIELD_DP32(data, STATUS, RXFULL, 0);
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK; data = FIELD_DP32(data, STATUS, RXEMPTY, 1);
s->regs[IBEX_SPI_HOST_STATUS] = data;
} }
static void ibex_spi_txfifo_reset(IbexSPIHostState *s) static void ibex_spi_txfifo_reset(IbexSPIHostState *s)
{ {
uint32_t data = s->regs[IBEX_SPI_HOST_STATUS];
/* Empty the TX FIFO and assert TXEMPTY */ /* Empty the TX FIFO and assert TXEMPTY */
fifo8_reset(&s->tx_fifo); fifo8_reset(&s->tx_fifo);
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK; data = FIELD_DP32(data, STATUS, TXFULL, 0);
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXEMPTY_MASK; data = FIELD_DP32(data, STATUS, TXEMPTY, 1);
s->regs[IBEX_SPI_HOST_STATUS] = data;
} }
static void ibex_spi_host_reset(DeviceState *dev) static void ibex_spi_host_reset(DeviceState *dev)
@ -162,37 +166,38 @@ static void ibex_spi_host_reset(DeviceState *dev)
*/ */
static void ibex_spi_host_irq(IbexSPIHostState *s) static void ibex_spi_host_irq(IbexSPIHostState *s)
{ {
bool error_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE] uint32_t intr_test_reg = s->regs[IBEX_SPI_HOST_INTR_TEST];
& R_INTR_ENABLE_ERROR_MASK; uint32_t intr_en_reg = s->regs[IBEX_SPI_HOST_INTR_ENABLE];
bool event_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE] uint32_t intr_state_reg = s->regs[IBEX_SPI_HOST_INTR_STATE];
& R_INTR_ENABLE_SPI_EVENT_MASK;
bool err_pending = s->regs[IBEX_SPI_HOST_INTR_STATE] uint32_t err_en_reg = s->regs[IBEX_SPI_HOST_ERROR_ENABLE];
& R_INTR_STATE_ERROR_MASK; uint32_t event_en_reg = s->regs[IBEX_SPI_HOST_EVENT_ENABLE];
bool status_pending = s->regs[IBEX_SPI_HOST_INTR_STATE] uint32_t err_status_reg = s->regs[IBEX_SPI_HOST_ERROR_STATUS];
& R_INTR_STATE_SPI_EVENT_MASK; uint32_t status_reg = s->regs[IBEX_SPI_HOST_STATUS];
bool error_en = FIELD_EX32(intr_en_reg, INTR_ENABLE, ERROR);
bool event_en = FIELD_EX32(intr_en_reg, INTR_ENABLE, SPI_EVENT);
bool err_pending = FIELD_EX32(intr_state_reg, INTR_STATE, ERROR);
bool status_pending = FIELD_EX32(intr_state_reg, INTR_STATE, SPI_EVENT);
int err_irq = 0, event_irq = 0; int err_irq = 0, event_irq = 0;
/* Error IRQ enabled and Error IRQ Cleared */ /* Error IRQ enabled and Error IRQ Cleared */
if (error_en && !err_pending) { if (error_en && !err_pending) {
/* Event enabled, Interrupt Test Error */ /* Event enabled, Interrupt Test Error */
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_ERROR_MASK) { if (FIELD_EX32(intr_test_reg, INTR_TEST, ERROR)) {
err_irq = 1; err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] } else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CMDBUSY) &&
& R_ERROR_ENABLE_CMDBUSY_MASK) && FIELD_EX32(err_status_reg, ERROR_STATUS, CMDBUSY)) {
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CMDBUSY_MASK) {
/* Wrote to COMMAND when not READY */ /* Wrote to COMMAND when not READY */
err_irq = 1; err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] } else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CMDINVAL) &&
& R_ERROR_ENABLE_CMDINVAL_MASK) && FIELD_EX32(err_status_reg, ERROR_STATUS, CMDINVAL)) {
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CMDINVAL_MASK) {
/* Invalid command segment */ /* Invalid command segment */
err_irq = 1; err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE] } else if (FIELD_EX32(err_en_reg, ERROR_ENABLE, CSIDINVAL) &&
& R_ERROR_ENABLE_CSIDINVAL_MASK) && FIELD_EX32(err_status_reg, ERROR_STATUS, CSIDINVAL)) {
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CSIDINVAL_MASK) {
/* Invalid value for CSID */ /* Invalid value for CSID */
err_irq = 1; err_irq = 1;
} }
@ -204,22 +209,19 @@ static void ibex_spi_host_irq(IbexSPIHostState *s)
/* Event IRQ Enabled and Event IRQ Cleared */ /* Event IRQ Enabled and Event IRQ Cleared */
if (event_en && !status_pending) { if (event_en && !status_pending) {
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_SPI_EVENT_MASK) { if (FIELD_EX32(intr_test_reg, INTR_STATE, SPI_EVENT)) {
/* Event enabled, Interrupt Test Event */ /* Event enabled, Interrupt Test Event */
event_irq = 1; event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] } else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, READY) &&
& R_EVENT_ENABLE_READY_MASK) && FIELD_EX32(status_reg, STATUS, READY)) {
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
/* SPI Host ready for next command */ /* SPI Host ready for next command */
event_irq = 1; event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] } else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, TXEMPTY) &&
& R_EVENT_ENABLE_TXEMPTY_MASK) && FIELD_EX32(status_reg, STATUS, TXEMPTY)) {
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_TXEMPTY_MASK)) {
/* SPI TXEMPTY, TXFIFO drained */ /* SPI TXEMPTY, TXFIFO drained */
event_irq = 1; event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE] } else if (FIELD_EX32(event_en_reg, EVENT_ENABLE, RXFULL) &&
& R_EVENT_ENABLE_RXFULL_MASK) && FIELD_EX32(status_reg, STATUS, RXFULL)) {
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_RXFULL_MASK)) {
/* SPI RXFULL, RXFIFO full */ /* SPI RXFULL, RXFIFO full */
event_irq = 1; event_irq = 1;
} }
@ -232,10 +234,11 @@ static void ibex_spi_host_irq(IbexSPIHostState *s)
static void ibex_spi_host_transfer(IbexSPIHostState *s) static void ibex_spi_host_transfer(IbexSPIHostState *s)
{ {
uint32_t rx, tx; uint32_t rx, tx, data;
/* Get num of one byte transfers */ /* Get num of one byte transfers */
uint8_t segment_len = ((s->regs[IBEX_SPI_HOST_COMMAND] & R_COMMAND_LEN_MASK) uint8_t segment_len = FIELD_EX32(s->regs[IBEX_SPI_HOST_COMMAND],
>> R_COMMAND_LEN_SHIFT); COMMAND, LEN);
while (segment_len > 0) { while (segment_len > 0) {
if (fifo8_is_empty(&s->tx_fifo)) { if (fifo8_is_empty(&s->tx_fifo)) {
/* Assert Stall */ /* Assert Stall */
@ -262,22 +265,21 @@ static void ibex_spi_host_transfer(IbexSPIHostState *s)
--segment_len; --segment_len;
} }
data = s->regs[IBEX_SPI_HOST_STATUS];
/* Assert Ready */ /* Assert Ready */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK; data = FIELD_DP32(data, STATUS, READY, 1);
/* Set RXQD */ /* Set RXQD */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXQD_MASK; data = FIELD_DP32(data, STATUS, RXQD, div4_round_up(segment_len));
s->regs[IBEX_SPI_HOST_STATUS] |= (R_STATUS_RXQD_MASK
& div4_round_up(segment_len));
/* Set TXQD */ /* Set TXQD */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK; data = FIELD_DP32(data, STATUS, TXQD, fifo8_num_used(&s->tx_fifo) / 4);
s->regs[IBEX_SPI_HOST_STATUS] |= (fifo8_num_used(&s->tx_fifo) / 4)
& R_STATUS_TXQD_MASK;
/* Clear TXFULL */ /* Clear TXFULL */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK; data = FIELD_DP32(data, STATUS, TXFULL, 0);
/* Assert TXEMPTY and drop remaining bytes that exceed segment_len */
ibex_spi_txfifo_reset(s);
/* Reset RXEMPTY */ /* Reset RXEMPTY */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXEMPTY_MASK; data = FIELD_DP32(data, STATUS, RXEMPTY, 0);
/* Update register status */
s->regs[IBEX_SPI_HOST_STATUS] = data;
/* Drop remaining bytes that exceed segment_len */
ibex_spi_txfifo_reset(s);
ibex_spi_host_irq(s); ibex_spi_host_irq(s);
} }
@ -340,7 +342,7 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
{ {
IbexSPIHostState *s = opaque; IbexSPIHostState *s = opaque;
uint32_t val32 = val64; uint32_t val32 = val64;
uint32_t shift_mask = 0xff; uint32_t shift_mask = 0xff, status = 0, data = 0;
uint8_t txqd_len; uint8_t txqd_len;
trace_ibex_spi_host_write(addr, size, val64); trace_ibex_spi_host_write(addr, size, val64);
@ -350,7 +352,17 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
switch (addr) { switch (addr) {
/* Skipping any R/O registers */ /* Skipping any R/O registers */
case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE: case IBEX_SPI_HOST_INTR_STATE:
/* rw1c status register */
if (FIELD_EX32(val32, INTR_STATE, ERROR)) {
data = FIELD_DP32(data, INTR_STATE, ERROR, 0);
}
if (FIELD_EX32(val32, INTR_STATE, SPI_EVENT)) {
data = FIELD_DP32(data, INTR_STATE, SPI_EVENT, 0);
}
s->regs[addr] = data;
break;
case IBEX_SPI_HOST_INTR_ENABLE:
s->regs[addr] = val32; s->regs[addr] = val32;
break; break;
case IBEX_SPI_HOST_INTR_TEST: case IBEX_SPI_HOST_INTR_TEST:
@ -397,21 +409,23 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
s->regs[addr] = val32; s->regs[addr] = val32;
/* STALL, IP not enabled */ /* STALL, IP not enabled */
if (!(s->regs[IBEX_SPI_HOST_CONTROL] & R_CONTROL_SPIEN_MASK)) { if (!(FIELD_EX32(s->regs[IBEX_SPI_HOST_CONTROL],
CONTROL, SPIEN))) {
return; return;
} }
/* SPI not ready, IRQ Error */ /* SPI not ready, IRQ Error */
if (!(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) { if (!(FIELD_EX32(s->regs[IBEX_SPI_HOST_STATUS],
STATUS, READY))) {
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK; s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK;
ibex_spi_host_irq(s); ibex_spi_host_irq(s);
return; return;
} }
/* Assert Not Ready */ /* Assert Not Ready */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK; s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK;
if (((val32 & R_COMMAND_DIRECTION_MASK) >> R_COMMAND_DIRECTION_SHIFT) if (FIELD_EX32(val32, COMMAND, DIRECTION) != BIDIRECTIONAL_TRANSFER) {
!= BIDIRECTIONAL_TRANSFER) {
qemu_log_mask(LOG_UNIMP, qemu_log_mask(LOG_UNIMP,
"%s: Rx Only/Tx Only are not supported\n", __func__); "%s: Rx Only/Tx Only are not supported\n", __func__);
} }
@ -452,8 +466,8 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
return; return;
} }
/* Byte ordering is set by the IP */ /* Byte ordering is set by the IP */
if ((s->regs[IBEX_SPI_HOST_STATUS] & status = s->regs[IBEX_SPI_HOST_STATUS];
R_STATUS_BYTEORDER_MASK) == 0) { if (FIELD_EX32(status, STATUS, BYTEORDER) == 0) {
/* LE: LSB transmitted first (default for ibex processor) */ /* LE: LSB transmitted first (default for ibex processor) */
shift_mask = 0xff << (i * 8); shift_mask = 0xff << (i * 8);
} else { } else {
@ -464,18 +478,18 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8)); fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8));
} }
status = s->regs[IBEX_SPI_HOST_STATUS];
/* Reset TXEMPTY */ /* Reset TXEMPTY */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXEMPTY_MASK; status = FIELD_DP32(status, STATUS, TXEMPTY, 0);
/* Update TXQD */ /* Update TXQD */
txqd_len = (s->regs[IBEX_SPI_HOST_STATUS] & txqd_len = FIELD_EX32(status, STATUS, TXQD);
R_STATUS_TXQD_MASK) >> R_STATUS_TXQD_SHIFT;
/* Partial bytes (size < 4) are padded, in words. */ /* Partial bytes (size < 4) are padded, in words. */
txqd_len += 1; txqd_len += 1;
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK; status = FIELD_DP32(status, STATUS, TXQD, txqd_len);
s->regs[IBEX_SPI_HOST_STATUS] |= txqd_len;
/* Assert Ready */ /* Assert Ready */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK; status = FIELD_DP32(status, STATUS, READY, 1);
/* Update register status */
s->regs[IBEX_SPI_HOST_STATUS] = status;
break; break;
case IBEX_SPI_HOST_ERROR_ENABLE: case IBEX_SPI_HOST_ERROR_ENABLE:
s->regs[addr] = val32; s->regs[addr] = val32;
@ -491,7 +505,27 @@ static void ibex_spi_host_write(void *opaque, hwaddr addr,
* When an error occurs, the corresponding bit must be cleared * When an error occurs, the corresponding bit must be cleared
* here before issuing any further commands * here before issuing any further commands
*/ */
s->regs[addr] = val32; status = s->regs[addr];
/* rw1c status register */
if (FIELD_EX32(val32, ERROR_STATUS, CMDBUSY)) {
status = FIELD_DP32(status, ERROR_STATUS, CMDBUSY, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, OVERFLOW)) {
status = FIELD_DP32(status, ERROR_STATUS, OVERFLOW, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, UNDERFLOW)) {
status = FIELD_DP32(status, ERROR_STATUS, UNDERFLOW, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, CMDINVAL)) {
status = FIELD_DP32(status, ERROR_STATUS, CMDINVAL, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, CSIDINVAL)) {
status = FIELD_DP32(status, ERROR_STATUS, CSIDINVAL, 0);
}
if (FIELD_EX32(val32, ERROR_STATUS, ACCESSINVAL)) {
status = FIELD_DP32(status, ERROR_STATUS, ACCESSINVAL, 0);
}
s->regs[addr] = status;
break; break;
case IBEX_SPI_HOST_EVENT_ENABLE: case IBEX_SPI_HOST_EVENT_ENABLE:
/* Controls which classes of SPI events raise an interrupt. */ /* Controls which classes of SPI events raise an interrupt. */

View File

@ -74,8 +74,6 @@ static void virtio_iommu_pci_class_init(ObjectClass *klass, void *data)
k->realize = virtio_iommu_pci_realize; k->realize = virtio_iommu_pci_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories); set_bit(DEVICE_CATEGORY_MISC, dc->categories);
device_class_set_props(dc, virtio_iommu_pci_properties); device_class_set_props(dc, virtio_iommu_pci_properties);
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_IOMMU;
pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
pcidev_k->class_id = PCI_CLASS_OTHERS; pcidev_k->class_id = PCI_CLASS_OTHERS;
dc->hotpluggable = false; dc->hotpluggable = false;

View File

@ -104,8 +104,6 @@ static void virtio_mem_pci_class_init(ObjectClass *klass, void *data)
k->realize = virtio_mem_pci_realize; k->realize = virtio_mem_pci_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories); set_bit(DEVICE_CATEGORY_MISC, dc->categories);
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_MEM;
pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
pcidev_k->class_id = PCI_CLASS_OTHERS; pcidev_k->class_id = PCI_CLASS_OTHERS;

View File

@ -1688,7 +1688,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
pci_set_word(config + PCI_VENDOR_ID, pci_set_word(config + PCI_VENDOR_ID,
PCI_VENDOR_ID_REDHAT_QUMRANET); PCI_VENDOR_ID_REDHAT_QUMRANET);
pci_set_word(config + PCI_DEVICE_ID, pci_set_word(config + PCI_DEVICE_ID,
0x1040 + virtio_bus_get_vdev_id(bus)); PCI_DEVICE_ID_VIRTIO_10_BASE + virtio_bus_get_vdev_id(bus));
pci_config_set_revision(config, 1); pci_config_set_revision(config, 1);
} }
config[PCI_INTERRUPT_PIN] = 1; config[PCI_INTERRUPT_PIN] = 1;

View File

@ -90,8 +90,6 @@ static void virtio_pmem_pci_class_init(ObjectClass *klass, void *data)
k->realize = virtio_pmem_pci_realize; k->realize = virtio_pmem_pci_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories); set_bit(DEVICE_CATEGORY_MISC, dc->categories);
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_PMEM;
pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
pcidev_k->class_id = PCI_CLASS_OTHERS; pcidev_k->class_id = PCI_CLASS_OTHERS;

View File

@ -364,4 +364,25 @@ bool fw_cfg_dma_enabled(void *opaque);
*/ */
const char *fw_cfg_arch_key_name(uint16_t key); const char *fw_cfg_arch_key_name(uint16_t key);
/**
* load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified
* by key.
* @fw_cfg: The firmware config instance to store the data in.
* @size_key: The firmware config key to store the size of the loaded
* data under, with fw_cfg_add_i32().
* @data_key: The firmware config key to store the loaded data under,
* with fw_cfg_add_bytes().
* @image_name: The name of the image file to load. If it is NULL, the
* function returns without doing anything.
* @try_decompress: Whether the image should be decompressed (gunzipped) before
* adding it to fw_cfg. If decompression fails, the image is
* loaded as-is.
*
* In case of failure, the function prints an error message to stderr and the
* process exits with status 1.
*/
void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress);
#endif #endif

View File

@ -76,6 +76,7 @@ extern bool pci_available;
#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4 #define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4
#define PCI_SUBDEVICE_ID_QEMU 0x1100 #define PCI_SUBDEVICE_ID_QEMU 0x1100
/* legacy virtio-pci devices */
#define PCI_DEVICE_ID_VIRTIO_NET 0x1000 #define PCI_DEVICE_ID_VIRTIO_NET 0x1000
#define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001 #define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001
#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002
@ -84,9 +85,15 @@ extern bool pci_available;
#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005
#define PCI_DEVICE_ID_VIRTIO_9P 0x1009 #define PCI_DEVICE_ID_VIRTIO_9P 0x1009
#define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012 #define PCI_DEVICE_ID_VIRTIO_VSOCK 0x1012
#define PCI_DEVICE_ID_VIRTIO_PMEM 0x1013
#define PCI_DEVICE_ID_VIRTIO_IOMMU 0x1014 /*
#define PCI_DEVICE_ID_VIRTIO_MEM 0x1015 * modern virtio-pci devices get their id assigned automatically,
* there is no need to add #defines here. It gets calculated as
*
* PCI_DEVICE_ID = PCI_DEVICE_ID_VIRTIO_10_BASE +
* virtio_bus_get_vdev_id(bus)
*/
#define PCI_DEVICE_ID_VIRTIO_10_BASE 0x1040
#define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_VENDOR_ID_REDHAT 0x1b36
#define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 #define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001

View File

@ -57,5 +57,6 @@ void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base,
uint32_t reset_vec_size, uint32_t reset_vec_size,
uint64_t kernel_entry); uint64_t kernel_entry);
void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr); void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr);
void riscv_setup_firmware_boot(MachineState *machine);
#endif /* RISCV_BOOT_H */ #endif /* RISCV_BOOT_H */

View File

@ -40,7 +40,7 @@
OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST) OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST)
/* SPI Registers */ /* SPI Registers */
#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw */ #define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw1c */
#define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */ #define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */
#define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */ #define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */
#define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */ #define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */
@ -54,7 +54,7 @@
#define IBEX_SPI_HOST_TXDATA (0x28 / 4) #define IBEX_SPI_HOST_TXDATA (0x28 / 4)
#define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */ #define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */
#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw */ #define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw1c */
#define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */ #define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */
/* FIFO Len in Bytes */ /* FIFO Len in Bytes */

View File

@ -41,7 +41,10 @@ struct QIOChannelCommand {
QIOChannel parent; QIOChannel parent;
int writefd; int writefd;
int readfd; int readfd;
pid_t pid; GPid pid;
#ifdef WIN32
bool blocking;
#endif
}; };

View File

@ -349,6 +349,8 @@ bool kvm_device_supported(int vmfd, uint64_t type);
extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; extern const KVMCapabilityInfo kvm_arch_required_capabilities[];
void kvm_arch_accel_class_init(ObjectClass *oc);
void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run); void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run);
MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run); MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run);

View File

@ -10,6 +10,7 @@
#define QEMU_KVM_INT_H #define QEMU_KVM_INT_H
#include "exec/memory.h" #include "exec/memory.h"
#include "qapi/qapi-types-common.h"
#include "qemu/accel.h" #include "qemu/accel.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
@ -36,6 +37,81 @@ typedef struct KVMMemoryListener {
int as_id; int as_id;
} KVMMemoryListener; } KVMMemoryListener;
#define KVM_MSI_HASHTAB_SIZE 256
enum KVMDirtyRingReaperState {
KVM_DIRTY_RING_REAPER_NONE = 0,
/* The reaper is sleeping */
KVM_DIRTY_RING_REAPER_WAIT,
/* The reaper is reaping for dirty pages */
KVM_DIRTY_RING_REAPER_REAPING,
};
/*
* KVM reaper instance, responsible for collecting the KVM dirty bits
* via the dirty ring.
*/
struct KVMDirtyRingReaper {
/* The reaper thread */
QemuThread reaper_thr;
volatile uint64_t reaper_iteration; /* iteration number of reaper thr */
volatile enum KVMDirtyRingReaperState reaper_state; /* reap thr state */
};
struct KVMState
{
AccelState parent_obj;
int nr_slots;
int fd;
int vmfd;
int coalesced_mmio;
int coalesced_pio;
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
bool coalesced_flush_in_progress;
int vcpu_events;
int robust_singlestep;
int debugregs;
#ifdef KVM_CAP_SET_GUEST_DEBUG
QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
#endif
int max_nested_state_len;
int many_ioeventfds;
int intx_set_mask;
int kvm_shadow_mem;
bool kernel_irqchip_allowed;
bool kernel_irqchip_required;
OnOffAuto kernel_irqchip_split;
bool sync_mmu;
uint64_t manual_dirty_log_protect;
/* The man page (and posix) say ioctl numbers are signed int, but
* they're not. Linux, glibc and *BSD all treat ioctl numbers as
* unsigned, and treating them as signed here can break things */
unsigned irq_set_ioctl;
unsigned int sigmask_len;
GHashTable *gsimap;
#ifdef KVM_CAP_IRQ_ROUTING
struct kvm_irq_routing *irq_routes;
int nr_allocated_irq_routes;
unsigned long *used_gsi_bitmap;
unsigned int gsi_count;
QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
#endif
KVMMemoryListener memory_listener;
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
/* For "info mtree -f" to tell if an MR is registered in KVM */
int nr_as;
struct KVMAs {
KVMMemoryListener *ml;
AddressSpace *as;
} *as;
uint64_t kvm_dirty_ring_bytes; /* Size of the per-vcpu dirty ring */
uint32_t kvm_dirty_ring_size; /* Number of dirty GFNs per ring */
struct KVMDirtyRingReaper reaper;
NotifyVmexitOption notify_vmexit;
uint32_t notify_window;
};
void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
AddressSpace *as, int as_id, const char *name); AddressSpace *as, int as_id, const char *name);

View File

@ -26,12 +26,11 @@
#include "qemu/sockets.h" #include "qemu/sockets.h"
#include "trace.h" #include "trace.h"
#ifndef WIN32
/** /**
* qio_channel_command_new_pid: * qio_channel_command_new_pid:
* @writefd: the FD connected to the command's stdin * @writefd: the FD connected to the command's stdin
* @readfd: the FD connected to the command's stdout * @readfd: the FD connected to the command's stdout
* @pid: the PID of the running child command * @pid: the PID/HANDLE of the running child command
* @errp: pointer to a NULL-initialized error object * @errp: pointer to a NULL-initialized error object
* *
* Create a channel for performing I/O with the * Create a channel for performing I/O with the
@ -50,7 +49,7 @@
static QIOChannelCommand * static QIOChannelCommand *
qio_channel_command_new_pid(int writefd, qio_channel_command_new_pid(int writefd,
int readfd, int readfd,
pid_t pid) GPid pid)
{ {
QIOChannelCommand *ioc; QIOChannelCommand *ioc;
@ -60,7 +59,13 @@ qio_channel_command_new_pid(int writefd,
ioc->writefd = writefd; ioc->writefd = writefd;
ioc->pid = pid; ioc->pid = pid;
trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid); trace_qio_channel_command_new_pid(ioc, writefd, readfd,
#ifdef WIN32
GetProcessId(pid)
#else
pid
#endif
);
return ioc; return ioc;
} }
@ -69,107 +74,25 @@ qio_channel_command_new_spawn(const char *const argv[],
int flags, int flags,
Error **errp) Error **errp)
{ {
pid_t pid = -1; g_autoptr(GError) err = NULL;
int stdinfd[2] = { -1, -1 }; GPid pid = 0;
int stdoutfd[2] = { -1, -1 }; GSpawnFlags gflags = G_SPAWN_CLOEXEC_PIPES | G_SPAWN_DO_NOT_REAP_CHILD;
int devnull = -1; int stdinfd = -1, stdoutfd = -1;
bool stdinnull = false, stdoutnull = false;
QIOChannelCommand *ioc;
flags = flags & O_ACCMODE; flags = flags & O_ACCMODE;
gflags |= flags == O_WRONLY ? G_SPAWN_STDOUT_TO_DEV_NULL : 0;
if (flags == O_RDONLY) { if (!g_spawn_async_with_pipes(NULL, (char **)argv, NULL, gflags, NULL, NULL,
stdinnull = true; &pid,
} flags == O_RDONLY ? NULL : &stdinfd,
if (flags == O_WRONLY) { flags == O_WRONLY ? NULL : &stdoutfd,
stdoutnull = true; NULL, &err)) {
} error_setg(errp, "%s", err->message);
if (stdinnull || stdoutnull) {
devnull = open("/dev/null", O_RDWR);
if (devnull < 0) {
error_setg_errno(errp, errno,
"Unable to open /dev/null");
goto error;
}
}
if ((!stdinnull && !g_unix_open_pipe(stdinfd, FD_CLOEXEC, NULL)) ||
(!stdoutnull && !g_unix_open_pipe(stdoutfd, FD_CLOEXEC, NULL))) {
error_setg_errno(errp, errno,
"Unable to open pipe");
goto error;
}
pid = qemu_fork(errp);
if (pid < 0) {
goto error;
}
if (pid == 0) { /* child */
dup2(stdinnull ? devnull : stdinfd[0], STDIN_FILENO);
dup2(stdoutnull ? devnull : stdoutfd[1], STDOUT_FILENO);
/* Leave stderr connected to qemu's stderr */
if (!stdinnull) {
close(stdinfd[0]);
close(stdinfd[1]);
}
if (!stdoutnull) {
close(stdoutfd[0]);
close(stdoutfd[1]);
}
if (devnull != -1) {
close(devnull);
}
execv(argv[0], (char * const *)argv);
_exit(1);
}
if (!stdinnull) {
close(stdinfd[0]);
}
if (!stdoutnull) {
close(stdoutfd[1]);
}
ioc = qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1],
stdoutnull ? devnull : stdoutfd[0],
pid);
trace_qio_channel_command_new_spawn(ioc, argv[0], flags);
return ioc;
error:
if (devnull != -1) {
close(devnull);
}
if (stdinfd[0] != -1) {
close(stdinfd[0]);
}
if (stdinfd[1] != -1) {
close(stdinfd[1]);
}
if (stdoutfd[0] != -1) {
close(stdoutfd[0]);
}
if (stdoutfd[1] != -1) {
close(stdoutfd[1]);
}
return NULL; return NULL;
} }
#else /* WIN32 */ return qio_channel_command_new_pid(stdinfd, stdoutfd, pid);
QIOChannelCommand *
qio_channel_command_new_spawn(const char *const argv[],
int flags,
Error **errp)
{
error_setg_errno(errp, ENOSYS,
"Command spawn not supported on this platform");
return NULL;
} }
#endif /* WIN32 */
#ifndef WIN32 #ifndef WIN32
static int qio_channel_command_abort(QIOChannelCommand *ioc, static int qio_channel_command_abort(QIOChannelCommand *ioc,
@ -213,6 +136,23 @@ static int qio_channel_command_abort(QIOChannelCommand *ioc,
return 0; return 0;
} }
#else
static int qio_channel_command_abort(QIOChannelCommand *ioc,
Error **errp)
{
DWORD ret;
TerminateProcess(ioc->pid, 0);
ret = WaitForSingleObject(ioc->pid, 1000);
if (ret != WAIT_OBJECT_0) {
error_setg(errp,
"Process %llu refused to die",
(unsigned long long)GetProcessId(ioc->pid));
return -1;
}
return 0;
}
#endif /* ! WIN32 */ #endif /* ! WIN32 */
@ -221,7 +161,7 @@ static void qio_channel_command_init(Object *obj)
QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj); QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj);
ioc->readfd = -1; ioc->readfd = -1;
ioc->writefd = -1; ioc->writefd = -1;
ioc->pid = -1; ioc->pid = 0;
} }
static void qio_channel_command_finalize(Object *obj) static void qio_channel_command_finalize(Object *obj)
@ -236,12 +176,27 @@ static void qio_channel_command_finalize(Object *obj)
} }
ioc->writefd = ioc->readfd = -1; ioc->writefd = ioc->readfd = -1;
if (ioc->pid > 0) { if (ioc->pid > 0) {
#ifndef WIN32
qio_channel_command_abort(ioc, NULL); qio_channel_command_abort(ioc, NULL);
#endif g_spawn_close_pid(ioc->pid);
} }
} }
#ifdef WIN32
static bool win32_fd_poll(int fd, gushort events)
{
GPollFD pfd = { .fd = _get_osfhandle(fd), .events = events };
int res;
do {
res = g_poll(&pfd, 1, 0);
} while (res < 0 && errno == EINTR);
if (res == 0) {
return false;
}
return true;
}
#endif
static ssize_t qio_channel_command_readv(QIOChannel *ioc, static ssize_t qio_channel_command_readv(QIOChannel *ioc,
const struct iovec *iov, const struct iovec *iov,
@ -253,6 +208,12 @@ static ssize_t qio_channel_command_readv(QIOChannel *ioc,
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
ssize_t ret; ssize_t ret;
#ifdef WIN32
if (!cioc->blocking && !win32_fd_poll(cioc->readfd, G_IO_IN)) {
return QIO_CHANNEL_ERR_BLOCK;
}
#endif
retry: retry:
ret = readv(cioc->readfd, iov, niov); ret = readv(cioc->readfd, iov, niov);
if (ret < 0) { if (ret < 0) {
@ -282,6 +243,12 @@ static ssize_t qio_channel_command_writev(QIOChannel *ioc,
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
ssize_t ret; ssize_t ret;
#ifdef WIN32
if (!cioc->blocking && !win32_fd_poll(cioc->writefd, G_IO_OUT)) {
return QIO_CHANNEL_ERR_BLOCK;
}
#endif
retry: retry:
ret = writev(cioc->writefd, iov, niov); ret = writev(cioc->writefd, iov, niov);
if (ret <= 0) { if (ret <= 0) {
@ -302,14 +269,14 @@ static int qio_channel_command_set_blocking(QIOChannel *ioc,
bool enabled, bool enabled,
Error **errp) Error **errp)
{ {
#ifdef WIN32
/* command spawn is not supported on win32 */
g_assert_not_reached();
#else
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
if (!g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL) || #ifdef WIN32
!g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL)) { cioc->blocking = enabled;
#else
if ((cioc->writefd >= 0 && !g_unix_set_fd_nonblocking(cioc->writefd, !enabled, NULL)) ||
(cioc->readfd >= 0 && !g_unix_set_fd_nonblocking(cioc->readfd, !enabled, NULL))) {
error_setg_errno(errp, errno, "Failed to set FD nonblocking"); error_setg_errno(errp, errno, "Failed to set FD nonblocking");
return -1; return -1;
} }
@ -350,6 +317,8 @@ static int qio_channel_command_close(QIOChannel *ioc,
(unsigned long long)cioc->pid); (unsigned long long)cioc->pid);
return -1; return -1;
} }
#else
WaitForSingleObject(cioc->pid, INFINITE);
#endif #endif
if (rv < 0) { if (rv < 0) {

View File

@ -24,6 +24,10 @@
/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ /* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */
#define TARGET_FP_XSTATE_MAGIC1 0x46505853U /* FPXS */
#define TARGET_FP_XSTATE_MAGIC2 0x46505845U /* FPXE */
#define TARGET_FP_XSTATE_MAGIC2_SIZE 4
struct target_fpreg { struct target_fpreg {
uint16_t significand[4]; uint16_t significand[4];
uint16_t exponent; uint16_t exponent;
@ -39,6 +43,35 @@ struct target_xmmreg {
uint32_t element[4]; uint32_t element[4];
}; };
struct target_fpx_sw_bytes {
uint32_t magic1;
uint32_t extended_size;
uint64_t xfeatures;
uint32_t xstate_size;
uint32_t reserved[7];
};
QEMU_BUILD_BUG_ON(sizeof(struct target_fpx_sw_bytes) != 12*4);
struct target_fpstate_fxsave {
/* FXSAVE format */
uint16_t cw;
uint16_t sw;
uint16_t twd;
uint16_t fop;
uint64_t rip;
uint64_t rdp;
uint32_t mxcsr;
uint32_t mxcsr_mask;
uint32_t st_space[32];
uint32_t xmm_space[64];
uint32_t hw_reserved[12];
struct target_fpx_sw_bytes sw_reserved;
uint8_t xfeatures[];
};
#define TARGET_FXSAVE_SIZE sizeof(struct target_fpstate_fxsave)
QEMU_BUILD_BUG_ON(TARGET_FXSAVE_SIZE != 512);
QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_fxsave, sw_reserved) != 464);
struct target_fpstate_32 { struct target_fpstate_32 {
/* Regular FPU environment */ /* Regular FPU environment */
uint32_t cw; uint32_t cw;
@ -51,35 +84,21 @@ struct target_fpstate_32 {
struct target_fpreg st[8]; struct target_fpreg st[8];
uint16_t status; uint16_t status;
uint16_t magic; /* 0xffff = regular FPU data only */ uint16_t magic; /* 0xffff = regular FPU data only */
struct target_fpstate_fxsave fxsave;
/* FXSR FPU environment */
uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */
uint32_t mxcsr;
uint32_t reserved;
struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */
struct target_xmmreg xmm[8];
uint32_t padding[56];
}; };
struct target_fpstate_64 { /*
/* FXSAVE format */ * For simplicity, setup_frame aligns struct target_fpstate_32 to
uint16_t cw; * 16 bytes, so ensure that the FXSAVE area is also aligned.
uint16_t sw; */
uint16_t twd; QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_32, fxsave) & 15);
uint16_t fop;
uint64_t rip;
uint64_t rdp;
uint32_t mxcsr;
uint32_t mxcsr_mask;
uint32_t st_space[32];
uint32_t xmm_space[64];
uint32_t reserved[24];
};
#ifndef TARGET_X86_64 #ifndef TARGET_X86_64
# define target_fpstate target_fpstate_32 # define target_fpstate target_fpstate_32
# define TARGET_FPSTATE_FXSAVE_OFFSET offsetof(struct target_fpstate_32, fxsave)
#else #else
# define target_fpstate target_fpstate_64 # define target_fpstate target_fpstate_fxsave
# define TARGET_FPSTATE_FXSAVE_OFFSET 0
#endif #endif
struct target_sigcontext_32 { struct target_sigcontext_32 {
@ -163,10 +182,25 @@ struct sigframe {
abi_ulong pretcode; abi_ulong pretcode;
int sig; int sig;
struct target_sigcontext sc; struct target_sigcontext sc;
struct target_fpstate fpstate; /*
* The actual fpstate is placed after retcode[] below, to make
* room for the variable-sized xsave data. The older unused fpstate
* has to be kept to avoid changing the offset of extramask[], which
* is part of the ABI.
*/
struct target_fpstate fpstate_unused;
abi_ulong extramask[TARGET_NSIG_WORDS-1]; abi_ulong extramask[TARGET_NSIG_WORDS-1];
char retcode[8]; char retcode[8];
/*
* This field will be 16-byte aligned in memory. Applying QEMU_ALIGNED
* to it ensures that the base of the frame has an appropriate alignment
* too.
*/
struct target_fpstate fpstate QEMU_ALIGNED(8);
}; };
#define TARGET_SIGFRAME_FXSAVE_OFFSET ( \
offsetof(struct sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET)
struct rt_sigframe { struct rt_sigframe {
abi_ulong pretcode; abi_ulong pretcode;
@ -175,26 +209,62 @@ struct rt_sigframe {
abi_ulong puc; abi_ulong puc;
struct target_siginfo info; struct target_siginfo info;
struct target_ucontext uc; struct target_ucontext uc;
struct target_fpstate fpstate;
char retcode[8]; char retcode[8];
struct target_fpstate fpstate QEMU_ALIGNED(8);
}; };
#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \
offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET)
#else #else
struct rt_sigframe { struct rt_sigframe {
abi_ulong pretcode; abi_ulong pretcode;
struct target_ucontext uc; struct target_ucontext uc;
struct target_siginfo info; struct target_siginfo info;
struct target_fpstate fpstate; struct target_fpstate fpstate QEMU_ALIGNED(16);
}; };
#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \
offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET)
#endif #endif
/* /*
* Set up a signal frame. * Set up a signal frame.
*/ */
/* XXX: save x87 state */ static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave,
abi_ulong fxsave_addr)
{
if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) {
/* fxsave_addr must be 16 byte aligned for fxsave */
assert(!(fxsave_addr & 0xf));
cpu_x86_fxsave(env, fxsave_addr);
__put_user(0, &fxsave->sw_reserved.magic1);
} else {
uint32_t xstate_size = xsave_area_size(env->xcr0, false);
uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE;
/*
* extended_size is the offset from fpstate_addr to right after the end
* of the extended save states. On 32-bit that includes the legacy
* FSAVE area.
*/
uint32_t extended_size = TARGET_FPSTATE_FXSAVE_OFFSET
+ xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE;
/* fxsave_addr must be 64 byte aligned for xsave */
assert(!(fxsave_addr & 0x3f));
/* Zero the header, XSAVE *adds* features to an existing save state. */
memset(fxsave->xfeatures, 0, 64);
cpu_x86_xsave(env, fxsave_addr);
__put_user(TARGET_FP_XSTATE_MAGIC1, &fxsave->sw_reserved.magic1);
__put_user(extended_size, &fxsave->sw_reserved.extended_size);
__put_user(env->xcr0, &fxsave->sw_reserved.xfeatures);
__put_user(xstate_size, &fxsave->sw_reserved.xstate_size);
__put_user(TARGET_FP_XSTATE_MAGIC2, (uint32_t *) &fxsave->xfeatures[xfeatures_size]);
}
}
static void setup_sigcontext(struct target_sigcontext *sc, static void setup_sigcontext(struct target_sigcontext *sc,
struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask,
abi_ulong fpstate_addr) abi_ulong fpstate_addr)
@ -226,13 +296,14 @@ static void setup_sigcontext(struct target_sigcontext *sc,
cpu_x86_fsave(env, fpstate_addr, 1); cpu_x86_fsave(env, fpstate_addr, 1);
fpstate->status = fpstate->sw; fpstate->status = fpstate->sw;
if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) {
magic = 0xffff; magic = 0xffff;
} else {
xsave_sigcontext(env, &fpstate->fxsave,
fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET);
magic = 0;
}
__put_user(magic, &fpstate->magic); __put_user(magic, &fpstate->magic);
__put_user(fpstate_addr, &sc->fpstate);
/* non-iBCS2 extensions.. */
__put_user(mask, &sc->oldmask);
__put_user(env->cr[2], &sc->cr2);
#else #else
__put_user(env->regs[R_EDI], &sc->rdi); __put_user(env->regs[R_EDI], &sc->rdi);
__put_user(env->regs[R_ESI], &sc->rsi); __put_user(env->regs[R_ESI], &sc->rsi);
@ -262,15 +333,14 @@ static void setup_sigcontext(struct target_sigcontext *sc,
__put_user((uint16_t)0, &sc->fs); __put_user((uint16_t)0, &sc->fs);
__put_user(env->segs[R_SS].selector, &sc->ss); __put_user(env->segs[R_SS].selector, &sc->ss);
xsave_sigcontext(env, fpstate, fpstate_addr);
#endif
__put_user(fpstate_addr, &sc->fpstate);
/* non-iBCS2 extensions.. */
__put_user(mask, &sc->oldmask); __put_user(mask, &sc->oldmask);
__put_user(env->cr[2], &sc->cr2); __put_user(env->cr[2], &sc->cr2);
/* fpstate_addr must be 16 byte aligned for fxsave */
assert(!(fpstate_addr & 0xf));
cpu_x86_fxsave(env, fpstate_addr);
__put_user(fpstate_addr, &sc->fpstate);
#endif
} }
/* /*
@ -278,7 +348,7 @@ static void setup_sigcontext(struct target_sigcontext *sc,
*/ */
static inline abi_ulong static inline abi_ulong
get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t fxsave_offset)
{ {
unsigned long esp; unsigned long esp;
@ -302,11 +372,15 @@ get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size)
#endif #endif
} }
#ifndef TARGET_X86_64 if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) {
return (esp - frame_size) & -8ul; return (esp - (fxsave_offset + TARGET_FXSAVE_SIZE)) & -8ul;
#else } else if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) {
return ((esp - frame_size) & (~15ul)) - 8; return ((esp - TARGET_FXSAVE_SIZE) & -16ul) - fxsave_offset;
#endif } else {
size_t xstate_size =
xsave_area_size(env->xcr0, false) + TARGET_FP_XSTATE_MAGIC2_SIZE;
return ((esp - xstate_size) & -64ul) - fxsave_offset;
}
} }
#ifndef TARGET_X86_64 #ifndef TARGET_X86_64
@ -334,7 +408,7 @@ void setup_frame(int sig, struct target_sigaction *ka,
struct sigframe *frame; struct sigframe *frame;
int i; int i;
frame_addr = get_sigframe(ka, env, sizeof(*frame)); frame_addr = get_sigframe(ka, env, TARGET_SIGFRAME_FXSAVE_OFFSET);
trace_user_setup_frame(env, frame_addr); trace_user_setup_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
@ -390,7 +464,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
struct rt_sigframe *frame; struct rt_sigframe *frame;
int i; int i;
frame_addr = get_sigframe(ka, env, sizeof(*frame)); frame_addr = get_sigframe(ka, env, TARGET_RT_SIGFRAME_FXSAVE_OFFSET);
trace_user_setup_rt_frame(env, frame_addr); trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
@ -409,7 +483,11 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
} }
/* Create the ucontext. */ /* Create the ucontext. */
if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
__put_user(1, &frame->uc.tuc_flags);
} else {
__put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_flags);
}
__put_user(0, &frame->uc.tuc_link); __put_user(0, &frame->uc.tuc_link);
target_save_altstack(&frame->uc.tuc_stack, env); target_save_altstack(&frame->uc.tuc_stack, env);
setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env,
@ -463,10 +541,37 @@ give_sigsegv:
force_sigsegv(sig); force_sigsegv(sig);
} }
static int xrstor_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave,
abi_ulong fxsave_addr)
{
if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
uint32_t extended_size = tswapl(fxsave->sw_reserved.extended_size);
uint32_t xstate_size = tswapl(fxsave->sw_reserved.xstate_size);
uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE;
/* Linux checks MAGIC2 using xstate_size, not extended_size. */
if (tswapl(fxsave->sw_reserved.magic1) == TARGET_FP_XSTATE_MAGIC1 &&
extended_size >= TARGET_FPSTATE_FXSAVE_OFFSET + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE) {
if (!access_ok(env_cpu(env), VERIFY_READ, fxsave_addr,
extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) {
return 1;
}
if (tswapl(*(uint32_t *) &fxsave->xfeatures[xfeatures_size]) == TARGET_FP_XSTATE_MAGIC2) {
cpu_x86_xrstor(env, fxsave_addr);
return 0;
}
}
/* fall through to fxrstor */
}
cpu_x86_fxrstor(env, fxsave_addr);
return 0;
}
static int static int
restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
{ {
unsigned int err = 0; int err = 1;
abi_ulong fpstate_addr; abi_ulong fpstate_addr;
unsigned int tmpflags; unsigned int tmpflags;
@ -517,20 +622,28 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
fpstate_addr = tswapl(sc->fpstate); fpstate_addr = tswapl(sc->fpstate);
if (fpstate_addr != 0) { if (fpstate_addr != 0) {
if (!access_ok(env_cpu(env), VERIFY_READ, fpstate_addr, struct target_fpstate *fpstate;
if (!lock_user_struct(VERIFY_READ, fpstate, fpstate_addr,
sizeof(struct target_fpstate))) { sizeof(struct target_fpstate))) {
goto badframe; return err;
} }
#ifndef TARGET_X86_64 #ifndef TARGET_X86_64
if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) {
cpu_x86_frstor(env, fpstate_addr, 1); cpu_x86_frstor(env, fpstate_addr, 1);
err = 0;
} else {
err = xrstor_sigcontext(env, &fpstate->fxsave,
fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET);
}
#else #else
cpu_x86_fxrstor(env, fpstate_addr); err = xrstor_sigcontext(env, fpstate, fpstate_addr);
#endif #endif
unlock_user_struct(fpstate, fpstate_addr, 0);
} else {
err = 0;
} }
return err; return err;
badframe:
return 1;
} }
/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ /* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */

View File

@ -643,3 +643,20 @@
{ 'struct': 'MemoryFailureFlags', { 'struct': 'MemoryFailureFlags',
'data': { 'action-required': 'bool', 'data': { 'action-required': 'bool',
'recursive': 'bool'} } 'recursive': 'bool'} }
##
# @NotifyVmexitOption:
#
# An enumeration of the options specified when enabling notify VM exit
#
# @run: enable the feature, do nothing and continue if the notify VM exit happens.
#
# @internal-error: enable the feature, raise a internal error if the notify
# VM exit happens.
#
# @disable: disable the feature.
#
# Since: 7.2
##
{ 'enum': 'NotifyVmexitOption',
'data': [ 'run', 'internal-error', 'disable' ] }

View File

@ -1199,13 +1199,16 @@
# interfaces (e.g. VGA and virtual console character devices) # interfaces (e.g. VGA and virtual console character devices)
# by default. # by default.
# Since 7.1 # Since 7.1
# @show-menubar: Display the main window menubar. Defaults to "on".
# Since 8.0
# #
# Since: 2.12 # Since: 2.12
## ##
{ 'struct' : 'DisplayGTK', { 'struct' : 'DisplayGTK',
'data' : { '*grab-on-hover' : 'bool', 'data' : { '*grab-on-hover' : 'bool',
'*zoom-to-fit' : 'bool', '*zoom-to-fit' : 'bool',
'*show-tabs' : 'bool' } } '*show-tabs' : 'bool',
'*show-menubar' : 'bool' } }
## ##
# @DisplayEGLHeadless: # @DisplayEGLHeadless:

View File

@ -92,6 +92,10 @@ int main(int argc, char *argv[])
fprintf(stderr, "not a number: %s\n", optarg); fprintf(stderr, "not a number: %s\n", optarg);
exit(1); exit(1);
} }
if (dpi == 0) {
fprintf(stderr, "cannot be zero: %s\n", optarg);
exit(1);
}
break; break;
case 'v': case 'v':
info.vendor = optarg; info.vendor = optarg;

View File

@ -191,6 +191,7 @@ DEF("accel", HAS_ARG, QEMU_OPTION_accel,
" split-wx=on|off (enable TCG split w^x mapping)\n" " split-wx=on|off (enable TCG split w^x mapping)\n"
" tb-size=n (TCG translation block cache size)\n" " tb-size=n (TCG translation block cache size)\n"
" dirty-ring-size=n (KVM dirty ring GFN count, default 0)\n" " dirty-ring-size=n (KVM dirty ring GFN count, default 0)\n"
" notify-vmexit=run|internal-error|disable,notify-window=n (enable notify VM exit and set notify window, x86 only)\n"
" thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL) " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL)
SRST SRST
``-accel name[,prop=value[,...]]`` ``-accel name[,prop=value[,...]]``
@ -242,6 +243,16 @@ SRST
is disabled (dirty-ring-size=0). When enabled, KVM will instead is disabled (dirty-ring-size=0). When enabled, KVM will instead
record dirty pages in a bitmap. record dirty pages in a bitmap.
``notify-vmexit=run|internal-error|disable,notify-window=n``
Enables or disables notify VM exit support on x86 host and specify
the corresponding notify window to trigger the VM exit if enabled.
``run`` option enables the feature. It does nothing and continue
if the exit happens. ``internal-error`` option enables the feature.
It raises a internal error. ``disable`` option doesn't enable the feature.
This feature can mitigate the CPU stuck issue due to event windows don't
open up for a specified of time (i.e. notify-window).
Default: notify-vmexit=run,notify-window=0.
ERST ERST
DEF("smp", HAS_ARG, QEMU_OPTION_smp, DEF("smp", HAS_ARG, QEMU_OPTION_smp,
@ -1969,6 +1980,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
#if defined(CONFIG_GTK) #if defined(CONFIG_GTK)
"-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n" "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
" [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n" " [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n"
" [,show-menubar=on|off]\n"
#endif #endif
#if defined(CONFIG_VNC) #if defined(CONFIG_VNC)
"-display vnc=<display>[,<optargs>]\n" "-display vnc=<display>[,<optargs>]\n"
@ -2061,6 +2073,8 @@ SRST
``window-close=on|off`` : Allow to quit qemu with window close button ``window-close=on|off`` : Allow to quit qemu with window close button
``show-menubar=on|off`` : Display the main window menubar, defaults to "on"
``curses[,charset=<encoding>]`` ``curses[,charset=<encoding>]``
Display video output via curses. For graphics device models Display video output via curses. For graphics device models
which support a text mode, QEMU can display this output using a which support a text mode, QEMU can display this output using a

View File

@ -1058,3 +1058,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
{ {
return true; return true;
} }
void kvm_arch_accel_class_init(ObjectClass *oc)
{
}

View File

@ -25,4 +25,8 @@
#define TARGET_PAGE_BITS 12 #define TARGET_PAGE_BITS 12
#define NB_MMU_MODES 3 #define NB_MMU_MODES 3
#ifndef CONFIG_USER_ONLY
# define TARGET_TB_PCREL 1
#endif
#endif #endif

View File

@ -1467,7 +1467,7 @@ ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = {
}, },
}; };
static uint32_t xsave_area_size(uint64_t mask, bool compacted) uint32_t xsave_area_size(uint64_t mask, bool compacted)
{ {
uint64_t ret = x86_ext_save_areas[0].size; uint64_t ret = x86_ext_save_areas[0].size;
const ExtSaveArea *esa; const ExtSaveArea *esa;
@ -6017,6 +6017,7 @@ static void x86_cpu_reset(DeviceState *dev)
env->exception_has_payload = false; env->exception_has_payload = false;
env->exception_payload = 0; env->exception_payload = 0;
env->nmi_injected = false; env->nmi_injected = false;
env->triple_fault_pending = false;
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
/* We hard-wire the BSP to the first CPU. */ /* We hard-wire the BSP to the first CPU. */
apic_designate_bsp(cpu->apic_state, s->cpu_index == 0); apic_designate_bsp(cpu->apic_state, s->cpu_index == 0);

View File

@ -1739,6 +1739,7 @@ typedef struct CPUArchState {
uint8_t has_error_code; uint8_t has_error_code;
uint8_t exception_has_payload; uint8_t exception_has_payload;
uint64_t exception_payload; uint64_t exception_payload;
uint8_t triple_fault_pending;
uint32_t ins_len; uint32_t ins_len;
uint32_t sipi_vector; uint32_t sipi_vector;
bool tsc_valid; bool tsc_valid;
@ -2070,6 +2071,8 @@ void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32);
void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32); void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32);
void cpu_x86_fxsave(CPUX86State *s, target_ulong ptr); void cpu_x86_fxsave(CPUX86State *s, target_ulong ptr);
void cpu_x86_fxrstor(CPUX86State *s, target_ulong ptr); void cpu_x86_fxrstor(CPUX86State *s, target_ulong ptr);
void cpu_x86_xsave(CPUX86State *s, target_ulong ptr);
void cpu_x86_xrstor(CPUX86State *s, target_ulong ptr);
/* cpu.c */ /* cpu.c */
void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
@ -2326,6 +2329,7 @@ bool cpu_is_bsp(X86CPU *cpu);
void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen); void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen);
void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen); void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen);
uint32_t xsave_area_size(uint64_t mask, bool compacted);
void x86_update_hflags(CPUX86State* env); void x86_update_hflags(CPUX86State* env);
static inline bool hyperv_feat_enabled(X86CPU *cpu, int feat) static inline bool hyperv_feat_enabled(X86CPU *cpu, int feat)

View File

@ -37,7 +37,7 @@ DEF_HELPER_2(lldt, void, env, int)
DEF_HELPER_2(ltr, void, env, int) DEF_HELPER_2(ltr, void, env, int)
DEF_HELPER_3(load_seg, void, env, int, int) DEF_HELPER_3(load_seg, void, env, int, int)
DEF_HELPER_4(ljmp_protected, void, env, int, tl, tl) DEF_HELPER_4(ljmp_protected, void, env, int, tl, tl)
DEF_HELPER_5(lcall_real, void, env, int, tl, int, int) DEF_HELPER_5(lcall_real, void, env, i32, i32, int, i32)
DEF_HELPER_5(lcall_protected, void, env, int, tl, int, tl) DEF_HELPER_5(lcall_protected, void, env, int, tl, int, tl)
DEF_HELPER_2(iret_real, void, env, int) DEF_HELPER_2(iret_real, void, env, int)
DEF_HELPER_3(iret_protected, void, env, int, int) DEF_HELPER_3(iret_protected, void, env, int, int)

View File

@ -15,6 +15,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/qapi-events-run-state.h" #include "qapi/qapi-events-run-state.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/visitor.h"
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/syscall.h> #include <sys/syscall.h>
@ -132,6 +133,7 @@ static int has_xcrs;
static int has_pit_state2; static int has_pit_state2;
static int has_sregs2; static int has_sregs2;
static int has_exception_payload; static int has_exception_payload;
static int has_triple_fault_event;
static bool has_msr_mcg_ext_ctl; static bool has_msr_mcg_ext_ctl;
@ -139,6 +141,8 @@ static struct kvm_cpuid2 *cpuid_cache;
static struct kvm_cpuid2 *hv_cpuid_cache; static struct kvm_cpuid2 *hv_cpuid_cache;
static struct kvm_msr_list *kvm_feature_msrs; static struct kvm_msr_list *kvm_feature_msrs;
static KVMMSRHandlers msr_handlers[KVM_MSR_FILTER_MAX_RANGES];
#define BUS_LOCK_SLICE_TIME 1000000000ULL /* ns */ #define BUS_LOCK_SLICE_TIME 1000000000ULL /* ns */
static RateLimit bus_lock_ratelimit_ctrl; static RateLimit bus_lock_ratelimit_ctrl;
static int kvm_get_one_msr(X86CPU *cpu, int index, uint64_t *value); static int kvm_get_one_msr(X86CPU *cpu, int index, uint64_t *value);
@ -2397,6 +2401,17 @@ static int kvm_get_supported_msrs(KVMState *s)
return ret; return ret;
} }
static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr,
uint64_t *val)
{
CPUState *cs = CPU(cpu);
*val = cs->nr_threads * cs->nr_cores; /* thread count, bits 15..0 */
*val |= ((uint32_t)cs->nr_cores << 16); /* core count, bits 31..16 */
return true;
}
static Notifier smram_machine_done; static Notifier smram_machine_done;
static KVMMemoryListener smram_listener; static KVMMemoryListener smram_listener;
static AddressSpace smram_address_space; static AddressSpace smram_address_space;
@ -2479,6 +2494,16 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
} }
} }
has_triple_fault_event = kvm_check_extension(s, KVM_CAP_X86_TRIPLE_FAULT_EVENT);
if (has_triple_fault_event) {
ret = kvm_vm_enable_cap(s, KVM_CAP_X86_TRIPLE_FAULT_EVENT, 0, true);
if (ret < 0) {
error_report("kvm: Failed to enable triple fault event cap: %s",
strerror(-ret));
return ret;
}
}
ret = kvm_get_supported_msrs(s); ret = kvm_get_supported_msrs(s);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -2584,6 +2609,40 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
} }
} }
if (s->notify_vmexit != NOTIFY_VMEXIT_OPTION_DISABLE &&
kvm_check_extension(s, KVM_CAP_X86_NOTIFY_VMEXIT)) {
uint64_t notify_window_flags =
((uint64_t)s->notify_window << 32) |
KVM_X86_NOTIFY_VMEXIT_ENABLED |
KVM_X86_NOTIFY_VMEXIT_USER;
ret = kvm_vm_enable_cap(s, KVM_CAP_X86_NOTIFY_VMEXIT, 0,
notify_window_flags);
if (ret < 0) {
error_report("kvm: Failed to enable notify vmexit cap: %s",
strerror(-ret));
return ret;
}
}
if (kvm_vm_check_extension(s, KVM_CAP_X86_USER_SPACE_MSR)) {
bool r;
ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0,
KVM_MSR_EXIT_REASON_FILTER);
if (ret) {
error_report("Could not enable user space MSRs: %s",
strerror(-ret));
exit(1);
}
r = kvm_filter_msr(s, MSR_CORE_THREAD_COUNT,
kvm_rdmsr_core_thread_count, NULL);
if (!r) {
error_report("Could not install MSR_CORE_THREAD_COUNT handler: %s",
strerror(-ret));
exit(1);
}
}
return 0; return 0;
} }
@ -4295,6 +4354,11 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level)
} }
} }
if (has_triple_fault_event) {
events.flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT;
events.triple_fault.pending = env->triple_fault_pending;
}
return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events); return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events);
} }
@ -4364,6 +4428,10 @@ static int kvm_get_vcpu_events(X86CPU *cpu)
} }
} }
if (events.flags & KVM_VCPUEVENT_VALID_TRIPLE_FAULT) {
env->triple_fault_pending = events.triple_fault.pending;
}
env->sipi_vector = events.sipi_vector; env->sipi_vector = events.sipi_vector;
return 0; return 0;
@ -5073,6 +5141,108 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
} }
} }
static bool kvm_install_msr_filters(KVMState *s)
{
uint64_t zero = 0;
struct kvm_msr_filter filter = {
.flags = KVM_MSR_FILTER_DEFAULT_ALLOW,
};
int r, i, j = 0;
for (i = 0; i < KVM_MSR_FILTER_MAX_RANGES; i++) {
KVMMSRHandlers *handler = &msr_handlers[i];
if (handler->msr) {
struct kvm_msr_filter_range *range = &filter.ranges[j++];
*range = (struct kvm_msr_filter_range) {
.flags = 0,
.nmsrs = 1,
.base = handler->msr,
.bitmap = (__u8 *)&zero,
};
if (handler->rdmsr) {
range->flags |= KVM_MSR_FILTER_READ;
}
if (handler->wrmsr) {
range->flags |= KVM_MSR_FILTER_WRITE;
}
}
}
r = kvm_vm_ioctl(s, KVM_X86_SET_MSR_FILTER, &filter);
if (r) {
return false;
}
return true;
}
bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr,
QEMUWRMSRHandler *wrmsr)
{
int i;
for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) {
if (!msr_handlers[i].msr) {
msr_handlers[i] = (KVMMSRHandlers) {
.msr = msr,
.rdmsr = rdmsr,
.wrmsr = wrmsr,
};
if (!kvm_install_msr_filters(s)) {
msr_handlers[i] = (KVMMSRHandlers) { };
return false;
}
return true;
}
}
return false;
}
static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run)
{
int i;
bool r;
for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) {
KVMMSRHandlers *handler = &msr_handlers[i];
if (run->msr.index == handler->msr) {
if (handler->rdmsr) {
r = handler->rdmsr(cpu, handler->msr,
(uint64_t *)&run->msr.data);
run->msr.error = r ? 0 : 1;
return 0;
}
}
}
assert(false);
}
static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run)
{
int i;
bool r;
for (i = 0; i < ARRAY_SIZE(msr_handlers); i++) {
KVMMSRHandlers *handler = &msr_handlers[i];
if (run->msr.index == handler->msr) {
if (handler->wrmsr) {
r = handler->wrmsr(cpu, handler->msr, run->msr.data);
run->msr.error = r ? 0 : 1;
return 0;
}
}
}
assert(false);
}
static bool has_sgx_provisioning; static bool has_sgx_provisioning;
static bool __kvm_enable_sgx_provisioning(KVMState *s) static bool __kvm_enable_sgx_provisioning(KVMState *s)
@ -5117,6 +5287,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
X86CPU *cpu = X86_CPU(cs); X86CPU *cpu = X86_CPU(cs);
uint64_t code; uint64_t code;
int ret; int ret;
bool ctx_invalid;
char str[256];
KVMState *state;
switch (run->exit_reason) { switch (run->exit_reason) {
case KVM_EXIT_HLT: case KVM_EXIT_HLT:
@ -5172,6 +5345,31 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
/* already handled in kvm_arch_post_run */ /* already handled in kvm_arch_post_run */
ret = 0; ret = 0;
break; break;
case KVM_EXIT_NOTIFY:
ctx_invalid = !!(run->notify.flags & KVM_NOTIFY_CONTEXT_INVALID);
state = KVM_STATE(current_accel());
sprintf(str, "Encounter a notify exit with %svalid context in"
" guest. There can be possible misbehaves in guest."
" Please have a look.", ctx_invalid ? "in" : "");
if (ctx_invalid ||
state->notify_vmexit == NOTIFY_VMEXIT_OPTION_INTERNAL_ERROR) {
warn_report("KVM internal error: %s", str);
ret = -1;
} else {
warn_report_once("KVM: %s", str);
ret = 0;
}
break;
case KVM_EXIT_X86_RDMSR:
/* We only enable MSR filtering, any other exit is bogus */
assert(run->msr.reason == KVM_MSR_EXIT_REASON_FILTER);
ret = kvm_handle_rdmsr(cpu, run);
break;
case KVM_EXIT_X86_WRMSR:
/* We only enable MSR filtering, any other exit is bogus */
assert(run->msr.reason == KVM_MSR_EXIT_REASON_FILTER);
ret = kvm_handle_wrmsr(cpu, run);
break;
default: default:
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
ret = -1; ret = -1;
@ -5448,3 +5646,71 @@ void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask)
mask &= ~BIT_ULL(bit); mask &= ~BIT_ULL(bit);
} }
} }
static int kvm_arch_get_notify_vmexit(Object *obj, Error **errp)
{
KVMState *s = KVM_STATE(obj);
return s->notify_vmexit;
}
static void kvm_arch_set_notify_vmexit(Object *obj, int value, Error **errp)
{
KVMState *s = KVM_STATE(obj);
if (s->fd != -1) {
error_setg(errp, "Cannot set properties after the accelerator has been initialized");
return;
}
s->notify_vmexit = value;
}
static void kvm_arch_get_notify_window(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
KVMState *s = KVM_STATE(obj);
uint32_t value = s->notify_window;
visit_type_uint32(v, name, &value, errp);
}
static void kvm_arch_set_notify_window(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
KVMState *s = KVM_STATE(obj);
Error *error = NULL;
uint32_t value;
if (s->fd != -1) {
error_setg(errp, "Cannot set properties after the accelerator has been initialized");
return;
}
visit_type_uint32(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
}
s->notify_window = value;
}
void kvm_arch_accel_class_init(ObjectClass *oc)
{
object_class_property_add_enum(oc, "notify-vmexit", "NotifyVMexitOption",
&NotifyVmexitOption_lookup,
kvm_arch_get_notify_vmexit,
kvm_arch_set_notify_vmexit);
object_class_property_set_description(oc, "notify-vmexit",
"Enable notify VM exit");
object_class_property_add(oc, "notify-window", "uint32",
kvm_arch_get_notify_window,
kvm_arch_set_notify_window,
NULL, NULL);
object_class_property_set_description(oc, "notify-window",
"Clock cycles without an event window "
"after which a notification VM exit occurs");
}

View File

@ -54,4 +54,15 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address);
bool kvm_enable_sgx_provisioning(KVMState *s); bool kvm_enable_sgx_provisioning(KVMState *s);
void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask);
typedef bool QEMURDMSRHandler(X86CPU *cpu, uint32_t msr, uint64_t *val);
typedef bool QEMUWRMSRHandler(X86CPU *cpu, uint32_t msr, uint64_t val);
typedef struct kvm_msr_handlers {
uint32_t msr;
QEMURDMSRHandler *rdmsr;
QEMUWRMSRHandler *wrmsr;
} KVMMSRHandlers;
bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr,
QEMUWRMSRHandler *wrmsr);
#endif #endif

View File

@ -1562,6 +1562,25 @@ static const VMStateDescription vmstate_arch_lbr = {
} }
}; };
static bool triple_fault_needed(void *opaque)
{
X86CPU *cpu = opaque;
CPUX86State *env = &cpu->env;
return env->triple_fault_pending;
}
static const VMStateDescription vmstate_triple_fault = {
.name = "cpu/triple_fault",
.version_id = 1,
.minimum_version_id = 1,
.needed = triple_fault_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(env.triple_fault_pending, X86CPU),
VMSTATE_END_OF_LIST()
}
};
const VMStateDescription vmstate_x86_cpu = { const VMStateDescription vmstate_x86_cpu = {
.name = "cpu", .name = "cpu",
.version_id = 12, .version_id = 12,
@ -1706,6 +1725,7 @@ const VMStateDescription vmstate_x86_cpu = {
&vmstate_amx_xtile, &vmstate_amx_xtile,
#endif #endif
&vmstate_arch_lbr, &vmstate_arch_lbr,
&vmstate_triple_fault,
NULL NULL
} }
}; };

View File

@ -2502,18 +2502,6 @@ void helper_frstor(CPUX86State *env, target_ulong ptr, int data32)
do_frstor(env, ptr, data32, GETPC()); do_frstor(env, ptr, data32, GETPC());
} }
#if defined(CONFIG_USER_ONLY)
void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32)
{
do_fsave(env, ptr, data32, 0);
}
void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32)
{
do_frstor(env, ptr, data32, 0);
}
#endif
#define XO(X) offsetof(X86XSaveArea, X) #define XO(X) offsetof(X86XSaveArea, X)
static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra)
@ -2787,21 +2775,8 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr)
do_fxrstor(env, ptr, GETPC()); do_fxrstor(env, ptr, GETPC());
} }
#if defined(CONFIG_USER_ONLY) static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr_t ra)
void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr)
{ {
do_fxsave(env, ptr, 0);
}
void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr)
{
do_fxrstor(env, ptr, 0);
}
#endif
void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
{
uintptr_t ra = GETPC();
uint64_t xstate_bv, xcomp_bv, reserve0; uint64_t xstate_bv, xcomp_bv, reserve0;
rfbm &= env->xcr0; rfbm &= env->xcr0;
@ -2894,6 +2869,43 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
#undef XO #undef XO
void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
{
do_xrstor(env, ptr, rfbm, GETPC());
}
#if defined(CONFIG_USER_ONLY)
void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32)
{
do_fsave(env, ptr, data32, 0);
}
void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32)
{
do_frstor(env, ptr, data32, 0);
}
void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr)
{
do_fxsave(env, ptr, 0);
}
void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr)
{
do_fxrstor(env, ptr, 0);
}
void cpu_x86_xsave(CPUX86State *env, target_ulong ptr)
{
do_xsave(env, ptr, -1, get_xinuse(env), -1, 0);
}
void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr)
{
do_xrstor(env, ptr, -1, 0);
}
#endif
uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx)
{ {
/* The OS must have enabled XSAVE. */ /* The OS must have enabled XSAVE. */

View File

@ -1504,14 +1504,12 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip,
} }
/* real mode call */ /* real mode call */
void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, void helper_lcall_real(CPUX86State *env, uint32_t new_cs, uint32_t new_eip,
int shift, int next_eip) int shift, uint32_t next_eip)
{ {
int new_eip;
uint32_t esp, esp_mask; uint32_t esp, esp_mask;
target_ulong ssp; target_ulong ssp;
new_eip = new_eip1;
esp = env->regs[R_ESP]; esp = env->regs[R_ESP];
esp_mask = get_sp_mask(env->segs[R_SS].flags); esp_mask = get_sp_mask(env->segs[R_SS].flags);
ssp = env->segs[R_SS].base; ssp = env->segs[R_SS].base;

View File

@ -450,6 +450,11 @@ void helper_rdmsr(CPUX86State *env)
case MSR_IA32_UCODE_REV: case MSR_IA32_UCODE_REV:
val = x86_cpu->ucode_rev; val = x86_cpu->ucode_rev;
break; break;
case MSR_CORE_THREAD_COUNT: {
CPUState *cs = CPU(x86_cpu);
val = (cs->nr_threads * cs->nr_cores) | (cs->nr_cores << 16);
break;
}
default: default:
if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL
&& (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL +

View File

@ -49,9 +49,11 @@ static void x86_cpu_exec_exit(CPUState *cs)
static void x86_cpu_synchronize_from_tb(CPUState *cs, static void x86_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb) const TranslationBlock *tb)
{ {
X86CPU *cpu = X86_CPU(cs); /* The instruction pointer is always up to date with TARGET_TB_PCREL. */
if (!TARGET_TB_PCREL) {
cpu->env.eip = tb_pc(tb) - tb->cs_base; CPUX86State *env = cs->env_ptr;
env->eip = tb_pc(tb) - tb->cs_base;
}
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY

File diff suppressed because it is too large Load Diff

View File

@ -1294,3 +1294,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
{ {
return true; return true;
} }
void kvm_arch_accel_class_init(ObjectClass *oc)
{
}

View File

@ -2966,3 +2966,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
{ {
return true; return true;
} }
void kvm_arch_accel_class_init(ObjectClass *oc)
{
}

View File

@ -532,3 +532,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
{ {
return true; return true;
} }
void kvm_arch_accel_class_init(ObjectClass *oc)
{
}

View File

@ -628,6 +628,18 @@ bool pmp_is_range_in_tlb(CPURISCVState *env, hwaddr tlb_sa,
} }
if (*tlb_size != 0) { if (*tlb_size != 0) {
/*
* At this point we have a tlb_size that is the smallest possible size
* That fits within a TARGET_PAGE_SIZE and the PMP region.
*
* If the size is less then TARGET_PAGE_SIZE we drop the size to 1.
* This means the result isn't cached in the TLB and is only used for
* a single translation.
*/
if (*tlb_size < TARGET_PAGE_SIZE) {
*tlb_size = 1;
}
return true; return true;
} }

View File

@ -2581,3 +2581,7 @@ int kvm_s390_get_zpci_op(void)
{ {
return cap_zpci_op; return cap_zpci_op;
} }
void kvm_arch_accel_class_init(ObjectClass *oc)
{
}

View File

@ -381,6 +381,8 @@ class BootLinuxConsole(LinuxKernelTest):
:avocado: tags=u-boot :avocado: tags=u-boot
:avocado: tags=accel:tcg :avocado: tags=accel:tcg
""" """
self.require_netdev('user')
uboot_url = ('https://raw.githubusercontent.com/' uboot_url = ('https://raw.githubusercontent.com/'
'Subbaraya-Sundeep/qemu-test-binaries/' 'Subbaraya-Sundeep/qemu-test-binaries/'
'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot') 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot')
@ -779,6 +781,8 @@ class BootLinuxConsole(LinuxKernelTest):
:avocado: tags=machine:orangepi-pc :avocado: tags=machine:orangepi-pc
:avocado: tags=device:sd :avocado: tags=device:sd
""" """
self.require_netdev('user')
deb_url = ('https://apt.armbian.com/pool/main/l/' deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'

View File

@ -93,6 +93,8 @@ class AST2x00Machine(QemuSystemTest):
self.do_test_arm_aspeed(image_path) self.do_test_arm_aspeed(image_path)
def do_test_arm_aspeed_buidroot_start(self, image, cpu_id): def do_test_arm_aspeed_buidroot_start(self, image, cpu_id):
self.require_netdev('user')
self.vm.set_console() self.vm.set_console()
self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
'-net', 'nic', '-net', 'user') '-net', 'nic', '-net', 'user')
@ -193,6 +195,7 @@ class AST2x00MachineSDK(QemuSystemTest):
vm=vm) vm=vm)
def do_test_arm_aspeed_sdk_start(self, image, cpu_id): def do_test_arm_aspeed_sdk_start(self, image, cpu_id):
self.require_netdev('user')
self.vm.set_console() self.vm.set_console()
self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw',
'-net', 'nic', '-net', 'user') '-net', 'nic', '-net', 'user')

View File

@ -23,6 +23,7 @@ class BambooMachine(QemuSystemTest):
:avocado: tags=accel:tcg :avocado: tags=accel:tcg
""" """
self.require_accelerator("tcg") self.require_accelerator("tcg")
self.require_netdev('user')
tar_url = ('http://landley.net/aboriginal/downloads/binaries/' tar_url = ('http://landley.net/aboriginal/downloads/binaries/'
'system-image-powerpc-440fp.tar.gz') 'system-image-powerpc-440fp.tar.gz')
tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9' tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9'

View File

@ -65,7 +65,6 @@ class Engine(object):
return records return records
def _cpu_timing(self, pid): def _cpu_timing(self, pid):
records = []
now = time.time() now = time.time()
jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK']) jiffies_per_sec = os.sysconf(os.sysconf_names['SC_CLK_TCK'])

View File

@ -725,7 +725,7 @@ static char *test_acpi_create_args(test_data *data, const char *params,
} }
} else { } else {
args = g_strdup_printf("-machine %s %s -accel tcg " args = g_strdup_printf("-machine %s %s -accel tcg "
"-net none -display none %s " "-net none %s "
"-drive id=hd0,if=none,file=%s,format=raw " "-drive id=hd0,if=none,file=%s,format=raw "
"-device %s,drive=hd0 ", "-device %s,drive=hd0 ",
data->machine, data->tcg_only ? "" : "-accel kvm", data->machine, data->tcg_only ? "" : "-accel kvm",

View File

@ -15,17 +15,6 @@
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
static void device_del(QTestState *qtest, const char *id)
{
QDict *resp;
resp = qtest_qmp(qtest,
"{'execute': 'device_del', 'arguments': { 'id': %s } }", id);
g_assert(qdict_haskey(resp, "return"));
qobject_unref(resp);
}
static void system_reset(QTestState *qtest) static void system_reset(QTestState *qtest)
{ {
QDict *resp; QDict *resp;
@ -68,7 +57,7 @@ static void process_device_remove(QTestState *qtest, const char *id)
* be processed. However during system reset, the removal will be * be processed. However during system reset, the removal will be
* handled, removing the device. * handled, removing the device.
*/ */
device_del(qtest, id); qtest_qmp_device_del_send(qtest, id);
system_reset(qtest); system_reset(qtest);
wait_device_deleted_event(qtest, id); wait_device_deleted_event(qtest, id);
} }
@ -90,6 +79,19 @@ static void test_pci_unplug_request(void)
qtest_quit(qtest); qtest_quit(qtest);
} }
static void test_q35_pci_unplug_request(void)
{
QTestState *qtest = qtest_initf("-machine q35 "
"-device pcie-root-port,id=p1 "
"-device pcie-pci-bridge,bus=p1,id=b1 "
"-device virtio-mouse-pci,bus=b1,id=dev0");
process_device_remove(qtest, "dev0");
qtest_quit(qtest);
}
static void test_pci_unplug_json_request(void) static void test_pci_unplug_json_request(void)
{ {
const char *arch = qtest_get_arch(); const char *arch = qtest_get_arch();
@ -108,11 +110,32 @@ static void test_pci_unplug_json_request(void)
qtest_quit(qtest); qtest_quit(qtest);
} }
static void test_q35_pci_unplug_json_request(void)
{
const char *port = "-device '{\"driver\": \"pcie-root-port\", "
"\"id\": \"p1\"}'";
const char *bridge = "-device '{\"driver\": \"pcie-pci-bridge\", "
"\"id\": \"b1\", "
"\"bus\": \"p1\"}'";
const char *device = "-device '{\"driver\": \"virtio-mouse-pci\", "
"\"bus\": \"b1\", "
"\"id\": \"dev0\"}'";
QTestState *qtest = qtest_initf("-machine q35 %s %s %s",
port, bridge, device);
process_device_remove(qtest, "dev0");
qtest_quit(qtest);
}
static void test_ccw_unplug(void) static void test_ccw_unplug(void)
{ {
QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
device_del(qtest, "dev0"); qtest_qmp_device_del_send(qtest, "dev0");
wait_device_deleted_event(qtest, "dev0"); wait_device_deleted_event(qtest, "dev0");
qtest_quit(qtest); qtest_quit(qtest);
@ -187,5 +210,12 @@ int main(int argc, char **argv)
test_spapr_phb_unplug_request); test_spapr_phb_unplug_request);
} }
if (!strcmp(arch, "x86_64") && qtest_has_machine("q35")) {
qtest_add_func("/device-plug/q35-pci-unplug-request",
test_q35_pci_unplug_request);
qtest_add_func("/device-plug/q35-pci-unplug-json-request",
test_q35_pci_unplug_json_request);
}
return g_test_run(); return g_test_run();
} }

View File

@ -123,12 +123,10 @@ static const char *qvirtio_get_dev_type(void)
static void device_add(QTestState *qts) static void device_add(QTestState *qts)
{ {
QDict *response; g_autofree char *driver = g_strdup_printf("virtio-blk-%s",
char driver[32];
snprintf(driver, sizeof(driver), "virtio-blk-%s",
qvirtio_get_dev_type()); qvirtio_get_dev_type());
QDict *response =
response = qtest_qmp(qts, "{'execute': 'device_add'," qtest_qmp(qts, "{'execute': 'device_add',"
" 'arguments': {" " 'arguments': {"
" 'driver': %s," " 'driver': %s,"
" 'drive': 'drive0'," " 'drive': 'drive0',"
@ -143,11 +141,7 @@ static void device_del(QTestState *qts, bool and_reset)
{ {
QDict *response; QDict *response;
response = qtest_qmp(qts, "{'execute': 'device_del'," qtest_qmp_device_del_send(qts, "dev0");
" 'arguments': { 'id': 'dev0' } }");
g_assert(response);
g_assert(qdict_haskey(response, "return"));
qobject_unref(response);
if (and_reset) { if (and_reset) {
response = qtest_qmp(qts, "{'execute': 'system_reset' }"); response = qtest_qmp(qts, "{'execute': 'system_reset' }");
@ -258,6 +252,27 @@ static void test_cli_device_del(void)
qtest_quit(qts); qtest_quit(qts);
} }
static void test_cli_device_del_q35(void)
{
QTestState *qts;
/*
* -drive/-device and device_del. Start with a drive used by a
* device that unplugs after reset.
*/
qts = qtest_initf("-drive if=none,id=drive0,file=null-co://,"
"file.read-zeroes=on,format=raw "
"-machine q35 -device pcie-root-port,id=p1 "
"-device pcie-pci-bridge,bus=p1,id=b1 "
"-device virtio-blk-%s,drive=drive0,bus=b1,id=dev0",
qvirtio_get_dev_type());
device_del(qts, true);
g_assert(!has_drive(qts));
qtest_quit(qts);
}
static void test_empty_device_del(void) static void test_empty_device_del(void)
{ {
QTestState *qts; QTestState *qts;
@ -294,6 +309,43 @@ static void test_device_add_and_del(void)
qtest_quit(qts); qtest_quit(qts);
} }
static void device_add_q35(QTestState *qts)
{
g_autofree char *driver = g_strdup_printf("virtio-blk-%s",
qvirtio_get_dev_type());
QDict *response =
qtest_qmp(qts, "{'execute': 'device_add',"
" 'arguments': {"
" 'driver': %s,"
" 'drive': 'drive0',"
" 'id': 'dev0',"
" 'bus': 'b1'"
"}}", driver);
g_assert(response);
g_assert(qdict_haskey(response, "return"));
qobject_unref(response);
}
static void test_device_add_and_del_q35(void)
{
QTestState *qts;
/*
* -drive/device_add and device_del. Start with a drive used by a
* device that unplugs after reset.
*/
qts = qtest_initf("-machine q35 -device pcie-root-port,id=p1 "
"-device pcie-pci-bridge,bus=p1,id=b1 "
"-drive if=none,id=drive0,file=null-co://,"
"file.read-zeroes=on,format=raw");
device_add_q35(qts);
device_del(qts, true);
g_assert(!has_drive(qts));
qtest_quit(qts);
}
static void test_drive_add_device_add_and_del(void) static void test_drive_add_device_add_and_del(void)
{ {
QTestState *qts; QTestState *qts;
@ -318,6 +370,25 @@ static void test_drive_add_device_add_and_del(void)
qtest_quit(qts); qtest_quit(qts);
} }
static void test_drive_add_device_add_and_del_q35(void)
{
QTestState *qts;
qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 "
"-device pcie-pci-bridge,bus=p1,id=b1");
/*
* drive_add/device_add and device_del. The drive is used by a
* device that unplugs after reset.
*/
drive_add_with_media(qts);
device_add_q35(qts);
device_del(qts, true);
g_assert(!has_drive(qts));
qtest_quit(qts);
}
static void test_blockdev_add_device_add_and_del(void) static void test_blockdev_add_device_add_and_del(void)
{ {
QTestState *qts; QTestState *qts;
@ -331,7 +402,7 @@ static void test_blockdev_add_device_add_and_del(void)
qts = qtest_init(machine_addition); qts = qtest_init(machine_addition);
/* /*
* blockdev_add/device_add and device_del. The it drive is used by a * blockdev_add/device_add and device_del. The drive is used by a
* device that unplugs after reset, but it doesn't go away. * device that unplugs after reset, but it doesn't go away.
*/ */
blockdev_add_with_media(qts); blockdev_add_with_media(qts);
@ -342,6 +413,25 @@ static void test_blockdev_add_device_add_and_del(void)
qtest_quit(qts); qtest_quit(qts);
} }
static void test_blockdev_add_device_add_and_del_q35(void)
{
QTestState *qts;
qts = qtest_init("-machine q35 -device pcie-root-port,id=p1 "
"-device pcie-pci-bridge,bus=p1,id=b1");
/*
* blockdev_add/device_add and device_del. The drive is used by a
* device that unplugs after reset, but it doesn't go away.
*/
blockdev_add_with_media(qts);
device_add_q35(qts);
device_del(qts, true);
g_assert(has_blockdev(qts));
qtest_quit(qts);
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
@ -363,6 +453,17 @@ int main(int argc, char **argv)
test_empty_device_del); test_empty_device_del);
qtest_add_func("/device_del/blockdev", qtest_add_func("/device_del/blockdev",
test_blockdev_add_device_add_and_del); test_blockdev_add_device_add_and_del);
if (qtest_has_machine("q35")) {
qtest_add_func("/device_del/drive/cli_device_q35",
test_cli_device_del_q35);
qtest_add_func("/device_del/drive/device_add_q35",
test_device_add_and_del_q35);
qtest_add_func("/device_del/drive/drive_add_device_add_q35",
test_drive_add_device_add_and_del_q35);
qtest_add_func("/device_del/blockdev_q35",
test_blockdev_add_device_add_and_del_q35);
}
} }
return g_test_run(); return g_test_run();

View File

@ -21,7 +21,7 @@ static void test_lsi_do_msgout_cancel_req(void)
return; return;
} }
s = qtest_init("-M q35 -m 2G -display none -nodefaults " s = qtest_init("-M q35 -m 2G -nodefaults "
"-device lsi53c895a,id=scsi " "-device lsi53c895a,id=scsi "
"-device scsi-hd,drive=disk0 " "-device scsi-hd,drive=disk0 "
"-drive file=null-co://,id=disk0,if=none,format=raw"); "-drive file=null-co://,id=disk0,if=none,format=raw");

View File

@ -40,7 +40,7 @@ static void test_lp1878263_megasas_zero_iov_cnt(void)
*/ */
static void test_gitlab_issue521_megasas_sgl_ovf(void) static void test_gitlab_issue521_megasas_sgl_ovf(void)
{ {
QTestState *s = qtest_init("-display none -m 32M -machine q35 " QTestState *s = qtest_init("-m 32M -machine q35 "
"-nodefaults -device megasas " "-nodefaults -device megasas "
"-device scsi-cd,drive=null0 " "-device scsi-cd,drive=null0 "
"-blockdev " "-blockdev "

View File

@ -15,7 +15,7 @@
*/ */
static void test_fuzz_sb16_0x1c(void) static void test_fuzz_sb16_0x1c(void)
{ {
QTestState *s = qtest_init("-M q35 -display none " QTestState *s = qtest_init("-M q35 "
"-device sb16,audiodev=snd0 " "-device sb16,audiodev=snd0 "
"-audiodev none,id=snd0"); "-audiodev none,id=snd0");
qtest_outw(s, 0x22c, 0x41); qtest_outw(s, 0x22c, 0x41);
@ -27,7 +27,7 @@ static void test_fuzz_sb16_0x1c(void)
static void test_fuzz_sb16_0x91(void) static void test_fuzz_sb16_0x91(void)
{ {
QTestState *s = qtest_init("-M pc -display none " QTestState *s = qtest_init("-M pc "
"-device sb16,audiodev=none " "-device sb16,audiodev=none "
"-audiodev id=none,driver=none"); "-audiodev id=none,driver=none");
qtest_outw(s, 0x22c, 0xf141); qtest_outw(s, 0x22c, 0xf141);
@ -43,7 +43,7 @@ static void test_fuzz_sb16_0x91(void)
*/ */
static void test_fuzz_sb16_0xd4(void) static void test_fuzz_sb16_0xd4(void)
{ {
QTestState *s = qtest_init("-M pc -display none " QTestState *s = qtest_init("-M pc "
"-device sb16,audiodev=none " "-device sb16,audiodev=none "
"-audiodev id=none,driver=none"); "-audiodev id=none,driver=none");
qtest_outb(s, 0x22c, 0x41); qtest_outb(s, 0x22c, 0x41);

View File

@ -18,7 +18,7 @@ static void oss_fuzz_29225(void)
{ {
QTestState *s; QTestState *s;
s = qtest_init(" -display none -m 512m -nodefaults -nographic" s = qtest_init(" -m 512m -nodefaults -nographic"
" -device sdhci-pci,sd-spec-version=3" " -device sdhci-pci,sd-spec-version=3"
" -device sd-card,drive=d0" " -device sd-card,drive=d0"
" -drive if=none,index=0,file=null-co://,format=raw,id=d0"); " -drive if=none,index=0,file=null-co://,format=raw,id=d0");
@ -61,7 +61,7 @@ static void oss_fuzz_36217(void)
{ {
QTestState *s; QTestState *s;
s = qtest_init(" -display none -m 32 -nodefaults -nographic" s = qtest_init(" -m 32 -nodefaults -nographic"
" -device sdhci-pci,sd-spec-version=3 " " -device sdhci-pci,sd-spec-version=3 "
"-device sd-card,drive=d0 " "-device sd-card,drive=d0 "
"-drive if=none,index=0,file=null-co://,format=raw,id=d0"); "-drive if=none,index=0,file=null-co://,format=raw,id=d0");
@ -95,7 +95,7 @@ static void oss_fuzz_36391(void)
{ {
QTestState *s; QTestState *s;
s = qtest_init(" -display none -m 512M -nodefaults -nographic" s = qtest_init(" -m 512M -nodefaults -nographic"
" -device sdhci-pci,sd-spec-version=3" " -device sdhci-pci,sd-spec-version=3"
" -device sd-card,drive=drv" " -device sd-card,drive=drv"
" -drive if=none,index=0,file=null-co://,format=raw,id=drv"); " -drive if=none,index=0,file=null-co://,format=raw,id=drv");

View File

@ -19,7 +19,7 @@ static void test_mmio_oob_from_memory_region_cache(void)
{ {
QTestState *s; QTestState *s;
s = qtest_init("-M pc-q35-5.2 -display none -m 512M " s = qtest_init("-M pc-q35-5.2 -m 512M "
"-device virtio-scsi,num_queues=8,addr=03.0 "); "-device virtio-scsi,num_queues=8,addr=03.0 ");
qtest_outl(s, 0xcf8, 0x80001811); qtest_outl(s, 0xcf8, 0x80001811);

View File

@ -14,7 +14,7 @@
*/ */
static void test_fuzz_xlnx_dp_0x3ac(void) static void test_fuzz_xlnx_dp_0x3ac(void)
{ {
QTestState *s = qtest_init("-M xlnx-zcu102 -display none "); QTestState *s = qtest_init("-M xlnx-zcu102 ");
qtest_readl(s, 0xfd4a03ac); qtest_readl(s, 0xfd4a03ac);
qtest_quit(s); qtest_quit(s);
} }

View File

@ -691,7 +691,8 @@ static void add_virtio_disk(TestArgs *args,
args->n_virtio_disks++; args->n_virtio_disks++;
} }
static void test_override(TestArgs *args, CHSResult expected[]) static void test_override(TestArgs *args, const char *arch,
CHSResult expected[])
{ {
QTestState *qts; QTestState *qts;
char *joined_args; char *joined_args;
@ -700,7 +701,7 @@ static void test_override(TestArgs *args, CHSResult expected[])
joined_args = g_strjoinv(" ", args->argv); joined_args = g_strjoinv(" ", args->argv);
qts = qtest_initf("-machine pc %s", joined_args); qts = qtest_initf("-machine %s %s", arch, joined_args);
fw_cfg = pc_fw_cfg_init(qts); fw_cfg = pc_fw_cfg_init(qts);
read_bootdevices(fw_cfg, expected); read_bootdevices(fw_cfg, expected);
@ -737,7 +738,28 @@ static void test_override_ide(void)
add_ide_disk(args, 1, 0, 1, 9000, 120, 30); add_ide_disk(args, 1, 0, 1, 9000, 120, 30);
add_ide_disk(args, 2, 1, 0, 0, 1, 1); add_ide_disk(args, 2, 1, 0, 0, 1, 1);
add_ide_disk(args, 3, 1, 1, 1, 0, 0); add_ide_disk(args, 3, 1, 1, 1, 0, 0);
test_override(args, expected); test_override(args, "pc", expected);
}
static void test_override_sata(void)
{
TestArgs *args = create_args();
CHSResult expected[] = {
{"/pci@i0cf8/pci8086,2922@1f,2/drive@0/disk@0", {10000, 120, 30} },
{"/pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0", {9000, 120, 30} },
{"/pci@i0cf8/pci8086,2922@1f,2/drive@2/disk@0", {0, 1, 1} },
{"/pci@i0cf8/pci8086,2922@1f,2/drive@3/disk@0", {1, 0, 0} },
{NULL, {0, 0, 0} }
};
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
add_ide_disk(args, 0, 0, 0, 10000, 120, 30);
add_ide_disk(args, 1, 1, 0, 9000, 120, 30);
add_ide_disk(args, 2, 2, 0, 0, 1, 1);
add_ide_disk(args, 3, 3, 0, 1, 0, 0);
test_override(args, "q35", expected);
} }
static void test_override_scsi(void) static void test_override_scsi(void)
@ -759,7 +781,43 @@ static void test_override_scsi(void)
add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30);
add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0); add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0);
add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0); add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0);
test_override(args, expected); test_override(args, "pc", expected);
}
static void setup_pci_bridge(TestArgs *args, const char *id, const char *rootid)
{
char *root, *br;
root = g_strdup_printf("-device pcie-root-port,id=%s", rootid);
br = g_strdup_printf("-device pcie-pci-bridge,bus=%s,id=%s", rootid, id);
args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, root);
args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, br);
}
static void test_override_scsi_q35(void)
{
TestArgs *args = create_args();
CHSResult expected[] = {
{ "/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@0,0",
{10000, 120, 30}
},
{"/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@1,0", {9000, 120, 30} },
{"/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@2,0", {1, 0, 0} },
{"/pci@i0cf8/pci-bridge@1/scsi@3/channel@0/disk@3,0", {0, 1, 0} },
{NULL, {0, 0, 0} }
};
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
setup_pci_bridge(args, "pcie.0", "br");
add_scsi_controller(args, "lsi53c895a", "br", 3);
add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30);
add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0);
add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0);
test_override(args, "q35", expected);
} }
static void test_override_scsi_2_controllers(void) static void test_override_scsi_2_controllers(void)
@ -782,7 +840,7 @@ static void test_override_scsi_2_controllers(void)
add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30); add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30);
add_scsi_disk(args, 2, 1, 0, 0, 1, 1, 0, 0); add_scsi_disk(args, 2, 1, 0, 0, 1, 1, 0, 0);
add_scsi_disk(args, 3, 1, 0, 1, 2, 0, 1, 0); add_scsi_disk(args, 3, 1, 0, 1, 2, 0, 1, 0);
test_override(args, expected); test_override(args, "pc", expected);
} }
static void test_override_virtio_blk(void) static void test_override_virtio_blk(void)
@ -797,7 +855,23 @@ static void test_override_virtio_blk(void)
add_drive_with_mbr(args, empty_mbr, 1); add_drive_with_mbr(args, empty_mbr, 1);
add_virtio_disk(args, 0, "pci.0", 3, 10000, 120, 30); add_virtio_disk(args, 0, "pci.0", 3, 10000, 120, 30);
add_virtio_disk(args, 1, "pci.0", 4, 9000, 120, 30); add_virtio_disk(args, 1, "pci.0", 4, 9000, 120, 30);
test_override(args, expected); test_override(args, "pc", expected);
}
static void test_override_virtio_blk_q35(void)
{
TestArgs *args = create_args();
CHSResult expected[] = {
{"/pci@i0cf8/pci-bridge@1/scsi@3/disk@0,0", {10000, 120, 30} },
{"/pci@i0cf8/pci-bridge@1/scsi@4/disk@0,0", {9000, 120, 30} },
{NULL, {0, 0, 0} }
};
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
setup_pci_bridge(args, "pcie.0", "br");
add_virtio_disk(args, 0, "br", 3, 10000, 120, 30);
add_virtio_disk(args, 1, "br", 4, 9000, 120, 30);
test_override(args, "q35", expected);
} }
static void test_override_zero_chs(void) static void test_override_zero_chs(void)
@ -808,16 +882,65 @@ static void test_override_zero_chs(void)
}; };
add_drive_with_mbr(args, empty_mbr, 1); add_drive_with_mbr(args, empty_mbr, 1);
add_ide_disk(args, 0, 1, 1, 0, 0, 0); add_ide_disk(args, 0, 1, 1, 0, 0, 0);
test_override(args, expected); test_override(args, "pc", expected);
} }
static void test_override_scsi_hot_unplug(void) static void test_override_zero_chs_q35(void)
{
TestArgs *args = create_args();
CHSResult expected[] = {
{NULL, {0, 0, 0} }
};
add_drive_with_mbr(args, empty_mbr, 1);
add_ide_disk(args, 0, 0, 0, 0, 0, 0);
test_override(args, "q35", expected);
}
static void test_override_hot_unplug(TestArgs *args, const char *devid,
CHSResult expected[], CHSResult expected2[])
{ {
QTestState *qts; QTestState *qts;
char *joined_args; char *joined_args;
QFWCFG *fw_cfg; QFWCFG *fw_cfg;
QDict *response; QDict *response;
int i; int i;
joined_args = g_strjoinv(" ", args->argv);
qts = qtest_initf("%s", joined_args);
fw_cfg = pc_fw_cfg_init(qts);
read_bootdevices(fw_cfg, expected);
/* unplug device an restart */
qtest_qmp_device_del_send(qts, devid);
response = qtest_qmp(qts,
"{ 'execute': 'system_reset', 'arguments': { }}");
g_assert(response);
g_assert(!qdict_haskey(response, "error"));
qobject_unref(response);
qtest_qmp_eventwait(qts, "RESET");
read_bootdevices(fw_cfg, expected2);
g_free(joined_args);
qtest_quit(qts);
g_free(fw_cfg);
for (i = 0; i < args->n_drives; i++) {
unlink(args->drives[i]);
g_free(args->drives[i]);
}
g_free(args->drives);
g_strfreev(args->argv);
g_free(args);
}
static void test_override_scsi_hot_unplug(void)
{
TestArgs *args = create_args(); TestArgs *args = create_args();
CHSResult expected[] = { CHSResult expected[] = {
{"/pci@i0cf8/scsi@2/channel@0/disk@0,0", {10000, 120, 30} }, {"/pci@i0cf8/scsi@2/channel@0/disk@0,0", {10000, 120, 30} },
@ -834,51 +957,50 @@ static void test_override_scsi_hot_unplug(void)
add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30); add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20); add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20);
joined_args = g_strjoinv(" ", args->argv); args->argc = append_arg(args->argc, args->argv, ARGV_SIZE,
g_strdup("-machine pc"));
qts = qtest_initf("-machine pc %s", joined_args); test_override_hot_unplug(args, "scsi-disk0", expected, expected2);
fw_cfg = pc_fw_cfg_init(qts);
read_bootdevices(fw_cfg, expected);
/* unplug device an restart */
response = qtest_qmp(qts,
"{ 'execute': 'device_del',"
" 'arguments': {'id': 'scsi-disk0' }}");
g_assert(response);
g_assert(!qdict_haskey(response, "error"));
qobject_unref(response);
response = qtest_qmp(qts,
"{ 'execute': 'system_reset', 'arguments': { }}");
g_assert(response);
g_assert(!qdict_haskey(response, "error"));
qobject_unref(response);
qtest_qmp_eventwait(qts, "RESET");
read_bootdevices(fw_cfg, expected2);
g_free(joined_args);
qtest_quit(qts);
g_free(fw_cfg);
for (i = 0; i < args->n_drives; i++) {
unlink(args->drives[i]);
g_free(args->drives[i]);
} }
g_free(args->drives);
g_strfreev(args->argv); static void test_override_scsi_hot_unplug_q35(void)
g_free(args); {
TestArgs *args = create_args();
CHSResult expected[] = {
{
"/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/channel@0/disk@0,0",
{10000, 120, 30}
},
{
"/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/channel@0/disk@1,0",
{20, 20, 20}
},
{NULL, {0, 0, 0} }
};
CHSResult expected2[] = {
{
"/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/channel@0/disk@1,0",
{20, 20, 20}
},
{NULL, {0, 0, 0} }
};
args->argc = append_arg(args->argc, args->argv, ARGV_SIZE,
g_strdup("-device pcie-root-port,id=p0 "
"-device pcie-pci-bridge,bus=p0,id=b1 "
"-machine q35"));
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
add_scsi_controller(args, "virtio-scsi-pci", "b1", 2);
add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20);
test_override_hot_unplug(args, "scsi-disk0", expected, expected2);
} }
static void test_override_virtio_hot_unplug(void) static void test_override_virtio_hot_unplug(void)
{ {
QTestState *qts;
char *joined_args;
QFWCFG *fw_cfg;
QDict *response;
int i;
TestArgs *args = create_args(); TestArgs *args = create_args();
CHSResult expected[] = { CHSResult expected[] = {
{"/pci@i0cf8/scsi@2/disk@0,0", {10000, 120, 30} }, {"/pci@i0cf8/scsi@2/disk@0,0", {10000, 120, 30} },
@ -894,42 +1016,45 @@ static void test_override_virtio_hot_unplug(void)
add_virtio_disk(args, 0, "pci.0", 2, 10000, 120, 30); add_virtio_disk(args, 0, "pci.0", 2, 10000, 120, 30);
add_virtio_disk(args, 1, "pci.0", 3, 20, 20, 20); add_virtio_disk(args, 1, "pci.0", 3, 20, 20, 20);
joined_args = g_strjoinv(" ", args->argv); args->argc = append_arg(args->argc, args->argv, ARGV_SIZE,
g_strdup("-machine pc"));
qts = qtest_initf("-machine pc %s", joined_args); test_override_hot_unplug(args, "virtio-disk0", expected, expected2);
fw_cfg = pc_fw_cfg_init(qts);
read_bootdevices(fw_cfg, expected);
/* unplug device an restart */
response = qtest_qmp(qts,
"{ 'execute': 'device_del',"
" 'arguments': {'id': 'virtio-disk0' }}");
g_assert(response);
g_assert(!qdict_haskey(response, "error"));
qobject_unref(response);
response = qtest_qmp(qts,
"{ 'execute': 'system_reset', 'arguments': { }}");
g_assert(response);
g_assert(!qdict_haskey(response, "error"));
qobject_unref(response);
qtest_qmp_eventwait(qts, "RESET");
read_bootdevices(fw_cfg, expected2);
g_free(joined_args);
qtest_quit(qts);
g_free(fw_cfg);
for (i = 0; i < args->n_drives; i++) {
unlink(args->drives[i]);
g_free(args->drives[i]);
} }
g_free(args->drives);
g_strfreev(args->argv); static void test_override_virtio_hot_unplug_q35(void)
g_free(args); {
TestArgs *args = create_args();
CHSResult expected[] = {
{
"/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@2/disk@0,0",
{10000, 120, 30}
},
{
"/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@3/disk@0,0",
{20, 20, 20}
},
{NULL, {0, 0, 0} }
};
CHSResult expected2[] = {
{
"/pci@i0cf8/pci-bridge@1/pci-bridge@0/scsi@3/disk@0,0",
{20, 20, 20}
},
{NULL, {0, 0, 0} }
};
args->argc = append_arg(args->argc, args->argv, ARGV_SIZE,
g_strdup("-device pcie-root-port,id=p0 "
"-device pcie-pci-bridge,bus=p0,id=b1 "
"-machine q35"));
add_drive_with_mbr(args, empty_mbr, 1);
add_drive_with_mbr(args, empty_mbr, 1);
add_virtio_disk(args, 0, "b1", 2, 10000, 120, 30);
add_virtio_disk(args, 1, "b1", 3, 20, 20, 20);
test_override_hot_unplug(args, "virtio-disk0", expected, expected2);
} }
int main(int argc, char **argv) int main(int argc, char **argv)
@ -974,6 +1099,22 @@ int main(int argc, char **argv)
test_override_scsi_hot_unplug); test_override_scsi_hot_unplug);
qtest_add_func("hd-geo/override/virtio_hot_unplug", qtest_add_func("hd-geo/override/virtio_hot_unplug",
test_override_virtio_hot_unplug); test_override_virtio_hot_unplug);
if (qtest_has_machine("q35")) {
qtest_add_func("hd-geo/override/sata", test_override_sata);
qtest_add_func("hd-geo/override/virtio_blk_q35",
test_override_virtio_blk_q35);
qtest_add_func("hd-geo/override/zero_chs_q35",
test_override_zero_chs_q35);
if (qtest_has_device("lsi53c895a")) {
qtest_add_func("hd-geo/override/scsi_q35",
test_override_scsi_q35);
}
qtest_add_func("hd-geo/override/scsi_hot_unplug_q35",
test_override_scsi_hot_unplug_q35);
qtest_add_func("hd-geo/override/virtio_hot_unplug_q35",
test_override_virtio_hot_unplug_q35);
}
} else { } else {
g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; "
"skipping hd-geo/override/* tests"); "skipping hd-geo/override/* tests");

View File

@ -378,6 +378,20 @@ static void test_ivshmem_server(void)
close(thread.pipe[0]); close(thread.pipe[0]);
} }
static void test_ivshmem_hotplug_q35(void)
{
QTestState *qts = qtest_init("-object memory-backend-ram,size=1M,id=mb1 "
"-device pcie-root-port,id=p1 "
"-device pcie-pci-bridge,bus=p1,id=b1 "
"-machine q35");
qtest_qmp_device_add(qts, "ivshmem-plain", "iv1",
"{'memdev': 'mb1', 'bus': 'b1'}");
qtest_qmp_device_del_send(qts, "iv1");
qtest_quit(qts);
}
#define PCI_SLOT_HP 0x06 #define PCI_SLOT_HP 0x06
static void test_ivshmem_hotplug(void) static void test_ivshmem_hotplug(void)
@ -469,6 +483,7 @@ int main(int argc, char **argv)
{ {
int ret, fd; int ret, fd;
gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; gchar dir[] = "/tmp/ivshmem-test.XXXXXX";
const char *arch = qtest_get_arch();
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
@ -494,6 +509,9 @@ int main(int argc, char **argv)
qtest_add_func("/ivshmem/pair", test_ivshmem_pair); qtest_add_func("/ivshmem/pair", test_ivshmem_pair);
qtest_add_func("/ivshmem/server", test_ivshmem_server); qtest_add_func("/ivshmem/server", test_ivshmem_server);
} }
if (!strcmp(arch, "x86_64") && qtest_has_machine("q35")) {
qtest_add_func("/ivshmem/hotplug-q35", test_ivshmem_hotplug_q35);
}
out: out:
ret = g_test_run(); ret = g_test_run();

View File

@ -179,13 +179,7 @@ void qpci_free_pc(QPCIBus *bus)
void qpci_unplug_acpi_device_test(QTestState *qts, const char *id, uint8_t slot) void qpci_unplug_acpi_device_test(QTestState *qts, const char *id, uint8_t slot)
{ {
QDict *response; qtest_qmp_device_del_send(qts, id);
response = qtest_qmp(qts, "{'execute': 'device_del',"
" 'arguments': {'id': %s}}", id);
g_assert(response);
g_assert(!qdict_haskey(response, "error"));
qobject_unref(response);
qtest_outl(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); qtest_outl(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot);

View File

@ -66,7 +66,7 @@ struct QTestState
}; };
static GHookList abrt_hooks; static GHookList abrt_hooks;
static struct sigaction sigact_old; static void (*sighandler_old)(int);
static int qtest_query_target_endianness(QTestState *s); static int qtest_query_target_endianness(QTestState *s);
@ -179,20 +179,12 @@ static void sigabrt_handler(int signo)
static void setup_sigabrt_handler(void) static void setup_sigabrt_handler(void)
{ {
struct sigaction sigact; sighandler_old = signal(SIGABRT, sigabrt_handler);
/* Catch SIGABRT to clean up on g_assert() failure */
sigact = (struct sigaction){
.sa_handler = sigabrt_handler,
.sa_flags = SA_RESETHAND,
};
sigemptyset(&sigact.sa_mask);
sigaction(SIGABRT, &sigact, &sigact_old);
} }
static void cleanup_sigabrt_handler(void) static void cleanup_sigabrt_handler(void)
{ {
sigaction(SIGABRT, &sigact_old, NULL); signal(SIGABRT, sighandler_old);
} }
static bool hook_list_is_empty(GHookList *hook_list) static bool hook_list_is_empty(GHookList *hook_list)
@ -1371,15 +1363,19 @@ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd)
* *
* {"return": {}} * {"return": {}}
*/ */
void qtest_qmp_device_del_send(QTestState *qts, const char *id)
{
QDict *rsp = qtest_qmp(qts, "{'execute': 'device_del', "
"'arguments': {'id': %s}}", id);
g_assert(rsp);
g_assert(qdict_haskey(rsp, "return"));
g_assert(!qdict_haskey(rsp, "error"));
qobject_unref(rsp);
}
void qtest_qmp_device_del(QTestState *qts, const char *id) void qtest_qmp_device_del(QTestState *qts, const char *id)
{ {
QDict *rsp; qtest_qmp_device_del_send(qts, id);
rsp = qtest_qmp(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}",
id);
g_assert(qdict_haskey(rsp, "return"));
qobject_unref(rsp);
qtest_qmp_eventwait(qts, "DEVICE_DELETED"); qtest_qmp_eventwait(qts, "DEVICE_DELETED");
} }

View File

@ -761,12 +761,22 @@ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id,
void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd); void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd);
#endif /* _WIN32 */ #endif /* _WIN32 */
/**
* qtest_qmp_device_del_send:
* @qts: QTestState instance to operate on
* @id: Identification string
*
* Generic hot-unplugging test via the device_del QMP command.
*/
void qtest_qmp_device_del_send(QTestState *qts, const char *id);
/** /**
* qtest_qmp_device_del: * qtest_qmp_device_del:
* @qts: QTestState instance to operate on * @qts: QTestState instance to operate on
* @id: Identification string * @id: Identification string
* *
* Generic hot-unplugging test via the device_del QMP command. * Generic hot-unplugging test via the device_del QMP command.
* Waiting for command completion event.
*/ */
void qtest_qmp_device_del(QTestState *qts, const char *id); void qtest_qmp_device_del(QTestState *qts, const char *id);

View File

@ -306,6 +306,12 @@ qtests = {
'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
} }
gvnc = dependency('gvnc-1.0', required: false)
if gvnc.found()
qtests += {'vnc-display-test': [gvnc]}
qtests_generic += [ 'vnc-display-test' ]
endif
if dbus_display if dbus_display
qtests += {'dbus-display-test': [dbus_display1, gio]} qtests += {'dbus-display-test': [dbus_display1, gio]}
endif endif

View File

@ -102,7 +102,7 @@ static bool ufd_version_check(void)
#endif #endif
static const char *tmpfs; static char *tmpfs;
/* The boot file modifies memory area in [start_address, end_address) /* The boot file modifies memory area in [start_address, end_address)
* repeatedly. It outputs a 'B' at a fixed rate while it's still running. * repeatedly. It outputs a 'B' at a fixed rate while it's still running.
@ -2451,10 +2451,10 @@ static bool kvm_dirty_ring_supported(void)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
char template[] = "/tmp/migration-test-XXXXXX";
const bool has_kvm = qtest_has_accel("kvm"); const bool has_kvm = qtest_has_accel("kvm");
const bool has_uffd = ufd_version_check(); const bool has_uffd = ufd_version_check();
const char *arch = qtest_get_arch(); const char *arch = qtest_get_arch();
g_autoptr(GError) err = NULL;
int ret; int ret;
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
@ -2479,9 +2479,10 @@ int main(int argc, char **argv)
return g_test_run(); return g_test_run();
} }
tmpfs = g_mkdtemp(template); tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err);
if (!tmpfs) { if (!tmpfs) {
g_test_message("g_mkdtemp on path (%s): %s", template, strerror(errno)); g_test_message("g_dir_make_tmp on path (%s): %s", tmpfs,
err->message);
} }
g_assert(tmpfs); g_assert(tmpfs);
@ -2612,6 +2613,7 @@ int main(int argc, char **argv)
g_test_message("unable to rmdir: path (%s): %s", g_test_message("unable to rmdir: path (%s): %s",
tmpfs, strerror(errno)); tmpfs, strerror(errno));
} }
g_free(tmpfs);
return ret; return ret;
} }

View File

@ -0,0 +1,103 @@
/*
* VNC display tests
*
* Copyright (c) 2022 Red Hat, Inc.
*
* 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 "qemu/sockets.h"
#include "libqtest.h"
#include <gio/gio.h>
#include <gvnc.h>
typedef struct Test {
QTestState *qts;
VncConnection *conn;
GMainLoop *loop;
} Test;
static void on_vnc_error(VncConnection* self,
const char* msg)
{
g_error("vnc-error: %s", msg);
}
static void on_vnc_auth_failure(VncConnection *self,
const char *msg)
{
g_error("vnc-auth-failure: %s", msg);
}
static bool
test_setup(Test *test)
{
#ifdef WIN32
g_test_skip("Not supported on Windows yet");
return false;
#else
int pair[2];
test->qts = qtest_init("-vnc none -name vnc-test");
g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
qtest_qmp_add_client(test->qts, "vnc", pair[1]);
test->conn = vnc_connection_new();
g_signal_connect(test->conn, "vnc-error",
G_CALLBACK(on_vnc_error), NULL);
g_signal_connect(test->conn, "vnc-auth-failure",
G_CALLBACK(on_vnc_auth_failure), NULL);
vnc_connection_set_auth_type(test->conn, VNC_CONNECTION_AUTH_NONE);
vnc_connection_open_fd(test->conn, pair[0]);
test->loop = g_main_loop_new(NULL, FALSE);
return true;
#endif
}
static void
test_vnc_basic_on_vnc_initialized(VncConnection *self,
Test *test)
{
const char *name = vnc_connection_get_name(test->conn);
g_assert_cmpstr(name, ==, "QEMU (vnc-test)");
g_main_loop_quit(test->loop);
}
static void
test_vnc_basic(void)
{
Test test;
if (!test_setup(&test)) {
return;
}
g_signal_connect(test.conn, "vnc-initialized",
G_CALLBACK(test_vnc_basic_on_vnc_initialized), &test);
g_main_loop_run(test.loop);
qtest_quit(test.qts);
g_object_unref(test.conn);
g_main_loop_unref(test.loop);
}
int
main(int argc, char **argv)
{
if (getenv("GTK_VNC_DEBUG")) {
vnc_util_set_debug(true);
}
g_test_init(&argc, &argv, NULL);
qtest_add_func("/vnc-display/basic", test_vnc_basic);
return g_test_run();
}

View File

@ -25,7 +25,6 @@
struct QIOChannelTest { struct QIOChannelTest {
QIOChannel *src; QIOChannel *src;
QIOChannel *dst; QIOChannel *dst;
bool blocking;
size_t len; size_t len;
size_t niov; size_t niov;
char *input; char *input;
@ -42,8 +41,6 @@ static gpointer test_io_thread_writer(gpointer opaque)
{ {
QIOChannelTest *data = opaque; QIOChannelTest *data = opaque;
qio_channel_set_blocking(data->src, data->blocking, NULL);
qio_channel_writev_all(data->src, qio_channel_writev_all(data->src,
data->inputv, data->inputv,
data->niov, data->niov,
@ -58,8 +55,6 @@ static gpointer test_io_thread_reader(gpointer opaque)
{ {
QIOChannelTest *data = opaque; QIOChannelTest *data = opaque;
qio_channel_set_blocking(data->dst, data->blocking, NULL);
qio_channel_readv_all(data->dst, qio_channel_readv_all(data->dst,
data->outputv, data->outputv,
data->niov, data->niov,
@ -113,7 +108,9 @@ void qio_channel_test_run_threads(QIOChannelTest *test,
test->src = src; test->src = src;
test->dst = dst; test->dst = dst;
test->blocking = blocking;
qio_channel_set_blocking(test->dst, blocking, NULL);
qio_channel_set_blocking(test->src, blocking, NULL);
reader = g_thread_new("reader", reader = g_thread_new("reader",
test_io_thread_reader, test_io_thread_reader,

View File

@ -79,7 +79,7 @@ static void test_image_locking_basic(void)
g_autofree char *img_path = NULL; g_autofree char *img_path = NULL;
uint64_t perm, shared_perm; uint64_t perm, shared_perm;
int fd = g_file_open_tmp("qtest.XXXXXX", &img_path, NULL); int fd = g_file_open_tmp("qemu-tst-img-lock.XXXXXX", &img_path, NULL);
assert(fd >= 0); assert(fd >= 0);
perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ;
@ -120,7 +120,7 @@ static void test_set_perm_abort(void)
g_autofree char *img_path = NULL; g_autofree char *img_path = NULL;
uint64_t perm, shared_perm; uint64_t perm, shared_perm;
int r; int r;
int fd = g_file_open_tmp("qtest.XXXXXX", &img_path, NULL); int fd = g_file_open_tmp("qemu-tst-img-lock.XXXXXX", &img_path, NULL);
assert(fd >= 0); assert(fd >= 0);
perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ;
@ -140,6 +140,8 @@ static void test_set_perm_abort(void)
check_locked_bytes(fd, perm, ~shared_perm); check_locked_bytes(fd, perm, ~shared_perm);
blk_unref(blk1); blk_unref(blk1);
blk_unref(blk2); blk_unref(blk2);
close(fd);
unlink(img_path);
} }
int main(int argc, char **argv) int main(int argc, char **argv)

View File

@ -24,29 +24,30 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/module.h" #include "qemu/module.h"
#ifndef WIN32 #define TEST_FIFO "test-io-channel-command.fifo"
#define SOCAT_SRC "PIPE:" TEST_FIFO ",wronly"
#define SOCAT_DST "PIPE:" TEST_FIFO ",rdonly"
static char *socat = NULL;
static void test_io_channel_command_fifo(bool async) static void test_io_channel_command_fifo(bool async)
{ {
#define TEST_FIFO "tests/test-io-channel-command.fifo"
QIOChannel *src, *dst; QIOChannel *src, *dst;
QIOChannelTest *test; QIOChannelTest *test;
const char *srcfifo = "PIPE:" TEST_FIFO ",wronly";
const char *dstfifo = "PIPE:" TEST_FIFO ",rdonly";
const char *srcargv[] = { const char *srcargv[] = {
"/bin/socat", "-", srcfifo, NULL, socat, "-", SOCAT_SRC, NULL,
}; };
const char *dstargv[] = { const char *dstargv[] = {
"/bin/socat", dstfifo, "-", NULL, socat, SOCAT_DST, "-", NULL,
}; };
unlink(TEST_FIFO); if (!socat) {
if (access("/bin/socat", X_OK) < 0) { g_test_skip("socat is not found in PATH");
g_test_skip("socat is missing");
return; return;
} }
if (mkfifo(TEST_FIFO, 0600) < 0) {
abort(); unlink(TEST_FIFO);
}
src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv, src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv,
O_WRONLY, O_WRONLY,
&error_abort)); &error_abort));
@ -81,11 +82,12 @@ static void test_io_channel_command_echo(bool async)
QIOChannel *ioc; QIOChannel *ioc;
QIOChannelTest *test; QIOChannelTest *test;
const char *socatargv[] = { const char *socatargv[] = {
"/bin/socat", "-", "-", NULL, socat, "-", "-", NULL,
}; };
if (access("/bin/socat", X_OK) < 0) { if (!socat) {
return; /* Pretend success if socat is not present */ g_test_skip("socat is not found in PATH");
return;
} }
ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv, ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv,
@ -108,7 +110,6 @@ static void test_io_channel_command_echo_sync(void)
{ {
test_io_channel_command_echo(false); test_io_channel_command_echo(false);
} }
#endif
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -116,7 +117,8 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
#ifndef WIN32 socat = g_find_program_in_path("socat");
g_test_add_func("/io/channel/command/fifo/sync", g_test_add_func("/io/channel/command/fifo/sync",
test_io_channel_command_fifo_sync); test_io_channel_command_fifo_sync);
g_test_add_func("/io/channel/command/fifo/async", g_test_add_func("/io/channel/command/fifo/async",
@ -125,7 +127,6 @@ int main(int argc, char **argv)
test_io_channel_command_echo_sync); test_io_channel_command_echo_sync);
g_test_add_func("/io/channel/command/echo/async", g_test_add_func("/io/channel/command/echo/async",
test_io_channel_command_echo_async); test_io_channel_command_echo_async);
#endif
return g_test_run(); return g_test_run();
} }

View File

@ -195,6 +195,9 @@ void gd_egl_switch(DisplayChangeListener *dcl,
if (resized) { if (resized) {
gd_update_windowsize(vc); gd_update_windowsize(vc);
} }
eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
} }
QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc, QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,

View File

@ -681,9 +681,13 @@ static void gd_mouse_mode_change(Notifier *notify, void *data)
s = container_of(notify, GtkDisplayState, mouse_mode_notifier); s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
/* release the grab at switching to absolute mode */ /* release the grab at switching to absolute mode */
if (qemu_input_is_absolute() && gd_is_grab_active(s)) { if (qemu_input_is_absolute() && s->ptr_owner) {
if (!s->ptr_owner->window) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
FALSE); FALSE);
} else {
gd_ungrab_pointer(s);
}
} }
for (i = 0; i < s->nb_vcs; i++) { for (i = 0; i < s->nb_vcs; i++) {
VirtualConsole *vc = &s->vc[i]; VirtualConsole *vc = &s->vc[i];
@ -2167,7 +2171,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
return group; return group;
} }
static GtkWidget *gd_create_menu_view(GtkDisplayState *s) static GtkWidget *gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts)
{ {
GSList *group = NULL; GSList *group = NULL;
GtkWidget *view_menu; GtkWidget *view_menu;
@ -2265,7 +2269,8 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic( s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
_("Show Menubar")); _("Show Menubar"));
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item), gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
TRUE); !opts->u.gtk.has_show_menubar ||
opts->u.gtk.show_menubar);
gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0, gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL)); g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
gtk_accel_label_set_accel( gtk_accel_label_set_accel(
@ -2276,13 +2281,13 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
return view_menu; return view_menu;
} }
static void gd_create_menus(GtkDisplayState *s) static void gd_create_menus(GtkDisplayState *s, DisplayOptions *opts)
{ {
GtkSettings *settings; GtkSettings *settings;
s->accel_group = gtk_accel_group_new(); s->accel_group = gtk_accel_group_new();
s->machine_menu = gd_create_menu_machine(s); s->machine_menu = gd_create_menu_machine(s);
s->view_menu = gd_create_menu_view(s); s->view_menu = gd_create_menu_view(s, opts);
s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
@ -2359,7 +2364,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu"); gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu");
gd_create_menus(s); gd_create_menus(s, opts);
gd_connect_signals(s); gd_connect_signals(s);
@ -2374,6 +2379,10 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
gtk_container_add(GTK_CONTAINER(s->window), s->vbox); gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
gtk_widget_show_all(s->window); gtk_widget_show_all(s->window);
if (opts->u.gtk.has_show_menubar &&
!opts->u.gtk.show_menubar) {
gtk_widget_hide(s->menu_bar);
}
vc = gd_vc_find_current(s); vc = gd_vc_find_current(s);
gtk_widget_set_sensitive(s->view_menu, vc != NULL); gtk_widget_set_sensitive(s->view_menu, vc != NULL);

View File

@ -2442,8 +2442,8 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
if (len == 1) { if (len == 1) {
return 8; return 8;
} }
if (len == 8) {
uint32_t dlen = abs(read_s32(data, 4)); uint32_t dlen = abs(read_s32(data, 4));
if (len == 8) {
if (dlen > (1 << 20)) { if (dlen > (1 << 20)) {
error_report("vnc: client_cut_text msg payload has %u bytes" error_report("vnc: client_cut_text msg payload has %u bytes"
" which exceeds our limit of 1MB.", dlen); " which exceeds our limit of 1MB.", dlen);
@ -2456,8 +2456,13 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
} }
if (read_s32(data, 4) < 0) { if (read_s32(data, 4) < 0) {
vnc_client_cut_text_ext(vs, abs(read_s32(data, 4)), if (dlen < 4) {
read_u32(data, 8), data + 12); error_report("vnc: malformed payload (header less than 4 bytes)"
" in extended clipboard pseudo-encoding.");
vnc_client_error(vs);
break;
}
vnc_client_cut_text_ext(vs, dlen, read_u32(data, 8), data + 12);
break; break;
} }
vnc_client_cut_text(vs, read_u32(data, 4), data + 8); vnc_client_cut_text(vs, read_u32(data, 4), data + 8);

View File

@ -111,12 +111,17 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
/*XXX Note: windows has WSASend() and WSARecv() */ /*XXX Note: windows has WSASend() and WSARecv() */
unsigned i = 0; unsigned i = 0;
ssize_t ret = 0; ssize_t ret = 0;
ssize_t off = 0;
while (i < iov_cnt) { while (i < iov_cnt) {
ssize_t r = do_send ssize_t r = do_send
? send(sockfd, iov[i].iov_base, iov[i].iov_len, 0) ? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0)
: recv(sockfd, iov[i].iov_base, iov[i].iov_len, 0); : recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0);
if (r > 0) { if (r > 0) {
ret += r; ret += r;
off += r;
if (off < iov[i].iov_len) {
continue;
}
} else if (!r) { } else if (!r) {
break; break;
} else if (errno == EINTR) { } else if (errno == EINTR) {
@ -129,6 +134,7 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send)
} }
break; break;
} }
off = 0;
i++; i++;
} }
return ret; return ret;

View File

@ -538,18 +538,22 @@ int socket_init(void)
#ifndef CONFIG_IOVEC #ifndef CONFIG_IOVEC
/* helper function for iov_send_recv() */
static ssize_t static ssize_t
readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write) readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write)
{ {
unsigned i = 0; unsigned i = 0;
ssize_t ret = 0; ssize_t ret = 0;
ssize_t off = 0;
while (i < iov_cnt) { while (i < iov_cnt) {
ssize_t r = do_write ssize_t r = do_write
? write(fd, iov[i].iov_base, iov[i].iov_len) ? write(fd, iov[i].iov_base + off, iov[i].iov_len - off)
: read(fd, iov[i].iov_base, iov[i].iov_len); : read(fd, iov[i].iov_base + off, iov[i].iov_len - off);
if (r > 0) { if (r > 0) {
ret += r; ret += r;
off += r;
if (off < iov[i].iov_len) {
continue;
}
} else if (!r) { } else if (!r) {
break; break;
} else if (errno == EINTR) { } else if (errno == EINTR) {
@ -562,6 +566,7 @@ readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write)
} }
break; break;
} }
off = 0;
i++; i++;
} }
return ret; return ret;

View File

@ -19,12 +19,39 @@
static bool name_threads; static bool name_threads;
typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread,
PCWSTR lpThreadDescription);
static pSetThreadDescription SetThreadDescriptionFunc;
static HMODULE kernel32_module;
static bool load_set_thread_description(void)
{
static gsize _init_once = 0;
if (g_once_init_enter(&_init_once)) {
kernel32_module = LoadLibrary("kernel32.dll");
if (kernel32_module) {
SetThreadDescriptionFunc =
(pSetThreadDescription)GetProcAddress(kernel32_module,
"SetThreadDescription");
if (!SetThreadDescriptionFunc) {
FreeLibrary(kernel32_module);
}
}
g_once_init_leave(&_init_once, 1);
}
return !!SetThreadDescriptionFunc;
}
void qemu_thread_naming(bool enable) void qemu_thread_naming(bool enable)
{ {
/* But note we don't actually name them on Windows yet */
name_threads = enable; name_threads = enable;
if (enable && !load_set_thread_description()) {
fprintf(stderr, "qemu: thread naming not supported on this host\n"); fprintf(stderr, "qemu: thread naming not supported on this host\n");
name_threads = false;
}
} }
static void error_exit(int err, const char *msg) static void error_exit(int err, const char *msg)
@ -400,6 +427,25 @@ void *qemu_thread_join(QemuThread *thread)
return ret; return ret;
} }
static bool set_thread_description(HANDLE h, const char *name)
{
HRESULT hr;
g_autofree wchar_t *namew = NULL;
if (!load_set_thread_description()) {
return false;
}
namew = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
if (!namew) {
return false;
}
hr = SetThreadDescriptionFunc(h, namew);
return SUCCEEDED(hr);
}
void qemu_thread_create(QemuThread *thread, const char *name, void qemu_thread_create(QemuThread *thread, const char *name,
void *(*start_routine)(void *), void *(*start_routine)(void *),
void *arg, int mode) void *arg, int mode)
@ -423,7 +469,11 @@ void qemu_thread_create(QemuThread *thread, const char *name,
if (!hThread) { if (!hThread) {
error_exit(GetLastError(), __func__); error_exit(GetLastError(), __func__);
} }
if (name_threads && name && !set_thread_description(hThread, name)) {
fprintf(stderr, "qemu: failed to set thread description: %s\n", name);
}
CloseHandle(hThread); CloseHandle(hThread);
thread->data = data; thread->data = data;
} }