Merge remote-tracking branch 'upstream/master' into main
This commit is contained in:
commit
97211a7b7c
@ -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 = {
|
||||||
|
@ -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,
|
||||||
|
|
||||||
|
109
audio/audio.c
109
audio/audio.c
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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)) {
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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) {
|
||||||
|
1432
disas/riscv.c
1432
disas/riscv.c
File diff suppressed because it is too large
Load Diff
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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)) {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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. */
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
@ -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 */
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 */
|
||||||
|
@ -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' ] }
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -1058,3 +1058,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_arch_accel_class_init(ObjectClass *oc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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. */
|
||||||
|
@ -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;
|
||||||
|
@ -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 +
|
||||||
|
@ -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
@ -1294,3 +1294,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_arch_accel_class_init(ObjectClass *oc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -2966,3 +2966,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_arch_accel_class_init(ObjectClass *oc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -532,3 +532,7 @@ bool kvm_arch_cpu_check_are_resettable(void)
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kvm_arch_accel_class_init(ObjectClass *oc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@ -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'
|
||||||
|
@ -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')
|
||||||
|
@ -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'
|
||||||
|
@ -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'])
|
||||||
|
@ -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",
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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");
|
||||||
|
@ -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 "
|
||||||
|
@ -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);
|
||||||
|
@ -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");
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
static void test_override_scsi_hot_unplug_q35(void)
|
||||||
|
{
|
||||||
|
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} }
|
||||||
|
};
|
||||||
|
|
||||||
/* unplug device an restart */
|
args->argc = append_arg(args->argc, args->argv, ARGV_SIZE,
|
||||||
response = qtest_qmp(qts,
|
g_strdup("-device pcie-root-port,id=p0 "
|
||||||
"{ 'execute': 'device_del',"
|
"-device pcie-pci-bridge,bus=p0,id=b1 "
|
||||||
" 'arguments': {'id': 'scsi-disk0' }}");
|
"-machine q35"));
|
||||||
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");
|
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);
|
||||||
|
|
||||||
read_bootdevices(fw_cfg, expected2);
|
test_override_hot_unplug(args, "scsi-disk0", expected, 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_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);
|
static void test_override_virtio_hot_unplug_q35(void)
|
||||||
|
{
|
||||||
|
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} }
|
||||||
|
};
|
||||||
|
|
||||||
/* unplug device an restart */
|
args->argc = append_arg(args->argc, args->argv, ARGV_SIZE,
|
||||||
response = qtest_qmp(qts,
|
g_strdup("-device pcie-root-port,id=p0 "
|
||||||
"{ 'execute': 'device_del',"
|
"-device pcie-pci-bridge,bus=p0,id=b1 "
|
||||||
" 'arguments': {'id': 'virtio-disk0' }}");
|
"-machine q35"));
|
||||||
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");
|
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);
|
||||||
|
|
||||||
read_bootdevices(fw_cfg, expected2);
|
test_override_hot_unplug(args, "virtio-disk0", expected, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -306,8 +306,14 @@ 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
|
||||||
|
|
||||||
qtest_executables = {}
|
qtest_executables = {}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
103
tests/qtest/vnc-display-test.c
Normal file
103
tests/qtest/vnc-display-test.c
Normal 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();
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
21
ui/gtk.c
21
ui/gtk.c
@ -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);
|
||||||
|
11
ui/vnc.c
11
ui/vnc.c
@ -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);
|
||||||
|
10
util/iov.c
10
util/iov.c
@ -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;
|
||||||
|
11
util/osdep.c
11
util/osdep.c
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user